mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Bridge to handle port mapping
- libnetwork cares for list of exposed ports, driver cares for list of port bindings. At endpoint creation: - list of exposed ports will be passed as libnetwork otion - list of port mapping will be passed as driver option Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
781bcc94a7
commit
da1cddc320
18 changed files with 451 additions and 275 deletions
libnetwork
Godeps
drivers/bridge
endpoint.golibnetwork_test.gonetutils
network.go
6
libnetwork/Godeps/Godeps.json
generated
6
libnetwork/Godeps/Godeps.json
generated
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"ImportPath": "github.com/docker/docker/vendor/src/github.com/docker/libnetwork",
|
||||
"ImportPath": "github.com/docker/libnetwork",
|
||||
"GoVersion": "go1.4.2",
|
||||
"Packages": [
|
||||
"./..."
|
||||
|
@ -52,8 +52,8 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/docker/libcontainer/user",
|
||||
"Comment": "v1.4.0",
|
||||
"Rev": "53eca435e63db58b06cf796d3a9326db5fd42253"
|
||||
"Comment": "v1.4.0-495-g3e66118",
|
||||
"Rev": "3e661186ba24f259d3860f067df052c7f6904bee"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netlink",
|
||||
|
|
93
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
generated
vendored
93
libnetwork/Godeps/_workspace/src/github.com/docker/docker/pkg/parsers/kernel/kernel.go
generated
vendored
|
@ -1,93 +0,0 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type KernelVersionInfo struct {
|
||||
Kernel int
|
||||
Major int
|
||||
Minor int
|
||||
Flavor string
|
||||
}
|
||||
|
||||
func (k *KernelVersionInfo) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d%s", k.Kernel, k.Major, k.Minor, k.Flavor)
|
||||
}
|
||||
|
||||
// Compare two KernelVersionInfo struct.
|
||||
// Returns -1 if a < b, 0 if a == b, 1 it a > b
|
||||
func CompareKernelVersion(a, b *KernelVersionInfo) int {
|
||||
if a.Kernel < b.Kernel {
|
||||
return -1
|
||||
} else if a.Kernel > b.Kernel {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Major < b.Major {
|
||||
return -1
|
||||
} else if a.Major > b.Major {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.Minor < b.Minor {
|
||||
return -1
|
||||
} else if a.Minor > b.Minor {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
||||
uts, err := uname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
release := make([]byte, len(uts.Release))
|
||||
|
||||
i := 0
|
||||
for _, c := range uts.Release {
|
||||
release[i] = byte(c)
|
||||
i++
|
||||
}
|
||||
|
||||
// Remove the \x00 from the release for Atoi to parse correctly
|
||||
release = release[:bytes.IndexByte(release, 0)]
|
||||
|
||||
return ParseRelease(string(release))
|
||||
}
|
||||
|
||||
func ParseRelease(release string) (*KernelVersionInfo, error) {
|
||||
var (
|
||||
kernel, major, minor, parsed int
|
||||
flavor, partial string
|
||||
)
|
||||
|
||||
// Ignore error from Sscanf to allow an empty flavor. Instead, just
|
||||
// make sure we got all the version numbers.
|
||||
parsed, _ = fmt.Sscanf(release, "%d.%d%s", &kernel, &major, &partial)
|
||||
if parsed < 2 {
|
||||
return nil, errors.New("Can't parse kernel version " + release)
|
||||
}
|
||||
|
||||
// sometimes we have 3.12.25-gentoo, but sometimes we just have 3.12-1-amd64
|
||||
parsed, _ = fmt.Sscanf(partial, ".%d%s", &minor, &flavor)
|
||||
if parsed < 1 {
|
||||
flavor = partial
|
||||
}
|
||||
|
||||
return &KernelVersionInfo{
|
||||
Kernel: kernel,
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
Flavor: flavor,
|
||||
}, nil
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertParseRelease(t *testing.T, release string, b *KernelVersionInfo, result int) {
|
||||
var (
|
||||
a *KernelVersionInfo
|
||||
)
|
||||
a, _ = ParseRelease(release)
|
||||
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
if a.Flavor != b.Flavor {
|
||||
t.Fatalf("Unexpected parsed kernel flavor. Found %s, expected %s", a.Flavor, b.Flavor)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRelease(t *testing.T) {
|
||||
assertParseRelease(t, "3.8.0", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}, 0)
|
||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||
assertParseRelease(t, "3.4.54.longterm-1", &KernelVersionInfo{Kernel: 3, Major: 4, Minor: 54, Flavor: ".longterm-1"}, 0)
|
||||
assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "-19-generic"}, 0)
|
||||
assertParseRelease(t, "3.12.8tag", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 8, Flavor: "tag"}, 0)
|
||||
assertParseRelease(t, "3.12-1-amd64", &KernelVersionInfo{Kernel: 3, Major: 12, Minor: 0, Flavor: "-1-amd64"}, 0)
|
||||
}
|
||||
|
||||
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
||||
if r := CompareKernelVersion(a, b); r != result {
|
||||
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareKernelVersion(t *testing.T) {
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
-1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 2, Major: 6, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
0)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 5},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
1)
|
||||
assertKernelVersion(t,
|
||||
&KernelVersionInfo{Kernel: 3, Major: 0, Minor: 20},
|
||||
&KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0},
|
||||
-1)
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Utsname syscall.Utsname
|
||||
|
||||
func uname() (*syscall.Utsname, error) {
|
||||
uts := &syscall.Utsname{}
|
||||
|
||||
if err := syscall.Uname(uts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uts, nil
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// +build !linux
|
||||
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
type Utsname struct {
|
||||
Release [65]byte
|
||||
}
|
||||
|
||||
func uname() (*Utsname, error) {
|
||||
return nil, errors.New("Kernel version detection is available only on linux")
|
||||
}
|
1
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/user/MAINTAINERS
generated
vendored
1
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/user/MAINTAINERS
generated
vendored
|
@ -1 +1,2 @@
|
|||
Tianon Gravi <admwiggin@gmail.com> (@tianon)
|
||||
Aleksa Sarai <cyphar@cyphar.com> (@cyphar)
|
||||
|
|
16
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/user/lookup_unix.go
generated
vendored
16
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/user/lookup_unix.go
generated
vendored
|
@ -9,22 +9,22 @@ import (
|
|||
|
||||
// Unix-specific path to the passwd and group formatted files.
|
||||
const (
|
||||
unixPasswdFile = "/etc/passwd"
|
||||
unixGroupFile = "/etc/group"
|
||||
unixPasswdPath = "/etc/passwd"
|
||||
unixGroupPath = "/etc/group"
|
||||
)
|
||||
|
||||
func GetPasswdFile() (string, error) {
|
||||
return unixPasswdFile, nil
|
||||
func GetPasswdPath() (string, error) {
|
||||
return unixPasswdPath, nil
|
||||
}
|
||||
|
||||
func GetPasswd() (io.ReadCloser, error) {
|
||||
return os.Open(unixPasswdFile)
|
||||
return os.Open(unixPasswdPath)
|
||||
}
|
||||
|
||||
func GetGroupFile() (string, error) {
|
||||
return unixGroupFile, nil
|
||||
func GetGroupPath() (string, error) {
|
||||
return unixGroupPath, nil
|
||||
}
|
||||
|
||||
func GetGroup() (io.ReadCloser, error) {
|
||||
return os.Open(unixGroupFile)
|
||||
return os.Open(unixGroupPath)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ package user
|
|||
|
||||
import "io"
|
||||
|
||||
func GetPasswdFile() (string, error) {
|
||||
func GetPasswdPath() (string, error) {
|
||||
return "", ErrUnsupported
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ func GetPasswd() (io.ReadCloser, error) {
|
|||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
func GetGroupFile() (string, error) {
|
||||
func GetGroupPath() (string, error) {
|
||||
return "", ErrUnsupported
|
||||
}
|
||||
|
||||
|
|
4
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/user/user.go
generated
vendored
4
libnetwork/Godeps/_workspace/src/github.com/docker/libcontainer/user/user.go
generated
vendored
|
@ -197,11 +197,11 @@ type ExecUser struct {
|
|||
Home string
|
||||
}
|
||||
|
||||
// GetExecUserFile is a wrapper for GetExecUser. It reads data from each of the
|
||||
// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the
|
||||
// given file paths and uses that data as the arguments to GetExecUser. If the
|
||||
// files cannot be opened for any reason, the error is ignored and a nil
|
||||
// io.Reader is passed instead.
|
||||
func GetExecUserFile(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||
passwd, err := os.Open(passwdPath)
|
||||
if err != nil {
|
||||
passwd = nil
|
||||
|
|
|
@ -16,10 +16,11 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
networkType = "bridge"
|
||||
vethPrefix = "veth"
|
||||
vethLen = 7
|
||||
containerVeth = "eth0"
|
||||
networkType = "bridge"
|
||||
vethPrefix = "veth"
|
||||
vethLen = 7
|
||||
containerVeth = "eth0"
|
||||
maxAllocatePortAttempts = 10
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -42,11 +43,13 @@ type Configuration struct {
|
|||
Mtu int
|
||||
DefaultGatewayIPv4 net.IP
|
||||
DefaultGatewayIPv6 net.IP
|
||||
DefaultBindingIP net.IP
|
||||
}
|
||||
|
||||
// EndpointConfiguration represents the user specified configuration for the sandbox endpoint
|
||||
type EndpointConfiguration struct {
|
||||
MacAddress net.HardwareAddr
|
||||
MacAddress net.HardwareAddr
|
||||
PortBindings []netutils.PortBinding
|
||||
}
|
||||
|
||||
// ContainerConfiguration represents the user specified configuration for a container
|
||||
|
@ -55,9 +58,10 @@ type ContainerConfiguration struct {
|
|||
}
|
||||
|
||||
type bridgeEndpoint struct {
|
||||
id types.UUID
|
||||
port *sandbox.Interface
|
||||
config *EndpointConfiguration // User specified parameters
|
||||
id types.UUID
|
||||
port *sandbox.Interface
|
||||
config *EndpointConfiguration // User specified parameters
|
||||
portMapping []netutils.PortBinding // Operation port bindings
|
||||
}
|
||||
|
||||
type bridgeNetwork struct {
|
||||
|
@ -343,7 +347,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
|
|||
// Try to convert the options to endpoint configuration
|
||||
epConfig, err := parseEndpointOptions(epOptions)
|
||||
if err != nil {
|
||||
n.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -462,14 +465,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
|
|||
ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
|
||||
}
|
||||
|
||||
// Store the sandbox side pipe interface
|
||||
// This is needed for cleanup on DeleteEndpoint()
|
||||
// Create the sandbox side pipe interface
|
||||
intf := &sandbox.Interface{}
|
||||
intf.SrcName = name2
|
||||
intf.DstName = containerVeth
|
||||
intf.Address = ipv4Addr
|
||||
|
||||
// Update endpoint with the sandbox interface info
|
||||
// Store the interface in endpoint, this is needed for cleanup on DeleteEndpoint()
|
||||
endpoint.port = intf
|
||||
|
||||
// Generate the sandbox info to return
|
||||
|
@ -482,6 +484,12 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions map[string]interf
|
|||
sinfo.GatewayIPv6 = n.bridge.gatewayIPv6
|
||||
}
|
||||
|
||||
// Program any required port mapping and store them in the endpoint
|
||||
endpoint.portMapping, err = allocatePorts(epConfig, sinfo, config.DefaultBindingIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
|
@ -531,6 +539,9 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
|
|||
}
|
||||
}()
|
||||
|
||||
// Remove port mappings. Do not stop endpoint delete on unmap failure
|
||||
releasePorts(ep)
|
||||
|
||||
// Release the v4 address allocated to this endpoint's sandbox interface
|
||||
err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.port.Address.IP)
|
||||
if err != nil {
|
||||
|
@ -573,22 +584,26 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat
|
|||
if epOptions == nil {
|
||||
return nil, nil
|
||||
}
|
||||
genericData := epOptions[options.GenericData]
|
||||
if genericData == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch opt := genericData.(type) {
|
||||
case options.Generic:
|
||||
opaqueConfig, err := options.GenerateFromModel(opt, &EndpointConfiguration{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
ec := &EndpointConfiguration{}
|
||||
|
||||
if opt, ok := epOptions[options.MacAddress]; ok {
|
||||
if mac, ok := opt.(net.HardwareAddr); ok {
|
||||
ec.MacAddress = mac
|
||||
} else {
|
||||
return nil, ErrInvalidEndpointConfig
|
||||
}
|
||||
return opaqueConfig.(*EndpointConfiguration), nil
|
||||
case *EndpointConfiguration:
|
||||
return opt, nil
|
||||
default:
|
||||
return nil, ErrInvalidEndpointConfig
|
||||
}
|
||||
|
||||
if opt, ok := epOptions[options.PortMap]; ok {
|
||||
if bs, ok := opt.([]netutils.PortBinding); ok {
|
||||
ec.PortBindings = bs
|
||||
} else {
|
||||
return nil, ErrInvalidEndpointConfig
|
||||
}
|
||||
}
|
||||
|
||||
return ec, nil
|
||||
}
|
||||
|
||||
func parseContainerOptions(cOptions interface{}) (*ContainerConfiguration, error) {
|
||||
|
|
|
@ -74,10 +74,10 @@ func TestCreateLinkWithOptions(t *testing.T) {
|
|||
_, d := New()
|
||||
|
||||
config := &Configuration{BridgeName: DefaultBridgeName}
|
||||
genericOption := make(map[string]interface{})
|
||||
genericOption[options.GenericData] = config
|
||||
driverOptions := make(map[string]interface{})
|
||||
driverOptions[options.GenericData] = config
|
||||
|
||||
if err := d.Config(genericOption); err != nil {
|
||||
if err := d.Config(driverOptions); err != nil {
|
||||
t.Fatalf("Failed to setup driver config: %v", err)
|
||||
}
|
||||
|
||||
|
@ -87,10 +87,10 @@ func TestCreateLinkWithOptions(t *testing.T) {
|
|||
}
|
||||
|
||||
mac := net.HardwareAddr([]byte{0x1e, 0x67, 0x66, 0x44, 0x55, 0x66})
|
||||
epConf := &EndpointConfiguration{MacAddress: mac}
|
||||
genericOption[options.GenericData] = epConf
|
||||
epOptions := make(map[string]interface{})
|
||||
epOptions[options.MacAddress] = mac
|
||||
|
||||
sinfo, err := d.CreateEndpoint("net1", "ep", genericOption)
|
||||
sinfo, err := d.CreateEndpoint("net1", "ep", epOptions)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a link: %s", err.Error())
|
||||
}
|
||||
|
|
|
@ -37,10 +37,31 @@ var (
|
|||
// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid.
|
||||
ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network")
|
||||
|
||||
// ErrInvalidMtu is returned when the user provided MTU is not valid
|
||||
// ErrInvalidMtu is returned when the user provided MTU is not valid.
|
||||
ErrInvalidMtu = errors.New("invalid MTU number")
|
||||
)
|
||||
|
||||
// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
|
||||
type ErrInvalidPort string
|
||||
|
||||
func (ip ErrInvalidPort) Error() string {
|
||||
return fmt.Sprintf("invalid transport port: %s", string(ip))
|
||||
}
|
||||
|
||||
// ErrUnsupportedAddressType is returned when the specified address type is not supported.
|
||||
type ErrUnsupportedAddressType string
|
||||
|
||||
func (uat ErrUnsupportedAddressType) Error() string {
|
||||
return fmt.Sprintf("unsupported address type: %s", string(uat))
|
||||
}
|
||||
|
||||
// ErrInvalidAddressBinding is returned when the host address specfied in the port binding is not valid.
|
||||
type ErrInvalidAddressBinding string
|
||||
|
||||
func (iab ErrInvalidAddressBinding) Error() string {
|
||||
return fmt.Sprintf("invalid host address in port binding: %s", string(iab))
|
||||
}
|
||||
|
||||
// ActiveEndpointsError is returned when there are
|
||||
// still active endpoints in the network being deleted.
|
||||
type ActiveEndpointsError string
|
||||
|
|
124
libnetwork/drivers/bridge/port_mapping.go
Normal file
124
libnetwork/drivers/bridge/port_mapping.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/sandbox"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultBindingIP = net.IPv4(0, 0, 0, 0)
|
||||
)
|
||||
|
||||
func allocatePorts(epConfig *EndpointConfiguration, sinfo *sandbox.Info, reqDefBindIP net.IP) ([]netutils.PortBinding, error) {
|
||||
if epConfig == nil || epConfig.PortBindings == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
defHostIP := defaultBindingIP
|
||||
if reqDefBindIP != nil {
|
||||
defHostIP = reqDefBindIP
|
||||
}
|
||||
|
||||
return allocatePortsInternal(epConfig.PortBindings, sinfo.Interfaces[0].Address.IP, defHostIP)
|
||||
}
|
||||
|
||||
func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP) ([]netutils.PortBinding, error) {
|
||||
bs := make([]netutils.PortBinding, 0, len(bindings))
|
||||
for _, c := range bindings {
|
||||
b := c.GetCopy()
|
||||
if err := allocatePort(&b, containerIP, defHostIP); err != nil {
|
||||
// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
|
||||
if cuErr := releasePortsInternal(bs); cuErr != nil {
|
||||
logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
bs = append(bs, b)
|
||||
}
|
||||
return bs, nil
|
||||
}
|
||||
|
||||
func allocatePort(bnd *netutils.PortBinding, containerIP, defHostIP net.IP) error {
|
||||
var (
|
||||
host net.Addr
|
||||
err error
|
||||
)
|
||||
|
||||
// Store the container interface address in the operational binding
|
||||
bnd.IP = containerIP
|
||||
|
||||
// Adjust the host address in the operational binding
|
||||
if len(bnd.HostIP) == 0 {
|
||||
bnd.HostIP = defHostIP
|
||||
}
|
||||
|
||||
// Construct the container side transport address
|
||||
container, err := bnd.ContainerAddr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
|
||||
for i := 0; i < maxAllocatePortAttempts; i++ {
|
||||
if host, err = portMapper.Map(container, bnd.HostIP, int(bnd.HostPort)); err == nil {
|
||||
break
|
||||
}
|
||||
// There is no point in immediately retrying to map an explicitly chosen port.
|
||||
if bnd.HostPort != 0 {
|
||||
logrus.Warnf("Failed to allocate and map port %d: %s", bnd.HostPort, err)
|
||||
break
|
||||
}
|
||||
logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save the host port (regardless it was or not specified in the binding)
|
||||
switch netAddr := host.(type) {
|
||||
case *net.TCPAddr:
|
||||
bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
|
||||
return nil
|
||||
case *net.UDPAddr:
|
||||
bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
|
||||
return nil
|
||||
default:
|
||||
// For completeness
|
||||
return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
|
||||
}
|
||||
}
|
||||
|
||||
func releasePorts(ep *bridgeEndpoint) error {
|
||||
return releasePortsInternal(ep.portMapping)
|
||||
}
|
||||
|
||||
func releasePortsInternal(bindings []netutils.PortBinding) error {
|
||||
var errorBuf bytes.Buffer
|
||||
|
||||
// Attempt to release all port bindings, do not stop on failure
|
||||
for _, m := range bindings {
|
||||
if err := releasePort(m); err != nil {
|
||||
errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
|
||||
}
|
||||
}
|
||||
|
||||
if errorBuf.Len() != 0 {
|
||||
return errors.New(errorBuf.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func releasePort(bnd netutils.PortBinding) error {
|
||||
// Construct the host side transport address
|
||||
host, err := bnd.HostAddr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return portMapper.Unmap(host)
|
||||
}
|
72
libnetwork/drivers/bridge/port_mapping_test.go
Normal file
72
libnetwork/drivers/bridge/port_mapping_test.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/options"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestPortMappingConfig(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
_, d := New()
|
||||
|
||||
binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)}
|
||||
binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)}
|
||||
portBindings := []netutils.PortBinding{binding1, binding2}
|
||||
|
||||
epOptions := make(map[string]interface{})
|
||||
epOptions[options.PortMap] = portBindings
|
||||
|
||||
driverConfig := &Configuration{
|
||||
BridgeName: DefaultBridgeName,
|
||||
EnableIPTables: true,
|
||||
}
|
||||
driverOptions := make(map[string]interface{})
|
||||
driverOptions[options.GenericData] = driverConfig
|
||||
|
||||
if err := d.Config(driverOptions); err != nil {
|
||||
t.Fatalf("Failed to setup driver config: %v", err)
|
||||
}
|
||||
|
||||
err := d.CreateNetwork("dummy", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create bridge: %v", err)
|
||||
}
|
||||
|
||||
_, err = d.CreateEndpoint("dummy", "ep1", epOptions)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create the endpoint: %s", err.Error())
|
||||
}
|
||||
|
||||
dd := d.(*driver)
|
||||
ep, _ := dd.network.endpoints["ep1"]
|
||||
if len(ep.portMapping) != 2 {
|
||||
t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
|
||||
}
|
||||
if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port ||
|
||||
ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port {
|
||||
t.Fatalf("bridgeEndpoint has incorrect port mapping values")
|
||||
}
|
||||
if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 ||
|
||||
ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 {
|
||||
t.Fatalf("operational port mapping data not found on bridgeEndpoint")
|
||||
}
|
||||
|
||||
fmt.Printf("\nendpoint: %v\n", ep.portMapping)
|
||||
|
||||
err = releasePorts(ep)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to release mapped ports: %v", err)
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/etchosts"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/options"
|
||||
"github.com/docker/libnetwork/sandbox"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -65,14 +67,15 @@ type containerInfo struct {
|
|||
}
|
||||
|
||||
type endpoint struct {
|
||||
name string
|
||||
id types.UUID
|
||||
network *network
|
||||
sandboxInfo *sandbox.Info
|
||||
sandBox sandbox.Sandbox
|
||||
container *containerInfo
|
||||
generic map[string]interface{}
|
||||
context map[string]interface{}
|
||||
name string
|
||||
id types.UUID
|
||||
network *network
|
||||
sandboxInfo *sandbox.Info
|
||||
sandBox sandbox.Sandbox
|
||||
container *containerInfo
|
||||
exposedPorts []netutils.TransportPort
|
||||
generic map[string]interface{}
|
||||
context map[string]interface{}
|
||||
}
|
||||
|
||||
const prefix = "/var/lib/docker/network/files"
|
||||
|
@ -186,7 +189,7 @@ func (ep *endpoint) Join(containerID string, options ...JoinOption) (*ContainerD
|
|||
}()
|
||||
|
||||
n := ep.network
|
||||
err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.Config.generic)
|
||||
err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.config.generic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -297,12 +300,26 @@ func JoinOptionDomainname(name string) JoinOption {
|
|||
}
|
||||
}
|
||||
|
||||
// CreateOptionPortMapping function returns an option setter for the container exposed
|
||||
// ports option to be passed to endpoint Join method.
|
||||
func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption {
|
||||
return func(ep *endpoint) {
|
||||
// Store endpoint label
|
||||
ep.generic[options.PortMap] = portBindings
|
||||
// Extract exposed ports as this is the only concern of libnetwork endpoint
|
||||
ep.exposedPorts = make([]netutils.TransportPort, 0, len(portBindings))
|
||||
for _, b := range portBindings {
|
||||
ep.exposedPorts = append(ep.exposedPorts, netutils.TransportPort{Proto: b.Proto, Port: b.Port})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// JoinOptionGeneric function returns an option setter for Generic configuration
|
||||
// that is not managed by libNetwork but can be used by the Drivers during the call to
|
||||
// endpoint join method. Container Labels are a good example.
|
||||
func JoinOptionGeneric(generic map[string]interface{}) JoinOption {
|
||||
return func(ep *endpoint) {
|
||||
ep.container.Config.generic = generic
|
||||
ep.container.config.generic = generic
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,28 @@ package libnetwork_test
|
|||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/pkg/options"
|
||||
)
|
||||
|
||||
const (
|
||||
netType = "bridge"
|
||||
bridgeName = "dockertest0"
|
||||
bridgeNetType = "bridge"
|
||||
bridgeName = "docker0"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func createTestNetwork(networkType, networkName string, option options.Generic) (libnetwork.Network, error) {
|
||||
controller := libnetwork.New()
|
||||
genericOption := make(map[string]interface{})
|
||||
|
@ -39,6 +48,14 @@ func getEmptyGenericOption() map[string]interface{} {
|
|||
return genericOption
|
||||
}
|
||||
|
||||
func getPortMapping() []netutils.PortBinding {
|
||||
return []netutils.PortBinding{
|
||||
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
|
||||
netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
|
||||
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
|
||||
}
|
||||
}
|
||||
|
||||
func TestNull(t *testing.T) {
|
||||
network, err := createTestNetwork("null", "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
|
@ -104,12 +121,12 @@ func TestBridge(t *testing.T) {
|
|||
"EnableIPForwarding": true,
|
||||
"AllowNonDefaultBridge": true}
|
||||
|
||||
network, err := createTestNetwork(netType, "testnetwork", option)
|
||||
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := network.CreateEndpoint("testep")
|
||||
ep, err := network.CreateEndpoint("testep", libnetwork.CreateOptionPortMapping(getPortMapping()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -171,17 +188,17 @@ func TestDuplicateNetwork(t *testing.T) {
|
|||
genericOption := make(map[string]interface{})
|
||||
genericOption[options.GenericData] = options.Generic{}
|
||||
|
||||
err := controller.ConfigureNetworkDriver(netType, genericOption)
|
||||
err := controller.ConfigureNetworkDriver(bridgeNetType, genericOption)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = controller.NewNetwork(netType, "testnetwork", nil)
|
||||
_, err = controller.NewNetwork(bridgeNetType, "testnetwork", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = controller.NewNetwork(netType, "testnetwork")
|
||||
_, err = controller.NewNetwork(bridgeNetType, "testnetwork")
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail. But instead succeeded")
|
||||
}
|
||||
|
@ -192,9 +209,10 @@ func TestDuplicateNetwork(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNetworkName(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
networkName := "testnetwork"
|
||||
|
||||
n, err := createTestNetwork(netType, networkName, options.Generic{})
|
||||
n, err := createTestNetwork(bridgeNetType, networkName, options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -205,22 +223,21 @@ func TestNetworkName(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNetworkType(t *testing.T) {
|
||||
networkType := netType
|
||||
|
||||
n, err := createTestNetwork(networkType, "testnetwork", options.Generic{})
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if n.Type() != networkType {
|
||||
t.Fatalf("Expected network type %s, got %s", networkType, n.Type())
|
||||
if n.Type() != bridgeNetType {
|
||||
t.Fatalf("Expected network type %s, got %s", bridgeNetType, n.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkID(t *testing.T) {
|
||||
networkType := netType
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork(networkType, "testnetwork", options.Generic{})
|
||||
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -236,7 +253,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) {
|
|||
"BridgeName": bridgeName,
|
||||
"AllowNonDefaultBridge": true}
|
||||
|
||||
network, err := createTestNetwork(netType, "testnetwork", option)
|
||||
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -271,7 +288,7 @@ func TestUnknownNetwork(t *testing.T) {
|
|||
"BridgeName": bridgeName,
|
||||
"AllowNonDefaultBridge": true}
|
||||
|
||||
network, err := createTestNetwork(netType, "testnetwork", option)
|
||||
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -304,7 +321,7 @@ func TestUnknownEndpoint(t *testing.T) {
|
|||
"AddressIPv4": subnet,
|
||||
"AllowNonDefaultBridge": true}
|
||||
|
||||
network, err := createTestNetwork(netType, "testnetwork", option)
|
||||
network, err := createTestNetwork(bridgeNetType, "testnetwork", option)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -337,15 +354,14 @@ func TestUnknownEndpoint(t *testing.T) {
|
|||
func TestNetworkEndpointsWalkers(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
controller := libnetwork.New()
|
||||
netType := "bridge"
|
||||
|
||||
err := controller.ConfigureNetworkDriver(netType, getEmptyGenericOption())
|
||||
err := controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create network 1 and add 2 endpoint: ep11, ep12
|
||||
net1, err := controller.NewNetwork(netType, "network1")
|
||||
net1, err := controller.NewNetwork(bridgeNetType, "network1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -416,15 +432,14 @@ func TestNetworkEndpointsWalkers(t *testing.T) {
|
|||
func TestControllerQuery(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
controller := libnetwork.New()
|
||||
netType := "bridge"
|
||||
|
||||
err := controller.ConfigureNetworkDriver(netType, getEmptyGenericOption())
|
||||
err := controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create network 1
|
||||
net1, err := controller.NewNetwork(netType, "network1")
|
||||
net1, err := controller.NewNetwork(bridgeNetType, "network1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -461,15 +476,14 @@ func TestControllerQuery(t *testing.T) {
|
|||
func TestNetworkQuery(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
controller := libnetwork.New()
|
||||
netType := "bridge"
|
||||
|
||||
err := controller.ConfigureNetworkDriver(netType, getEmptyGenericOption())
|
||||
err := controller.ConfigureNetworkDriver(bridgeNetType, getEmptyGenericOption())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create network 1 and add 2 endpoint: ep11, ep12
|
||||
net1, err := controller.NewNetwork(netType, "network1")
|
||||
net1, err := controller.NewNetwork(bridgeNetType, "network1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -514,7 +528,7 @@ const containerID = "valid_container"
|
|||
func TestEndpointJoin(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -540,7 +554,7 @@ func TestEndpointJoin(t *testing.T) {
|
|||
func TestEndpointJoinInvalidContainerId(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -563,7 +577,7 @@ func TestEndpointJoinInvalidContainerId(t *testing.T) {
|
|||
func TestEndpointMultipleJoins(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -599,7 +613,7 @@ func TestEndpointMultipleJoins(t *testing.T) {
|
|||
func TestEndpointInvalidLeave(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
@ -25,6 +26,102 @@ var (
|
|||
networkGetRoutesFct = netlink.RouteList
|
||||
)
|
||||
|
||||
// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
|
||||
type ErrInvalidProtocolBinding string
|
||||
|
||||
func (ipb ErrInvalidProtocolBinding) Error() string {
|
||||
return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
|
||||
}
|
||||
|
||||
// TransportPort represent a local Layer 4 endpoint
|
||||
type TransportPort struct {
|
||||
Proto Protocol
|
||||
Port uint16
|
||||
}
|
||||
|
||||
// PortBinding represent a port binding between the container an the host
|
||||
type PortBinding struct {
|
||||
Proto Protocol
|
||||
IP net.IP
|
||||
Port uint16
|
||||
HostIP net.IP
|
||||
HostPort uint16
|
||||
}
|
||||
|
||||
// HostAddr returns the host side tranport address
|
||||
func (p PortBinding) HostAddr() (net.Addr, error) {
|
||||
switch p.Proto {
|
||||
case UDP:
|
||||
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
|
||||
case TCP:
|
||||
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
|
||||
default:
|
||||
return nil, ErrInvalidProtocolBinding(p.Proto.String())
|
||||
}
|
||||
}
|
||||
|
||||
// ContainerAddr returns the container side tranport address
|
||||
func (p PortBinding) ContainerAddr() (net.Addr, error) {
|
||||
switch p.Proto {
|
||||
case UDP:
|
||||
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
|
||||
case TCP:
|
||||
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
|
||||
default:
|
||||
return nil, ErrInvalidProtocolBinding(p.Proto.String())
|
||||
}
|
||||
}
|
||||
|
||||
// GetCopy returns a copy of this PortBinding structure instance
|
||||
func (p *PortBinding) GetCopy() PortBinding {
|
||||
return PortBinding{
|
||||
Proto: p.Proto,
|
||||
IP: GetIPCopy(p.IP),
|
||||
Port: p.Port,
|
||||
HostIP: GetIPCopy(p.HostIP),
|
||||
HostPort: p.HostPort,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// ICMP is for the ICMP ip protocol
|
||||
ICMP = 1
|
||||
// TCP is for the TCP ip protocol
|
||||
TCP = 6
|
||||
// UDP is for the UDP ip protocol
|
||||
UDP = 17
|
||||
)
|
||||
|
||||
// Protocol represents a IP protocol number
|
||||
type Protocol uint8
|
||||
|
||||
func (p Protocol) String() string {
|
||||
switch p {
|
||||
case 1:
|
||||
return "icmp"
|
||||
case 6:
|
||||
return "tcp"
|
||||
case 17:
|
||||
return "udp"
|
||||
default:
|
||||
return fmt.Sprintf("%d", p)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseProtocol returns the respective Protocol type for the passed string
|
||||
func ParseProtocol(s string) Protocol {
|
||||
switch strings.ToLower(s) {
|
||||
case "icmp":
|
||||
return 1
|
||||
case "udp":
|
||||
return 6
|
||||
case "tcp":
|
||||
return 17
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers
|
||||
func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
|
||||
if len(nameservers) > 0 {
|
||||
|
|
|
@ -127,7 +127,7 @@ func (n *network) Delete() error {
|
|||
}
|
||||
|
||||
func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) {
|
||||
ep := &endpoint{name: name}
|
||||
ep := &endpoint{name: name, generic: make(map[string]interface{})}
|
||||
ep.id = types.UUID(stringid.GenerateRandomID())
|
||||
ep.network = n
|
||||
ep.processOptions(options...)
|
||||
|
|
Loading…
Add table
Reference in a new issue