Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added API to set ephemeral port allocator range. #2476

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions config/config.go
@@ -1,6 +1,7 @@
package config

import (
"fmt"
"strings"

"github.com/BurntSushi/toml"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/docker/libnetwork/ipamutils"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/osl"
"github.com/docker/libnetwork/portallocator"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -238,6 +240,23 @@ func OptionExperimental(exp bool) Option {
}
}

// OptionDynamicPortRange function returns an option setter for service port allocation range
func OptionDynamicPortRange(in string) Option {
return func(c *Config) {
start, end := 0, 0
if len(in) > 0 {
n, err := fmt.Sscanf(in, "%d-%d", &start, &end)
if n != 2 || err != nil {
logrus.Errorf("Failed to parse range string with err %v", err)
return
}
}
if err := portallocator.Get().SetPortRange(start, end); err != nil {
logrus.Errorf("Failed to set port range with err %v", err)
}
}
}

// OptionNetworkControlPlaneMTU function returns an option setter for control plane MTU
func OptionNetworkControlPlaneMTU(exp int) Option {
return func(c *Config) {
Expand Down
70 changes: 63 additions & 7 deletions portallocator/portallocator.go
Expand Up @@ -3,17 +3,36 @@ package portallocator
import (
"errors"
"fmt"
"github.com/sirupsen/logrus"
"net"
"sync"
)

const (
// DefaultPortRangeStart indicates the first port in port range
DefaultPortRangeStart = 49153
// DefaultPortRangeEnd indicates the last port in port range
DefaultPortRangeEnd = 65535
var (
// defaultPortRangeStart indicates the first port in port range
defaultPortRangeStart = 49153
// defaultPortRangeEnd indicates the last port in port range
// consistent with default /proc/sys/net/ipv4/ip_local_port_range
// upper bound on linux
defaultPortRangeEnd = 60999
)

func sanitizePortRange(start int, end int) (newStart, newEnd int, err error) {
if start > defaultPortRangeEnd || end < defaultPortRangeStart || start > end {
return 0, 0, fmt.Errorf("Request out allowed range [%v, %v]",
defaultPortRangeStart, defaultPortRangeEnd)
}
err = nil
newStart, newEnd = start, end
if start < defaultPortRangeStart {
newStart = defaultPortRangeStart
}
if end > defaultPortRangeEnd {
newEnd = defaultPortRangeEnd
}
return
}

type ipMapping map[string]protoMap

var (
Expand Down Expand Up @@ -92,11 +111,19 @@ func Get() *PortAllocator {
return instance
}

func newInstance() *PortAllocator {
func getDefaultPortRange() (int, int) {
start, end, err := getDynamicPortRange()
if err == nil {
start, end, err = sanitizePortRange(start, end)
}
if err != nil {
start, end = DefaultPortRangeStart, DefaultPortRangeEnd
start, end = defaultPortRangeStart, defaultPortRangeEnd
}
return start, end
}

func newInstance() *PortAllocator {
start, end := getDefaultPortRange()
return &PortAllocator{
ipMap: ipMapping{},
Begin: start,
Expand Down Expand Up @@ -170,6 +197,35 @@ func (p *PortAllocator) ReleasePort(ip net.IP, proto string, port int) error {
return nil
}

// SetPortRange sets dynamic port allocation range.
// if both portBegin and portEnd are 0, the port range reverts to default
// value. Otherwise they are sanitized against the default values to
// ensure their validity.
func (p *PortAllocator) SetPortRange(portBegin, portEnd int) error {
// if begin and end is zero, revert to default values
var begin, end int
var err error
if portBegin == 0 && portEnd == 0 {
begin, end = getDefaultPortRange()

} else {
begin, end, err = sanitizePortRange(portBegin, portEnd)
if err != nil {
return err
}
}
logrus.Debugf("Setting up port allocator to range %v-%v, current %v-%v",
begin, end, p.Begin, p.End)
p.mutex.Lock()
defer p.mutex.Unlock()
if p.Begin == begin && p.End == end {
return nil
}
p.ipMap = ipMapping{}
p.Begin, p.End = begin, end
return nil
}

func (p *PortAllocator) newPortMap() *portMap {
defaultKey := getRangeKey(p.Begin, p.End)
pm := &portMap{
Expand Down
2 changes: 1 addition & 1 deletion portallocator/portallocator_freebsd.go
Expand Up @@ -8,7 +8,7 @@ import (

func getDynamicPortRange() (start int, end int, err error) {
portRangeKernelSysctl := []string{"net.inet.ip.portrange.hifirst", "net.ip.portrange.hilast"}
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd)
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", defaultPortRangeStart, defaultPortRangeEnd)
portRangeLowCmd := exec.Command("/sbin/sysctl", portRangeKernelSysctl[0])
var portRangeLowOut bytes.Buffer
portRangeLowCmd.Stdout = &portRangeLowOut
Expand Down
2 changes: 1 addition & 1 deletion portallocator/portallocator_linux.go
Expand Up @@ -8,7 +8,7 @@ import (

func getDynamicPortRange() (start int, end int, err error) {
const portRangeKernelParam = "/proc/sys/net/ipv4/ip_local_port_range"
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", DefaultPortRangeStart, DefaultPortRangeEnd)
portRangeFallback := fmt.Sprintf("using fallback port range %d-%d", defaultPortRangeStart, defaultPortRangeEnd)
file, err := os.Open(portRangeKernelParam)
if err != nil {
return 0, 0, fmt.Errorf("port allocator - %s due to error: %v", portRangeFallback, err)
Expand Down
45 changes: 45 additions & 0 deletions portallocator/portallocator_test.go
@@ -1,6 +1,7 @@
package portallocator

import (
"fmt"
"net"
"testing"

Expand Down Expand Up @@ -321,3 +322,47 @@ func TestNoDuplicateBPR(t *testing.T) {
t.Fatalf("Acquire(0) allocated the same port twice: %d", port)
}
}

func TestChangePortRange(t *testing.T) {
var tests = []struct {
begin int
end int
setErr error
reqRlt int
}{
{defaultPortRangeEnd + 1, defaultPortRangeEnd + 10, fmt.Errorf("begin out of range"), 0},
{defaultPortRangeStart - 10, defaultPortRangeStart - 1, fmt.Errorf("end out of range"), 0},
{defaultPortRangeEnd, defaultPortRangeStart, fmt.Errorf("out of order"), 0},
{defaultPortRangeStart + 100, defaultPortRangeEnd + 10, nil, defaultPortRangeStart + 100},
{0, 0, nil, defaultPortRangeStart}, // revert to default if no value given
{defaultPortRangeStart - 100, defaultPortRangeEnd, nil, defaultPortRangeStart + 1},
}
p := Get()
port := 0
for _, c := range tests {
t.Logf("test: port allocate range %v-%v, setErr=%v, reqPort=%v",
c.begin, c.end, c.setErr, c.reqRlt)
err := p.SetPortRange(c.begin, c.end)
if (c.setErr == nil && c.setErr != err) ||
(c.setErr != nil && err == nil) {
t.Fatalf("Unexpected set range result, expected=%v, actual=%v", c.setErr, err)
}
if err != nil {
continue
}
if port > 0 {
err := p.ReleasePort(defaultIP, "tcp", port)
if err != nil {
t.Fatalf("Releasing port %v failed, err=%v", port, err)
}
}

port, err = p.RequestPort(defaultIP, "tcp", 0)
if err != nil {
t.Fatalf("Request failed, err %v", err)
}
if port != c.reqRlt {
t.Fatalf("Incorrect port returned, expected=%v, actual=%v", c.reqRlt, port)
}
}
}
10 changes: 5 additions & 5 deletions portallocator/portallocator_windows.go
@@ -1,10 +1,10 @@
package portallocator

const (
StartPortRange = 60000
EndPortRange = 65000
)
func init() {
defaultPortRangeStart = 60000
defaultPortRangeEnd = 65000
}

func getDynamicPortRange() (start int, end int, err error) {
return StartPortRange, EndPortRange, nil
return defaultPortRangeStart, defaultPortRangeEnd, nil
}