This makes it easier to spot if code is only used on Linux. Note that "all of"
the bridge driver is Linux-only.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,1504 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "net" |
|
| 10 |
- "os" |
|
| 11 |
- "os/exec" |
|
| 12 |
- "strconv" |
|
| 13 |
- "sync" |
|
| 14 |
- |
|
| 15 |
- "github.com/containerd/containerd/log" |
|
| 16 |
- "github.com/docker/docker/libnetwork/datastore" |
|
| 17 |
- "github.com/docker/docker/libnetwork/driverapi" |
|
| 18 |
- "github.com/docker/docker/libnetwork/iptables" |
|
| 19 |
- "github.com/docker/docker/libnetwork/netlabel" |
|
| 20 |
- "github.com/docker/docker/libnetwork/netutils" |
|
| 21 |
- "github.com/docker/docker/libnetwork/ns" |
|
| 22 |
- "github.com/docker/docker/libnetwork/options" |
|
| 23 |
- "github.com/docker/docker/libnetwork/portallocator" |
|
| 24 |
- "github.com/docker/docker/libnetwork/portmapper" |
|
| 25 |
- "github.com/docker/docker/libnetwork/scope" |
|
| 26 |
- "github.com/docker/docker/libnetwork/types" |
|
| 27 |
- "github.com/vishvananda/netlink" |
|
| 28 |
-) |
|
| 29 |
- |
|
| 30 |
-const ( |
|
| 31 |
- NetworkType = "bridge" |
|
| 32 |
- vethPrefix = "veth" |
|
| 33 |
- vethLen = len(vethPrefix) + 7 |
|
| 34 |
- defaultContainerVethPrefix = "eth" |
|
| 35 |
- maxAllocatePortAttempts = 10 |
|
| 36 |
-) |
|
| 37 |
- |
|
| 38 |
-const ( |
|
| 39 |
- // DefaultGatewayV4AuxKey represents the default-gateway configured by the user |
|
| 40 |
- DefaultGatewayV4AuxKey = "DefaultGatewayIPv4" |
|
| 41 |
- // DefaultGatewayV6AuxKey represents the ipv6 default-gateway configured by the user |
|
| 42 |
- DefaultGatewayV6AuxKey = "DefaultGatewayIPv6" |
|
| 43 |
-) |
|
| 44 |
- |
|
| 45 |
-type defaultBridgeNetworkConflict struct {
|
|
| 46 |
- ID string |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-func (d defaultBridgeNetworkConflict) Error() string {
|
|
| 50 |
- return fmt.Sprintf("Stale default bridge network %s", d.ID)
|
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-type ( |
|
| 54 |
- iptableCleanFunc func() error |
|
| 55 |
- iptablesCleanFuncs []iptableCleanFunc |
|
| 56 |
-) |
|
| 57 |
- |
|
| 58 |
-// configuration info for the "bridge" driver. |
|
| 59 |
-type configuration struct {
|
|
| 60 |
- EnableIPForwarding bool |
|
| 61 |
- EnableIPTables bool |
|
| 62 |
- EnableIP6Tables bool |
|
| 63 |
- EnableUserlandProxy bool |
|
| 64 |
- UserlandProxyPath string |
|
| 65 |
-} |
|
| 66 |
- |
|
| 67 |
-// networkConfiguration for network specific configuration |
|
| 68 |
-type networkConfiguration struct {
|
|
| 69 |
- ID string |
|
| 70 |
- BridgeName string |
|
| 71 |
- EnableIPv6 bool |
|
| 72 |
- EnableIPMasquerade bool |
|
| 73 |
- EnableICC bool |
|
| 74 |
- InhibitIPv4 bool |
|
| 75 |
- Mtu int |
|
| 76 |
- DefaultBindingIP net.IP |
|
| 77 |
- DefaultBridge bool |
|
| 78 |
- HostIP net.IP |
|
| 79 |
- ContainerIfacePrefix string |
|
| 80 |
- // Internal fields set after ipam data parsing |
|
| 81 |
- AddressIPv4 *net.IPNet |
|
| 82 |
- AddressIPv6 *net.IPNet |
|
| 83 |
- DefaultGatewayIPv4 net.IP |
|
| 84 |
- DefaultGatewayIPv6 net.IP |
|
| 85 |
- dbIndex uint64 |
|
| 86 |
- dbExists bool |
|
| 87 |
- Internal bool |
|
| 88 |
- |
|
| 89 |
- BridgeIfaceCreator ifaceCreator |
|
| 90 |
-} |
|
| 91 |
- |
|
| 92 |
-// ifaceCreator represents how the bridge interface was created |
|
| 93 |
-type ifaceCreator int8 |
|
| 94 |
- |
|
| 95 |
-const ( |
|
| 96 |
- ifaceCreatorUnknown ifaceCreator = iota |
|
| 97 |
- ifaceCreatedByLibnetwork |
|
| 98 |
- ifaceCreatedByUser |
|
| 99 |
-) |
|
| 100 |
- |
|
| 101 |
-// endpointConfiguration represents the user specified configuration for the sandbox endpoint |
|
| 102 |
-type endpointConfiguration struct {
|
|
| 103 |
- MacAddress net.HardwareAddr |
|
| 104 |
-} |
|
| 105 |
- |
|
| 106 |
-// containerConfiguration represents the user specified configuration for a container |
|
| 107 |
-type containerConfiguration struct {
|
|
| 108 |
- ParentEndpoints []string |
|
| 109 |
- ChildEndpoints []string |
|
| 110 |
-} |
|
| 111 |
- |
|
| 112 |
-// connectivityConfiguration represents the user specified configuration regarding the external connectivity |
|
| 113 |
-type connectivityConfiguration struct {
|
|
| 114 |
- PortBindings []types.PortBinding |
|
| 115 |
- ExposedPorts []types.TransportPort |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-type bridgeEndpoint struct {
|
|
| 119 |
- id string |
|
| 120 |
- nid string |
|
| 121 |
- srcName string |
|
| 122 |
- addr *net.IPNet |
|
| 123 |
- addrv6 *net.IPNet |
|
| 124 |
- macAddress net.HardwareAddr |
|
| 125 |
- config *endpointConfiguration // User specified parameters |
|
| 126 |
- containerConfig *containerConfiguration |
|
| 127 |
- extConnConfig *connectivityConfiguration |
|
| 128 |
- portMapping []types.PortBinding // Operation port bindings |
|
| 129 |
- dbIndex uint64 |
|
| 130 |
- dbExists bool |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-type bridgeNetwork struct {
|
|
| 134 |
- id string |
|
| 135 |
- bridge *bridgeInterface // The bridge's L3 interface |
|
| 136 |
- config *networkConfiguration |
|
| 137 |
- endpoints map[string]*bridgeEndpoint // key: endpoint id |
|
| 138 |
- portMapper *portmapper.PortMapper |
|
| 139 |
- portMapperV6 *portmapper.PortMapper |
|
| 140 |
- driver *driver // The network's driver |
|
| 141 |
- iptCleanFuncs iptablesCleanFuncs |
|
| 142 |
- sync.Mutex |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-type driver struct {
|
|
| 146 |
- config configuration |
|
| 147 |
- natChain *iptables.ChainInfo |
|
| 148 |
- filterChain *iptables.ChainInfo |
|
| 149 |
- isolationChain1 *iptables.ChainInfo |
|
| 150 |
- isolationChain2 *iptables.ChainInfo |
|
| 151 |
- natChainV6 *iptables.ChainInfo |
|
| 152 |
- filterChainV6 *iptables.ChainInfo |
|
| 153 |
- isolationChain1V6 *iptables.ChainInfo |
|
| 154 |
- isolationChain2V6 *iptables.ChainInfo |
|
| 155 |
- networks map[string]*bridgeNetwork |
|
| 156 |
- store *datastore.Store |
|
| 157 |
- nlh *netlink.Handle |
|
| 158 |
- configNetwork sync.Mutex |
|
| 159 |
- portAllocator *portallocator.PortAllocator // Overridable for tests. |
|
| 160 |
- sync.Mutex |
|
| 161 |
-} |
|
| 162 |
- |
|
| 163 |
-// New constructs a new bridge driver |
|
| 164 |
-func newDriver() *driver {
|
|
| 165 |
- return &driver{
|
|
| 166 |
- networks: map[string]*bridgeNetwork{},
|
|
| 167 |
- portAllocator: portallocator.Get(), |
|
| 168 |
- } |
|
| 169 |
-} |
|
| 170 |
- |
|
| 171 |
-// Register registers a new instance of bridge driver. |
|
| 172 |
-func Register(r driverapi.Registerer, config map[string]interface{}) error {
|
|
| 173 |
- d := newDriver() |
|
| 174 |
- if err := d.configure(config); err != nil {
|
|
| 175 |
- return err |
|
| 176 |
- } |
|
| 177 |
- return r.RegisterDriver(NetworkType, d, driverapi.Capability{
|
|
| 178 |
- DataScope: scope.Local, |
|
| 179 |
- ConnectivityScope: scope.Local, |
|
| 180 |
- }) |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-// Validate performs a static validation on the network configuration parameters. |
|
| 184 |
-// Whatever can be assessed a priori before attempting any programming. |
|
| 185 |
-func (c *networkConfiguration) Validate() error {
|
|
| 186 |
- if c.Mtu < 0 {
|
|
| 187 |
- return ErrInvalidMtu(c.Mtu) |
|
| 188 |
- } |
|
| 189 |
- |
|
| 190 |
- // If bridge v4 subnet is specified |
|
| 191 |
- if c.AddressIPv4 != nil {
|
|
| 192 |
- // If default gw is specified, it must be part of bridge subnet |
|
| 193 |
- if c.DefaultGatewayIPv4 != nil {
|
|
| 194 |
- if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) {
|
|
| 195 |
- return &ErrInvalidGateway{}
|
|
| 196 |
- } |
|
| 197 |
- } |
|
| 198 |
- } |
|
| 199 |
- |
|
| 200 |
- // If default v6 gw is specified, AddressIPv6 must be specified and gw must belong to AddressIPv6 subnet |
|
| 201 |
- if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil {
|
|
| 202 |
- if c.AddressIPv6 == nil || !c.AddressIPv6.Contains(c.DefaultGatewayIPv6) {
|
|
| 203 |
- return &ErrInvalidGateway{}
|
|
| 204 |
- } |
|
| 205 |
- } |
|
| 206 |
- return nil |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-// Conflicts check if two NetworkConfiguration objects overlap |
|
| 210 |
-func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
|
|
| 211 |
- if o == nil {
|
|
| 212 |
- return errors.New("same configuration")
|
|
| 213 |
- } |
|
| 214 |
- |
|
| 215 |
- // Also empty, because only one network with empty name is allowed |
|
| 216 |
- if c.BridgeName == o.BridgeName {
|
|
| 217 |
- return errors.New("networks have same bridge name")
|
|
| 218 |
- } |
|
| 219 |
- |
|
| 220 |
- // They must be in different subnets |
|
| 221 |
- if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) && |
|
| 222 |
- (c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) {
|
|
| 223 |
- return errors.New("networks have overlapping IPv4")
|
|
| 224 |
- } |
|
| 225 |
- |
|
| 226 |
- // They must be in different v6 subnets |
|
| 227 |
- if (c.AddressIPv6 != nil && o.AddressIPv6 != nil) && |
|
| 228 |
- (c.AddressIPv6.Contains(o.AddressIPv6.IP) || o.AddressIPv6.Contains(c.AddressIPv6.IP)) {
|
|
| 229 |
- return errors.New("networks have overlapping IPv6")
|
|
| 230 |
- } |
|
| 231 |
- |
|
| 232 |
- return nil |
|
| 233 |
-} |
|
| 234 |
- |
|
| 235 |
-func (c *networkConfiguration) fromLabels(labels map[string]string) error {
|
|
| 236 |
- var err error |
|
| 237 |
- for label, value := range labels {
|
|
| 238 |
- switch label {
|
|
| 239 |
- case BridgeName: |
|
| 240 |
- c.BridgeName = value |
|
| 241 |
- case netlabel.DriverMTU: |
|
| 242 |
- if c.Mtu, err = strconv.Atoi(value); err != nil {
|
|
| 243 |
- return parseErr(label, value, err.Error()) |
|
| 244 |
- } |
|
| 245 |
- case netlabel.EnableIPv6: |
|
| 246 |
- if c.EnableIPv6, err = strconv.ParseBool(value); err != nil {
|
|
| 247 |
- return parseErr(label, value, err.Error()) |
|
| 248 |
- } |
|
| 249 |
- case EnableIPMasquerade: |
|
| 250 |
- if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil {
|
|
| 251 |
- return parseErr(label, value, err.Error()) |
|
| 252 |
- } |
|
| 253 |
- case EnableICC: |
|
| 254 |
- if c.EnableICC, err = strconv.ParseBool(value); err != nil {
|
|
| 255 |
- return parseErr(label, value, err.Error()) |
|
| 256 |
- } |
|
| 257 |
- case InhibitIPv4: |
|
| 258 |
- if c.InhibitIPv4, err = strconv.ParseBool(value); err != nil {
|
|
| 259 |
- return parseErr(label, value, err.Error()) |
|
| 260 |
- } |
|
| 261 |
- case DefaultBridge: |
|
| 262 |
- if c.DefaultBridge, err = strconv.ParseBool(value); err != nil {
|
|
| 263 |
- return parseErr(label, value, err.Error()) |
|
| 264 |
- } |
|
| 265 |
- case DefaultBindingIP: |
|
| 266 |
- if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil {
|
|
| 267 |
- return parseErr(label, value, "nil ip") |
|
| 268 |
- } |
|
| 269 |
- case netlabel.ContainerIfacePrefix: |
|
| 270 |
- c.ContainerIfacePrefix = value |
|
| 271 |
- case netlabel.HostIP: |
|
| 272 |
- if c.HostIP = net.ParseIP(value); c.HostIP == nil {
|
|
| 273 |
- return parseErr(label, value, "nil ip") |
|
| 274 |
- } |
|
| 275 |
- } |
|
| 276 |
- } |
|
| 277 |
- |
|
| 278 |
- return nil |
|
| 279 |
-} |
|
| 280 |
- |
|
| 281 |
-func parseErr(label, value, errString string) error {
|
|
| 282 |
- return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString)
|
|
| 283 |
-} |
|
| 284 |
- |
|
| 285 |
-func (n *bridgeNetwork) registerIptCleanFunc(clean iptableCleanFunc) {
|
|
| 286 |
- n.iptCleanFuncs = append(n.iptCleanFuncs, clean) |
|
| 287 |
-} |
|
| 288 |
- |
|
| 289 |
-func (n *bridgeNetwork) getDriverChains(version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
|
|
| 290 |
- n.Lock() |
|
| 291 |
- defer n.Unlock() |
|
| 292 |
- |
|
| 293 |
- if n.driver == nil {
|
|
| 294 |
- return nil, nil, nil, nil, types.BadRequestErrorf("no driver found")
|
|
| 295 |
- } |
|
| 296 |
- |
|
| 297 |
- if version == iptables.IPv6 {
|
|
| 298 |
- return n.driver.natChainV6, n.driver.filterChainV6, n.driver.isolationChain1V6, n.driver.isolationChain2V6, nil |
|
| 299 |
- } |
|
| 300 |
- |
|
| 301 |
- return n.driver.natChain, n.driver.filterChain, n.driver.isolationChain1, n.driver.isolationChain2, nil |
|
| 302 |
-} |
|
| 303 |
- |
|
| 304 |
-func (n *bridgeNetwork) getNetworkBridgeName() string {
|
|
| 305 |
- n.Lock() |
|
| 306 |
- config := n.config |
|
| 307 |
- n.Unlock() |
|
| 308 |
- |
|
| 309 |
- return config.BridgeName |
|
| 310 |
-} |
|
| 311 |
- |
|
| 312 |
-func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) {
|
|
| 313 |
- if eid == "" {
|
|
| 314 |
- return nil, InvalidEndpointIDError(eid) |
|
| 315 |
- } |
|
| 316 |
- |
|
| 317 |
- n.Lock() |
|
| 318 |
- defer n.Unlock() |
|
| 319 |
- if ep, ok := n.endpoints[eid]; ok {
|
|
| 320 |
- return ep, nil |
|
| 321 |
- } |
|
| 322 |
- |
|
| 323 |
- return nil, nil |
|
| 324 |
-} |
|
| 325 |
- |
|
| 326 |
-// Install/Removes the iptables rules needed to isolate this network |
|
| 327 |
-// from each of the other networks |
|
| 328 |
-func (n *bridgeNetwork) isolateNetwork(enable bool) error {
|
|
| 329 |
- n.Lock() |
|
| 330 |
- thisConfig := n.config |
|
| 331 |
- n.Unlock() |
|
| 332 |
- |
|
| 333 |
- if thisConfig.Internal {
|
|
| 334 |
- return nil |
|
| 335 |
- } |
|
| 336 |
- |
|
| 337 |
- // Install the rules to isolate this network against each of the other networks |
|
| 338 |
- if n.driver.config.EnableIP6Tables {
|
|
| 339 |
- err := setINC(iptables.IPv6, thisConfig.BridgeName, enable) |
|
| 340 |
- if err != nil {
|
|
| 341 |
- return err |
|
| 342 |
- } |
|
| 343 |
- } |
|
| 344 |
- |
|
| 345 |
- if n.driver.config.EnableIPTables {
|
|
| 346 |
- return setINC(iptables.IPv4, thisConfig.BridgeName, enable) |
|
| 347 |
- } |
|
| 348 |
- return nil |
|
| 349 |
-} |
|
| 350 |
- |
|
| 351 |
-func (d *driver) configure(option map[string]interface{}) error {
|
|
| 352 |
- var ( |
|
| 353 |
- config configuration |
|
| 354 |
- err error |
|
| 355 |
- natChain *iptables.ChainInfo |
|
| 356 |
- filterChain *iptables.ChainInfo |
|
| 357 |
- isolationChain1 *iptables.ChainInfo |
|
| 358 |
- isolationChain2 *iptables.ChainInfo |
|
| 359 |
- natChainV6 *iptables.ChainInfo |
|
| 360 |
- filterChainV6 *iptables.ChainInfo |
|
| 361 |
- isolationChain1V6 *iptables.ChainInfo |
|
| 362 |
- isolationChain2V6 *iptables.ChainInfo |
|
| 363 |
- ) |
|
| 364 |
- |
|
| 365 |
- switch opt := option[netlabel.GenericData].(type) {
|
|
| 366 |
- case options.Generic: |
|
| 367 |
- opaqueConfig, err := options.GenerateFromModel(opt, &configuration{})
|
|
| 368 |
- if err != nil {
|
|
| 369 |
- return err |
|
| 370 |
- } |
|
| 371 |
- config = *opaqueConfig.(*configuration) |
|
| 372 |
- case *configuration: |
|
| 373 |
- config = *opt |
|
| 374 |
- case nil: |
|
| 375 |
- // No GenericData option set. Use defaults. |
|
| 376 |
- default: |
|
| 377 |
- return &ErrInvalidDriverConfig{}
|
|
| 378 |
- } |
|
| 379 |
- |
|
| 380 |
- if config.EnableIPTables || config.EnableIP6Tables {
|
|
| 381 |
- if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
|
|
| 382 |
- if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
|
|
| 383 |
- log.G(context.TODO()).Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
|
|
| 384 |
- } |
|
| 385 |
- } |
|
| 386 |
- } |
|
| 387 |
- |
|
| 388 |
- if config.EnableIPTables {
|
|
| 389 |
- removeIPChains(iptables.IPv4) |
|
| 390 |
- |
|
| 391 |
- natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config, iptables.IPv4) |
|
| 392 |
- if err != nil {
|
|
| 393 |
- return err |
|
| 394 |
- } |
|
| 395 |
- |
|
| 396 |
- // Make sure on firewall reload, first thing being re-played is chains creation |
|
| 397 |
- iptables.OnReloaded(func() {
|
|
| 398 |
- log.G(context.TODO()).Debugf("Recreating iptables chains on firewall reload")
|
|
| 399 |
- if _, _, _, _, err := setupIPChains(config, iptables.IPv4); err != nil {
|
|
| 400 |
- log.G(context.TODO()).WithError(err).Error("Error reloading iptables chains")
|
|
| 401 |
- } |
|
| 402 |
- }) |
|
| 403 |
- } |
|
| 404 |
- |
|
| 405 |
- if config.EnableIP6Tables {
|
|
| 406 |
- removeIPChains(iptables.IPv6) |
|
| 407 |
- |
|
| 408 |
- natChainV6, filterChainV6, isolationChain1V6, isolationChain2V6, err = setupIPChains(config, iptables.IPv6) |
|
| 409 |
- if err != nil {
|
|
| 410 |
- return err |
|
| 411 |
- } |
|
| 412 |
- |
|
| 413 |
- // Make sure on firewall reload, first thing being re-played is chains creation |
|
| 414 |
- iptables.OnReloaded(func() {
|
|
| 415 |
- log.G(context.TODO()).Debugf("Recreating ip6tables chains on firewall reload")
|
|
| 416 |
- if _, _, _, _, err := setupIPChains(config, iptables.IPv6); err != nil {
|
|
| 417 |
- log.G(context.TODO()).WithError(err).Error("Error reloading ip6tables chains")
|
|
| 418 |
- } |
|
| 419 |
- }) |
|
| 420 |
- } |
|
| 421 |
- |
|
| 422 |
- if config.EnableIPForwarding {
|
|
| 423 |
- err = setupIPForwarding(config.EnableIPTables, config.EnableIP6Tables) |
|
| 424 |
- if err != nil {
|
|
| 425 |
- log.G(context.TODO()).Warn(err) |
|
| 426 |
- return err |
|
| 427 |
- } |
|
| 428 |
- } |
|
| 429 |
- |
|
| 430 |
- d.Lock() |
|
| 431 |
- d.natChain = natChain |
|
| 432 |
- d.filterChain = filterChain |
|
| 433 |
- d.isolationChain1 = isolationChain1 |
|
| 434 |
- d.isolationChain2 = isolationChain2 |
|
| 435 |
- d.natChainV6 = natChainV6 |
|
| 436 |
- d.filterChainV6 = filterChainV6 |
|
| 437 |
- d.isolationChain1V6 = isolationChain1V6 |
|
| 438 |
- d.isolationChain2V6 = isolationChain2V6 |
|
| 439 |
- d.config = config |
|
| 440 |
- d.Unlock() |
|
| 441 |
- |
|
| 442 |
- return d.initStore(option) |
|
| 443 |
-} |
|
| 444 |
- |
|
| 445 |
-func (d *driver) getNetwork(id string) (*bridgeNetwork, error) {
|
|
| 446 |
- d.Lock() |
|
| 447 |
- defer d.Unlock() |
|
| 448 |
- |
|
| 449 |
- if id == "" {
|
|
| 450 |
- return nil, types.BadRequestErrorf("invalid network id: %s", id)
|
|
| 451 |
- } |
|
| 452 |
- |
|
| 453 |
- if nw, ok := d.networks[id]; ok {
|
|
| 454 |
- return nw, nil |
|
| 455 |
- } |
|
| 456 |
- |
|
| 457 |
- return nil, types.NotFoundErrorf("network not found: %s", id)
|
|
| 458 |
-} |
|
| 459 |
- |
|
| 460 |
-func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) {
|
|
| 461 |
- var ( |
|
| 462 |
- err error |
|
| 463 |
- config *networkConfiguration |
|
| 464 |
- ) |
|
| 465 |
- |
|
| 466 |
- switch opt := data.(type) {
|
|
| 467 |
- case *networkConfiguration: |
|
| 468 |
- config = opt |
|
| 469 |
- case map[string]string: |
|
| 470 |
- config = &networkConfiguration{
|
|
| 471 |
- EnableICC: true, |
|
| 472 |
- EnableIPMasquerade: true, |
|
| 473 |
- } |
|
| 474 |
- err = config.fromLabels(opt) |
|
| 475 |
- case options.Generic: |
|
| 476 |
- var opaqueConfig interface{}
|
|
| 477 |
- if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
|
|
| 478 |
- config = opaqueConfig.(*networkConfiguration) |
|
| 479 |
- } |
|
| 480 |
- default: |
|
| 481 |
- err = types.BadRequestErrorf("do not recognize network configuration format: %T", opt)
|
|
| 482 |
- } |
|
| 483 |
- |
|
| 484 |
- return config, err |
|
| 485 |
-} |
|
| 486 |
- |
|
| 487 |
-func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
|
| 488 |
- if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 {
|
|
| 489 |
- return types.ForbiddenErrorf("bridge driver doesn't support multiple subnets")
|
|
| 490 |
- } |
|
| 491 |
- |
|
| 492 |
- if len(ipamV4Data) == 0 {
|
|
| 493 |
- return types.BadRequestErrorf("bridge network %s requires ipv4 configuration", id)
|
|
| 494 |
- } |
|
| 495 |
- |
|
| 496 |
- if ipamV4Data[0].Gateway != nil {
|
|
| 497 |
- c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway) |
|
| 498 |
- } |
|
| 499 |
- |
|
| 500 |
- if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok {
|
|
| 501 |
- c.DefaultGatewayIPv4 = gw.IP |
|
| 502 |
- } |
|
| 503 |
- |
|
| 504 |
- if len(ipamV6Data) > 0 {
|
|
| 505 |
- c.AddressIPv6 = ipamV6Data[0].Pool |
|
| 506 |
- |
|
| 507 |
- if ipamV6Data[0].Gateway != nil {
|
|
| 508 |
- c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway) |
|
| 509 |
- } |
|
| 510 |
- |
|
| 511 |
- if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok {
|
|
| 512 |
- c.DefaultGatewayIPv6 = gw.IP |
|
| 513 |
- } |
|
| 514 |
- } |
|
| 515 |
- |
|
| 516 |
- return nil |
|
| 517 |
-} |
|
| 518 |
- |
|
| 519 |
-func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) {
|
|
| 520 |
- var ( |
|
| 521 |
- err error |
|
| 522 |
- config = &networkConfiguration{}
|
|
| 523 |
- ) |
|
| 524 |
- |
|
| 525 |
- // Parse generic label first, config will be re-assigned |
|
| 526 |
- if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
|
|
| 527 |
- if config, err = parseNetworkGenericOptions(genData); err != nil {
|
|
| 528 |
- return nil, err |
|
| 529 |
- } |
|
| 530 |
- } |
|
| 531 |
- |
|
| 532 |
- // Process well-known labels next |
|
| 533 |
- if val, ok := option[netlabel.EnableIPv6]; ok {
|
|
| 534 |
- config.EnableIPv6 = val.(bool) |
|
| 535 |
- } |
|
| 536 |
- |
|
| 537 |
- if val, ok := option[netlabel.Internal]; ok {
|
|
| 538 |
- if internal, ok := val.(bool); ok && internal {
|
|
| 539 |
- config.Internal = true |
|
| 540 |
- } |
|
| 541 |
- } |
|
| 542 |
- |
|
| 543 |
- // Finally validate the configuration |
|
| 544 |
- if err = config.Validate(); err != nil {
|
|
| 545 |
- return nil, err |
|
| 546 |
- } |
|
| 547 |
- |
|
| 548 |
- if config.BridgeName == "" && !config.DefaultBridge {
|
|
| 549 |
- config.BridgeName = "br-" + id[:12] |
|
| 550 |
- } |
|
| 551 |
- |
|
| 552 |
- exists, err := bridgeInterfaceExists(config.BridgeName) |
|
| 553 |
- if err != nil {
|
|
| 554 |
- return nil, err |
|
| 555 |
- } |
|
| 556 |
- |
|
| 557 |
- if !exists {
|
|
| 558 |
- config.BridgeIfaceCreator = ifaceCreatedByLibnetwork |
|
| 559 |
- } else {
|
|
| 560 |
- config.BridgeIfaceCreator = ifaceCreatedByUser |
|
| 561 |
- } |
|
| 562 |
- |
|
| 563 |
- config.ID = id |
|
| 564 |
- return config, nil |
|
| 565 |
-} |
|
| 566 |
- |
|
| 567 |
-// Return a slice of networks over which caller can iterate safely |
|
| 568 |
-func (d *driver) getNetworks() []*bridgeNetwork {
|
|
| 569 |
- d.Lock() |
|
| 570 |
- defer d.Unlock() |
|
| 571 |
- |
|
| 572 |
- ls := make([]*bridgeNetwork, 0, len(d.networks)) |
|
| 573 |
- for _, nw := range d.networks {
|
|
| 574 |
- ls = append(ls, nw) |
|
| 575 |
- } |
|
| 576 |
- return ls |
|
| 577 |
-} |
|
| 578 |
- |
|
| 579 |
-func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
|
| 580 |
- return nil, types.NotImplementedErrorf("not implemented")
|
|
| 581 |
-} |
|
| 582 |
- |
|
| 583 |
-func (d *driver) NetworkFree(id string) error {
|
|
| 584 |
- return types.NotImplementedErrorf("not implemented")
|
|
| 585 |
-} |
|
| 586 |
- |
|
| 587 |
-func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
|
| 588 |
-} |
|
| 589 |
- |
|
| 590 |
-func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
|
|
| 591 |
- return "", nil |
|
| 592 |
-} |
|
| 593 |
- |
|
| 594 |
-// Create a new network using bridge plugin |
|
| 595 |
-func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
|
| 596 |
- if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
|
|
| 597 |
- return types.BadRequestErrorf("ipv4 pool is empty")
|
|
| 598 |
- } |
|
| 599 |
- // Sanity checks |
|
| 600 |
- d.Lock() |
|
| 601 |
- if _, ok := d.networks[id]; ok {
|
|
| 602 |
- d.Unlock() |
|
| 603 |
- return types.ForbiddenErrorf("network %s exists", id)
|
|
| 604 |
- } |
|
| 605 |
- d.Unlock() |
|
| 606 |
- |
|
| 607 |
- // Parse and validate the config. It should not be conflict with existing networks' config |
|
| 608 |
- config, err := parseNetworkOptions(id, option) |
|
| 609 |
- if err != nil {
|
|
| 610 |
- return err |
|
| 611 |
- } |
|
| 612 |
- |
|
| 613 |
- if err = config.processIPAM(id, ipV4Data, ipV6Data); err != nil {
|
|
| 614 |
- return err |
|
| 615 |
- } |
|
| 616 |
- |
|
| 617 |
- // start the critical section, from this point onward we are dealing with the list of networks |
|
| 618 |
- // so to be consistent we cannot allow that the list changes |
|
| 619 |
- d.configNetwork.Lock() |
|
| 620 |
- defer d.configNetwork.Unlock() |
|
| 621 |
- |
|
| 622 |
- // check network conflicts |
|
| 623 |
- if err = d.checkConflict(config); err != nil {
|
|
| 624 |
- nerr, ok := err.(defaultBridgeNetworkConflict) |
|
| 625 |
- if !ok {
|
|
| 626 |
- return err |
|
| 627 |
- } |
|
| 628 |
- // Got a conflict with a stale default network, clean that up and continue |
|
| 629 |
- log.G(context.TODO()).Warn(nerr) |
|
| 630 |
- if err := d.deleteNetwork(nerr.ID); err != nil {
|
|
| 631 |
- log.G(context.TODO()).WithError(err).Debug("Error while cleaning up network on conflict")
|
|
| 632 |
- } |
|
| 633 |
- } |
|
| 634 |
- |
|
| 635 |
- // there is no conflict, now create the network |
|
| 636 |
- if err = d.createNetwork(config); err != nil {
|
|
| 637 |
- return err |
|
| 638 |
- } |
|
| 639 |
- |
|
| 640 |
- return d.storeUpdate(config) |
|
| 641 |
-} |
|
| 642 |
- |
|
| 643 |
-func (d *driver) checkConflict(config *networkConfiguration) error {
|
|
| 644 |
- networkList := d.getNetworks() |
|
| 645 |
- for _, nw := range networkList {
|
|
| 646 |
- nw.Lock() |
|
| 647 |
- nwConfig := nw.config |
|
| 648 |
- nw.Unlock() |
|
| 649 |
- if err := nwConfig.Conflicts(config); err != nil {
|
|
| 650 |
- if nwConfig.DefaultBridge {
|
|
| 651 |
- // We encountered and identified a stale default network |
|
| 652 |
- // We must delete it as libnetwork is the source of truth |
|
| 653 |
- // The default network being created must be the only one |
|
| 654 |
- // This can happen only from docker 1.12 on ward |
|
| 655 |
- log.G(context.TODO()).Infof("Found stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName)
|
|
| 656 |
- return defaultBridgeNetworkConflict{nwConfig.ID}
|
|
| 657 |
- } |
|
| 658 |
- |
|
| 659 |
- return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s",
|
|
| 660 |
- config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error()) |
|
| 661 |
- } |
|
| 662 |
- } |
|
| 663 |
- return nil |
|
| 664 |
-} |
|
| 665 |
- |
|
| 666 |
-func (d *driver) createNetwork(config *networkConfiguration) (err error) {
|
|
| 667 |
- // Initialize handle when needed |
|
| 668 |
- d.Lock() |
|
| 669 |
- if d.nlh == nil {
|
|
| 670 |
- d.nlh = ns.NlHandle() |
|
| 671 |
- } |
|
| 672 |
- d.Unlock() |
|
| 673 |
- |
|
| 674 |
- // Create or retrieve the bridge L3 interface |
|
| 675 |
- bridgeIface, err := newInterface(d.nlh, config) |
|
| 676 |
- if err != nil {
|
|
| 677 |
- return err |
|
| 678 |
- } |
|
| 679 |
- |
|
| 680 |
- // Create and set network handler in driver |
|
| 681 |
- network := &bridgeNetwork{
|
|
| 682 |
- id: config.ID, |
|
| 683 |
- endpoints: make(map[string]*bridgeEndpoint), |
|
| 684 |
- config: config, |
|
| 685 |
- portMapper: portmapper.NewWithPortAllocator(d.portAllocator, d.config.UserlandProxyPath), |
|
| 686 |
- portMapperV6: portmapper.NewWithPortAllocator(d.portAllocator, d.config.UserlandProxyPath), |
|
| 687 |
- bridge: bridgeIface, |
|
| 688 |
- driver: d, |
|
| 689 |
- } |
|
| 690 |
- |
|
| 691 |
- d.Lock() |
|
| 692 |
- d.networks[config.ID] = network |
|
| 693 |
- d.Unlock() |
|
| 694 |
- |
|
| 695 |
- // On failure make sure to reset driver network handler to nil |
|
| 696 |
- defer func() {
|
|
| 697 |
- if err != nil {
|
|
| 698 |
- d.Lock() |
|
| 699 |
- delete(d.networks, config.ID) |
|
| 700 |
- d.Unlock() |
|
| 701 |
- } |
|
| 702 |
- }() |
|
| 703 |
- |
|
| 704 |
- // Add inter-network communication rules. |
|
| 705 |
- setupNetworkIsolationRules := func(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 706 |
- if err := network.isolateNetwork(true); err != nil {
|
|
| 707 |
- if err = network.isolateNetwork(false); err != nil {
|
|
| 708 |
- log.G(context.TODO()).Warnf("Failed on removing the inter-network iptables rules on cleanup: %v", err)
|
|
| 709 |
- } |
|
| 710 |
- return err |
|
| 711 |
- } |
|
| 712 |
- // register the cleanup function |
|
| 713 |
- network.registerIptCleanFunc(func() error {
|
|
| 714 |
- return network.isolateNetwork(false) |
|
| 715 |
- }) |
|
| 716 |
- return nil |
|
| 717 |
- } |
|
| 718 |
- |
|
| 719 |
- // Prepare the bridge setup configuration |
|
| 720 |
- bridgeSetup := newBridgeSetup(config, bridgeIface) |
|
| 721 |
- |
|
| 722 |
- // If the bridge interface doesn't exist, we need to start the setup steps |
|
| 723 |
- // by creating a new device and assigning it an IPv4 address. |
|
| 724 |
- bridgeAlreadyExists := bridgeIface.exists() |
|
| 725 |
- if !bridgeAlreadyExists {
|
|
| 726 |
- bridgeSetup.queueStep(setupDevice) |
|
| 727 |
- bridgeSetup.queueStep(setupDefaultSysctl) |
|
| 728 |
- } |
|
| 729 |
- |
|
| 730 |
- // For the default bridge, set expected sysctls |
|
| 731 |
- if config.DefaultBridge {
|
|
| 732 |
- bridgeSetup.queueStep(setupDefaultSysctl) |
|
| 733 |
- } |
|
| 734 |
- |
|
| 735 |
- // Even if a bridge exists try to setup IPv4. |
|
| 736 |
- bridgeSetup.queueStep(setupBridgeIPv4) |
|
| 737 |
- |
|
| 738 |
- enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil |
|
| 739 |
- |
|
| 740 |
- // Conditionally queue setup steps depending on configuration values. |
|
| 741 |
- for _, step := range []struct {
|
|
| 742 |
- Condition bool |
|
| 743 |
- Fn setupStep |
|
| 744 |
- }{
|
|
| 745 |
- // Enable IPv6 on the bridge if required. We do this even for a |
|
| 746 |
- // previously existing bridge, as it may be here from a previous |
|
| 747 |
- // installation where IPv6 wasn't supported yet and needs to be |
|
| 748 |
- // assigned an IPv6 link-local address. |
|
| 749 |
- {config.EnableIPv6, setupBridgeIPv6},
|
|
| 750 |
- |
|
| 751 |
- // We ensure that the bridge has the expectedIPv4 and IPv6 addresses in |
|
| 752 |
- // the case of a previously existing device. |
|
| 753 |
- {bridgeAlreadyExists && !config.InhibitIPv4, setupVerifyAndReconcile},
|
|
| 754 |
- |
|
| 755 |
- // Enable IPv6 Forwarding |
|
| 756 |
- {enableIPv6Forwarding, setupIPv6Forwarding},
|
|
| 757 |
- |
|
| 758 |
- // Setup Loopback Addresses Routing |
|
| 759 |
- {!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting},
|
|
| 760 |
- |
|
| 761 |
- // Setup IPTables. |
|
| 762 |
- {d.config.EnableIPTables, network.setupIP4Tables},
|
|
| 763 |
- |
|
| 764 |
- // Setup IP6Tables. |
|
| 765 |
- {config.EnableIPv6 && d.config.EnableIP6Tables, network.setupIP6Tables},
|
|
| 766 |
- |
|
| 767 |
- // We want to track firewalld configuration so that |
|
| 768 |
- // if it is started/reloaded, the rules can be applied correctly |
|
| 769 |
- {d.config.EnableIPTables, network.setupFirewalld},
|
|
| 770 |
- // same for IPv6 |
|
| 771 |
- {config.EnableIPv6 && d.config.EnableIP6Tables, network.setupFirewalld6},
|
|
| 772 |
- |
|
| 773 |
- // Setup DefaultGatewayIPv4 |
|
| 774 |
- {config.DefaultGatewayIPv4 != nil, setupGatewayIPv4},
|
|
| 775 |
- |
|
| 776 |
- // Setup DefaultGatewayIPv6 |
|
| 777 |
- {config.DefaultGatewayIPv6 != nil, setupGatewayIPv6},
|
|
| 778 |
- |
|
| 779 |
- // Add inter-network communication rules. |
|
| 780 |
- {d.config.EnableIPTables, setupNetworkIsolationRules},
|
|
| 781 |
- |
|
| 782 |
- // Configure bridge networking filtering if ICC is off and IP tables are enabled |
|
| 783 |
- {!config.EnableICC && d.config.EnableIPTables, setupBridgeNetFiltering},
|
|
| 784 |
- } {
|
|
| 785 |
- if step.Condition {
|
|
| 786 |
- bridgeSetup.queueStep(step.Fn) |
|
| 787 |
- } |
|
| 788 |
- } |
|
| 789 |
- |
|
| 790 |
- // Apply the prepared list of steps, and abort at the first error. |
|
| 791 |
- bridgeSetup.queueStep(setupDeviceUp) |
|
| 792 |
- return bridgeSetup.apply() |
|
| 793 |
-} |
|
| 794 |
- |
|
| 795 |
-func (d *driver) DeleteNetwork(nid string) error {
|
|
| 796 |
- d.configNetwork.Lock() |
|
| 797 |
- defer d.configNetwork.Unlock() |
|
| 798 |
- |
|
| 799 |
- return d.deleteNetwork(nid) |
|
| 800 |
-} |
|
| 801 |
- |
|
| 802 |
-func (d *driver) deleteNetwork(nid string) error {
|
|
| 803 |
- var err error |
|
| 804 |
- |
|
| 805 |
- // Get network handler and remove it from driver |
|
| 806 |
- d.Lock() |
|
| 807 |
- n, ok := d.networks[nid] |
|
| 808 |
- d.Unlock() |
|
| 809 |
- |
|
| 810 |
- if !ok {
|
|
| 811 |
- return types.InternalMaskableErrorf("network %s does not exist", nid)
|
|
| 812 |
- } |
|
| 813 |
- |
|
| 814 |
- n.Lock() |
|
| 815 |
- config := n.config |
|
| 816 |
- n.Unlock() |
|
| 817 |
- |
|
| 818 |
- // delele endpoints belong to this network |
|
| 819 |
- for _, ep := range n.endpoints {
|
|
| 820 |
- if err := n.releasePorts(ep); err != nil {
|
|
| 821 |
- log.G(context.TODO()).Warn(err) |
|
| 822 |
- } |
|
| 823 |
- if link, err := d.nlh.LinkByName(ep.srcName); err == nil {
|
|
| 824 |
- if err := d.nlh.LinkDel(link); err != nil {
|
|
| 825 |
- log.G(context.TODO()).WithError(err).Errorf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id)
|
|
| 826 |
- } |
|
| 827 |
- } |
|
| 828 |
- |
|
| 829 |
- if err := d.storeDelete(ep); err != nil {
|
|
| 830 |
- log.G(context.TODO()).Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err)
|
|
| 831 |
- } |
|
| 832 |
- } |
|
| 833 |
- |
|
| 834 |
- d.Lock() |
|
| 835 |
- delete(d.networks, nid) |
|
| 836 |
- d.Unlock() |
|
| 837 |
- |
|
| 838 |
- // On failure set network handler back in driver, but |
|
| 839 |
- // only if is not already taken over by some other thread |
|
| 840 |
- defer func() {
|
|
| 841 |
- if err != nil {
|
|
| 842 |
- d.Lock() |
|
| 843 |
- if _, ok := d.networks[nid]; !ok {
|
|
| 844 |
- d.networks[nid] = n |
|
| 845 |
- } |
|
| 846 |
- d.Unlock() |
|
| 847 |
- } |
|
| 848 |
- }() |
|
| 849 |
- |
|
| 850 |
- switch config.BridgeIfaceCreator {
|
|
| 851 |
- case ifaceCreatedByLibnetwork, ifaceCreatorUnknown: |
|
| 852 |
- // We only delete the bridge if it was created by the bridge driver and |
|
| 853 |
- // it is not the default one (to keep the backward compatible behavior.) |
|
| 854 |
- if !config.DefaultBridge {
|
|
| 855 |
- if err := d.nlh.LinkDel(n.bridge.Link); err != nil {
|
|
| 856 |
- log.G(context.TODO()).Warnf("Failed to remove bridge interface %s on network %s delete: %v", config.BridgeName, nid, err)
|
|
| 857 |
- } |
|
| 858 |
- } |
|
| 859 |
- case ifaceCreatedByUser: |
|
| 860 |
- // Don't delete the bridge interface if it was not created by libnetwork. |
|
| 861 |
- } |
|
| 862 |
- |
|
| 863 |
- // clean all relevant iptables rules |
|
| 864 |
- for _, cleanFunc := range n.iptCleanFuncs {
|
|
| 865 |
- if errClean := cleanFunc(); errClean != nil {
|
|
| 866 |
- log.G(context.TODO()).Warnf("Failed to clean iptables rules for bridge network: %v", errClean)
|
|
| 867 |
- } |
|
| 868 |
- } |
|
| 869 |
- return d.storeDelete(config) |
|
| 870 |
-} |
|
| 871 |
- |
|
| 872 |
-func addToBridge(nlh *netlink.Handle, ifaceName, bridgeName string) error {
|
|
| 873 |
- lnk, err := nlh.LinkByName(ifaceName) |
|
| 874 |
- if err != nil {
|
|
| 875 |
- return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
|
|
| 876 |
- } |
|
| 877 |
- if err := nlh.LinkSetMaster(lnk, &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridgeName}}); err != nil {
|
|
| 878 |
- log.G(context.TODO()).WithError(err).Errorf("Failed to add %s to bridge via netlink", ifaceName)
|
|
| 879 |
- return err |
|
| 880 |
- } |
|
| 881 |
- return nil |
|
| 882 |
-} |
|
| 883 |
- |
|
| 884 |
-func setHairpinMode(nlh *netlink.Handle, link netlink.Link, enable bool) error {
|
|
| 885 |
- err := nlh.LinkSetHairpin(link, enable) |
|
| 886 |
- if err != nil {
|
|
| 887 |
- return fmt.Errorf("unable to set hairpin mode on %s via netlink: %v",
|
|
| 888 |
- link.Attrs().Name, err) |
|
| 889 |
- } |
|
| 890 |
- return nil |
|
| 891 |
-} |
|
| 892 |
- |
|
| 893 |
-func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
|
| 894 |
- if ifInfo == nil {
|
|
| 895 |
- return errors.New("invalid interface info passed")
|
|
| 896 |
- } |
|
| 897 |
- |
|
| 898 |
- // Get the network handler and make sure it exists |
|
| 899 |
- d.Lock() |
|
| 900 |
- n, ok := d.networks[nid] |
|
| 901 |
- dconfig := d.config |
|
| 902 |
- d.Unlock() |
|
| 903 |
- |
|
| 904 |
- if !ok {
|
|
| 905 |
- return types.NotFoundErrorf("network %s does not exist", nid)
|
|
| 906 |
- } |
|
| 907 |
- if n == nil {
|
|
| 908 |
- return driverapi.ErrNoNetwork(nid) |
|
| 909 |
- } |
|
| 910 |
- |
|
| 911 |
- // Sanity check |
|
| 912 |
- n.Lock() |
|
| 913 |
- if n.id != nid {
|
|
| 914 |
- n.Unlock() |
|
| 915 |
- return InvalidNetworkIDError(nid) |
|
| 916 |
- } |
|
| 917 |
- n.Unlock() |
|
| 918 |
- |
|
| 919 |
- // Check if endpoint id is good and retrieve correspondent endpoint |
|
| 920 |
- ep, err := n.getEndpoint(eid) |
|
| 921 |
- if err != nil {
|
|
| 922 |
- return err |
|
| 923 |
- } |
|
| 924 |
- |
|
| 925 |
- // Endpoint with that id exists either on desired or other sandbox |
|
| 926 |
- if ep != nil {
|
|
| 927 |
- return driverapi.ErrEndpointExists(eid) |
|
| 928 |
- } |
|
| 929 |
- |
|
| 930 |
- // Try to convert the options to endpoint configuration |
|
| 931 |
- epConfig, err := parseEndpointOptions(epOptions) |
|
| 932 |
- if err != nil {
|
|
| 933 |
- return err |
|
| 934 |
- } |
|
| 935 |
- |
|
| 936 |
- // Create and add the endpoint |
|
| 937 |
- n.Lock() |
|
| 938 |
- endpoint := &bridgeEndpoint{id: eid, nid: nid, config: epConfig}
|
|
| 939 |
- n.endpoints[eid] = endpoint |
|
| 940 |
- n.Unlock() |
|
| 941 |
- |
|
| 942 |
- // On failure make sure to remove the endpoint |
|
| 943 |
- defer func() {
|
|
| 944 |
- if err != nil {
|
|
| 945 |
- n.Lock() |
|
| 946 |
- delete(n.endpoints, eid) |
|
| 947 |
- n.Unlock() |
|
| 948 |
- } |
|
| 949 |
- }() |
|
| 950 |
- |
|
| 951 |
- // Generate a name for what will be the host side pipe interface |
|
| 952 |
- hostIfName, err := netutils.GenerateIfaceName(d.nlh, vethPrefix, vethLen) |
|
| 953 |
- if err != nil {
|
|
| 954 |
- return err |
|
| 955 |
- } |
|
| 956 |
- |
|
| 957 |
- // Generate a name for what will be the sandbox side pipe interface |
|
| 958 |
- containerIfName, err := netutils.GenerateIfaceName(d.nlh, vethPrefix, vethLen) |
|
| 959 |
- if err != nil {
|
|
| 960 |
- return err |
|
| 961 |
- } |
|
| 962 |
- |
|
| 963 |
- // Generate and add the interface pipe host <-> sandbox |
|
| 964 |
- veth := &netlink.Veth{
|
|
| 965 |
- LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0},
|
|
| 966 |
- PeerName: containerIfName, |
|
| 967 |
- } |
|
| 968 |
- if err = d.nlh.LinkAdd(veth); err != nil {
|
|
| 969 |
- return types.InternalErrorf("failed to add the host (%s) <=> sandbox (%s) pair interfaces: %v", hostIfName, containerIfName, err)
|
|
| 970 |
- } |
|
| 971 |
- |
|
| 972 |
- // Get the host side pipe interface handler |
|
| 973 |
- host, err := d.nlh.LinkByName(hostIfName) |
|
| 974 |
- if err != nil {
|
|
| 975 |
- return types.InternalErrorf("failed to find host side interface %s: %v", hostIfName, err)
|
|
| 976 |
- } |
|
| 977 |
- defer func() {
|
|
| 978 |
- if err != nil {
|
|
| 979 |
- if err := d.nlh.LinkDel(host); err != nil {
|
|
| 980 |
- log.G(context.TODO()).WithError(err).Warnf("Failed to delete host side interface (%s)'s link", hostIfName)
|
|
| 981 |
- } |
|
| 982 |
- } |
|
| 983 |
- }() |
|
| 984 |
- |
|
| 985 |
- // Get the sandbox side pipe interface handler |
|
| 986 |
- sbox, err := d.nlh.LinkByName(containerIfName) |
|
| 987 |
- if err != nil {
|
|
| 988 |
- return types.InternalErrorf("failed to find sandbox side interface %s: %v", containerIfName, err)
|
|
| 989 |
- } |
|
| 990 |
- defer func() {
|
|
| 991 |
- if err != nil {
|
|
| 992 |
- if err := d.nlh.LinkDel(sbox); err != nil {
|
|
| 993 |
- log.G(context.TODO()).WithError(err).Warnf("Failed to delete sandbox side interface (%s)'s link", containerIfName)
|
|
| 994 |
- } |
|
| 995 |
- } |
|
| 996 |
- }() |
|
| 997 |
- |
|
| 998 |
- n.Lock() |
|
| 999 |
- config := n.config |
|
| 1000 |
- n.Unlock() |
|
| 1001 |
- |
|
| 1002 |
- // Add bridge inherited attributes to pipe interfaces |
|
| 1003 |
- if config.Mtu != 0 {
|
|
| 1004 |
- err = d.nlh.LinkSetMTU(host, config.Mtu) |
|
| 1005 |
- if err != nil {
|
|
| 1006 |
- return types.InternalErrorf("failed to set MTU on host interface %s: %v", hostIfName, err)
|
|
| 1007 |
- } |
|
| 1008 |
- err = d.nlh.LinkSetMTU(sbox, config.Mtu) |
|
| 1009 |
- if err != nil {
|
|
| 1010 |
- return types.InternalErrorf("failed to set MTU on sandbox interface %s: %v", containerIfName, err)
|
|
| 1011 |
- } |
|
| 1012 |
- } |
|
| 1013 |
- |
|
| 1014 |
- // Attach host side pipe interface into the bridge |
|
| 1015 |
- if err = addToBridge(d.nlh, hostIfName, config.BridgeName); err != nil {
|
|
| 1016 |
- return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
|
|
| 1017 |
- } |
|
| 1018 |
- |
|
| 1019 |
- if !dconfig.EnableUserlandProxy {
|
|
| 1020 |
- err = setHairpinMode(d.nlh, host, true) |
|
| 1021 |
- if err != nil {
|
|
| 1022 |
- return err |
|
| 1023 |
- } |
|
| 1024 |
- } |
|
| 1025 |
- |
|
| 1026 |
- // Store the sandbox side pipe interface parameters |
|
| 1027 |
- endpoint.srcName = containerIfName |
|
| 1028 |
- endpoint.macAddress = ifInfo.MacAddress() |
|
| 1029 |
- endpoint.addr = ifInfo.Address() |
|
| 1030 |
- endpoint.addrv6 = ifInfo.AddressIPv6() |
|
| 1031 |
- |
|
| 1032 |
- // Set the sbox's MAC if not provided. If specified, use the one configured by user, otherwise generate one based on IP. |
|
| 1033 |
- if endpoint.macAddress == nil {
|
|
| 1034 |
- endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP) |
|
| 1035 |
- if err = ifInfo.SetMacAddress(endpoint.macAddress); err != nil {
|
|
| 1036 |
- return err |
|
| 1037 |
- } |
|
| 1038 |
- } |
|
| 1039 |
- |
|
| 1040 |
- // Up the host interface after finishing all netlink configuration |
|
| 1041 |
- if err = d.nlh.LinkSetUp(host); err != nil {
|
|
| 1042 |
- return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
|
|
| 1043 |
- } |
|
| 1044 |
- |
|
| 1045 |
- if endpoint.addrv6 == nil && config.EnableIPv6 {
|
|
| 1046 |
- var ip6 net.IP |
|
| 1047 |
- network := n.bridge.bridgeIPv6 |
|
| 1048 |
- if config.AddressIPv6 != nil {
|
|
| 1049 |
- network = config.AddressIPv6 |
|
| 1050 |
- } |
|
| 1051 |
- |
|
| 1052 |
- ones, _ := network.Mask.Size() |
|
| 1053 |
- if ones > 80 {
|
|
| 1054 |
- err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
|
|
| 1055 |
- return err |
|
| 1056 |
- } |
|
| 1057 |
- |
|
| 1058 |
- ip6 = make(net.IP, len(network.IP)) |
|
| 1059 |
- copy(ip6, network.IP) |
|
| 1060 |
- for i, h := range endpoint.macAddress {
|
|
| 1061 |
- ip6[i+10] = h |
|
| 1062 |
- } |
|
| 1063 |
- |
|
| 1064 |
- endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
|
|
| 1065 |
- if err = ifInfo.SetIPAddress(endpoint.addrv6); err != nil {
|
|
| 1066 |
- return err |
|
| 1067 |
- } |
|
| 1068 |
- } |
|
| 1069 |
- |
|
| 1070 |
- if err = d.storeUpdate(endpoint); err != nil {
|
|
| 1071 |
- return fmt.Errorf("failed to save bridge endpoint %.7s to store: %v", endpoint.id, err)
|
|
| 1072 |
- } |
|
| 1073 |
- |
|
| 1074 |
- return nil |
|
| 1075 |
-} |
|
| 1076 |
- |
|
| 1077 |
-func (d *driver) DeleteEndpoint(nid, eid string) error {
|
|
| 1078 |
- var err error |
|
| 1079 |
- |
|
| 1080 |
- // Get the network handler and make sure it exists |
|
| 1081 |
- d.Lock() |
|
| 1082 |
- n, ok := d.networks[nid] |
|
| 1083 |
- d.Unlock() |
|
| 1084 |
- |
|
| 1085 |
- if !ok {
|
|
| 1086 |
- return types.InternalMaskableErrorf("network %s does not exist", nid)
|
|
| 1087 |
- } |
|
| 1088 |
- if n == nil {
|
|
| 1089 |
- return driverapi.ErrNoNetwork(nid) |
|
| 1090 |
- } |
|
| 1091 |
- |
|
| 1092 |
- // Sanity Check |
|
| 1093 |
- n.Lock() |
|
| 1094 |
- if n.id != nid {
|
|
| 1095 |
- n.Unlock() |
|
| 1096 |
- return InvalidNetworkIDError(nid) |
|
| 1097 |
- } |
|
| 1098 |
- n.Unlock() |
|
| 1099 |
- |
|
| 1100 |
- // Check endpoint id and if an endpoint is actually there |
|
| 1101 |
- ep, err := n.getEndpoint(eid) |
|
| 1102 |
- if err != nil {
|
|
| 1103 |
- return err |
|
| 1104 |
- } |
|
| 1105 |
- if ep == nil {
|
|
| 1106 |
- return EndpointNotFoundError(eid) |
|
| 1107 |
- } |
|
| 1108 |
- |
|
| 1109 |
- // Remove it |
|
| 1110 |
- n.Lock() |
|
| 1111 |
- delete(n.endpoints, eid) |
|
| 1112 |
- n.Unlock() |
|
| 1113 |
- |
|
| 1114 |
- // On failure make sure to set back ep in n.endpoints, but only |
|
| 1115 |
- // if it hasn't been taken over already by some other thread. |
|
| 1116 |
- defer func() {
|
|
| 1117 |
- if err != nil {
|
|
| 1118 |
- n.Lock() |
|
| 1119 |
- if _, ok := n.endpoints[eid]; !ok {
|
|
| 1120 |
- n.endpoints[eid] = ep |
|
| 1121 |
- } |
|
| 1122 |
- n.Unlock() |
|
| 1123 |
- } |
|
| 1124 |
- }() |
|
| 1125 |
- |
|
| 1126 |
- // Try removal of link. Discard error: it is a best effort. |
|
| 1127 |
- // Also make sure defer does not see this error either. |
|
| 1128 |
- if link, err := d.nlh.LinkByName(ep.srcName); err == nil {
|
|
| 1129 |
- if err := d.nlh.LinkDel(link); err != nil {
|
|
| 1130 |
- log.G(context.TODO()).WithError(err).Errorf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id)
|
|
| 1131 |
- } |
|
| 1132 |
- } |
|
| 1133 |
- |
|
| 1134 |
- if err := d.storeDelete(ep); err != nil {
|
|
| 1135 |
- log.G(context.TODO()).Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err)
|
|
| 1136 |
- } |
|
| 1137 |
- |
|
| 1138 |
- return nil |
|
| 1139 |
-} |
|
| 1140 |
- |
|
| 1141 |
-func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
|
| 1142 |
- // Get the network handler and make sure it exists |
|
| 1143 |
- d.Lock() |
|
| 1144 |
- n, ok := d.networks[nid] |
|
| 1145 |
- d.Unlock() |
|
| 1146 |
- if !ok {
|
|
| 1147 |
- return nil, types.NotFoundErrorf("network %s does not exist", nid)
|
|
| 1148 |
- } |
|
| 1149 |
- if n == nil {
|
|
| 1150 |
- return nil, driverapi.ErrNoNetwork(nid) |
|
| 1151 |
- } |
|
| 1152 |
- |
|
| 1153 |
- // Sanity check |
|
| 1154 |
- n.Lock() |
|
| 1155 |
- if n.id != nid {
|
|
| 1156 |
- n.Unlock() |
|
| 1157 |
- return nil, InvalidNetworkIDError(nid) |
|
| 1158 |
- } |
|
| 1159 |
- n.Unlock() |
|
| 1160 |
- |
|
| 1161 |
- // Check if endpoint id is good and retrieve correspondent endpoint |
|
| 1162 |
- ep, err := n.getEndpoint(eid) |
|
| 1163 |
- if err != nil {
|
|
| 1164 |
- return nil, err |
|
| 1165 |
- } |
|
| 1166 |
- if ep == nil {
|
|
| 1167 |
- return nil, driverapi.ErrNoEndpoint(eid) |
|
| 1168 |
- } |
|
| 1169 |
- |
|
| 1170 |
- m := make(map[string]interface{})
|
|
| 1171 |
- |
|
| 1172 |
- if ep.extConnConfig != nil && ep.extConnConfig.ExposedPorts != nil {
|
|
| 1173 |
- // Return a copy of the config data |
|
| 1174 |
- epc := make([]types.TransportPort, 0, len(ep.extConnConfig.ExposedPorts)) |
|
| 1175 |
- for _, tp := range ep.extConnConfig.ExposedPorts {
|
|
| 1176 |
- epc = append(epc, tp.GetCopy()) |
|
| 1177 |
- } |
|
| 1178 |
- m[netlabel.ExposedPorts] = epc |
|
| 1179 |
- } |
|
| 1180 |
- |
|
| 1181 |
- if ep.portMapping != nil {
|
|
| 1182 |
- // Return a copy of the operational data |
|
| 1183 |
- pmc := make([]types.PortBinding, 0, len(ep.portMapping)) |
|
| 1184 |
- for _, pm := range ep.portMapping {
|
|
| 1185 |
- pmc = append(pmc, pm.GetCopy()) |
|
| 1186 |
- } |
|
| 1187 |
- m[netlabel.PortMap] = pmc |
|
| 1188 |
- } |
|
| 1189 |
- |
|
| 1190 |
- if len(ep.macAddress) != 0 {
|
|
| 1191 |
- m[netlabel.MacAddress] = ep.macAddress |
|
| 1192 |
- } |
|
| 1193 |
- |
|
| 1194 |
- return m, nil |
|
| 1195 |
-} |
|
| 1196 |
- |
|
| 1197 |
-// Join method is invoked when a Sandbox is attached to an endpoint. |
|
| 1198 |
-func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
|
| 1199 |
- network, err := d.getNetwork(nid) |
|
| 1200 |
- if err != nil {
|
|
| 1201 |
- return err |
|
| 1202 |
- } |
|
| 1203 |
- |
|
| 1204 |
- endpoint, err := network.getEndpoint(eid) |
|
| 1205 |
- if err != nil {
|
|
| 1206 |
- return err |
|
| 1207 |
- } |
|
| 1208 |
- |
|
| 1209 |
- if endpoint == nil {
|
|
| 1210 |
- return EndpointNotFoundError(eid) |
|
| 1211 |
- } |
|
| 1212 |
- |
|
| 1213 |
- endpoint.containerConfig, err = parseContainerOptions(options) |
|
| 1214 |
- if err != nil {
|
|
| 1215 |
- return err |
|
| 1216 |
- } |
|
| 1217 |
- |
|
| 1218 |
- iNames := jinfo.InterfaceName() |
|
| 1219 |
- containerVethPrefix := defaultContainerVethPrefix |
|
| 1220 |
- if network.config.ContainerIfacePrefix != "" {
|
|
| 1221 |
- containerVethPrefix = network.config.ContainerIfacePrefix |
|
| 1222 |
- } |
|
| 1223 |
- err = iNames.SetNames(endpoint.srcName, containerVethPrefix) |
|
| 1224 |
- if err != nil {
|
|
| 1225 |
- return err |
|
| 1226 |
- } |
|
| 1227 |
- |
|
| 1228 |
- err = jinfo.SetGateway(network.bridge.gatewayIPv4) |
|
| 1229 |
- if err != nil {
|
|
| 1230 |
- return err |
|
| 1231 |
- } |
|
| 1232 |
- |
|
| 1233 |
- err = jinfo.SetGatewayIPv6(network.bridge.gatewayIPv6) |
|
| 1234 |
- if err != nil {
|
|
| 1235 |
- return err |
|
| 1236 |
- } |
|
| 1237 |
- |
|
| 1238 |
- return nil |
|
| 1239 |
-} |
|
| 1240 |
- |
|
| 1241 |
-// Leave method is invoked when a Sandbox detaches from an endpoint. |
|
| 1242 |
-func (d *driver) Leave(nid, eid string) error {
|
|
| 1243 |
- network, err := d.getNetwork(nid) |
|
| 1244 |
- if err != nil {
|
|
| 1245 |
- return types.InternalMaskableErrorf("%s", err)
|
|
| 1246 |
- } |
|
| 1247 |
- |
|
| 1248 |
- endpoint, err := network.getEndpoint(eid) |
|
| 1249 |
- if err != nil {
|
|
| 1250 |
- return err |
|
| 1251 |
- } |
|
| 1252 |
- |
|
| 1253 |
- if endpoint == nil {
|
|
| 1254 |
- return EndpointNotFoundError(eid) |
|
| 1255 |
- } |
|
| 1256 |
- |
|
| 1257 |
- if !network.config.EnableICC {
|
|
| 1258 |
- if err = d.link(network, endpoint, false); err != nil {
|
|
| 1259 |
- return err |
|
| 1260 |
- } |
|
| 1261 |
- } |
|
| 1262 |
- |
|
| 1263 |
- return nil |
|
| 1264 |
-} |
|
| 1265 |
- |
|
| 1266 |
-func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
|
|
| 1267 |
- network, err := d.getNetwork(nid) |
|
| 1268 |
- if err != nil {
|
|
| 1269 |
- return err |
|
| 1270 |
- } |
|
| 1271 |
- |
|
| 1272 |
- endpoint, err := network.getEndpoint(eid) |
|
| 1273 |
- if err != nil {
|
|
| 1274 |
- return err |
|
| 1275 |
- } |
|
| 1276 |
- |
|
| 1277 |
- if endpoint == nil {
|
|
| 1278 |
- return EndpointNotFoundError(eid) |
|
| 1279 |
- } |
|
| 1280 |
- |
|
| 1281 |
- endpoint.extConnConfig, err = parseConnectivityOptions(options) |
|
| 1282 |
- if err != nil {
|
|
| 1283 |
- return err |
|
| 1284 |
- } |
|
| 1285 |
- |
|
| 1286 |
- // Program any required port mapping and store them in the endpoint |
|
| 1287 |
- endpoint.portMapping, err = network.allocatePorts(endpoint, network.config.DefaultBindingIP, d.config.EnableUserlandProxy) |
|
| 1288 |
- if err != nil {
|
|
| 1289 |
- return err |
|
| 1290 |
- } |
|
| 1291 |
- |
|
| 1292 |
- defer func() {
|
|
| 1293 |
- if err != nil {
|
|
| 1294 |
- if e := network.releasePorts(endpoint); e != nil {
|
|
| 1295 |
- log.G(context.TODO()).Errorf("Failed to release ports allocated for the bridge endpoint %s on failure %v because of %v",
|
|
| 1296 |
- eid, err, e) |
|
| 1297 |
- } |
|
| 1298 |
- endpoint.portMapping = nil |
|
| 1299 |
- } |
|
| 1300 |
- }() |
|
| 1301 |
- |
|
| 1302 |
- // Clean the connection tracker state of the host for the specific endpoint. This is needed because some flows may |
|
| 1303 |
- // be bound to the local proxy, or to the host (for UDP packets), and won't be redirected to the new endpoints. |
|
| 1304 |
- clearConntrackEntries(d.nlh, endpoint) |
|
| 1305 |
- |
|
| 1306 |
- if err = d.storeUpdate(endpoint); err != nil {
|
|
| 1307 |
- return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err)
|
|
| 1308 |
- } |
|
| 1309 |
- |
|
| 1310 |
- if !network.config.EnableICC {
|
|
| 1311 |
- return d.link(network, endpoint, true) |
|
| 1312 |
- } |
|
| 1313 |
- |
|
| 1314 |
- return nil |
|
| 1315 |
-} |
|
| 1316 |
- |
|
| 1317 |
-func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
|
| 1318 |
- network, err := d.getNetwork(nid) |
|
| 1319 |
- if err != nil {
|
|
| 1320 |
- return err |
|
| 1321 |
- } |
|
| 1322 |
- |
|
| 1323 |
- endpoint, err := network.getEndpoint(eid) |
|
| 1324 |
- if err != nil {
|
|
| 1325 |
- return err |
|
| 1326 |
- } |
|
| 1327 |
- |
|
| 1328 |
- if endpoint == nil {
|
|
| 1329 |
- return EndpointNotFoundError(eid) |
|
| 1330 |
- } |
|
| 1331 |
- |
|
| 1332 |
- err = network.releasePorts(endpoint) |
|
| 1333 |
- if err != nil {
|
|
| 1334 |
- log.G(context.TODO()).Warn(err) |
|
| 1335 |
- } |
|
| 1336 |
- |
|
| 1337 |
- endpoint.portMapping = nil |
|
| 1338 |
- |
|
| 1339 |
- // Clean the connection tracker state of the host for the specific endpoint. This is a precautionary measure to |
|
| 1340 |
- // avoid new endpoints getting the same IP address to receive unexpected packets due to bad conntrack state leading |
|
| 1341 |
- // to bad NATing. |
|
| 1342 |
- clearConntrackEntries(d.nlh, endpoint) |
|
| 1343 |
- |
|
| 1344 |
- if err = d.storeUpdate(endpoint); err != nil {
|
|
| 1345 |
- return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err)
|
|
| 1346 |
- } |
|
| 1347 |
- |
|
| 1348 |
- return nil |
|
| 1349 |
-} |
|
| 1350 |
- |
|
| 1351 |
-func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, enable bool) (retErr error) {
|
|
| 1352 |
- cc := endpoint.containerConfig |
|
| 1353 |
- ec := endpoint.extConnConfig |
|
| 1354 |
- if cc == nil || ec == nil || (len(cc.ParentEndpoints) == 0 && len(cc.ChildEndpoints) == 0) {
|
|
| 1355 |
- // nothing to do |
|
| 1356 |
- return nil |
|
| 1357 |
- } |
|
| 1358 |
- |
|
| 1359 |
- // Try to keep things atomic. addedLinks keeps track of links that were |
|
| 1360 |
- // successfully added. If any error occurred, then roll back all. |
|
| 1361 |
- var addedLinks []*link |
|
| 1362 |
- defer func() {
|
|
| 1363 |
- if retErr == nil {
|
|
| 1364 |
- return |
|
| 1365 |
- } |
|
| 1366 |
- for _, l := range addedLinks {
|
|
| 1367 |
- l.Disable() |
|
| 1368 |
- } |
|
| 1369 |
- }() |
|
| 1370 |
- |
|
| 1371 |
- if ec.ExposedPorts != nil {
|
|
| 1372 |
- for _, p := range cc.ParentEndpoints {
|
|
| 1373 |
- parentEndpoint, err := network.getEndpoint(p) |
|
| 1374 |
- if err != nil {
|
|
| 1375 |
- return err |
|
| 1376 |
- } |
|
| 1377 |
- if parentEndpoint == nil {
|
|
| 1378 |
- return InvalidEndpointIDError(p) |
|
| 1379 |
- } |
|
| 1380 |
- |
|
| 1381 |
- l, err := newLink(parentEndpoint.addr.IP, endpoint.addr.IP, ec.ExposedPorts, network.config.BridgeName) |
|
| 1382 |
- if err != nil {
|
|
| 1383 |
- return err |
|
| 1384 |
- } |
|
| 1385 |
- if enable {
|
|
| 1386 |
- if err := l.Enable(); err != nil {
|
|
| 1387 |
- return err |
|
| 1388 |
- } |
|
| 1389 |
- addedLinks = append(addedLinks, l) |
|
| 1390 |
- } else {
|
|
| 1391 |
- l.Disable() |
|
| 1392 |
- } |
|
| 1393 |
- } |
|
| 1394 |
- } |
|
| 1395 |
- |
|
| 1396 |
- for _, c := range cc.ChildEndpoints {
|
|
| 1397 |
- childEndpoint, err := network.getEndpoint(c) |
|
| 1398 |
- if err != nil {
|
|
| 1399 |
- return err |
|
| 1400 |
- } |
|
| 1401 |
- if childEndpoint == nil {
|
|
| 1402 |
- return InvalidEndpointIDError(c) |
|
| 1403 |
- } |
|
| 1404 |
- if childEndpoint.extConnConfig == nil || childEndpoint.extConnConfig.ExposedPorts == nil {
|
|
| 1405 |
- continue |
|
| 1406 |
- } |
|
| 1407 |
- |
|
| 1408 |
- l, err := newLink(endpoint.addr.IP, childEndpoint.addr.IP, childEndpoint.extConnConfig.ExposedPorts, network.config.BridgeName) |
|
| 1409 |
- if err != nil {
|
|
| 1410 |
- return err |
|
| 1411 |
- } |
|
| 1412 |
- if enable {
|
|
| 1413 |
- if err := l.Enable(); err != nil {
|
|
| 1414 |
- return err |
|
| 1415 |
- } |
|
| 1416 |
- addedLinks = append(addedLinks, l) |
|
| 1417 |
- } else {
|
|
| 1418 |
- l.Disable() |
|
| 1419 |
- } |
|
| 1420 |
- } |
|
| 1421 |
- |
|
| 1422 |
- return nil |
|
| 1423 |
-} |
|
| 1424 |
- |
|
| 1425 |
-func (d *driver) Type() string {
|
|
| 1426 |
- return NetworkType |
|
| 1427 |
-} |
|
| 1428 |
- |
|
| 1429 |
-func (d *driver) IsBuiltIn() bool {
|
|
| 1430 |
- return true |
|
| 1431 |
-} |
|
| 1432 |
- |
|
| 1433 |
-func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfiguration, error) {
|
|
| 1434 |
- if epOptions == nil {
|
|
| 1435 |
- return nil, nil |
|
| 1436 |
- } |
|
| 1437 |
- |
|
| 1438 |
- ec := &endpointConfiguration{}
|
|
| 1439 |
- |
|
| 1440 |
- if opt, ok := epOptions[netlabel.MacAddress]; ok {
|
|
| 1441 |
- if mac, ok := opt.(net.HardwareAddr); ok {
|
|
| 1442 |
- ec.MacAddress = mac |
|
| 1443 |
- } else {
|
|
| 1444 |
- return nil, &ErrInvalidEndpointConfig{}
|
|
| 1445 |
- } |
|
| 1446 |
- } |
|
| 1447 |
- |
|
| 1448 |
- return ec, nil |
|
| 1449 |
-} |
|
| 1450 |
- |
|
| 1451 |
-func parseContainerOptions(cOptions map[string]interface{}) (*containerConfiguration, error) {
|
|
| 1452 |
- if cOptions == nil {
|
|
| 1453 |
- return nil, nil |
|
| 1454 |
- } |
|
| 1455 |
- genericData := cOptions[netlabel.GenericData] |
|
| 1456 |
- if genericData == nil {
|
|
| 1457 |
- return nil, nil |
|
| 1458 |
- } |
|
| 1459 |
- switch opt := genericData.(type) {
|
|
| 1460 |
- case options.Generic: |
|
| 1461 |
- opaqueConfig, err := options.GenerateFromModel(opt, &containerConfiguration{})
|
|
| 1462 |
- if err != nil {
|
|
| 1463 |
- return nil, err |
|
| 1464 |
- } |
|
| 1465 |
- return opaqueConfig.(*containerConfiguration), nil |
|
| 1466 |
- case *containerConfiguration: |
|
| 1467 |
- return opt, nil |
|
| 1468 |
- default: |
|
| 1469 |
- return nil, nil |
|
| 1470 |
- } |
|
| 1471 |
-} |
|
| 1472 |
- |
|
| 1473 |
-func parseConnectivityOptions(cOptions map[string]interface{}) (*connectivityConfiguration, error) {
|
|
| 1474 |
- if cOptions == nil {
|
|
| 1475 |
- return nil, nil |
|
| 1476 |
- } |
|
| 1477 |
- |
|
| 1478 |
- cc := &connectivityConfiguration{}
|
|
| 1479 |
- |
|
| 1480 |
- if opt, ok := cOptions[netlabel.PortMap]; ok {
|
|
| 1481 |
- if pb, ok := opt.([]types.PortBinding); ok {
|
|
| 1482 |
- cc.PortBindings = pb |
|
| 1483 |
- } else {
|
|
| 1484 |
- return nil, types.BadRequestErrorf("Invalid port mapping data in connectivity configuration: %v", opt)
|
|
| 1485 |
- } |
|
| 1486 |
- } |
|
| 1487 |
- |
|
| 1488 |
- if opt, ok := cOptions[netlabel.ExposedPorts]; ok {
|
|
| 1489 |
- if ports, ok := opt.([]types.TransportPort); ok {
|
|
| 1490 |
- cc.ExposedPorts = ports |
|
| 1491 |
- } else {
|
|
| 1492 |
- return nil, types.BadRequestErrorf("Invalid exposed ports data in connectivity configuration: %v", opt)
|
|
| 1493 |
- } |
|
| 1494 |
- } |
|
| 1495 |
- |
|
| 1496 |
- return cc, nil |
|
| 1497 |
-} |
|
| 1498 |
- |
|
| 1499 |
-func electMacAddress(epConfig *endpointConfiguration, ip net.IP) net.HardwareAddr {
|
|
| 1500 |
- if epConfig != nil && epConfig.MacAddress != nil {
|
|
| 1501 |
- return epConfig.MacAddress |
|
| 1502 |
- } |
|
| 1503 |
- return netutils.GenerateMACFromIP(ip) |
|
| 1504 |
-} |
| 1505 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1502 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "net" |
|
| 7 |
+ "os" |
|
| 8 |
+ "os/exec" |
|
| 9 |
+ "strconv" |
|
| 10 |
+ "sync" |
|
| 11 |
+ |
|
| 12 |
+ "github.com/containerd/containerd/log" |
|
| 13 |
+ "github.com/docker/docker/libnetwork/datastore" |
|
| 14 |
+ "github.com/docker/docker/libnetwork/driverapi" |
|
| 15 |
+ "github.com/docker/docker/libnetwork/iptables" |
|
| 16 |
+ "github.com/docker/docker/libnetwork/netlabel" |
|
| 17 |
+ "github.com/docker/docker/libnetwork/netutils" |
|
| 18 |
+ "github.com/docker/docker/libnetwork/ns" |
|
| 19 |
+ "github.com/docker/docker/libnetwork/options" |
|
| 20 |
+ "github.com/docker/docker/libnetwork/portallocator" |
|
| 21 |
+ "github.com/docker/docker/libnetwork/portmapper" |
|
| 22 |
+ "github.com/docker/docker/libnetwork/scope" |
|
| 23 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 24 |
+ "github.com/vishvananda/netlink" |
|
| 25 |
+) |
|
| 26 |
+ |
|
| 27 |
+const ( |
|
| 28 |
+ NetworkType = "bridge" |
|
| 29 |
+ vethPrefix = "veth" |
|
| 30 |
+ vethLen = len(vethPrefix) + 7 |
|
| 31 |
+ defaultContainerVethPrefix = "eth" |
|
| 32 |
+ maxAllocatePortAttempts = 10 |
|
| 33 |
+) |
|
| 34 |
+ |
|
| 35 |
+const ( |
|
| 36 |
+ // DefaultGatewayV4AuxKey represents the default-gateway configured by the user |
|
| 37 |
+ DefaultGatewayV4AuxKey = "DefaultGatewayIPv4" |
|
| 38 |
+ // DefaultGatewayV6AuxKey represents the ipv6 default-gateway configured by the user |
|
| 39 |
+ DefaultGatewayV6AuxKey = "DefaultGatewayIPv6" |
|
| 40 |
+) |
|
| 41 |
+ |
|
| 42 |
+type defaultBridgeNetworkConflict struct {
|
|
| 43 |
+ ID string |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func (d defaultBridgeNetworkConflict) Error() string {
|
|
| 47 |
+ return fmt.Sprintf("Stale default bridge network %s", d.ID)
|
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+type ( |
|
| 51 |
+ iptableCleanFunc func() error |
|
| 52 |
+ iptablesCleanFuncs []iptableCleanFunc |
|
| 53 |
+) |
|
| 54 |
+ |
|
| 55 |
+// configuration info for the "bridge" driver. |
|
| 56 |
+type configuration struct {
|
|
| 57 |
+ EnableIPForwarding bool |
|
| 58 |
+ EnableIPTables bool |
|
| 59 |
+ EnableIP6Tables bool |
|
| 60 |
+ EnableUserlandProxy bool |
|
| 61 |
+ UserlandProxyPath string |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// networkConfiguration for network specific configuration |
|
| 65 |
+type networkConfiguration struct {
|
|
| 66 |
+ ID string |
|
| 67 |
+ BridgeName string |
|
| 68 |
+ EnableIPv6 bool |
|
| 69 |
+ EnableIPMasquerade bool |
|
| 70 |
+ EnableICC bool |
|
| 71 |
+ InhibitIPv4 bool |
|
| 72 |
+ Mtu int |
|
| 73 |
+ DefaultBindingIP net.IP |
|
| 74 |
+ DefaultBridge bool |
|
| 75 |
+ HostIP net.IP |
|
| 76 |
+ ContainerIfacePrefix string |
|
| 77 |
+ // Internal fields set after ipam data parsing |
|
| 78 |
+ AddressIPv4 *net.IPNet |
|
| 79 |
+ AddressIPv6 *net.IPNet |
|
| 80 |
+ DefaultGatewayIPv4 net.IP |
|
| 81 |
+ DefaultGatewayIPv6 net.IP |
|
| 82 |
+ dbIndex uint64 |
|
| 83 |
+ dbExists bool |
|
| 84 |
+ Internal bool |
|
| 85 |
+ |
|
| 86 |
+ BridgeIfaceCreator ifaceCreator |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// ifaceCreator represents how the bridge interface was created |
|
| 90 |
+type ifaceCreator int8 |
|
| 91 |
+ |
|
| 92 |
+const ( |
|
| 93 |
+ ifaceCreatorUnknown ifaceCreator = iota |
|
| 94 |
+ ifaceCreatedByLibnetwork |
|
| 95 |
+ ifaceCreatedByUser |
|
| 96 |
+) |
|
| 97 |
+ |
|
| 98 |
+// endpointConfiguration represents the user specified configuration for the sandbox endpoint |
|
| 99 |
+type endpointConfiguration struct {
|
|
| 100 |
+ MacAddress net.HardwareAddr |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+// containerConfiguration represents the user specified configuration for a container |
|
| 104 |
+type containerConfiguration struct {
|
|
| 105 |
+ ParentEndpoints []string |
|
| 106 |
+ ChildEndpoints []string |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// connectivityConfiguration represents the user specified configuration regarding the external connectivity |
|
| 110 |
+type connectivityConfiguration struct {
|
|
| 111 |
+ PortBindings []types.PortBinding |
|
| 112 |
+ ExposedPorts []types.TransportPort |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+type bridgeEndpoint struct {
|
|
| 116 |
+ id string |
|
| 117 |
+ nid string |
|
| 118 |
+ srcName string |
|
| 119 |
+ addr *net.IPNet |
|
| 120 |
+ addrv6 *net.IPNet |
|
| 121 |
+ macAddress net.HardwareAddr |
|
| 122 |
+ config *endpointConfiguration // User specified parameters |
|
| 123 |
+ containerConfig *containerConfiguration |
|
| 124 |
+ extConnConfig *connectivityConfiguration |
|
| 125 |
+ portMapping []types.PortBinding // Operation port bindings |
|
| 126 |
+ dbIndex uint64 |
|
| 127 |
+ dbExists bool |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+type bridgeNetwork struct {
|
|
| 131 |
+ id string |
|
| 132 |
+ bridge *bridgeInterface // The bridge's L3 interface |
|
| 133 |
+ config *networkConfiguration |
|
| 134 |
+ endpoints map[string]*bridgeEndpoint // key: endpoint id |
|
| 135 |
+ portMapper *portmapper.PortMapper |
|
| 136 |
+ portMapperV6 *portmapper.PortMapper |
|
| 137 |
+ driver *driver // The network's driver |
|
| 138 |
+ iptCleanFuncs iptablesCleanFuncs |
|
| 139 |
+ sync.Mutex |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+type driver struct {
|
|
| 143 |
+ config configuration |
|
| 144 |
+ natChain *iptables.ChainInfo |
|
| 145 |
+ filterChain *iptables.ChainInfo |
|
| 146 |
+ isolationChain1 *iptables.ChainInfo |
|
| 147 |
+ isolationChain2 *iptables.ChainInfo |
|
| 148 |
+ natChainV6 *iptables.ChainInfo |
|
| 149 |
+ filterChainV6 *iptables.ChainInfo |
|
| 150 |
+ isolationChain1V6 *iptables.ChainInfo |
|
| 151 |
+ isolationChain2V6 *iptables.ChainInfo |
|
| 152 |
+ networks map[string]*bridgeNetwork |
|
| 153 |
+ store *datastore.Store |
|
| 154 |
+ nlh *netlink.Handle |
|
| 155 |
+ configNetwork sync.Mutex |
|
| 156 |
+ portAllocator *portallocator.PortAllocator // Overridable for tests. |
|
| 157 |
+ sync.Mutex |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+// New constructs a new bridge driver |
|
| 161 |
+func newDriver() *driver {
|
|
| 162 |
+ return &driver{
|
|
| 163 |
+ networks: map[string]*bridgeNetwork{},
|
|
| 164 |
+ portAllocator: portallocator.Get(), |
|
| 165 |
+ } |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+// Register registers a new instance of bridge driver. |
|
| 169 |
+func Register(r driverapi.Registerer, config map[string]interface{}) error {
|
|
| 170 |
+ d := newDriver() |
|
| 171 |
+ if err := d.configure(config); err != nil {
|
|
| 172 |
+ return err |
|
| 173 |
+ } |
|
| 174 |
+ return r.RegisterDriver(NetworkType, d, driverapi.Capability{
|
|
| 175 |
+ DataScope: scope.Local, |
|
| 176 |
+ ConnectivityScope: scope.Local, |
|
| 177 |
+ }) |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+// Validate performs a static validation on the network configuration parameters. |
|
| 181 |
+// Whatever can be assessed a priori before attempting any programming. |
|
| 182 |
+func (c *networkConfiguration) Validate() error {
|
|
| 183 |
+ if c.Mtu < 0 {
|
|
| 184 |
+ return ErrInvalidMtu(c.Mtu) |
|
| 185 |
+ } |
|
| 186 |
+ |
|
| 187 |
+ // If bridge v4 subnet is specified |
|
| 188 |
+ if c.AddressIPv4 != nil {
|
|
| 189 |
+ // If default gw is specified, it must be part of bridge subnet |
|
| 190 |
+ if c.DefaultGatewayIPv4 != nil {
|
|
| 191 |
+ if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) {
|
|
| 192 |
+ return &ErrInvalidGateway{}
|
|
| 193 |
+ } |
|
| 194 |
+ } |
|
| 195 |
+ } |
|
| 196 |
+ |
|
| 197 |
+ // If default v6 gw is specified, AddressIPv6 must be specified and gw must belong to AddressIPv6 subnet |
|
| 198 |
+ if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil {
|
|
| 199 |
+ if c.AddressIPv6 == nil || !c.AddressIPv6.Contains(c.DefaultGatewayIPv6) {
|
|
| 200 |
+ return &ErrInvalidGateway{}
|
|
| 201 |
+ } |
|
| 202 |
+ } |
|
| 203 |
+ return nil |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+// Conflicts check if two NetworkConfiguration objects overlap |
|
| 207 |
+func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
|
|
| 208 |
+ if o == nil {
|
|
| 209 |
+ return errors.New("same configuration")
|
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ // Also empty, because only one network with empty name is allowed |
|
| 213 |
+ if c.BridgeName == o.BridgeName {
|
|
| 214 |
+ return errors.New("networks have same bridge name")
|
|
| 215 |
+ } |
|
| 216 |
+ |
|
| 217 |
+ // They must be in different subnets |
|
| 218 |
+ if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) && |
|
| 219 |
+ (c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) {
|
|
| 220 |
+ return errors.New("networks have overlapping IPv4")
|
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ // They must be in different v6 subnets |
|
| 224 |
+ if (c.AddressIPv6 != nil && o.AddressIPv6 != nil) && |
|
| 225 |
+ (c.AddressIPv6.Contains(o.AddressIPv6.IP) || o.AddressIPv6.Contains(c.AddressIPv6.IP)) {
|
|
| 226 |
+ return errors.New("networks have overlapping IPv6")
|
|
| 227 |
+ } |
|
| 228 |
+ |
|
| 229 |
+ return nil |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+func (c *networkConfiguration) fromLabels(labels map[string]string) error {
|
|
| 233 |
+ var err error |
|
| 234 |
+ for label, value := range labels {
|
|
| 235 |
+ switch label {
|
|
| 236 |
+ case BridgeName: |
|
| 237 |
+ c.BridgeName = value |
|
| 238 |
+ case netlabel.DriverMTU: |
|
| 239 |
+ if c.Mtu, err = strconv.Atoi(value); err != nil {
|
|
| 240 |
+ return parseErr(label, value, err.Error()) |
|
| 241 |
+ } |
|
| 242 |
+ case netlabel.EnableIPv6: |
|
| 243 |
+ if c.EnableIPv6, err = strconv.ParseBool(value); err != nil {
|
|
| 244 |
+ return parseErr(label, value, err.Error()) |
|
| 245 |
+ } |
|
| 246 |
+ case EnableIPMasquerade: |
|
| 247 |
+ if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil {
|
|
| 248 |
+ return parseErr(label, value, err.Error()) |
|
| 249 |
+ } |
|
| 250 |
+ case EnableICC: |
|
| 251 |
+ if c.EnableICC, err = strconv.ParseBool(value); err != nil {
|
|
| 252 |
+ return parseErr(label, value, err.Error()) |
|
| 253 |
+ } |
|
| 254 |
+ case InhibitIPv4: |
|
| 255 |
+ if c.InhibitIPv4, err = strconv.ParseBool(value); err != nil {
|
|
| 256 |
+ return parseErr(label, value, err.Error()) |
|
| 257 |
+ } |
|
| 258 |
+ case DefaultBridge: |
|
| 259 |
+ if c.DefaultBridge, err = strconv.ParseBool(value); err != nil {
|
|
| 260 |
+ return parseErr(label, value, err.Error()) |
|
| 261 |
+ } |
|
| 262 |
+ case DefaultBindingIP: |
|
| 263 |
+ if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil {
|
|
| 264 |
+ return parseErr(label, value, "nil ip") |
|
| 265 |
+ } |
|
| 266 |
+ case netlabel.ContainerIfacePrefix: |
|
| 267 |
+ c.ContainerIfacePrefix = value |
|
| 268 |
+ case netlabel.HostIP: |
|
| 269 |
+ if c.HostIP = net.ParseIP(value); c.HostIP == nil {
|
|
| 270 |
+ return parseErr(label, value, "nil ip") |
|
| 271 |
+ } |
|
| 272 |
+ } |
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 275 |
+ return nil |
|
| 276 |
+} |
|
| 277 |
+ |
|
| 278 |
+func parseErr(label, value, errString string) error {
|
|
| 279 |
+ return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString)
|
|
| 280 |
+} |
|
| 281 |
+ |
|
| 282 |
+func (n *bridgeNetwork) registerIptCleanFunc(clean iptableCleanFunc) {
|
|
| 283 |
+ n.iptCleanFuncs = append(n.iptCleanFuncs, clean) |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+func (n *bridgeNetwork) getDriverChains(version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
|
|
| 287 |
+ n.Lock() |
|
| 288 |
+ defer n.Unlock() |
|
| 289 |
+ |
|
| 290 |
+ if n.driver == nil {
|
|
| 291 |
+ return nil, nil, nil, nil, types.BadRequestErrorf("no driver found")
|
|
| 292 |
+ } |
|
| 293 |
+ |
|
| 294 |
+ if version == iptables.IPv6 {
|
|
| 295 |
+ return n.driver.natChainV6, n.driver.filterChainV6, n.driver.isolationChain1V6, n.driver.isolationChain2V6, nil |
|
| 296 |
+ } |
|
| 297 |
+ |
|
| 298 |
+ return n.driver.natChain, n.driver.filterChain, n.driver.isolationChain1, n.driver.isolationChain2, nil |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+func (n *bridgeNetwork) getNetworkBridgeName() string {
|
|
| 302 |
+ n.Lock() |
|
| 303 |
+ config := n.config |
|
| 304 |
+ n.Unlock() |
|
| 305 |
+ |
|
| 306 |
+ return config.BridgeName |
|
| 307 |
+} |
|
| 308 |
+ |
|
| 309 |
+func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) {
|
|
| 310 |
+ if eid == "" {
|
|
| 311 |
+ return nil, InvalidEndpointIDError(eid) |
|
| 312 |
+ } |
|
| 313 |
+ |
|
| 314 |
+ n.Lock() |
|
| 315 |
+ defer n.Unlock() |
|
| 316 |
+ if ep, ok := n.endpoints[eid]; ok {
|
|
| 317 |
+ return ep, nil |
|
| 318 |
+ } |
|
| 319 |
+ |
|
| 320 |
+ return nil, nil |
|
| 321 |
+} |
|
| 322 |
+ |
|
| 323 |
+// Install/Removes the iptables rules needed to isolate this network |
|
| 324 |
+// from each of the other networks |
|
| 325 |
+func (n *bridgeNetwork) isolateNetwork(enable bool) error {
|
|
| 326 |
+ n.Lock() |
|
| 327 |
+ thisConfig := n.config |
|
| 328 |
+ n.Unlock() |
|
| 329 |
+ |
|
| 330 |
+ if thisConfig.Internal {
|
|
| 331 |
+ return nil |
|
| 332 |
+ } |
|
| 333 |
+ |
|
| 334 |
+ // Install the rules to isolate this network against each of the other networks |
|
| 335 |
+ if n.driver.config.EnableIP6Tables {
|
|
| 336 |
+ err := setINC(iptables.IPv6, thisConfig.BridgeName, enable) |
|
| 337 |
+ if err != nil {
|
|
| 338 |
+ return err |
|
| 339 |
+ } |
|
| 340 |
+ } |
|
| 341 |
+ |
|
| 342 |
+ if n.driver.config.EnableIPTables {
|
|
| 343 |
+ return setINC(iptables.IPv4, thisConfig.BridgeName, enable) |
|
| 344 |
+ } |
|
| 345 |
+ return nil |
|
| 346 |
+} |
|
| 347 |
+ |
|
| 348 |
+func (d *driver) configure(option map[string]interface{}) error {
|
|
| 349 |
+ var ( |
|
| 350 |
+ config configuration |
|
| 351 |
+ err error |
|
| 352 |
+ natChain *iptables.ChainInfo |
|
| 353 |
+ filterChain *iptables.ChainInfo |
|
| 354 |
+ isolationChain1 *iptables.ChainInfo |
|
| 355 |
+ isolationChain2 *iptables.ChainInfo |
|
| 356 |
+ natChainV6 *iptables.ChainInfo |
|
| 357 |
+ filterChainV6 *iptables.ChainInfo |
|
| 358 |
+ isolationChain1V6 *iptables.ChainInfo |
|
| 359 |
+ isolationChain2V6 *iptables.ChainInfo |
|
| 360 |
+ ) |
|
| 361 |
+ |
|
| 362 |
+ switch opt := option[netlabel.GenericData].(type) {
|
|
| 363 |
+ case options.Generic: |
|
| 364 |
+ opaqueConfig, err := options.GenerateFromModel(opt, &configuration{})
|
|
| 365 |
+ if err != nil {
|
|
| 366 |
+ return err |
|
| 367 |
+ } |
|
| 368 |
+ config = *opaqueConfig.(*configuration) |
|
| 369 |
+ case *configuration: |
|
| 370 |
+ config = *opt |
|
| 371 |
+ case nil: |
|
| 372 |
+ // No GenericData option set. Use defaults. |
|
| 373 |
+ default: |
|
| 374 |
+ return &ErrInvalidDriverConfig{}
|
|
| 375 |
+ } |
|
| 376 |
+ |
|
| 377 |
+ if config.EnableIPTables || config.EnableIP6Tables {
|
|
| 378 |
+ if _, err := os.Stat("/proc/sys/net/bridge"); err != nil {
|
|
| 379 |
+ if out, err := exec.Command("modprobe", "-va", "bridge", "br_netfilter").CombinedOutput(); err != nil {
|
|
| 380 |
+ log.G(context.TODO()).Warnf("Running modprobe bridge br_netfilter failed with message: %s, error: %v", out, err)
|
|
| 381 |
+ } |
|
| 382 |
+ } |
|
| 383 |
+ } |
|
| 384 |
+ |
|
| 385 |
+ if config.EnableIPTables {
|
|
| 386 |
+ removeIPChains(iptables.IPv4) |
|
| 387 |
+ |
|
| 388 |
+ natChain, filterChain, isolationChain1, isolationChain2, err = setupIPChains(config, iptables.IPv4) |
|
| 389 |
+ if err != nil {
|
|
| 390 |
+ return err |
|
| 391 |
+ } |
|
| 392 |
+ |
|
| 393 |
+ // Make sure on firewall reload, first thing being re-played is chains creation |
|
| 394 |
+ iptables.OnReloaded(func() {
|
|
| 395 |
+ log.G(context.TODO()).Debugf("Recreating iptables chains on firewall reload")
|
|
| 396 |
+ if _, _, _, _, err := setupIPChains(config, iptables.IPv4); err != nil {
|
|
| 397 |
+ log.G(context.TODO()).WithError(err).Error("Error reloading iptables chains")
|
|
| 398 |
+ } |
|
| 399 |
+ }) |
|
| 400 |
+ } |
|
| 401 |
+ |
|
| 402 |
+ if config.EnableIP6Tables {
|
|
| 403 |
+ removeIPChains(iptables.IPv6) |
|
| 404 |
+ |
|
| 405 |
+ natChainV6, filterChainV6, isolationChain1V6, isolationChain2V6, err = setupIPChains(config, iptables.IPv6) |
|
| 406 |
+ if err != nil {
|
|
| 407 |
+ return err |
|
| 408 |
+ } |
|
| 409 |
+ |
|
| 410 |
+ // Make sure on firewall reload, first thing being re-played is chains creation |
|
| 411 |
+ iptables.OnReloaded(func() {
|
|
| 412 |
+ log.G(context.TODO()).Debugf("Recreating ip6tables chains on firewall reload")
|
|
| 413 |
+ if _, _, _, _, err := setupIPChains(config, iptables.IPv6); err != nil {
|
|
| 414 |
+ log.G(context.TODO()).WithError(err).Error("Error reloading ip6tables chains")
|
|
| 415 |
+ } |
|
| 416 |
+ }) |
|
| 417 |
+ } |
|
| 418 |
+ |
|
| 419 |
+ if config.EnableIPForwarding {
|
|
| 420 |
+ err = setupIPForwarding(config.EnableIPTables, config.EnableIP6Tables) |
|
| 421 |
+ if err != nil {
|
|
| 422 |
+ log.G(context.TODO()).Warn(err) |
|
| 423 |
+ return err |
|
| 424 |
+ } |
|
| 425 |
+ } |
|
| 426 |
+ |
|
| 427 |
+ d.Lock() |
|
| 428 |
+ d.natChain = natChain |
|
| 429 |
+ d.filterChain = filterChain |
|
| 430 |
+ d.isolationChain1 = isolationChain1 |
|
| 431 |
+ d.isolationChain2 = isolationChain2 |
|
| 432 |
+ d.natChainV6 = natChainV6 |
|
| 433 |
+ d.filterChainV6 = filterChainV6 |
|
| 434 |
+ d.isolationChain1V6 = isolationChain1V6 |
|
| 435 |
+ d.isolationChain2V6 = isolationChain2V6 |
|
| 436 |
+ d.config = config |
|
| 437 |
+ d.Unlock() |
|
| 438 |
+ |
|
| 439 |
+ return d.initStore(option) |
|
| 440 |
+} |
|
| 441 |
+ |
|
| 442 |
+func (d *driver) getNetwork(id string) (*bridgeNetwork, error) {
|
|
| 443 |
+ d.Lock() |
|
| 444 |
+ defer d.Unlock() |
|
| 445 |
+ |
|
| 446 |
+ if id == "" {
|
|
| 447 |
+ return nil, types.BadRequestErrorf("invalid network id: %s", id)
|
|
| 448 |
+ } |
|
| 449 |
+ |
|
| 450 |
+ if nw, ok := d.networks[id]; ok {
|
|
| 451 |
+ return nw, nil |
|
| 452 |
+ } |
|
| 453 |
+ |
|
| 454 |
+ return nil, types.NotFoundErrorf("network not found: %s", id)
|
|
| 455 |
+} |
|
| 456 |
+ |
|
| 457 |
+func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) {
|
|
| 458 |
+ var ( |
|
| 459 |
+ err error |
|
| 460 |
+ config *networkConfiguration |
|
| 461 |
+ ) |
|
| 462 |
+ |
|
| 463 |
+ switch opt := data.(type) {
|
|
| 464 |
+ case *networkConfiguration: |
|
| 465 |
+ config = opt |
|
| 466 |
+ case map[string]string: |
|
| 467 |
+ config = &networkConfiguration{
|
|
| 468 |
+ EnableICC: true, |
|
| 469 |
+ EnableIPMasquerade: true, |
|
| 470 |
+ } |
|
| 471 |
+ err = config.fromLabels(opt) |
|
| 472 |
+ case options.Generic: |
|
| 473 |
+ var opaqueConfig interface{}
|
|
| 474 |
+ if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
|
|
| 475 |
+ config = opaqueConfig.(*networkConfiguration) |
|
| 476 |
+ } |
|
| 477 |
+ default: |
|
| 478 |
+ err = types.BadRequestErrorf("do not recognize network configuration format: %T", opt)
|
|
| 479 |
+ } |
|
| 480 |
+ |
|
| 481 |
+ return config, err |
|
| 482 |
+} |
|
| 483 |
+ |
|
| 484 |
+func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
|
| 485 |
+ if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 {
|
|
| 486 |
+ return types.ForbiddenErrorf("bridge driver doesn't support multiple subnets")
|
|
| 487 |
+ } |
|
| 488 |
+ |
|
| 489 |
+ if len(ipamV4Data) == 0 {
|
|
| 490 |
+ return types.BadRequestErrorf("bridge network %s requires ipv4 configuration", id)
|
|
| 491 |
+ } |
|
| 492 |
+ |
|
| 493 |
+ if ipamV4Data[0].Gateway != nil {
|
|
| 494 |
+ c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway) |
|
| 495 |
+ } |
|
| 496 |
+ |
|
| 497 |
+ if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok {
|
|
| 498 |
+ c.DefaultGatewayIPv4 = gw.IP |
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ if len(ipamV6Data) > 0 {
|
|
| 502 |
+ c.AddressIPv6 = ipamV6Data[0].Pool |
|
| 503 |
+ |
|
| 504 |
+ if ipamV6Data[0].Gateway != nil {
|
|
| 505 |
+ c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway) |
|
| 506 |
+ } |
|
| 507 |
+ |
|
| 508 |
+ if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok {
|
|
| 509 |
+ c.DefaultGatewayIPv6 = gw.IP |
|
| 510 |
+ } |
|
| 511 |
+ } |
|
| 512 |
+ |
|
| 513 |
+ return nil |
|
| 514 |
+} |
|
| 515 |
+ |
|
| 516 |
+func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) {
|
|
| 517 |
+ var ( |
|
| 518 |
+ err error |
|
| 519 |
+ config = &networkConfiguration{}
|
|
| 520 |
+ ) |
|
| 521 |
+ |
|
| 522 |
+ // Parse generic label first, config will be re-assigned |
|
| 523 |
+ if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
|
|
| 524 |
+ if config, err = parseNetworkGenericOptions(genData); err != nil {
|
|
| 525 |
+ return nil, err |
|
| 526 |
+ } |
|
| 527 |
+ } |
|
| 528 |
+ |
|
| 529 |
+ // Process well-known labels next |
|
| 530 |
+ if val, ok := option[netlabel.EnableIPv6]; ok {
|
|
| 531 |
+ config.EnableIPv6 = val.(bool) |
|
| 532 |
+ } |
|
| 533 |
+ |
|
| 534 |
+ if val, ok := option[netlabel.Internal]; ok {
|
|
| 535 |
+ if internal, ok := val.(bool); ok && internal {
|
|
| 536 |
+ config.Internal = true |
|
| 537 |
+ } |
|
| 538 |
+ } |
|
| 539 |
+ |
|
| 540 |
+ // Finally validate the configuration |
|
| 541 |
+ if err = config.Validate(); err != nil {
|
|
| 542 |
+ return nil, err |
|
| 543 |
+ } |
|
| 544 |
+ |
|
| 545 |
+ if config.BridgeName == "" && !config.DefaultBridge {
|
|
| 546 |
+ config.BridgeName = "br-" + id[:12] |
|
| 547 |
+ } |
|
| 548 |
+ |
|
| 549 |
+ exists, err := bridgeInterfaceExists(config.BridgeName) |
|
| 550 |
+ if err != nil {
|
|
| 551 |
+ return nil, err |
|
| 552 |
+ } |
|
| 553 |
+ |
|
| 554 |
+ if !exists {
|
|
| 555 |
+ config.BridgeIfaceCreator = ifaceCreatedByLibnetwork |
|
| 556 |
+ } else {
|
|
| 557 |
+ config.BridgeIfaceCreator = ifaceCreatedByUser |
|
| 558 |
+ } |
|
| 559 |
+ |
|
| 560 |
+ config.ID = id |
|
| 561 |
+ return config, nil |
|
| 562 |
+} |
|
| 563 |
+ |
|
| 564 |
+// Return a slice of networks over which caller can iterate safely |
|
| 565 |
+func (d *driver) getNetworks() []*bridgeNetwork {
|
|
| 566 |
+ d.Lock() |
|
| 567 |
+ defer d.Unlock() |
|
| 568 |
+ |
|
| 569 |
+ ls := make([]*bridgeNetwork, 0, len(d.networks)) |
|
| 570 |
+ for _, nw := range d.networks {
|
|
| 571 |
+ ls = append(ls, nw) |
|
| 572 |
+ } |
|
| 573 |
+ return ls |
|
| 574 |
+} |
|
| 575 |
+ |
|
| 576 |
+func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
|
| 577 |
+ return nil, types.NotImplementedErrorf("not implemented")
|
|
| 578 |
+} |
|
| 579 |
+ |
|
| 580 |
+func (d *driver) NetworkFree(id string) error {
|
|
| 581 |
+ return types.NotImplementedErrorf("not implemented")
|
|
| 582 |
+} |
|
| 583 |
+ |
|
| 584 |
+func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
|
| 585 |
+} |
|
| 586 |
+ |
|
| 587 |
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
|
|
| 588 |
+ return "", nil |
|
| 589 |
+} |
|
| 590 |
+ |
|
| 591 |
+// Create a new network using bridge plugin |
|
| 592 |
+func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
|
| 593 |
+ if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
|
|
| 594 |
+ return types.BadRequestErrorf("ipv4 pool is empty")
|
|
| 595 |
+ } |
|
| 596 |
+ // Sanity checks |
|
| 597 |
+ d.Lock() |
|
| 598 |
+ if _, ok := d.networks[id]; ok {
|
|
| 599 |
+ d.Unlock() |
|
| 600 |
+ return types.ForbiddenErrorf("network %s exists", id)
|
|
| 601 |
+ } |
|
| 602 |
+ d.Unlock() |
|
| 603 |
+ |
|
| 604 |
+ // Parse and validate the config. It should not be conflict with existing networks' config |
|
| 605 |
+ config, err := parseNetworkOptions(id, option) |
|
| 606 |
+ if err != nil {
|
|
| 607 |
+ return err |
|
| 608 |
+ } |
|
| 609 |
+ |
|
| 610 |
+ if err = config.processIPAM(id, ipV4Data, ipV6Data); err != nil {
|
|
| 611 |
+ return err |
|
| 612 |
+ } |
|
| 613 |
+ |
|
| 614 |
+ // start the critical section, from this point onward we are dealing with the list of networks |
|
| 615 |
+ // so to be consistent we cannot allow that the list changes |
|
| 616 |
+ d.configNetwork.Lock() |
|
| 617 |
+ defer d.configNetwork.Unlock() |
|
| 618 |
+ |
|
| 619 |
+ // check network conflicts |
|
| 620 |
+ if err = d.checkConflict(config); err != nil {
|
|
| 621 |
+ nerr, ok := err.(defaultBridgeNetworkConflict) |
|
| 622 |
+ if !ok {
|
|
| 623 |
+ return err |
|
| 624 |
+ } |
|
| 625 |
+ // Got a conflict with a stale default network, clean that up and continue |
|
| 626 |
+ log.G(context.TODO()).Warn(nerr) |
|
| 627 |
+ if err := d.deleteNetwork(nerr.ID); err != nil {
|
|
| 628 |
+ log.G(context.TODO()).WithError(err).Debug("Error while cleaning up network on conflict")
|
|
| 629 |
+ } |
|
| 630 |
+ } |
|
| 631 |
+ |
|
| 632 |
+ // there is no conflict, now create the network |
|
| 633 |
+ if err = d.createNetwork(config); err != nil {
|
|
| 634 |
+ return err |
|
| 635 |
+ } |
|
| 636 |
+ |
|
| 637 |
+ return d.storeUpdate(config) |
|
| 638 |
+} |
|
| 639 |
+ |
|
| 640 |
+func (d *driver) checkConflict(config *networkConfiguration) error {
|
|
| 641 |
+ networkList := d.getNetworks() |
|
| 642 |
+ for _, nw := range networkList {
|
|
| 643 |
+ nw.Lock() |
|
| 644 |
+ nwConfig := nw.config |
|
| 645 |
+ nw.Unlock() |
|
| 646 |
+ if err := nwConfig.Conflicts(config); err != nil {
|
|
| 647 |
+ if nwConfig.DefaultBridge {
|
|
| 648 |
+ // We encountered and identified a stale default network |
|
| 649 |
+ // We must delete it as libnetwork is the source of truth |
|
| 650 |
+ // The default network being created must be the only one |
|
| 651 |
+ // This can happen only from docker 1.12 on ward |
|
| 652 |
+ log.G(context.TODO()).Infof("Found stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName)
|
|
| 653 |
+ return defaultBridgeNetworkConflict{nwConfig.ID}
|
|
| 654 |
+ } |
|
| 655 |
+ |
|
| 656 |
+ return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s",
|
|
| 657 |
+ config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error()) |
|
| 658 |
+ } |
|
| 659 |
+ } |
|
| 660 |
+ return nil |
|
| 661 |
+} |
|
| 662 |
+ |
|
| 663 |
+func (d *driver) createNetwork(config *networkConfiguration) (err error) {
|
|
| 664 |
+ // Initialize handle when needed |
|
| 665 |
+ d.Lock() |
|
| 666 |
+ if d.nlh == nil {
|
|
| 667 |
+ d.nlh = ns.NlHandle() |
|
| 668 |
+ } |
|
| 669 |
+ d.Unlock() |
|
| 670 |
+ |
|
| 671 |
+ // Create or retrieve the bridge L3 interface |
|
| 672 |
+ bridgeIface, err := newInterface(d.nlh, config) |
|
| 673 |
+ if err != nil {
|
|
| 674 |
+ return err |
|
| 675 |
+ } |
|
| 676 |
+ |
|
| 677 |
+ // Create and set network handler in driver |
|
| 678 |
+ network := &bridgeNetwork{
|
|
| 679 |
+ id: config.ID, |
|
| 680 |
+ endpoints: make(map[string]*bridgeEndpoint), |
|
| 681 |
+ config: config, |
|
| 682 |
+ portMapper: portmapper.NewWithPortAllocator(d.portAllocator, d.config.UserlandProxyPath), |
|
| 683 |
+ portMapperV6: portmapper.NewWithPortAllocator(d.portAllocator, d.config.UserlandProxyPath), |
|
| 684 |
+ bridge: bridgeIface, |
|
| 685 |
+ driver: d, |
|
| 686 |
+ } |
|
| 687 |
+ |
|
| 688 |
+ d.Lock() |
|
| 689 |
+ d.networks[config.ID] = network |
|
| 690 |
+ d.Unlock() |
|
| 691 |
+ |
|
| 692 |
+ // On failure make sure to reset driver network handler to nil |
|
| 693 |
+ defer func() {
|
|
| 694 |
+ if err != nil {
|
|
| 695 |
+ d.Lock() |
|
| 696 |
+ delete(d.networks, config.ID) |
|
| 697 |
+ d.Unlock() |
|
| 698 |
+ } |
|
| 699 |
+ }() |
|
| 700 |
+ |
|
| 701 |
+ // Add inter-network communication rules. |
|
| 702 |
+ setupNetworkIsolationRules := func(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 703 |
+ if err := network.isolateNetwork(true); err != nil {
|
|
| 704 |
+ if err = network.isolateNetwork(false); err != nil {
|
|
| 705 |
+ log.G(context.TODO()).Warnf("Failed on removing the inter-network iptables rules on cleanup: %v", err)
|
|
| 706 |
+ } |
|
| 707 |
+ return err |
|
| 708 |
+ } |
|
| 709 |
+ // register the cleanup function |
|
| 710 |
+ network.registerIptCleanFunc(func() error {
|
|
| 711 |
+ return network.isolateNetwork(false) |
|
| 712 |
+ }) |
|
| 713 |
+ return nil |
|
| 714 |
+ } |
|
| 715 |
+ |
|
| 716 |
+ // Prepare the bridge setup configuration |
|
| 717 |
+ bridgeSetup := newBridgeSetup(config, bridgeIface) |
|
| 718 |
+ |
|
| 719 |
+ // If the bridge interface doesn't exist, we need to start the setup steps |
|
| 720 |
+ // by creating a new device and assigning it an IPv4 address. |
|
| 721 |
+ bridgeAlreadyExists := bridgeIface.exists() |
|
| 722 |
+ if !bridgeAlreadyExists {
|
|
| 723 |
+ bridgeSetup.queueStep(setupDevice) |
|
| 724 |
+ bridgeSetup.queueStep(setupDefaultSysctl) |
|
| 725 |
+ } |
|
| 726 |
+ |
|
| 727 |
+ // For the default bridge, set expected sysctls |
|
| 728 |
+ if config.DefaultBridge {
|
|
| 729 |
+ bridgeSetup.queueStep(setupDefaultSysctl) |
|
| 730 |
+ } |
|
| 731 |
+ |
|
| 732 |
+ // Even if a bridge exists try to setup IPv4. |
|
| 733 |
+ bridgeSetup.queueStep(setupBridgeIPv4) |
|
| 734 |
+ |
|
| 735 |
+ enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil |
|
| 736 |
+ |
|
| 737 |
+ // Conditionally queue setup steps depending on configuration values. |
|
| 738 |
+ for _, step := range []struct {
|
|
| 739 |
+ Condition bool |
|
| 740 |
+ Fn setupStep |
|
| 741 |
+ }{
|
|
| 742 |
+ // Enable IPv6 on the bridge if required. We do this even for a |
|
| 743 |
+ // previously existing bridge, as it may be here from a previous |
|
| 744 |
+ // installation where IPv6 wasn't supported yet and needs to be |
|
| 745 |
+ // assigned an IPv6 link-local address. |
|
| 746 |
+ {config.EnableIPv6, setupBridgeIPv6},
|
|
| 747 |
+ |
|
| 748 |
+ // We ensure that the bridge has the expectedIPv4 and IPv6 addresses in |
|
| 749 |
+ // the case of a previously existing device. |
|
| 750 |
+ {bridgeAlreadyExists && !config.InhibitIPv4, setupVerifyAndReconcile},
|
|
| 751 |
+ |
|
| 752 |
+ // Enable IPv6 Forwarding |
|
| 753 |
+ {enableIPv6Forwarding, setupIPv6Forwarding},
|
|
| 754 |
+ |
|
| 755 |
+ // Setup Loopback Addresses Routing |
|
| 756 |
+ {!d.config.EnableUserlandProxy, setupLoopbackAddressesRouting},
|
|
| 757 |
+ |
|
| 758 |
+ // Setup IPTables. |
|
| 759 |
+ {d.config.EnableIPTables, network.setupIP4Tables},
|
|
| 760 |
+ |
|
| 761 |
+ // Setup IP6Tables. |
|
| 762 |
+ {config.EnableIPv6 && d.config.EnableIP6Tables, network.setupIP6Tables},
|
|
| 763 |
+ |
|
| 764 |
+ // We want to track firewalld configuration so that |
|
| 765 |
+ // if it is started/reloaded, the rules can be applied correctly |
|
| 766 |
+ {d.config.EnableIPTables, network.setupFirewalld},
|
|
| 767 |
+ // same for IPv6 |
|
| 768 |
+ {config.EnableIPv6 && d.config.EnableIP6Tables, network.setupFirewalld6},
|
|
| 769 |
+ |
|
| 770 |
+ // Setup DefaultGatewayIPv4 |
|
| 771 |
+ {config.DefaultGatewayIPv4 != nil, setupGatewayIPv4},
|
|
| 772 |
+ |
|
| 773 |
+ // Setup DefaultGatewayIPv6 |
|
| 774 |
+ {config.DefaultGatewayIPv6 != nil, setupGatewayIPv6},
|
|
| 775 |
+ |
|
| 776 |
+ // Add inter-network communication rules. |
|
| 777 |
+ {d.config.EnableIPTables, setupNetworkIsolationRules},
|
|
| 778 |
+ |
|
| 779 |
+ // Configure bridge networking filtering if ICC is off and IP tables are enabled |
|
| 780 |
+ {!config.EnableICC && d.config.EnableIPTables, setupBridgeNetFiltering},
|
|
| 781 |
+ } {
|
|
| 782 |
+ if step.Condition {
|
|
| 783 |
+ bridgeSetup.queueStep(step.Fn) |
|
| 784 |
+ } |
|
| 785 |
+ } |
|
| 786 |
+ |
|
| 787 |
+ // Apply the prepared list of steps, and abort at the first error. |
|
| 788 |
+ bridgeSetup.queueStep(setupDeviceUp) |
|
| 789 |
+ return bridgeSetup.apply() |
|
| 790 |
+} |
|
| 791 |
+ |
|
| 792 |
+func (d *driver) DeleteNetwork(nid string) error {
|
|
| 793 |
+ d.configNetwork.Lock() |
|
| 794 |
+ defer d.configNetwork.Unlock() |
|
| 795 |
+ |
|
| 796 |
+ return d.deleteNetwork(nid) |
|
| 797 |
+} |
|
| 798 |
+ |
|
| 799 |
+func (d *driver) deleteNetwork(nid string) error {
|
|
| 800 |
+ var err error |
|
| 801 |
+ |
|
| 802 |
+ // Get network handler and remove it from driver |
|
| 803 |
+ d.Lock() |
|
| 804 |
+ n, ok := d.networks[nid] |
|
| 805 |
+ d.Unlock() |
|
| 806 |
+ |
|
| 807 |
+ if !ok {
|
|
| 808 |
+ return types.InternalMaskableErrorf("network %s does not exist", nid)
|
|
| 809 |
+ } |
|
| 810 |
+ |
|
| 811 |
+ n.Lock() |
|
| 812 |
+ config := n.config |
|
| 813 |
+ n.Unlock() |
|
| 814 |
+ |
|
| 815 |
+ // delele endpoints belong to this network |
|
| 816 |
+ for _, ep := range n.endpoints {
|
|
| 817 |
+ if err := n.releasePorts(ep); err != nil {
|
|
| 818 |
+ log.G(context.TODO()).Warn(err) |
|
| 819 |
+ } |
|
| 820 |
+ if link, err := d.nlh.LinkByName(ep.srcName); err == nil {
|
|
| 821 |
+ if err := d.nlh.LinkDel(link); err != nil {
|
|
| 822 |
+ log.G(context.TODO()).WithError(err).Errorf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id)
|
|
| 823 |
+ } |
|
| 824 |
+ } |
|
| 825 |
+ |
|
| 826 |
+ if err := d.storeDelete(ep); err != nil {
|
|
| 827 |
+ log.G(context.TODO()).Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err)
|
|
| 828 |
+ } |
|
| 829 |
+ } |
|
| 830 |
+ |
|
| 831 |
+ d.Lock() |
|
| 832 |
+ delete(d.networks, nid) |
|
| 833 |
+ d.Unlock() |
|
| 834 |
+ |
|
| 835 |
+ // On failure set network handler back in driver, but |
|
| 836 |
+ // only if is not already taken over by some other thread |
|
| 837 |
+ defer func() {
|
|
| 838 |
+ if err != nil {
|
|
| 839 |
+ d.Lock() |
|
| 840 |
+ if _, ok := d.networks[nid]; !ok {
|
|
| 841 |
+ d.networks[nid] = n |
|
| 842 |
+ } |
|
| 843 |
+ d.Unlock() |
|
| 844 |
+ } |
|
| 845 |
+ }() |
|
| 846 |
+ |
|
| 847 |
+ switch config.BridgeIfaceCreator {
|
|
| 848 |
+ case ifaceCreatedByLibnetwork, ifaceCreatorUnknown: |
|
| 849 |
+ // We only delete the bridge if it was created by the bridge driver and |
|
| 850 |
+ // it is not the default one (to keep the backward compatible behavior.) |
|
| 851 |
+ if !config.DefaultBridge {
|
|
| 852 |
+ if err := d.nlh.LinkDel(n.bridge.Link); err != nil {
|
|
| 853 |
+ log.G(context.TODO()).Warnf("Failed to remove bridge interface %s on network %s delete: %v", config.BridgeName, nid, err)
|
|
| 854 |
+ } |
|
| 855 |
+ } |
|
| 856 |
+ case ifaceCreatedByUser: |
|
| 857 |
+ // Don't delete the bridge interface if it was not created by libnetwork. |
|
| 858 |
+ } |
|
| 859 |
+ |
|
| 860 |
+ // clean all relevant iptables rules |
|
| 861 |
+ for _, cleanFunc := range n.iptCleanFuncs {
|
|
| 862 |
+ if errClean := cleanFunc(); errClean != nil {
|
|
| 863 |
+ log.G(context.TODO()).Warnf("Failed to clean iptables rules for bridge network: %v", errClean)
|
|
| 864 |
+ } |
|
| 865 |
+ } |
|
| 866 |
+ return d.storeDelete(config) |
|
| 867 |
+} |
|
| 868 |
+ |
|
| 869 |
+func addToBridge(nlh *netlink.Handle, ifaceName, bridgeName string) error {
|
|
| 870 |
+ lnk, err := nlh.LinkByName(ifaceName) |
|
| 871 |
+ if err != nil {
|
|
| 872 |
+ return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
|
|
| 873 |
+ } |
|
| 874 |
+ if err := nlh.LinkSetMaster(lnk, &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridgeName}}); err != nil {
|
|
| 875 |
+ log.G(context.TODO()).WithError(err).Errorf("Failed to add %s to bridge via netlink", ifaceName)
|
|
| 876 |
+ return err |
|
| 877 |
+ } |
|
| 878 |
+ return nil |
|
| 879 |
+} |
|
| 880 |
+ |
|
| 881 |
+func setHairpinMode(nlh *netlink.Handle, link netlink.Link, enable bool) error {
|
|
| 882 |
+ err := nlh.LinkSetHairpin(link, enable) |
|
| 883 |
+ if err != nil {
|
|
| 884 |
+ return fmt.Errorf("unable to set hairpin mode on %s via netlink: %v",
|
|
| 885 |
+ link.Attrs().Name, err) |
|
| 886 |
+ } |
|
| 887 |
+ return nil |
|
| 888 |
+} |
|
| 889 |
+ |
|
| 890 |
+func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
|
| 891 |
+ if ifInfo == nil {
|
|
| 892 |
+ return errors.New("invalid interface info passed")
|
|
| 893 |
+ } |
|
| 894 |
+ |
|
| 895 |
+ // Get the network handler and make sure it exists |
|
| 896 |
+ d.Lock() |
|
| 897 |
+ n, ok := d.networks[nid] |
|
| 898 |
+ dconfig := d.config |
|
| 899 |
+ d.Unlock() |
|
| 900 |
+ |
|
| 901 |
+ if !ok {
|
|
| 902 |
+ return types.NotFoundErrorf("network %s does not exist", nid)
|
|
| 903 |
+ } |
|
| 904 |
+ if n == nil {
|
|
| 905 |
+ return driverapi.ErrNoNetwork(nid) |
|
| 906 |
+ } |
|
| 907 |
+ |
|
| 908 |
+ // Sanity check |
|
| 909 |
+ n.Lock() |
|
| 910 |
+ if n.id != nid {
|
|
| 911 |
+ n.Unlock() |
|
| 912 |
+ return InvalidNetworkIDError(nid) |
|
| 913 |
+ } |
|
| 914 |
+ n.Unlock() |
|
| 915 |
+ |
|
| 916 |
+ // Check if endpoint id is good and retrieve correspondent endpoint |
|
| 917 |
+ ep, err := n.getEndpoint(eid) |
|
| 918 |
+ if err != nil {
|
|
| 919 |
+ return err |
|
| 920 |
+ } |
|
| 921 |
+ |
|
| 922 |
+ // Endpoint with that id exists either on desired or other sandbox |
|
| 923 |
+ if ep != nil {
|
|
| 924 |
+ return driverapi.ErrEndpointExists(eid) |
|
| 925 |
+ } |
|
| 926 |
+ |
|
| 927 |
+ // Try to convert the options to endpoint configuration |
|
| 928 |
+ epConfig, err := parseEndpointOptions(epOptions) |
|
| 929 |
+ if err != nil {
|
|
| 930 |
+ return err |
|
| 931 |
+ } |
|
| 932 |
+ |
|
| 933 |
+ // Create and add the endpoint |
|
| 934 |
+ n.Lock() |
|
| 935 |
+ endpoint := &bridgeEndpoint{id: eid, nid: nid, config: epConfig}
|
|
| 936 |
+ n.endpoints[eid] = endpoint |
|
| 937 |
+ n.Unlock() |
|
| 938 |
+ |
|
| 939 |
+ // On failure make sure to remove the endpoint |
|
| 940 |
+ defer func() {
|
|
| 941 |
+ if err != nil {
|
|
| 942 |
+ n.Lock() |
|
| 943 |
+ delete(n.endpoints, eid) |
|
| 944 |
+ n.Unlock() |
|
| 945 |
+ } |
|
| 946 |
+ }() |
|
| 947 |
+ |
|
| 948 |
+ // Generate a name for what will be the host side pipe interface |
|
| 949 |
+ hostIfName, err := netutils.GenerateIfaceName(d.nlh, vethPrefix, vethLen) |
|
| 950 |
+ if err != nil {
|
|
| 951 |
+ return err |
|
| 952 |
+ } |
|
| 953 |
+ |
|
| 954 |
+ // Generate a name for what will be the sandbox side pipe interface |
|
| 955 |
+ containerIfName, err := netutils.GenerateIfaceName(d.nlh, vethPrefix, vethLen) |
|
| 956 |
+ if err != nil {
|
|
| 957 |
+ return err |
|
| 958 |
+ } |
|
| 959 |
+ |
|
| 960 |
+ // Generate and add the interface pipe host <-> sandbox |
|
| 961 |
+ veth := &netlink.Veth{
|
|
| 962 |
+ LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0},
|
|
| 963 |
+ PeerName: containerIfName, |
|
| 964 |
+ } |
|
| 965 |
+ if err = d.nlh.LinkAdd(veth); err != nil {
|
|
| 966 |
+ return types.InternalErrorf("failed to add the host (%s) <=> sandbox (%s) pair interfaces: %v", hostIfName, containerIfName, err)
|
|
| 967 |
+ } |
|
| 968 |
+ |
|
| 969 |
+ // Get the host side pipe interface handler |
|
| 970 |
+ host, err := d.nlh.LinkByName(hostIfName) |
|
| 971 |
+ if err != nil {
|
|
| 972 |
+ return types.InternalErrorf("failed to find host side interface %s: %v", hostIfName, err)
|
|
| 973 |
+ } |
|
| 974 |
+ defer func() {
|
|
| 975 |
+ if err != nil {
|
|
| 976 |
+ if err := d.nlh.LinkDel(host); err != nil {
|
|
| 977 |
+ log.G(context.TODO()).WithError(err).Warnf("Failed to delete host side interface (%s)'s link", hostIfName)
|
|
| 978 |
+ } |
|
| 979 |
+ } |
|
| 980 |
+ }() |
|
| 981 |
+ |
|
| 982 |
+ // Get the sandbox side pipe interface handler |
|
| 983 |
+ sbox, err := d.nlh.LinkByName(containerIfName) |
|
| 984 |
+ if err != nil {
|
|
| 985 |
+ return types.InternalErrorf("failed to find sandbox side interface %s: %v", containerIfName, err)
|
|
| 986 |
+ } |
|
| 987 |
+ defer func() {
|
|
| 988 |
+ if err != nil {
|
|
| 989 |
+ if err := d.nlh.LinkDel(sbox); err != nil {
|
|
| 990 |
+ log.G(context.TODO()).WithError(err).Warnf("Failed to delete sandbox side interface (%s)'s link", containerIfName)
|
|
| 991 |
+ } |
|
| 992 |
+ } |
|
| 993 |
+ }() |
|
| 994 |
+ |
|
| 995 |
+ n.Lock() |
|
| 996 |
+ config := n.config |
|
| 997 |
+ n.Unlock() |
|
| 998 |
+ |
|
| 999 |
+ // Add bridge inherited attributes to pipe interfaces |
|
| 1000 |
+ if config.Mtu != 0 {
|
|
| 1001 |
+ err = d.nlh.LinkSetMTU(host, config.Mtu) |
|
| 1002 |
+ if err != nil {
|
|
| 1003 |
+ return types.InternalErrorf("failed to set MTU on host interface %s: %v", hostIfName, err)
|
|
| 1004 |
+ } |
|
| 1005 |
+ err = d.nlh.LinkSetMTU(sbox, config.Mtu) |
|
| 1006 |
+ if err != nil {
|
|
| 1007 |
+ return types.InternalErrorf("failed to set MTU on sandbox interface %s: %v", containerIfName, err)
|
|
| 1008 |
+ } |
|
| 1009 |
+ } |
|
| 1010 |
+ |
|
| 1011 |
+ // Attach host side pipe interface into the bridge |
|
| 1012 |
+ if err = addToBridge(d.nlh, hostIfName, config.BridgeName); err != nil {
|
|
| 1013 |
+ return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
|
|
| 1014 |
+ } |
|
| 1015 |
+ |
|
| 1016 |
+ if !dconfig.EnableUserlandProxy {
|
|
| 1017 |
+ err = setHairpinMode(d.nlh, host, true) |
|
| 1018 |
+ if err != nil {
|
|
| 1019 |
+ return err |
|
| 1020 |
+ } |
|
| 1021 |
+ } |
|
| 1022 |
+ |
|
| 1023 |
+ // Store the sandbox side pipe interface parameters |
|
| 1024 |
+ endpoint.srcName = containerIfName |
|
| 1025 |
+ endpoint.macAddress = ifInfo.MacAddress() |
|
| 1026 |
+ endpoint.addr = ifInfo.Address() |
|
| 1027 |
+ endpoint.addrv6 = ifInfo.AddressIPv6() |
|
| 1028 |
+ |
|
| 1029 |
+ // Set the sbox's MAC if not provided. If specified, use the one configured by user, otherwise generate one based on IP. |
|
| 1030 |
+ if endpoint.macAddress == nil {
|
|
| 1031 |
+ endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP) |
|
| 1032 |
+ if err = ifInfo.SetMacAddress(endpoint.macAddress); err != nil {
|
|
| 1033 |
+ return err |
|
| 1034 |
+ } |
|
| 1035 |
+ } |
|
| 1036 |
+ |
|
| 1037 |
+ // Up the host interface after finishing all netlink configuration |
|
| 1038 |
+ if err = d.nlh.LinkSetUp(host); err != nil {
|
|
| 1039 |
+ return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
|
|
| 1040 |
+ } |
|
| 1041 |
+ |
|
| 1042 |
+ if endpoint.addrv6 == nil && config.EnableIPv6 {
|
|
| 1043 |
+ var ip6 net.IP |
|
| 1044 |
+ network := n.bridge.bridgeIPv6 |
|
| 1045 |
+ if config.AddressIPv6 != nil {
|
|
| 1046 |
+ network = config.AddressIPv6 |
|
| 1047 |
+ } |
|
| 1048 |
+ |
|
| 1049 |
+ ones, _ := network.Mask.Size() |
|
| 1050 |
+ if ones > 80 {
|
|
| 1051 |
+ err = types.ForbiddenErrorf("Cannot self generate an IPv6 address on network %v: At least 48 host bits are needed.", network)
|
|
| 1052 |
+ return err |
|
| 1053 |
+ } |
|
| 1054 |
+ |
|
| 1055 |
+ ip6 = make(net.IP, len(network.IP)) |
|
| 1056 |
+ copy(ip6, network.IP) |
|
| 1057 |
+ for i, h := range endpoint.macAddress {
|
|
| 1058 |
+ ip6[i+10] = h |
|
| 1059 |
+ } |
|
| 1060 |
+ |
|
| 1061 |
+ endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
|
|
| 1062 |
+ if err = ifInfo.SetIPAddress(endpoint.addrv6); err != nil {
|
|
| 1063 |
+ return err |
|
| 1064 |
+ } |
|
| 1065 |
+ } |
|
| 1066 |
+ |
|
| 1067 |
+ if err = d.storeUpdate(endpoint); err != nil {
|
|
| 1068 |
+ return fmt.Errorf("failed to save bridge endpoint %.7s to store: %v", endpoint.id, err)
|
|
| 1069 |
+ } |
|
| 1070 |
+ |
|
| 1071 |
+ return nil |
|
| 1072 |
+} |
|
| 1073 |
+ |
|
| 1074 |
+func (d *driver) DeleteEndpoint(nid, eid string) error {
|
|
| 1075 |
+ var err error |
|
| 1076 |
+ |
|
| 1077 |
+ // Get the network handler and make sure it exists |
|
| 1078 |
+ d.Lock() |
|
| 1079 |
+ n, ok := d.networks[nid] |
|
| 1080 |
+ d.Unlock() |
|
| 1081 |
+ |
|
| 1082 |
+ if !ok {
|
|
| 1083 |
+ return types.InternalMaskableErrorf("network %s does not exist", nid)
|
|
| 1084 |
+ } |
|
| 1085 |
+ if n == nil {
|
|
| 1086 |
+ return driverapi.ErrNoNetwork(nid) |
|
| 1087 |
+ } |
|
| 1088 |
+ |
|
| 1089 |
+ // Sanity Check |
|
| 1090 |
+ n.Lock() |
|
| 1091 |
+ if n.id != nid {
|
|
| 1092 |
+ n.Unlock() |
|
| 1093 |
+ return InvalidNetworkIDError(nid) |
|
| 1094 |
+ } |
|
| 1095 |
+ n.Unlock() |
|
| 1096 |
+ |
|
| 1097 |
+ // Check endpoint id and if an endpoint is actually there |
|
| 1098 |
+ ep, err := n.getEndpoint(eid) |
|
| 1099 |
+ if err != nil {
|
|
| 1100 |
+ return err |
|
| 1101 |
+ } |
|
| 1102 |
+ if ep == nil {
|
|
| 1103 |
+ return EndpointNotFoundError(eid) |
|
| 1104 |
+ } |
|
| 1105 |
+ |
|
| 1106 |
+ // Remove it |
|
| 1107 |
+ n.Lock() |
|
| 1108 |
+ delete(n.endpoints, eid) |
|
| 1109 |
+ n.Unlock() |
|
| 1110 |
+ |
|
| 1111 |
+ // On failure make sure to set back ep in n.endpoints, but only |
|
| 1112 |
+ // if it hasn't been taken over already by some other thread. |
|
| 1113 |
+ defer func() {
|
|
| 1114 |
+ if err != nil {
|
|
| 1115 |
+ n.Lock() |
|
| 1116 |
+ if _, ok := n.endpoints[eid]; !ok {
|
|
| 1117 |
+ n.endpoints[eid] = ep |
|
| 1118 |
+ } |
|
| 1119 |
+ n.Unlock() |
|
| 1120 |
+ } |
|
| 1121 |
+ }() |
|
| 1122 |
+ |
|
| 1123 |
+ // Try removal of link. Discard error: it is a best effort. |
|
| 1124 |
+ // Also make sure defer does not see this error either. |
|
| 1125 |
+ if link, err := d.nlh.LinkByName(ep.srcName); err == nil {
|
|
| 1126 |
+ if err := d.nlh.LinkDel(link); err != nil {
|
|
| 1127 |
+ log.G(context.TODO()).WithError(err).Errorf("Failed to delete interface (%s)'s link on endpoint (%s) delete", ep.srcName, ep.id)
|
|
| 1128 |
+ } |
|
| 1129 |
+ } |
|
| 1130 |
+ |
|
| 1131 |
+ if err := d.storeDelete(ep); err != nil {
|
|
| 1132 |
+ log.G(context.TODO()).Warnf("Failed to remove bridge endpoint %.7s from store: %v", ep.id, err)
|
|
| 1133 |
+ } |
|
| 1134 |
+ |
|
| 1135 |
+ return nil |
|
| 1136 |
+} |
|
| 1137 |
+ |
|
| 1138 |
+func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
|
| 1139 |
+ // Get the network handler and make sure it exists |
|
| 1140 |
+ d.Lock() |
|
| 1141 |
+ n, ok := d.networks[nid] |
|
| 1142 |
+ d.Unlock() |
|
| 1143 |
+ if !ok {
|
|
| 1144 |
+ return nil, types.NotFoundErrorf("network %s does not exist", nid)
|
|
| 1145 |
+ } |
|
| 1146 |
+ if n == nil {
|
|
| 1147 |
+ return nil, driverapi.ErrNoNetwork(nid) |
|
| 1148 |
+ } |
|
| 1149 |
+ |
|
| 1150 |
+ // Sanity check |
|
| 1151 |
+ n.Lock() |
|
| 1152 |
+ if n.id != nid {
|
|
| 1153 |
+ n.Unlock() |
|
| 1154 |
+ return nil, InvalidNetworkIDError(nid) |
|
| 1155 |
+ } |
|
| 1156 |
+ n.Unlock() |
|
| 1157 |
+ |
|
| 1158 |
+ // Check if endpoint id is good and retrieve correspondent endpoint |
|
| 1159 |
+ ep, err := n.getEndpoint(eid) |
|
| 1160 |
+ if err != nil {
|
|
| 1161 |
+ return nil, err |
|
| 1162 |
+ } |
|
| 1163 |
+ if ep == nil {
|
|
| 1164 |
+ return nil, driverapi.ErrNoEndpoint(eid) |
|
| 1165 |
+ } |
|
| 1166 |
+ |
|
| 1167 |
+ m := make(map[string]interface{})
|
|
| 1168 |
+ |
|
| 1169 |
+ if ep.extConnConfig != nil && ep.extConnConfig.ExposedPorts != nil {
|
|
| 1170 |
+ // Return a copy of the config data |
|
| 1171 |
+ epc := make([]types.TransportPort, 0, len(ep.extConnConfig.ExposedPorts)) |
|
| 1172 |
+ for _, tp := range ep.extConnConfig.ExposedPorts {
|
|
| 1173 |
+ epc = append(epc, tp.GetCopy()) |
|
| 1174 |
+ } |
|
| 1175 |
+ m[netlabel.ExposedPorts] = epc |
|
| 1176 |
+ } |
|
| 1177 |
+ |
|
| 1178 |
+ if ep.portMapping != nil {
|
|
| 1179 |
+ // Return a copy of the operational data |
|
| 1180 |
+ pmc := make([]types.PortBinding, 0, len(ep.portMapping)) |
|
| 1181 |
+ for _, pm := range ep.portMapping {
|
|
| 1182 |
+ pmc = append(pmc, pm.GetCopy()) |
|
| 1183 |
+ } |
|
| 1184 |
+ m[netlabel.PortMap] = pmc |
|
| 1185 |
+ } |
|
| 1186 |
+ |
|
| 1187 |
+ if len(ep.macAddress) != 0 {
|
|
| 1188 |
+ m[netlabel.MacAddress] = ep.macAddress |
|
| 1189 |
+ } |
|
| 1190 |
+ |
|
| 1191 |
+ return m, nil |
|
| 1192 |
+} |
|
| 1193 |
+ |
|
| 1194 |
+// Join method is invoked when a Sandbox is attached to an endpoint. |
|
| 1195 |
+func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
|
| 1196 |
+ network, err := d.getNetwork(nid) |
|
| 1197 |
+ if err != nil {
|
|
| 1198 |
+ return err |
|
| 1199 |
+ } |
|
| 1200 |
+ |
|
| 1201 |
+ endpoint, err := network.getEndpoint(eid) |
|
| 1202 |
+ if err != nil {
|
|
| 1203 |
+ return err |
|
| 1204 |
+ } |
|
| 1205 |
+ |
|
| 1206 |
+ if endpoint == nil {
|
|
| 1207 |
+ return EndpointNotFoundError(eid) |
|
| 1208 |
+ } |
|
| 1209 |
+ |
|
| 1210 |
+ endpoint.containerConfig, err = parseContainerOptions(options) |
|
| 1211 |
+ if err != nil {
|
|
| 1212 |
+ return err |
|
| 1213 |
+ } |
|
| 1214 |
+ |
|
| 1215 |
+ iNames := jinfo.InterfaceName() |
|
| 1216 |
+ containerVethPrefix := defaultContainerVethPrefix |
|
| 1217 |
+ if network.config.ContainerIfacePrefix != "" {
|
|
| 1218 |
+ containerVethPrefix = network.config.ContainerIfacePrefix |
|
| 1219 |
+ } |
|
| 1220 |
+ err = iNames.SetNames(endpoint.srcName, containerVethPrefix) |
|
| 1221 |
+ if err != nil {
|
|
| 1222 |
+ return err |
|
| 1223 |
+ } |
|
| 1224 |
+ |
|
| 1225 |
+ err = jinfo.SetGateway(network.bridge.gatewayIPv4) |
|
| 1226 |
+ if err != nil {
|
|
| 1227 |
+ return err |
|
| 1228 |
+ } |
|
| 1229 |
+ |
|
| 1230 |
+ err = jinfo.SetGatewayIPv6(network.bridge.gatewayIPv6) |
|
| 1231 |
+ if err != nil {
|
|
| 1232 |
+ return err |
|
| 1233 |
+ } |
|
| 1234 |
+ |
|
| 1235 |
+ return nil |
|
| 1236 |
+} |
|
| 1237 |
+ |
|
| 1238 |
+// Leave method is invoked when a Sandbox detaches from an endpoint. |
|
| 1239 |
+func (d *driver) Leave(nid, eid string) error {
|
|
| 1240 |
+ network, err := d.getNetwork(nid) |
|
| 1241 |
+ if err != nil {
|
|
| 1242 |
+ return types.InternalMaskableErrorf("%s", err)
|
|
| 1243 |
+ } |
|
| 1244 |
+ |
|
| 1245 |
+ endpoint, err := network.getEndpoint(eid) |
|
| 1246 |
+ if err != nil {
|
|
| 1247 |
+ return err |
|
| 1248 |
+ } |
|
| 1249 |
+ |
|
| 1250 |
+ if endpoint == nil {
|
|
| 1251 |
+ return EndpointNotFoundError(eid) |
|
| 1252 |
+ } |
|
| 1253 |
+ |
|
| 1254 |
+ if !network.config.EnableICC {
|
|
| 1255 |
+ if err = d.link(network, endpoint, false); err != nil {
|
|
| 1256 |
+ return err |
|
| 1257 |
+ } |
|
| 1258 |
+ } |
|
| 1259 |
+ |
|
| 1260 |
+ return nil |
|
| 1261 |
+} |
|
| 1262 |
+ |
|
| 1263 |
+func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
|
|
| 1264 |
+ network, err := d.getNetwork(nid) |
|
| 1265 |
+ if err != nil {
|
|
| 1266 |
+ return err |
|
| 1267 |
+ } |
|
| 1268 |
+ |
|
| 1269 |
+ endpoint, err := network.getEndpoint(eid) |
|
| 1270 |
+ if err != nil {
|
|
| 1271 |
+ return err |
|
| 1272 |
+ } |
|
| 1273 |
+ |
|
| 1274 |
+ if endpoint == nil {
|
|
| 1275 |
+ return EndpointNotFoundError(eid) |
|
| 1276 |
+ } |
|
| 1277 |
+ |
|
| 1278 |
+ endpoint.extConnConfig, err = parseConnectivityOptions(options) |
|
| 1279 |
+ if err != nil {
|
|
| 1280 |
+ return err |
|
| 1281 |
+ } |
|
| 1282 |
+ |
|
| 1283 |
+ // Program any required port mapping and store them in the endpoint |
|
| 1284 |
+ endpoint.portMapping, err = network.allocatePorts(endpoint, network.config.DefaultBindingIP, d.config.EnableUserlandProxy) |
|
| 1285 |
+ if err != nil {
|
|
| 1286 |
+ return err |
|
| 1287 |
+ } |
|
| 1288 |
+ |
|
| 1289 |
+ defer func() {
|
|
| 1290 |
+ if err != nil {
|
|
| 1291 |
+ if e := network.releasePorts(endpoint); e != nil {
|
|
| 1292 |
+ log.G(context.TODO()).Errorf("Failed to release ports allocated for the bridge endpoint %s on failure %v because of %v",
|
|
| 1293 |
+ eid, err, e) |
|
| 1294 |
+ } |
|
| 1295 |
+ endpoint.portMapping = nil |
|
| 1296 |
+ } |
|
| 1297 |
+ }() |
|
| 1298 |
+ |
|
| 1299 |
+ // Clean the connection tracker state of the host for the specific endpoint. This is needed because some flows may |
|
| 1300 |
+ // be bound to the local proxy, or to the host (for UDP packets), and won't be redirected to the new endpoints. |
|
| 1301 |
+ clearConntrackEntries(d.nlh, endpoint) |
|
| 1302 |
+ |
|
| 1303 |
+ if err = d.storeUpdate(endpoint); err != nil {
|
|
| 1304 |
+ return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err)
|
|
| 1305 |
+ } |
|
| 1306 |
+ |
|
| 1307 |
+ if !network.config.EnableICC {
|
|
| 1308 |
+ return d.link(network, endpoint, true) |
|
| 1309 |
+ } |
|
| 1310 |
+ |
|
| 1311 |
+ return nil |
|
| 1312 |
+} |
|
| 1313 |
+ |
|
| 1314 |
+func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
|
| 1315 |
+ network, err := d.getNetwork(nid) |
|
| 1316 |
+ if err != nil {
|
|
| 1317 |
+ return err |
|
| 1318 |
+ } |
|
| 1319 |
+ |
|
| 1320 |
+ endpoint, err := network.getEndpoint(eid) |
|
| 1321 |
+ if err != nil {
|
|
| 1322 |
+ return err |
|
| 1323 |
+ } |
|
| 1324 |
+ |
|
| 1325 |
+ if endpoint == nil {
|
|
| 1326 |
+ return EndpointNotFoundError(eid) |
|
| 1327 |
+ } |
|
| 1328 |
+ |
|
| 1329 |
+ err = network.releasePorts(endpoint) |
|
| 1330 |
+ if err != nil {
|
|
| 1331 |
+ log.G(context.TODO()).Warn(err) |
|
| 1332 |
+ } |
|
| 1333 |
+ |
|
| 1334 |
+ endpoint.portMapping = nil |
|
| 1335 |
+ |
|
| 1336 |
+ // Clean the connection tracker state of the host for the specific endpoint. This is a precautionary measure to |
|
| 1337 |
+ // avoid new endpoints getting the same IP address to receive unexpected packets due to bad conntrack state leading |
|
| 1338 |
+ // to bad NATing. |
|
| 1339 |
+ clearConntrackEntries(d.nlh, endpoint) |
|
| 1340 |
+ |
|
| 1341 |
+ if err = d.storeUpdate(endpoint); err != nil {
|
|
| 1342 |
+ return fmt.Errorf("failed to update bridge endpoint %.7s to store: %v", endpoint.id, err)
|
|
| 1343 |
+ } |
|
| 1344 |
+ |
|
| 1345 |
+ return nil |
|
| 1346 |
+} |
|
| 1347 |
+ |
|
| 1348 |
+func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, enable bool) (retErr error) {
|
|
| 1349 |
+ cc := endpoint.containerConfig |
|
| 1350 |
+ ec := endpoint.extConnConfig |
|
| 1351 |
+ if cc == nil || ec == nil || (len(cc.ParentEndpoints) == 0 && len(cc.ChildEndpoints) == 0) {
|
|
| 1352 |
+ // nothing to do |
|
| 1353 |
+ return nil |
|
| 1354 |
+ } |
|
| 1355 |
+ |
|
| 1356 |
+ // Try to keep things atomic. addedLinks keeps track of links that were |
|
| 1357 |
+ // successfully added. If any error occurred, then roll back all. |
|
| 1358 |
+ var addedLinks []*link |
|
| 1359 |
+ defer func() {
|
|
| 1360 |
+ if retErr == nil {
|
|
| 1361 |
+ return |
|
| 1362 |
+ } |
|
| 1363 |
+ for _, l := range addedLinks {
|
|
| 1364 |
+ l.Disable() |
|
| 1365 |
+ } |
|
| 1366 |
+ }() |
|
| 1367 |
+ |
|
| 1368 |
+ if ec.ExposedPorts != nil {
|
|
| 1369 |
+ for _, p := range cc.ParentEndpoints {
|
|
| 1370 |
+ parentEndpoint, err := network.getEndpoint(p) |
|
| 1371 |
+ if err != nil {
|
|
| 1372 |
+ return err |
|
| 1373 |
+ } |
|
| 1374 |
+ if parentEndpoint == nil {
|
|
| 1375 |
+ return InvalidEndpointIDError(p) |
|
| 1376 |
+ } |
|
| 1377 |
+ |
|
| 1378 |
+ l, err := newLink(parentEndpoint.addr.IP, endpoint.addr.IP, ec.ExposedPorts, network.config.BridgeName) |
|
| 1379 |
+ if err != nil {
|
|
| 1380 |
+ return err |
|
| 1381 |
+ } |
|
| 1382 |
+ if enable {
|
|
| 1383 |
+ if err := l.Enable(); err != nil {
|
|
| 1384 |
+ return err |
|
| 1385 |
+ } |
|
| 1386 |
+ addedLinks = append(addedLinks, l) |
|
| 1387 |
+ } else {
|
|
| 1388 |
+ l.Disable() |
|
| 1389 |
+ } |
|
| 1390 |
+ } |
|
| 1391 |
+ } |
|
| 1392 |
+ |
|
| 1393 |
+ for _, c := range cc.ChildEndpoints {
|
|
| 1394 |
+ childEndpoint, err := network.getEndpoint(c) |
|
| 1395 |
+ if err != nil {
|
|
| 1396 |
+ return err |
|
| 1397 |
+ } |
|
| 1398 |
+ if childEndpoint == nil {
|
|
| 1399 |
+ return InvalidEndpointIDError(c) |
|
| 1400 |
+ } |
|
| 1401 |
+ if childEndpoint.extConnConfig == nil || childEndpoint.extConnConfig.ExposedPorts == nil {
|
|
| 1402 |
+ continue |
|
| 1403 |
+ } |
|
| 1404 |
+ |
|
| 1405 |
+ l, err := newLink(endpoint.addr.IP, childEndpoint.addr.IP, childEndpoint.extConnConfig.ExposedPorts, network.config.BridgeName) |
|
| 1406 |
+ if err != nil {
|
|
| 1407 |
+ return err |
|
| 1408 |
+ } |
|
| 1409 |
+ if enable {
|
|
| 1410 |
+ if err := l.Enable(); err != nil {
|
|
| 1411 |
+ return err |
|
| 1412 |
+ } |
|
| 1413 |
+ addedLinks = append(addedLinks, l) |
|
| 1414 |
+ } else {
|
|
| 1415 |
+ l.Disable() |
|
| 1416 |
+ } |
|
| 1417 |
+ } |
|
| 1418 |
+ |
|
| 1419 |
+ return nil |
|
| 1420 |
+} |
|
| 1421 |
+ |
|
| 1422 |
+func (d *driver) Type() string {
|
|
| 1423 |
+ return NetworkType |
|
| 1424 |
+} |
|
| 1425 |
+ |
|
| 1426 |
+func (d *driver) IsBuiltIn() bool {
|
|
| 1427 |
+ return true |
|
| 1428 |
+} |
|
| 1429 |
+ |
|
| 1430 |
+func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfiguration, error) {
|
|
| 1431 |
+ if epOptions == nil {
|
|
| 1432 |
+ return nil, nil |
|
| 1433 |
+ } |
|
| 1434 |
+ |
|
| 1435 |
+ ec := &endpointConfiguration{}
|
|
| 1436 |
+ |
|
| 1437 |
+ if opt, ok := epOptions[netlabel.MacAddress]; ok {
|
|
| 1438 |
+ if mac, ok := opt.(net.HardwareAddr); ok {
|
|
| 1439 |
+ ec.MacAddress = mac |
|
| 1440 |
+ } else {
|
|
| 1441 |
+ return nil, &ErrInvalidEndpointConfig{}
|
|
| 1442 |
+ } |
|
| 1443 |
+ } |
|
| 1444 |
+ |
|
| 1445 |
+ return ec, nil |
|
| 1446 |
+} |
|
| 1447 |
+ |
|
| 1448 |
+func parseContainerOptions(cOptions map[string]interface{}) (*containerConfiguration, error) {
|
|
| 1449 |
+ if cOptions == nil {
|
|
| 1450 |
+ return nil, nil |
|
| 1451 |
+ } |
|
| 1452 |
+ genericData := cOptions[netlabel.GenericData] |
|
| 1453 |
+ if genericData == nil {
|
|
| 1454 |
+ return nil, nil |
|
| 1455 |
+ } |
|
| 1456 |
+ switch opt := genericData.(type) {
|
|
| 1457 |
+ case options.Generic: |
|
| 1458 |
+ opaqueConfig, err := options.GenerateFromModel(opt, &containerConfiguration{})
|
|
| 1459 |
+ if err != nil {
|
|
| 1460 |
+ return nil, err |
|
| 1461 |
+ } |
|
| 1462 |
+ return opaqueConfig.(*containerConfiguration), nil |
|
| 1463 |
+ case *containerConfiguration: |
|
| 1464 |
+ return opt, nil |
|
| 1465 |
+ default: |
|
| 1466 |
+ return nil, nil |
|
| 1467 |
+ } |
|
| 1468 |
+} |
|
| 1469 |
+ |
|
| 1470 |
+func parseConnectivityOptions(cOptions map[string]interface{}) (*connectivityConfiguration, error) {
|
|
| 1471 |
+ if cOptions == nil {
|
|
| 1472 |
+ return nil, nil |
|
| 1473 |
+ } |
|
| 1474 |
+ |
|
| 1475 |
+ cc := &connectivityConfiguration{}
|
|
| 1476 |
+ |
|
| 1477 |
+ if opt, ok := cOptions[netlabel.PortMap]; ok {
|
|
| 1478 |
+ if pb, ok := opt.([]types.PortBinding); ok {
|
|
| 1479 |
+ cc.PortBindings = pb |
|
| 1480 |
+ } else {
|
|
| 1481 |
+ return nil, types.BadRequestErrorf("Invalid port mapping data in connectivity configuration: %v", opt)
|
|
| 1482 |
+ } |
|
| 1483 |
+ } |
|
| 1484 |
+ |
|
| 1485 |
+ if opt, ok := cOptions[netlabel.ExposedPorts]; ok {
|
|
| 1486 |
+ if ports, ok := opt.([]types.TransportPort); ok {
|
|
| 1487 |
+ cc.ExposedPorts = ports |
|
| 1488 |
+ } else {
|
|
| 1489 |
+ return nil, types.BadRequestErrorf("Invalid exposed ports data in connectivity configuration: %v", opt)
|
|
| 1490 |
+ } |
|
| 1491 |
+ } |
|
| 1492 |
+ |
|
| 1493 |
+ return cc, nil |
|
| 1494 |
+} |
|
| 1495 |
+ |
|
| 1496 |
+func electMacAddress(epConfig *endpointConfiguration, ip net.IP) net.HardwareAddr {
|
|
| 1497 |
+ if epConfig != nil && epConfig.MacAddress != nil {
|
|
| 1498 |
+ return epConfig.MacAddress |
|
| 1499 |
+ } |
|
| 1500 |
+ return netutils.GenerateMACFromIP(ip) |
|
| 1501 |
+} |
| 0 | 1502 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,1165 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "net" |
|
| 7 |
+ "regexp" |
|
| 8 |
+ "strconv" |
|
| 9 |
+ "testing" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 12 |
+ "github.com/docker/docker/libnetwork/driverapi" |
|
| 13 |
+ "github.com/docker/docker/libnetwork/ipamutils" |
|
| 14 |
+ "github.com/docker/docker/libnetwork/iptables" |
|
| 15 |
+ "github.com/docker/docker/libnetwork/netlabel" |
|
| 16 |
+ "github.com/docker/docker/libnetwork/netutils" |
|
| 17 |
+ "github.com/docker/docker/libnetwork/options" |
|
| 18 |
+ "github.com/docker/docker/libnetwork/portallocator" |
|
| 19 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 20 |
+ "github.com/vishvananda/netlink" |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+func TestEndpointMarshalling(t *testing.T) {
|
|
| 24 |
+ ip1, _ := types.ParseCIDR("172.22.0.9/16")
|
|
| 25 |
+ ip2, _ := types.ParseCIDR("2001:db8::9")
|
|
| 26 |
+ mac, _ := net.ParseMAC("ac:bd:24:57:66:77")
|
|
| 27 |
+ e := &bridgeEndpoint{
|
|
| 28 |
+ id: "d2c015a1fe5930650cbcd50493efba0500bcebd8ee1f4401a16319f8a567de33", |
|
| 29 |
+ nid: "ee33fbb43c323f1920b6b35a0101552ac22ede960d0e5245e9738bccc68b2415", |
|
| 30 |
+ addr: ip1, |
|
| 31 |
+ addrv6: ip2, |
|
| 32 |
+ macAddress: mac, |
|
| 33 |
+ srcName: "veth123456", |
|
| 34 |
+ config: &endpointConfiguration{MacAddress: mac},
|
|
| 35 |
+ containerConfig: &containerConfiguration{
|
|
| 36 |
+ ParentEndpoints: []string{"one", "due", "three"},
|
|
| 37 |
+ ChildEndpoints: []string{"four", "five", "six"},
|
|
| 38 |
+ }, |
|
| 39 |
+ extConnConfig: &connectivityConfiguration{
|
|
| 40 |
+ ExposedPorts: []types.TransportPort{
|
|
| 41 |
+ {
|
|
| 42 |
+ Proto: 6, |
|
| 43 |
+ Port: uint16(18), |
|
| 44 |
+ }, |
|
| 45 |
+ }, |
|
| 46 |
+ PortBindings: []types.PortBinding{
|
|
| 47 |
+ {
|
|
| 48 |
+ Proto: 6, |
|
| 49 |
+ IP: net.ParseIP("17210.33.9.56"),
|
|
| 50 |
+ Port: uint16(18), |
|
| 51 |
+ HostPort: uint16(3000), |
|
| 52 |
+ HostPortEnd: uint16(14000), |
|
| 53 |
+ }, |
|
| 54 |
+ }, |
|
| 55 |
+ }, |
|
| 56 |
+ portMapping: []types.PortBinding{
|
|
| 57 |
+ {
|
|
| 58 |
+ Proto: 17, |
|
| 59 |
+ IP: net.ParseIP("172.33.9.56"),
|
|
| 60 |
+ Port: uint16(99), |
|
| 61 |
+ HostIP: net.ParseIP("10.10.100.2"),
|
|
| 62 |
+ HostPort: uint16(9900), |
|
| 63 |
+ HostPortEnd: uint16(10000), |
|
| 64 |
+ }, |
|
| 65 |
+ {
|
|
| 66 |
+ Proto: 6, |
|
| 67 |
+ IP: net.ParseIP("171.33.9.56"),
|
|
| 68 |
+ Port: uint16(55), |
|
| 69 |
+ HostIP: net.ParseIP("10.11.100.2"),
|
|
| 70 |
+ HostPort: uint16(5500), |
|
| 71 |
+ HostPortEnd: uint16(55000), |
|
| 72 |
+ }, |
|
| 73 |
+ }, |
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ b, err := json.Marshal(e) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ t.Fatal(err) |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ ee := &bridgeEndpoint{}
|
|
| 82 |
+ err = json.Unmarshal(b, ee) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ t.Fatal(err) |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ if e.id != ee.id || e.nid != ee.nid || e.srcName != ee.srcName || !bytes.Equal(e.macAddress, ee.macAddress) || |
|
| 88 |
+ !types.CompareIPNet(e.addr, ee.addr) || !types.CompareIPNet(e.addrv6, ee.addrv6) || |
|
| 89 |
+ !compareEpConfig(e.config, ee.config) || |
|
| 90 |
+ !compareContainerConfig(e.containerConfig, ee.containerConfig) || |
|
| 91 |
+ !compareConnConfig(e.extConnConfig, ee.extConnConfig) || |
|
| 92 |
+ !compareBindings(e.portMapping, ee.portMapping) {
|
|
| 93 |
+ t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%#v\nDecoded:\n%#v", e, ee)
|
|
| 94 |
+ } |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func compareEpConfig(a, b *endpointConfiguration) bool {
|
|
| 98 |
+ if a == b {
|
|
| 99 |
+ return true |
|
| 100 |
+ } |
|
| 101 |
+ if a == nil || b == nil {
|
|
| 102 |
+ return false |
|
| 103 |
+ } |
|
| 104 |
+ return bytes.Equal(a.MacAddress, b.MacAddress) |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func compareContainerConfig(a, b *containerConfiguration) bool {
|
|
| 108 |
+ if a == b {
|
|
| 109 |
+ return true |
|
| 110 |
+ } |
|
| 111 |
+ if a == nil || b == nil {
|
|
| 112 |
+ return false |
|
| 113 |
+ } |
|
| 114 |
+ if len(a.ParentEndpoints) != len(b.ParentEndpoints) || |
|
| 115 |
+ len(a.ChildEndpoints) != len(b.ChildEndpoints) {
|
|
| 116 |
+ return false |
|
| 117 |
+ } |
|
| 118 |
+ for i := 0; i < len(a.ParentEndpoints); i++ {
|
|
| 119 |
+ if a.ParentEndpoints[i] != b.ParentEndpoints[i] {
|
|
| 120 |
+ return false |
|
| 121 |
+ } |
|
| 122 |
+ } |
|
| 123 |
+ for i := 0; i < len(a.ChildEndpoints); i++ {
|
|
| 124 |
+ if a.ChildEndpoints[i] != b.ChildEndpoints[i] {
|
|
| 125 |
+ return false |
|
| 126 |
+ } |
|
| 127 |
+ } |
|
| 128 |
+ return true |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func compareConnConfig(a, b *connectivityConfiguration) bool {
|
|
| 132 |
+ if a == b {
|
|
| 133 |
+ return true |
|
| 134 |
+ } |
|
| 135 |
+ if a == nil || b == nil {
|
|
| 136 |
+ return false |
|
| 137 |
+ } |
|
| 138 |
+ if len(a.ExposedPorts) != len(b.ExposedPorts) || |
|
| 139 |
+ len(a.PortBindings) != len(b.PortBindings) {
|
|
| 140 |
+ return false |
|
| 141 |
+ } |
|
| 142 |
+ for i := 0; i < len(a.ExposedPorts); i++ {
|
|
| 143 |
+ if !a.ExposedPorts[i].Equal(&b.ExposedPorts[i]) {
|
|
| 144 |
+ return false |
|
| 145 |
+ } |
|
| 146 |
+ } |
|
| 147 |
+ for i := 0; i < len(a.PortBindings); i++ {
|
|
| 148 |
+ if !comparePortBinding(&a.PortBindings[i], &b.PortBindings[i]) {
|
|
| 149 |
+ return false |
|
| 150 |
+ } |
|
| 151 |
+ } |
|
| 152 |
+ return true |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// comparePortBinding returns whether the given PortBindings are equal. |
|
| 156 |
+func comparePortBinding(p *types.PortBinding, o *types.PortBinding) bool {
|
|
| 157 |
+ if p == o {
|
|
| 158 |
+ return true |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ if o == nil {
|
|
| 162 |
+ return false |
|
| 163 |
+ } |
|
| 164 |
+ |
|
| 165 |
+ if p.Proto != o.Proto || p.Port != o.Port || |
|
| 166 |
+ p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd {
|
|
| 167 |
+ return false |
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ if p.IP != nil {
|
|
| 171 |
+ if !p.IP.Equal(o.IP) {
|
|
| 172 |
+ return false |
|
| 173 |
+ } |
|
| 174 |
+ } else {
|
|
| 175 |
+ if o.IP != nil {
|
|
| 176 |
+ return false |
|
| 177 |
+ } |
|
| 178 |
+ } |
|
| 179 |
+ |
|
| 180 |
+ if p.HostIP != nil {
|
|
| 181 |
+ if !p.HostIP.Equal(o.HostIP) {
|
|
| 182 |
+ return false |
|
| 183 |
+ } |
|
| 184 |
+ } else {
|
|
| 185 |
+ if o.HostIP != nil {
|
|
| 186 |
+ return false |
|
| 187 |
+ } |
|
| 188 |
+ } |
|
| 189 |
+ |
|
| 190 |
+ return true |
|
| 191 |
+} |
|
| 192 |
+ |
|
| 193 |
+func compareBindings(a, b []types.PortBinding) bool {
|
|
| 194 |
+ if len(a) != len(b) {
|
|
| 195 |
+ return false |
|
| 196 |
+ } |
|
| 197 |
+ for i := 0; i < len(a); i++ {
|
|
| 198 |
+ if !comparePortBinding(&a[i], &b[i]) {
|
|
| 199 |
+ return false |
|
| 200 |
+ } |
|
| 201 |
+ } |
|
| 202 |
+ return true |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+func getIPv4Data(t *testing.T, iface string) []driverapi.IPAMData {
|
|
| 206 |
+ ipd := driverapi.IPAMData{AddressSpace: "full"}
|
|
| 207 |
+ nw, err := netutils.FindAvailableNetwork(ipamutils.GetLocalScopeDefaultNetworks()) |
|
| 208 |
+ if err != nil {
|
|
| 209 |
+ t.Fatal(err) |
|
| 210 |
+ } |
|
| 211 |
+ ipd.Pool = nw |
|
| 212 |
+ // Set network gateway to X.X.X.1 |
|
| 213 |
+ ipd.Gateway = types.GetIPNetCopy(nw) |
|
| 214 |
+ ipd.Gateway.IP[len(ipd.Gateway.IP)-1] = 1 |
|
| 215 |
+ return []driverapi.IPAMData{ipd}
|
|
| 216 |
+} |
|
| 217 |
+ |
|
| 218 |
+func TestCreateFullOptions(t *testing.T) {
|
|
| 219 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 220 |
+ d := newDriver() |
|
| 221 |
+ |
|
| 222 |
+ config := &configuration{
|
|
| 223 |
+ EnableIPForwarding: true, |
|
| 224 |
+ EnableIPTables: true, |
|
| 225 |
+ } |
|
| 226 |
+ |
|
| 227 |
+ // Test this scenario: Default gw address does not belong to |
|
| 228 |
+ // container network and it's greater than bridge address |
|
| 229 |
+ cnw, _ := types.ParseCIDR("172.16.122.0/24")
|
|
| 230 |
+ bnw, _ := types.ParseCIDR("172.16.0.0/24")
|
|
| 231 |
+ br, _ := types.ParseCIDR("172.16.0.1/16")
|
|
| 232 |
+ defgw, _ := types.ParseCIDR("172.16.0.100/16")
|
|
| 233 |
+ |
|
| 234 |
+ genericOption := make(map[string]interface{})
|
|
| 235 |
+ genericOption[netlabel.GenericData] = config |
|
| 236 |
+ |
|
| 237 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 238 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ netOption := make(map[string]interface{})
|
|
| 242 |
+ netOption[netlabel.EnableIPv6] = true |
|
| 243 |
+ netOption[netlabel.GenericData] = &networkConfiguration{
|
|
| 244 |
+ BridgeName: DefaultBridgeName, |
|
| 245 |
+ } |
|
| 246 |
+ |
|
| 247 |
+ ipdList := []driverapi.IPAMData{
|
|
| 248 |
+ {
|
|
| 249 |
+ Pool: bnw, |
|
| 250 |
+ Gateway: br, |
|
| 251 |
+ AuxAddresses: map[string]*net.IPNet{DefaultGatewayV4AuxKey: defgw},
|
|
| 252 |
+ }, |
|
| 253 |
+ } |
|
| 254 |
+ err := d.CreateNetwork("dummy", netOption, nil, ipdList, nil)
|
|
| 255 |
+ if err != nil {
|
|
| 256 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ // Verify the IP address allocated for the endpoint belongs to the container network |
|
| 260 |
+ epOptions := make(map[string]interface{})
|
|
| 261 |
+ te := newTestEndpoint(cnw, 10) |
|
| 262 |
+ err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
|
|
| 263 |
+ if err != nil {
|
|
| 264 |
+ t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 265 |
+ } |
|
| 266 |
+ |
|
| 267 |
+ if !cnw.Contains(te.Interface().Address().IP) {
|
|
| 268 |
+ t.Fatalf("endpoint got assigned address outside of container network(%s): %s", cnw.String(), te.Interface().Address())
|
|
| 269 |
+ } |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+func TestCreateNoConfig(t *testing.T) {
|
|
| 273 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 274 |
+ d := newDriver() |
|
| 275 |
+ |
|
| 276 |
+ netconfig := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 277 |
+ genericOption := make(map[string]interface{})
|
|
| 278 |
+ genericOption[netlabel.GenericData] = netconfig |
|
| 279 |
+ |
|
| 280 |
+ if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 281 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 282 |
+ } |
|
| 283 |
+} |
|
| 284 |
+ |
|
| 285 |
+func TestCreateFullOptionsLabels(t *testing.T) {
|
|
| 286 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 287 |
+ d := newDriver() |
|
| 288 |
+ |
|
| 289 |
+ config := &configuration{
|
|
| 290 |
+ EnableIPForwarding: true, |
|
| 291 |
+ } |
|
| 292 |
+ genericOption := make(map[string]interface{})
|
|
| 293 |
+ genericOption[netlabel.GenericData] = config |
|
| 294 |
+ |
|
| 295 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 296 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 297 |
+ } |
|
| 298 |
+ |
|
| 299 |
+ bndIPs := "127.0.0.1" |
|
| 300 |
+ testHostIP := "1.2.3.4" |
|
| 301 |
+ nwV6s := "2001:db8:2600:2700:2800::/80" |
|
| 302 |
+ gwV6s := "2001:db8:2600:2700:2800::25/80" |
|
| 303 |
+ nwV6, _ := types.ParseCIDR(nwV6s) |
|
| 304 |
+ gwV6, _ := types.ParseCIDR(gwV6s) |
|
| 305 |
+ |
|
| 306 |
+ labels := map[string]string{
|
|
| 307 |
+ BridgeName: DefaultBridgeName, |
|
| 308 |
+ DefaultBridge: "true", |
|
| 309 |
+ EnableICC: "true", |
|
| 310 |
+ EnableIPMasquerade: "true", |
|
| 311 |
+ DefaultBindingIP: bndIPs, |
|
| 312 |
+ netlabel.HostIP: testHostIP, |
|
| 313 |
+ } |
|
| 314 |
+ |
|
| 315 |
+ netOption := make(map[string]interface{})
|
|
| 316 |
+ netOption[netlabel.EnableIPv6] = true |
|
| 317 |
+ netOption[netlabel.GenericData] = labels |
|
| 318 |
+ |
|
| 319 |
+ ipdList := getIPv4Data(t, "") |
|
| 320 |
+ ipd6List := []driverapi.IPAMData{
|
|
| 321 |
+ {
|
|
| 322 |
+ Pool: nwV6, |
|
| 323 |
+ AuxAddresses: map[string]*net.IPNet{
|
|
| 324 |
+ DefaultGatewayV6AuxKey: gwV6, |
|
| 325 |
+ }, |
|
| 326 |
+ }, |
|
| 327 |
+ } |
|
| 328 |
+ |
|
| 329 |
+ err := d.CreateNetwork("dummy", netOption, nil, ipdList, ipd6List)
|
|
| 330 |
+ if err != nil {
|
|
| 331 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 332 |
+ } |
|
| 333 |
+ |
|
| 334 |
+ nw, ok := d.networks["dummy"] |
|
| 335 |
+ if !ok {
|
|
| 336 |
+ t.Fatal("Cannot find dummy network in bridge driver")
|
|
| 337 |
+ } |
|
| 338 |
+ |
|
| 339 |
+ if nw.config.BridgeName != DefaultBridgeName {
|
|
| 340 |
+ t.Fatal("incongruent name in bridge network")
|
|
| 341 |
+ } |
|
| 342 |
+ |
|
| 343 |
+ if !nw.config.EnableIPv6 {
|
|
| 344 |
+ t.Fatal("incongruent EnableIPv6 in bridge network")
|
|
| 345 |
+ } |
|
| 346 |
+ |
|
| 347 |
+ if !nw.config.EnableICC {
|
|
| 348 |
+ t.Fatal("incongruent EnableICC in bridge network")
|
|
| 349 |
+ } |
|
| 350 |
+ |
|
| 351 |
+ if !nw.config.EnableIPMasquerade {
|
|
| 352 |
+ t.Fatal("incongruent EnableIPMasquerade in bridge network")
|
|
| 353 |
+ } |
|
| 354 |
+ |
|
| 355 |
+ bndIP := net.ParseIP(bndIPs) |
|
| 356 |
+ if !bndIP.Equal(nw.config.DefaultBindingIP) {
|
|
| 357 |
+ t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
|
|
| 358 |
+ } |
|
| 359 |
+ |
|
| 360 |
+ hostIP := net.ParseIP(testHostIP) |
|
| 361 |
+ if !hostIP.Equal(nw.config.HostIP) {
|
|
| 362 |
+ t.Fatalf("Unexpected: %v", nw.config.HostIP)
|
|
| 363 |
+ } |
|
| 364 |
+ |
|
| 365 |
+ if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
|
|
| 366 |
+ t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
|
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ if !gwV6.IP.Equal(nw.config.DefaultGatewayIPv6) {
|
|
| 370 |
+ t.Fatalf("Unexpected: %v", nw.config.DefaultGatewayIPv6)
|
|
| 371 |
+ } |
|
| 372 |
+ |
|
| 373 |
+ // In short here we are testing --fixed-cidr-v6 daemon option |
|
| 374 |
+ // plus --mac-address run option |
|
| 375 |
+ mac, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff")
|
|
| 376 |
+ epOptions := map[string]interface{}{netlabel.MacAddress: mac}
|
|
| 377 |
+ te := newTestEndpoint(ipdList[0].Pool, 20) |
|
| 378 |
+ err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
|
|
| 379 |
+ if err != nil {
|
|
| 380 |
+ t.Fatal(err) |
|
| 381 |
+ } |
|
| 382 |
+ |
|
| 383 |
+ if !nwV6.Contains(te.Interface().AddressIPv6().IP) {
|
|
| 384 |
+ t.Fatalf("endpoint got assigned address outside of container network(%s): %s", nwV6.String(), te.Interface().AddressIPv6())
|
|
| 385 |
+ } |
|
| 386 |
+ if te.Interface().AddressIPv6().IP.String() != "2001:db8:2600:2700:2800:aabb:ccdd:eeff" {
|
|
| 387 |
+ t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP)
|
|
| 388 |
+ } |
|
| 389 |
+} |
|
| 390 |
+ |
|
| 391 |
+func TestCreate(t *testing.T) {
|
|
| 392 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 393 |
+ |
|
| 394 |
+ d := newDriver() |
|
| 395 |
+ |
|
| 396 |
+ if err := d.configure(nil); err != nil {
|
|
| 397 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 398 |
+ } |
|
| 399 |
+ |
|
| 400 |
+ netconfig := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 401 |
+ genericOption := make(map[string]interface{})
|
|
| 402 |
+ genericOption[netlabel.GenericData] = netconfig |
|
| 403 |
+ |
|
| 404 |
+ if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 405 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 406 |
+ } |
|
| 407 |
+ |
|
| 408 |
+ err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil)
|
|
| 409 |
+ if err == nil {
|
|
| 410 |
+ t.Fatal("Expected bridge driver to refuse creation of second network with default name")
|
|
| 411 |
+ } |
|
| 412 |
+ if _, ok := err.(types.ForbiddenError); !ok {
|
|
| 413 |
+ t.Fatal("Creation of second network with default name failed with unexpected error type")
|
|
| 414 |
+ } |
|
| 415 |
+} |
|
| 416 |
+ |
|
| 417 |
+func TestCreateFail(t *testing.T) {
|
|
| 418 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 419 |
+ |
|
| 420 |
+ d := newDriver() |
|
| 421 |
+ |
|
| 422 |
+ if err := d.configure(nil); err != nil {
|
|
| 423 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 424 |
+ } |
|
| 425 |
+ |
|
| 426 |
+ netconfig := &networkConfiguration{BridgeName: "dummy0", DefaultBridge: true}
|
|
| 427 |
+ genericOption := make(map[string]interface{})
|
|
| 428 |
+ genericOption[netlabel.GenericData] = netconfig |
|
| 429 |
+ |
|
| 430 |
+ if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err == nil {
|
|
| 431 |
+ t.Fatal("Bridge creation was expected to fail")
|
|
| 432 |
+ } |
|
| 433 |
+} |
|
| 434 |
+ |
|
| 435 |
+func TestCreateMultipleNetworks(t *testing.T) {
|
|
| 436 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 437 |
+ |
|
| 438 |
+ d := newDriver() |
|
| 439 |
+ |
|
| 440 |
+ config := &configuration{
|
|
| 441 |
+ EnableIPTables: true, |
|
| 442 |
+ } |
|
| 443 |
+ genericOption := make(map[string]interface{})
|
|
| 444 |
+ genericOption[netlabel.GenericData] = config |
|
| 445 |
+ |
|
| 446 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 447 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 448 |
+ } |
|
| 449 |
+ |
|
| 450 |
+ config1 := &networkConfiguration{BridgeName: "net_test_1"}
|
|
| 451 |
+ genericOption = make(map[string]interface{})
|
|
| 452 |
+ genericOption[netlabel.GenericData] = config1 |
|
| 453 |
+ if err := d.CreateNetwork("1", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 454 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 455 |
+ } |
|
| 456 |
+ |
|
| 457 |
+ verifyV4INCEntries(d.networks, t) |
|
| 458 |
+ |
|
| 459 |
+ config2 := &networkConfiguration{BridgeName: "net_test_2"}
|
|
| 460 |
+ genericOption[netlabel.GenericData] = config2 |
|
| 461 |
+ if err := d.CreateNetwork("2", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 462 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 463 |
+ } |
|
| 464 |
+ |
|
| 465 |
+ verifyV4INCEntries(d.networks, t) |
|
| 466 |
+ |
|
| 467 |
+ config3 := &networkConfiguration{BridgeName: "net_test_3"}
|
|
| 468 |
+ genericOption[netlabel.GenericData] = config3 |
|
| 469 |
+ if err := d.CreateNetwork("3", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 470 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 471 |
+ } |
|
| 472 |
+ |
|
| 473 |
+ verifyV4INCEntries(d.networks, t) |
|
| 474 |
+ |
|
| 475 |
+ config4 := &networkConfiguration{BridgeName: "net_test_4"}
|
|
| 476 |
+ genericOption[netlabel.GenericData] = config4 |
|
| 477 |
+ if err := d.CreateNetwork("4", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 478 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 479 |
+ } |
|
| 480 |
+ |
|
| 481 |
+ verifyV4INCEntries(d.networks, t) |
|
| 482 |
+ |
|
| 483 |
+ if err := d.DeleteNetwork("1"); err != nil {
|
|
| 484 |
+ t.Log(err) |
|
| 485 |
+ } |
|
| 486 |
+ verifyV4INCEntries(d.networks, t) |
|
| 487 |
+ |
|
| 488 |
+ if err := d.DeleteNetwork("2"); err != nil {
|
|
| 489 |
+ t.Log(err) |
|
| 490 |
+ } |
|
| 491 |
+ verifyV4INCEntries(d.networks, t) |
|
| 492 |
+ |
|
| 493 |
+ if err := d.DeleteNetwork("3"); err != nil {
|
|
| 494 |
+ t.Log(err) |
|
| 495 |
+ } |
|
| 496 |
+ verifyV4INCEntries(d.networks, t) |
|
| 497 |
+ |
|
| 498 |
+ if err := d.DeleteNetwork("4"); err != nil {
|
|
| 499 |
+ t.Log(err) |
|
| 500 |
+ } |
|
| 501 |
+ verifyV4INCEntries(d.networks, t) |
|
| 502 |
+} |
|
| 503 |
+ |
|
| 504 |
+// Verify the network isolation rules are installed for each network |
|
| 505 |
+func verifyV4INCEntries(networks map[string]*bridgeNetwork, t *testing.T) {
|
|
| 506 |
+ iptable := iptables.GetIptable(iptables.IPv4) |
|
| 507 |
+ out1, err := iptable.Raw("-S", IsolationChain1)
|
|
| 508 |
+ if err != nil {
|
|
| 509 |
+ t.Fatal(err) |
|
| 510 |
+ } |
|
| 511 |
+ out2, err := iptable.Raw("-S", IsolationChain2)
|
|
| 512 |
+ if err != nil {
|
|
| 513 |
+ t.Fatal(err) |
|
| 514 |
+ } |
|
| 515 |
+ |
|
| 516 |
+ for _, n := range networks {
|
|
| 517 |
+ re := regexp.MustCompile(fmt.Sprintf("-i %s ! -o %s -j %s", n.config.BridgeName, n.config.BridgeName, IsolationChain2))
|
|
| 518 |
+ matches := re.FindAllString(string(out1[:]), -1) |
|
| 519 |
+ if len(matches) != 1 {
|
|
| 520 |
+ t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out1[:]))
|
|
| 521 |
+ } |
|
| 522 |
+ re = regexp.MustCompile(fmt.Sprintf("-o %s -j DROP", n.config.BridgeName))
|
|
| 523 |
+ matches = re.FindAllString(string(out2[:]), -1) |
|
| 524 |
+ if len(matches) != 1 {
|
|
| 525 |
+ t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out2[:]))
|
|
| 526 |
+ } |
|
| 527 |
+ } |
|
| 528 |
+} |
|
| 529 |
+ |
|
| 530 |
+type testInterface struct {
|
|
| 531 |
+ mac net.HardwareAddr |
|
| 532 |
+ addr *net.IPNet |
|
| 533 |
+ addrv6 *net.IPNet |
|
| 534 |
+ srcName string |
|
| 535 |
+ dstName string |
|
| 536 |
+} |
|
| 537 |
+ |
|
| 538 |
+type testEndpoint struct {
|
|
| 539 |
+ iface *testInterface |
|
| 540 |
+ gw net.IP |
|
| 541 |
+ gw6 net.IP |
|
| 542 |
+ routes []types.StaticRoute |
|
| 543 |
+} |
|
| 544 |
+ |
|
| 545 |
+func newTestEndpoint(nw *net.IPNet, ordinal byte) *testEndpoint {
|
|
| 546 |
+ addr := types.GetIPNetCopy(nw) |
|
| 547 |
+ addr.IP[len(addr.IP)-1] = ordinal |
|
| 548 |
+ return &testEndpoint{iface: &testInterface{addr: addr}}
|
|
| 549 |
+} |
|
| 550 |
+ |
|
| 551 |
+func (te *testEndpoint) Interface() driverapi.InterfaceInfo {
|
|
| 552 |
+ if te.iface != nil {
|
|
| 553 |
+ return te.iface |
|
| 554 |
+ } |
|
| 555 |
+ |
|
| 556 |
+ return nil |
|
| 557 |
+} |
|
| 558 |
+ |
|
| 559 |
+func (i *testInterface) MacAddress() net.HardwareAddr {
|
|
| 560 |
+ return i.mac |
|
| 561 |
+} |
|
| 562 |
+ |
|
| 563 |
+func (i *testInterface) Address() *net.IPNet {
|
|
| 564 |
+ return i.addr |
|
| 565 |
+} |
|
| 566 |
+ |
|
| 567 |
+func (i *testInterface) AddressIPv6() *net.IPNet {
|
|
| 568 |
+ return i.addrv6 |
|
| 569 |
+} |
|
| 570 |
+ |
|
| 571 |
+func (i *testInterface) SetMacAddress(mac net.HardwareAddr) error {
|
|
| 572 |
+ if i.mac != nil {
|
|
| 573 |
+ return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", i.mac, mac)
|
|
| 574 |
+ } |
|
| 575 |
+ if mac == nil {
|
|
| 576 |
+ return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
|
|
| 577 |
+ } |
|
| 578 |
+ i.mac = types.GetMacCopy(mac) |
|
| 579 |
+ return nil |
|
| 580 |
+} |
|
| 581 |
+ |
|
| 582 |
+func (i *testInterface) SetIPAddress(address *net.IPNet) error {
|
|
| 583 |
+ if address.IP == nil {
|
|
| 584 |
+ return types.BadRequestErrorf("tried to set nil IP address to endpoint interface")
|
|
| 585 |
+ } |
|
| 586 |
+ if address.IP.To4() == nil {
|
|
| 587 |
+ return setAddress(&i.addrv6, address) |
|
| 588 |
+ } |
|
| 589 |
+ return setAddress(&i.addr, address) |
|
| 590 |
+} |
|
| 591 |
+ |
|
| 592 |
+func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error {
|
|
| 593 |
+ if *ifaceAddr != nil {
|
|
| 594 |
+ return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address)
|
|
| 595 |
+ } |
|
| 596 |
+ *ifaceAddr = types.GetIPNetCopy(address) |
|
| 597 |
+ return nil |
|
| 598 |
+} |
|
| 599 |
+ |
|
| 600 |
+func (i *testInterface) SetNames(srcName string, dstName string) error {
|
|
| 601 |
+ i.srcName = srcName |
|
| 602 |
+ i.dstName = dstName |
|
| 603 |
+ return nil |
|
| 604 |
+} |
|
| 605 |
+ |
|
| 606 |
+func (te *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo {
|
|
| 607 |
+ if te.iface != nil {
|
|
| 608 |
+ return te.iface |
|
| 609 |
+ } |
|
| 610 |
+ |
|
| 611 |
+ return nil |
|
| 612 |
+} |
|
| 613 |
+ |
|
| 614 |
+func (te *testEndpoint) SetGateway(gw net.IP) error {
|
|
| 615 |
+ te.gw = gw |
|
| 616 |
+ return nil |
|
| 617 |
+} |
|
| 618 |
+ |
|
| 619 |
+func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error {
|
|
| 620 |
+ te.gw6 = gw6 |
|
| 621 |
+ return nil |
|
| 622 |
+} |
|
| 623 |
+ |
|
| 624 |
+func (te *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
|
|
| 625 |
+ te.routes = append(te.routes, types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop})
|
|
| 626 |
+ return nil |
|
| 627 |
+} |
|
| 628 |
+ |
|
| 629 |
+func (te *testEndpoint) AddTableEntry(tableName string, key string, value []byte) error {
|
|
| 630 |
+ return nil |
|
| 631 |
+} |
|
| 632 |
+ |
|
| 633 |
+func (te *testEndpoint) DisableGatewayService() {}
|
|
| 634 |
+ |
|
| 635 |
+func TestQueryEndpointInfo(t *testing.T) {
|
|
| 636 |
+ testQueryEndpointInfo(t, true) |
|
| 637 |
+} |
|
| 638 |
+ |
|
| 639 |
+func TestQueryEndpointInfoHairpin(t *testing.T) {
|
|
| 640 |
+ testQueryEndpointInfo(t, false) |
|
| 641 |
+} |
|
| 642 |
+ |
|
| 643 |
+func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
|
|
| 644 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 645 |
+ d := newDriver() |
|
| 646 |
+ d.portAllocator = portallocator.NewInstance() |
|
| 647 |
+ |
|
| 648 |
+ config := &configuration{
|
|
| 649 |
+ EnableIPTables: true, |
|
| 650 |
+ EnableUserlandProxy: ulPxyEnabled, |
|
| 651 |
+ } |
|
| 652 |
+ genericOption := make(map[string]interface{})
|
|
| 653 |
+ genericOption[netlabel.GenericData] = config |
|
| 654 |
+ |
|
| 655 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 656 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 657 |
+ } |
|
| 658 |
+ |
|
| 659 |
+ netconfig := &networkConfiguration{
|
|
| 660 |
+ BridgeName: DefaultBridgeName, |
|
| 661 |
+ EnableICC: false, |
|
| 662 |
+ } |
|
| 663 |
+ genericOption = make(map[string]interface{})
|
|
| 664 |
+ genericOption[netlabel.GenericData] = netconfig |
|
| 665 |
+ |
|
| 666 |
+ ipdList := getIPv4Data(t, "") |
|
| 667 |
+ err := d.CreateNetwork("net1", genericOption, nil, ipdList, nil)
|
|
| 668 |
+ if err != nil {
|
|
| 669 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 670 |
+ } |
|
| 671 |
+ |
|
| 672 |
+ sbOptions := make(map[string]interface{})
|
|
| 673 |
+ sbOptions[netlabel.PortMap] = getPortMapping() |
|
| 674 |
+ |
|
| 675 |
+ te := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 676 |
+ err = d.CreateEndpoint("net1", "ep1", te.Interface(), nil)
|
|
| 677 |
+ if err != nil {
|
|
| 678 |
+ t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 679 |
+ } |
|
| 680 |
+ |
|
| 681 |
+ err = d.Join("net1", "ep1", "sbox", te, sbOptions)
|
|
| 682 |
+ if err != nil {
|
|
| 683 |
+ t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 684 |
+ } |
|
| 685 |
+ |
|
| 686 |
+ err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions)
|
|
| 687 |
+ if err != nil {
|
|
| 688 |
+ t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 689 |
+ } |
|
| 690 |
+ |
|
| 691 |
+ network, ok := d.networks["net1"] |
|
| 692 |
+ if !ok {
|
|
| 693 |
+ t.Fatalf("Cannot find network %s inside driver", "net1")
|
|
| 694 |
+ } |
|
| 695 |
+ ep := network.endpoints["ep1"] |
|
| 696 |
+ data, err := d.EndpointOperInfo(network.id, ep.id) |
|
| 697 |
+ if err != nil {
|
|
| 698 |
+ t.Fatalf("Failed to ask for endpoint operational data: %v", err)
|
|
| 699 |
+ } |
|
| 700 |
+ pmd, ok := data[netlabel.PortMap] |
|
| 701 |
+ if !ok {
|
|
| 702 |
+ t.Fatal("Endpoint operational data does not contain port mapping data")
|
|
| 703 |
+ } |
|
| 704 |
+ pm, ok := pmd.([]types.PortBinding) |
|
| 705 |
+ if !ok {
|
|
| 706 |
+ t.Fatal("Unexpected format for port mapping in endpoint operational data")
|
|
| 707 |
+ } |
|
| 708 |
+ if len(ep.portMapping) != len(pm) {
|
|
| 709 |
+ t.Fatal("Incomplete data for port mapping in endpoint operational data")
|
|
| 710 |
+ } |
|
| 711 |
+ for i, pb := range ep.portMapping {
|
|
| 712 |
+ if !comparePortBinding(&pb, &pm[i]) {
|
|
| 713 |
+ t.Fatal("Unexpected data for port mapping in endpoint operational data")
|
|
| 714 |
+ } |
|
| 715 |
+ } |
|
| 716 |
+ |
|
| 717 |
+ err = d.RevokeExternalConnectivity("net1", "ep1")
|
|
| 718 |
+ if err != nil {
|
|
| 719 |
+ t.Fatal(err) |
|
| 720 |
+ } |
|
| 721 |
+ |
|
| 722 |
+ // release host mapped ports |
|
| 723 |
+ err = d.Leave("net1", "ep1")
|
|
| 724 |
+ if err != nil {
|
|
| 725 |
+ t.Fatal(err) |
|
| 726 |
+ } |
|
| 727 |
+} |
|
| 728 |
+ |
|
| 729 |
+func getExposedPorts() []types.TransportPort {
|
|
| 730 |
+ return []types.TransportPort{
|
|
| 731 |
+ {Proto: types.TCP, Port: uint16(5000)},
|
|
| 732 |
+ {Proto: types.UDP, Port: uint16(400)},
|
|
| 733 |
+ {Proto: types.TCP, Port: uint16(600)},
|
|
| 734 |
+ } |
|
| 735 |
+} |
|
| 736 |
+ |
|
| 737 |
+func getPortMapping() []types.PortBinding {
|
|
| 738 |
+ return []types.PortBinding{
|
|
| 739 |
+ {Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
|
|
| 740 |
+ {Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
|
|
| 741 |
+ {Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
|
|
| 742 |
+ } |
|
| 743 |
+} |
|
| 744 |
+ |
|
| 745 |
+func TestLinkContainers(t *testing.T) {
|
|
| 746 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 747 |
+ |
|
| 748 |
+ d := newDriver() |
|
| 749 |
+ iptable := iptables.GetIptable(iptables.IPv4) |
|
| 750 |
+ |
|
| 751 |
+ config := &configuration{
|
|
| 752 |
+ EnableIPTables: true, |
|
| 753 |
+ } |
|
| 754 |
+ genericOption := make(map[string]interface{})
|
|
| 755 |
+ genericOption[netlabel.GenericData] = config |
|
| 756 |
+ |
|
| 757 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 758 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 759 |
+ } |
|
| 760 |
+ |
|
| 761 |
+ netconfig := &networkConfiguration{
|
|
| 762 |
+ BridgeName: DefaultBridgeName, |
|
| 763 |
+ EnableICC: false, |
|
| 764 |
+ } |
|
| 765 |
+ genericOption = make(map[string]interface{})
|
|
| 766 |
+ genericOption[netlabel.GenericData] = netconfig |
|
| 767 |
+ |
|
| 768 |
+ ipdList := getIPv4Data(t, "") |
|
| 769 |
+ err := d.CreateNetwork("net1", genericOption, nil, ipdList, nil)
|
|
| 770 |
+ if err != nil {
|
|
| 771 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 772 |
+ } |
|
| 773 |
+ |
|
| 774 |
+ te1 := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 775 |
+ err = d.CreateEndpoint("net1", "ep1", te1.Interface(), nil)
|
|
| 776 |
+ if err != nil {
|
|
| 777 |
+ t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 778 |
+ } |
|
| 779 |
+ |
|
| 780 |
+ exposedPorts := getExposedPorts() |
|
| 781 |
+ sbOptions := make(map[string]interface{})
|
|
| 782 |
+ sbOptions[netlabel.ExposedPorts] = exposedPorts |
|
| 783 |
+ |
|
| 784 |
+ err = d.Join("net1", "ep1", "sbox", te1, sbOptions)
|
|
| 785 |
+ if err != nil {
|
|
| 786 |
+ t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 787 |
+ } |
|
| 788 |
+ |
|
| 789 |
+ err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions)
|
|
| 790 |
+ if err != nil {
|
|
| 791 |
+ t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 792 |
+ } |
|
| 793 |
+ |
|
| 794 |
+ addr1 := te1.iface.addr |
|
| 795 |
+ if addr1.IP.To4() == nil {
|
|
| 796 |
+ t.Fatal("No Ipv4 address assigned to the endpoint: ep1")
|
|
| 797 |
+ } |
|
| 798 |
+ |
|
| 799 |
+ te2 := newTestEndpoint(ipdList[0].Pool, 22) |
|
| 800 |
+ err = d.CreateEndpoint("net1", "ep2", te2.Interface(), nil)
|
|
| 801 |
+ if err != nil {
|
|
| 802 |
+ t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 803 |
+ } |
|
| 804 |
+ |
|
| 805 |
+ addr2 := te2.iface.addr |
|
| 806 |
+ if addr2.IP.To4() == nil {
|
|
| 807 |
+ t.Fatal("No Ipv4 address assigned to the endpoint: ep2")
|
|
| 808 |
+ } |
|
| 809 |
+ |
|
| 810 |
+ sbOptions = make(map[string]interface{})
|
|
| 811 |
+ sbOptions[netlabel.GenericData] = options.Generic{
|
|
| 812 |
+ "ChildEndpoints": []string{"ep1"},
|
|
| 813 |
+ } |
|
| 814 |
+ |
|
| 815 |
+ err = d.Join("net1", "ep2", "", te2, sbOptions)
|
|
| 816 |
+ if err != nil {
|
|
| 817 |
+ t.Fatal("Failed to link ep1 and ep2")
|
|
| 818 |
+ } |
|
| 819 |
+ |
|
| 820 |
+ err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions)
|
|
| 821 |
+ if err != nil {
|
|
| 822 |
+ t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 823 |
+ } |
|
| 824 |
+ |
|
| 825 |
+ out, _ := iptable.Raw("-L", DockerChain)
|
|
| 826 |
+ for _, pm := range exposedPorts {
|
|
| 827 |
+ regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
|
|
| 828 |
+ re := regexp.MustCompile(regex) |
|
| 829 |
+ matches := re.FindAllString(string(out[:]), -1) |
|
| 830 |
+ if len(matches) != 1 {
|
|
| 831 |
+ t.Fatalf("IP Tables programming failed %s", string(out[:]))
|
|
| 832 |
+ } |
|
| 833 |
+ |
|
| 834 |
+ regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
|
|
| 835 |
+ matched, _ := regexp.MatchString(regex, string(out[:])) |
|
| 836 |
+ if !matched {
|
|
| 837 |
+ t.Fatalf("IP Tables programming failed %s", string(out[:]))
|
|
| 838 |
+ } |
|
| 839 |
+ } |
|
| 840 |
+ |
|
| 841 |
+ err = d.RevokeExternalConnectivity("net1", "ep2")
|
|
| 842 |
+ if err != nil {
|
|
| 843 |
+ t.Fatalf("Failed to revoke external connectivity: %v", err)
|
|
| 844 |
+ } |
|
| 845 |
+ |
|
| 846 |
+ err = d.Leave("net1", "ep2")
|
|
| 847 |
+ if err != nil {
|
|
| 848 |
+ t.Fatal("Failed to unlink ep1 and ep2")
|
|
| 849 |
+ } |
|
| 850 |
+ |
|
| 851 |
+ out, _ = iptable.Raw("-L", DockerChain)
|
|
| 852 |
+ for _, pm := range exposedPorts {
|
|
| 853 |
+ regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
|
|
| 854 |
+ re := regexp.MustCompile(regex) |
|
| 855 |
+ matches := re.FindAllString(string(out[:]), -1) |
|
| 856 |
+ if len(matches) != 0 {
|
|
| 857 |
+ t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:]))
|
|
| 858 |
+ } |
|
| 859 |
+ |
|
| 860 |
+ regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
|
|
| 861 |
+ matched, _ := regexp.MatchString(regex, string(out[:])) |
|
| 862 |
+ if matched {
|
|
| 863 |
+ t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:]))
|
|
| 864 |
+ } |
|
| 865 |
+ } |
|
| 866 |
+ |
|
| 867 |
+ // Error condition test with an invalid endpoint-id "ep4" |
|
| 868 |
+ sbOptions = make(map[string]interface{})
|
|
| 869 |
+ sbOptions[netlabel.GenericData] = options.Generic{
|
|
| 870 |
+ "ChildEndpoints": []string{"ep1", "ep4"},
|
|
| 871 |
+ } |
|
| 872 |
+ |
|
| 873 |
+ err = d.Join("net1", "ep2", "", te2, sbOptions)
|
|
| 874 |
+ if err != nil {
|
|
| 875 |
+ t.Fatal(err) |
|
| 876 |
+ } |
|
| 877 |
+ err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions)
|
|
| 878 |
+ if err != nil {
|
|
| 879 |
+ out, _ = iptable.Raw("-L", DockerChain)
|
|
| 880 |
+ for _, pm := range exposedPorts {
|
|
| 881 |
+ regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
|
|
| 882 |
+ re := regexp.MustCompile(regex) |
|
| 883 |
+ matches := re.FindAllString(string(out[:]), -1) |
|
| 884 |
+ if len(matches) != 0 {
|
|
| 885 |
+ t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:]))
|
|
| 886 |
+ } |
|
| 887 |
+ |
|
| 888 |
+ regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
|
|
| 889 |
+ matched, _ := regexp.MatchString(regex, string(out[:])) |
|
| 890 |
+ if matched {
|
|
| 891 |
+ t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:]))
|
|
| 892 |
+ } |
|
| 893 |
+ } |
|
| 894 |
+ } else {
|
|
| 895 |
+ t.Fatal("Expected Join to fail given link conditions are not satisfied")
|
|
| 896 |
+ } |
|
| 897 |
+} |
|
| 898 |
+ |
|
| 899 |
+func TestValidateConfig(t *testing.T) {
|
|
| 900 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 901 |
+ |
|
| 902 |
+ // Test mtu |
|
| 903 |
+ c := networkConfiguration{Mtu: -2}
|
|
| 904 |
+ err := c.Validate() |
|
| 905 |
+ if err == nil {
|
|
| 906 |
+ t.Fatal("Failed to detect invalid MTU number")
|
|
| 907 |
+ } |
|
| 908 |
+ |
|
| 909 |
+ c.Mtu = 9000 |
|
| 910 |
+ err = c.Validate() |
|
| 911 |
+ if err != nil {
|
|
| 912 |
+ t.Fatal("unexpected validation error on MTU number")
|
|
| 913 |
+ } |
|
| 914 |
+ |
|
| 915 |
+ // Bridge network |
|
| 916 |
+ _, network, _ := net.ParseCIDR("172.28.0.0/16")
|
|
| 917 |
+ c = networkConfiguration{
|
|
| 918 |
+ AddressIPv4: network, |
|
| 919 |
+ } |
|
| 920 |
+ |
|
| 921 |
+ err = c.Validate() |
|
| 922 |
+ if err != nil {
|
|
| 923 |
+ t.Fatal(err) |
|
| 924 |
+ } |
|
| 925 |
+ |
|
| 926 |
+ // Test v4 gw |
|
| 927 |
+ c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234")
|
|
| 928 |
+ err = c.Validate() |
|
| 929 |
+ if err == nil {
|
|
| 930 |
+ t.Fatal("Failed to detect invalid default gateway")
|
|
| 931 |
+ } |
|
| 932 |
+ |
|
| 933 |
+ c.DefaultGatewayIPv4 = net.ParseIP("172.28.30.234")
|
|
| 934 |
+ err = c.Validate() |
|
| 935 |
+ if err != nil {
|
|
| 936 |
+ t.Fatal("Unexpected validation error on default gateway")
|
|
| 937 |
+ } |
|
| 938 |
+ |
|
| 939 |
+ // Test v6 gw |
|
| 940 |
+ _, v6nw, _ := net.ParseCIDR("2001:db8:ae:b004::/64")
|
|
| 941 |
+ c = networkConfiguration{
|
|
| 942 |
+ EnableIPv6: true, |
|
| 943 |
+ AddressIPv6: v6nw, |
|
| 944 |
+ DefaultGatewayIPv6: net.ParseIP("2001:db8:ac:b004::bad:a55"),
|
|
| 945 |
+ } |
|
| 946 |
+ err = c.Validate() |
|
| 947 |
+ if err == nil {
|
|
| 948 |
+ t.Fatal("Failed to detect invalid v6 default gateway")
|
|
| 949 |
+ } |
|
| 950 |
+ |
|
| 951 |
+ c.DefaultGatewayIPv6 = net.ParseIP("2001:db8:ae:b004::bad:a55")
|
|
| 952 |
+ err = c.Validate() |
|
| 953 |
+ if err != nil {
|
|
| 954 |
+ t.Fatal("Unexpected validation error on v6 default gateway")
|
|
| 955 |
+ } |
|
| 956 |
+ |
|
| 957 |
+ c.AddressIPv6 = nil |
|
| 958 |
+ err = c.Validate() |
|
| 959 |
+ if err == nil {
|
|
| 960 |
+ t.Fatal("Failed to detect invalid v6 default gateway")
|
|
| 961 |
+ } |
|
| 962 |
+ |
|
| 963 |
+ c.AddressIPv6 = nil |
|
| 964 |
+ err = c.Validate() |
|
| 965 |
+ if err == nil {
|
|
| 966 |
+ t.Fatal("Failed to detect invalid v6 default gateway")
|
|
| 967 |
+ } |
|
| 968 |
+} |
|
| 969 |
+ |
|
| 970 |
+func TestSetDefaultGw(t *testing.T) {
|
|
| 971 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 972 |
+ |
|
| 973 |
+ d := newDriver() |
|
| 974 |
+ |
|
| 975 |
+ if err := d.configure(nil); err != nil {
|
|
| 976 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 977 |
+ } |
|
| 978 |
+ |
|
| 979 |
+ _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80")
|
|
| 980 |
+ |
|
| 981 |
+ ipdList := getIPv4Data(t, "") |
|
| 982 |
+ gw4 := types.GetIPCopy(ipdList[0].Pool.IP).To4() |
|
| 983 |
+ gw4[3] = 254 |
|
| 984 |
+ gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254")
|
|
| 985 |
+ |
|
| 986 |
+ config := &networkConfiguration{
|
|
| 987 |
+ BridgeName: DefaultBridgeName, |
|
| 988 |
+ AddressIPv6: subnetv6, |
|
| 989 |
+ DefaultGatewayIPv4: gw4, |
|
| 990 |
+ DefaultGatewayIPv6: gw6, |
|
| 991 |
+ } |
|
| 992 |
+ |
|
| 993 |
+ genericOption := make(map[string]interface{})
|
|
| 994 |
+ genericOption[netlabel.EnableIPv6] = true |
|
| 995 |
+ genericOption[netlabel.GenericData] = config |
|
| 996 |
+ |
|
| 997 |
+ err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 998 |
+ if err != nil {
|
|
| 999 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 1000 |
+ } |
|
| 1001 |
+ |
|
| 1002 |
+ te := newTestEndpoint(ipdList[0].Pool, 10) |
|
| 1003 |
+ err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
|
|
| 1004 |
+ if err != nil {
|
|
| 1005 |
+ t.Fatalf("Failed to create endpoint: %v", err)
|
|
| 1006 |
+ } |
|
| 1007 |
+ |
|
| 1008 |
+ err = d.Join("dummy", "ep", "sbox", te, nil)
|
|
| 1009 |
+ if err != nil {
|
|
| 1010 |
+ t.Fatalf("Failed to join endpoint: %v", err)
|
|
| 1011 |
+ } |
|
| 1012 |
+ |
|
| 1013 |
+ if !gw4.Equal(te.gw) {
|
|
| 1014 |
+ t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, te.gw)
|
|
| 1015 |
+ } |
|
| 1016 |
+ |
|
| 1017 |
+ if !gw6.Equal(te.gw6) {
|
|
| 1018 |
+ t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, te.gw6)
|
|
| 1019 |
+ } |
|
| 1020 |
+} |
|
| 1021 |
+ |
|
| 1022 |
+func TestCleanupIptableRules(t *testing.T) {
|
|
| 1023 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 1024 |
+ bridgeChain := []iptables.ChainInfo{
|
|
| 1025 |
+ {Name: DockerChain, Table: iptables.Nat},
|
|
| 1026 |
+ {Name: DockerChain, Table: iptables.Filter},
|
|
| 1027 |
+ {Name: IsolationChain1, Table: iptables.Filter},
|
|
| 1028 |
+ } |
|
| 1029 |
+ |
|
| 1030 |
+ ipVersions := []iptables.IPVersion{iptables.IPv4, iptables.IPv6}
|
|
| 1031 |
+ |
|
| 1032 |
+ for _, version := range ipVersions {
|
|
| 1033 |
+ if _, _, _, _, err := setupIPChains(configuration{EnableIPTables: true}, version); err != nil {
|
|
| 1034 |
+ t.Fatalf("Error setting up ip chains for %s: %v", version, err)
|
|
| 1035 |
+ } |
|
| 1036 |
+ |
|
| 1037 |
+ iptable := iptables.GetIptable(version) |
|
| 1038 |
+ for _, chainInfo := range bridgeChain {
|
|
| 1039 |
+ if !iptable.ExistChain(chainInfo.Name, chainInfo.Table) {
|
|
| 1040 |
+ t.Fatalf("iptables version %s chain %s of %s table should have been created", version, chainInfo.Name, chainInfo.Table)
|
|
| 1041 |
+ } |
|
| 1042 |
+ } |
|
| 1043 |
+ removeIPChains(version) |
|
| 1044 |
+ for _, chainInfo := range bridgeChain {
|
|
| 1045 |
+ if iptable.ExistChain(chainInfo.Name, chainInfo.Table) {
|
|
| 1046 |
+ t.Fatalf("iptables version %s chain %s of %s table should have been deleted", version, chainInfo.Name, chainInfo.Table)
|
|
| 1047 |
+ } |
|
| 1048 |
+ } |
|
| 1049 |
+ } |
|
| 1050 |
+} |
|
| 1051 |
+ |
|
| 1052 |
+func TestCreateWithExistingBridge(t *testing.T) {
|
|
| 1053 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 1054 |
+ d := newDriver() |
|
| 1055 |
+ |
|
| 1056 |
+ if err := d.configure(nil); err != nil {
|
|
| 1057 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 1058 |
+ } |
|
| 1059 |
+ |
|
| 1060 |
+ brName := "br111" |
|
| 1061 |
+ br := &netlink.Bridge{
|
|
| 1062 |
+ LinkAttrs: netlink.LinkAttrs{
|
|
| 1063 |
+ Name: brName, |
|
| 1064 |
+ }, |
|
| 1065 |
+ } |
|
| 1066 |
+ if err := netlink.LinkAdd(br); err != nil {
|
|
| 1067 |
+ t.Fatalf("Failed to create bridge interface: %v", err)
|
|
| 1068 |
+ } |
|
| 1069 |
+ defer netlink.LinkDel(br) |
|
| 1070 |
+ if err := netlink.LinkSetUp(br); err != nil {
|
|
| 1071 |
+ t.Fatalf("Failed to set bridge interface up: %v", err)
|
|
| 1072 |
+ } |
|
| 1073 |
+ |
|
| 1074 |
+ ip := net.IP{192, 168, 122, 1}
|
|
| 1075 |
+ addr := &netlink.Addr{IPNet: &net.IPNet{
|
|
| 1076 |
+ IP: ip, |
|
| 1077 |
+ Mask: net.IPv4Mask(255, 255, 255, 0), |
|
| 1078 |
+ }} |
|
| 1079 |
+ if err := netlink.AddrAdd(br, addr); err != nil {
|
|
| 1080 |
+ t.Fatalf("Failed to add IP address to bridge: %v", err)
|
|
| 1081 |
+ } |
|
| 1082 |
+ |
|
| 1083 |
+ netconfig := &networkConfiguration{BridgeName: brName}
|
|
| 1084 |
+ genericOption := make(map[string]interface{})
|
|
| 1085 |
+ genericOption[netlabel.GenericData] = netconfig |
|
| 1086 |
+ |
|
| 1087 |
+ ipv4Data := []driverapi.IPAMData{{
|
|
| 1088 |
+ AddressSpace: "full", |
|
| 1089 |
+ Pool: types.GetIPNetCopy(addr.IPNet), |
|
| 1090 |
+ Gateway: types.GetIPNetCopy(addr.IPNet), |
|
| 1091 |
+ }} |
|
| 1092 |
+ // Set network gateway to X.X.X.1 |
|
| 1093 |
+ ipv4Data[0].Gateway.IP[len(ipv4Data[0].Gateway.IP)-1] = 1 |
|
| 1094 |
+ |
|
| 1095 |
+ if err := d.CreateNetwork(brName, genericOption, nil, ipv4Data, nil); err != nil {
|
|
| 1096 |
+ t.Fatalf("Failed to create bridge network: %v", err)
|
|
| 1097 |
+ } |
|
| 1098 |
+ |
|
| 1099 |
+ nw, err := d.getNetwork(brName) |
|
| 1100 |
+ if err != nil {
|
|
| 1101 |
+ t.Fatalf("Failed to getNetwork(%s): %v", brName, err)
|
|
| 1102 |
+ } |
|
| 1103 |
+ |
|
| 1104 |
+ addrs4, _, err := nw.bridge.addresses() |
|
| 1105 |
+ if err != nil {
|
|
| 1106 |
+ t.Fatalf("Failed to get the bridge network's address: %v", err)
|
|
| 1107 |
+ } |
|
| 1108 |
+ |
|
| 1109 |
+ if !addrs4[0].IP.Equal(ip) {
|
|
| 1110 |
+ t.Fatal("Creating bridge network with existing bridge interface unexpectedly modified the IP address of the bridge")
|
|
| 1111 |
+ } |
|
| 1112 |
+ |
|
| 1113 |
+ if err := d.DeleteNetwork(brName); err != nil {
|
|
| 1114 |
+ t.Fatalf("Failed to delete network %s: %v", brName, err)
|
|
| 1115 |
+ } |
|
| 1116 |
+ |
|
| 1117 |
+ if _, err := netlink.LinkByName(brName); err != nil {
|
|
| 1118 |
+ t.Fatal("Deleting bridge network that using existing bridge interface unexpectedly deleted the bridge interface")
|
|
| 1119 |
+ } |
|
| 1120 |
+} |
|
| 1121 |
+ |
|
| 1122 |
+func TestCreateParallel(t *testing.T) {
|
|
| 1123 |
+ c := netnsutils.SetupTestOSContextEx(t) |
|
| 1124 |
+ defer c.Cleanup(t) |
|
| 1125 |
+ |
|
| 1126 |
+ d := newDriver() |
|
| 1127 |
+ d.portAllocator = portallocator.NewInstance() |
|
| 1128 |
+ |
|
| 1129 |
+ if err := d.configure(nil); err != nil {
|
|
| 1130 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 1131 |
+ } |
|
| 1132 |
+ |
|
| 1133 |
+ ipV4Data := getIPv4Data(t, "docker0") |
|
| 1134 |
+ |
|
| 1135 |
+ ch := make(chan error, 100) |
|
| 1136 |
+ for i := 0; i < 100; i++ {
|
|
| 1137 |
+ name := "net" + strconv.Itoa(i) |
|
| 1138 |
+ c.Go(t, func() {
|
|
| 1139 |
+ config := &networkConfiguration{BridgeName: name}
|
|
| 1140 |
+ genericOption := make(map[string]interface{})
|
|
| 1141 |
+ genericOption[netlabel.GenericData] = config |
|
| 1142 |
+ if err := d.CreateNetwork(name, genericOption, nil, ipV4Data, nil); err != nil {
|
|
| 1143 |
+ ch <- fmt.Errorf("failed to create %s", name)
|
|
| 1144 |
+ return |
|
| 1145 |
+ } |
|
| 1146 |
+ if err := d.CreateNetwork(name, genericOption, nil, ipV4Data, nil); err == nil {
|
|
| 1147 |
+ ch <- fmt.Errorf("failed was able to create overlap %s", name)
|
|
| 1148 |
+ return |
|
| 1149 |
+ } |
|
| 1150 |
+ ch <- nil |
|
| 1151 |
+ }) |
|
| 1152 |
+ } |
|
| 1153 |
+ // wait for the go routines |
|
| 1154 |
+ var success int |
|
| 1155 |
+ for i := 0; i < 100; i++ {
|
|
| 1156 |
+ val := <-ch |
|
| 1157 |
+ if val == nil {
|
|
| 1158 |
+ success++ |
|
| 1159 |
+ } |
|
| 1160 |
+ } |
|
| 1161 |
+ if success != 1 {
|
|
| 1162 |
+ t.Fatalf("Success should be 1 instead: %d", success)
|
|
| 1163 |
+ } |
|
| 1164 |
+} |
| 0 | 1165 |
deleted file mode 100644 |
| ... | ... |
@@ -1,1167 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "bytes" |
|
| 7 |
- "encoding/json" |
|
| 8 |
- "fmt" |
|
| 9 |
- "net" |
|
| 10 |
- "regexp" |
|
| 11 |
- "strconv" |
|
| 12 |
- "testing" |
|
| 13 |
- |
|
| 14 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 15 |
- "github.com/docker/docker/libnetwork/driverapi" |
|
| 16 |
- "github.com/docker/docker/libnetwork/ipamutils" |
|
| 17 |
- "github.com/docker/docker/libnetwork/iptables" |
|
| 18 |
- "github.com/docker/docker/libnetwork/netlabel" |
|
| 19 |
- "github.com/docker/docker/libnetwork/netutils" |
|
| 20 |
- "github.com/docker/docker/libnetwork/options" |
|
| 21 |
- "github.com/docker/docker/libnetwork/portallocator" |
|
| 22 |
- "github.com/docker/docker/libnetwork/types" |
|
| 23 |
- "github.com/vishvananda/netlink" |
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-func TestEndpointMarshalling(t *testing.T) {
|
|
| 27 |
- ip1, _ := types.ParseCIDR("172.22.0.9/16")
|
|
| 28 |
- ip2, _ := types.ParseCIDR("2001:db8::9")
|
|
| 29 |
- mac, _ := net.ParseMAC("ac:bd:24:57:66:77")
|
|
| 30 |
- e := &bridgeEndpoint{
|
|
| 31 |
- id: "d2c015a1fe5930650cbcd50493efba0500bcebd8ee1f4401a16319f8a567de33", |
|
| 32 |
- nid: "ee33fbb43c323f1920b6b35a0101552ac22ede960d0e5245e9738bccc68b2415", |
|
| 33 |
- addr: ip1, |
|
| 34 |
- addrv6: ip2, |
|
| 35 |
- macAddress: mac, |
|
| 36 |
- srcName: "veth123456", |
|
| 37 |
- config: &endpointConfiguration{MacAddress: mac},
|
|
| 38 |
- containerConfig: &containerConfiguration{
|
|
| 39 |
- ParentEndpoints: []string{"one", "due", "three"},
|
|
| 40 |
- ChildEndpoints: []string{"four", "five", "six"},
|
|
| 41 |
- }, |
|
| 42 |
- extConnConfig: &connectivityConfiguration{
|
|
| 43 |
- ExposedPorts: []types.TransportPort{
|
|
| 44 |
- {
|
|
| 45 |
- Proto: 6, |
|
| 46 |
- Port: uint16(18), |
|
| 47 |
- }, |
|
| 48 |
- }, |
|
| 49 |
- PortBindings: []types.PortBinding{
|
|
| 50 |
- {
|
|
| 51 |
- Proto: 6, |
|
| 52 |
- IP: net.ParseIP("17210.33.9.56"),
|
|
| 53 |
- Port: uint16(18), |
|
| 54 |
- HostPort: uint16(3000), |
|
| 55 |
- HostPortEnd: uint16(14000), |
|
| 56 |
- }, |
|
| 57 |
- }, |
|
| 58 |
- }, |
|
| 59 |
- portMapping: []types.PortBinding{
|
|
| 60 |
- {
|
|
| 61 |
- Proto: 17, |
|
| 62 |
- IP: net.ParseIP("172.33.9.56"),
|
|
| 63 |
- Port: uint16(99), |
|
| 64 |
- HostIP: net.ParseIP("10.10.100.2"),
|
|
| 65 |
- HostPort: uint16(9900), |
|
| 66 |
- HostPortEnd: uint16(10000), |
|
| 67 |
- }, |
|
| 68 |
- {
|
|
| 69 |
- Proto: 6, |
|
| 70 |
- IP: net.ParseIP("171.33.9.56"),
|
|
| 71 |
- Port: uint16(55), |
|
| 72 |
- HostIP: net.ParseIP("10.11.100.2"),
|
|
| 73 |
- HostPort: uint16(5500), |
|
| 74 |
- HostPortEnd: uint16(55000), |
|
| 75 |
- }, |
|
| 76 |
- }, |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- b, err := json.Marshal(e) |
|
| 80 |
- if err != nil {
|
|
| 81 |
- t.Fatal(err) |
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- ee := &bridgeEndpoint{}
|
|
| 85 |
- err = json.Unmarshal(b, ee) |
|
| 86 |
- if err != nil {
|
|
| 87 |
- t.Fatal(err) |
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- if e.id != ee.id || e.nid != ee.nid || e.srcName != ee.srcName || !bytes.Equal(e.macAddress, ee.macAddress) || |
|
| 91 |
- !types.CompareIPNet(e.addr, ee.addr) || !types.CompareIPNet(e.addrv6, ee.addrv6) || |
|
| 92 |
- !compareEpConfig(e.config, ee.config) || |
|
| 93 |
- !compareContainerConfig(e.containerConfig, ee.containerConfig) || |
|
| 94 |
- !compareConnConfig(e.extConnConfig, ee.extConnConfig) || |
|
| 95 |
- !compareBindings(e.portMapping, ee.portMapping) {
|
|
| 96 |
- t.Fatalf("JSON marsh/unmarsh failed.\nOriginal:\n%#v\nDecoded:\n%#v", e, ee)
|
|
| 97 |
- } |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func compareEpConfig(a, b *endpointConfiguration) bool {
|
|
| 101 |
- if a == b {
|
|
| 102 |
- return true |
|
| 103 |
- } |
|
| 104 |
- if a == nil || b == nil {
|
|
| 105 |
- return false |
|
| 106 |
- } |
|
| 107 |
- return bytes.Equal(a.MacAddress, b.MacAddress) |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-func compareContainerConfig(a, b *containerConfiguration) bool {
|
|
| 111 |
- if a == b {
|
|
| 112 |
- return true |
|
| 113 |
- } |
|
| 114 |
- if a == nil || b == nil {
|
|
| 115 |
- return false |
|
| 116 |
- } |
|
| 117 |
- if len(a.ParentEndpoints) != len(b.ParentEndpoints) || |
|
| 118 |
- len(a.ChildEndpoints) != len(b.ChildEndpoints) {
|
|
| 119 |
- return false |
|
| 120 |
- } |
|
| 121 |
- for i := 0; i < len(a.ParentEndpoints); i++ {
|
|
| 122 |
- if a.ParentEndpoints[i] != b.ParentEndpoints[i] {
|
|
| 123 |
- return false |
|
| 124 |
- } |
|
| 125 |
- } |
|
| 126 |
- for i := 0; i < len(a.ChildEndpoints); i++ {
|
|
| 127 |
- if a.ChildEndpoints[i] != b.ChildEndpoints[i] {
|
|
| 128 |
- return false |
|
| 129 |
- } |
|
| 130 |
- } |
|
| 131 |
- return true |
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 |
-func compareConnConfig(a, b *connectivityConfiguration) bool {
|
|
| 135 |
- if a == b {
|
|
| 136 |
- return true |
|
| 137 |
- } |
|
| 138 |
- if a == nil || b == nil {
|
|
| 139 |
- return false |
|
| 140 |
- } |
|
| 141 |
- if len(a.ExposedPorts) != len(b.ExposedPorts) || |
|
| 142 |
- len(a.PortBindings) != len(b.PortBindings) {
|
|
| 143 |
- return false |
|
| 144 |
- } |
|
| 145 |
- for i := 0; i < len(a.ExposedPorts); i++ {
|
|
| 146 |
- if !a.ExposedPorts[i].Equal(&b.ExposedPorts[i]) {
|
|
| 147 |
- return false |
|
| 148 |
- } |
|
| 149 |
- } |
|
| 150 |
- for i := 0; i < len(a.PortBindings); i++ {
|
|
| 151 |
- if !comparePortBinding(&a.PortBindings[i], &b.PortBindings[i]) {
|
|
| 152 |
- return false |
|
| 153 |
- } |
|
| 154 |
- } |
|
| 155 |
- return true |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-// comparePortBinding returns whether the given PortBindings are equal. |
|
| 159 |
-func comparePortBinding(p *types.PortBinding, o *types.PortBinding) bool {
|
|
| 160 |
- if p == o {
|
|
| 161 |
- return true |
|
| 162 |
- } |
|
| 163 |
- |
|
| 164 |
- if o == nil {
|
|
| 165 |
- return false |
|
| 166 |
- } |
|
| 167 |
- |
|
| 168 |
- if p.Proto != o.Proto || p.Port != o.Port || |
|
| 169 |
- p.HostPort != o.HostPort || p.HostPortEnd != o.HostPortEnd {
|
|
| 170 |
- return false |
|
| 171 |
- } |
|
| 172 |
- |
|
| 173 |
- if p.IP != nil {
|
|
| 174 |
- if !p.IP.Equal(o.IP) {
|
|
| 175 |
- return false |
|
| 176 |
- } |
|
| 177 |
- } else {
|
|
| 178 |
- if o.IP != nil {
|
|
| 179 |
- return false |
|
| 180 |
- } |
|
| 181 |
- } |
|
| 182 |
- |
|
| 183 |
- if p.HostIP != nil {
|
|
| 184 |
- if !p.HostIP.Equal(o.HostIP) {
|
|
| 185 |
- return false |
|
| 186 |
- } |
|
| 187 |
- } else {
|
|
| 188 |
- if o.HostIP != nil {
|
|
| 189 |
- return false |
|
| 190 |
- } |
|
| 191 |
- } |
|
| 192 |
- |
|
| 193 |
- return true |
|
| 194 |
-} |
|
| 195 |
- |
|
| 196 |
-func compareBindings(a, b []types.PortBinding) bool {
|
|
| 197 |
- if len(a) != len(b) {
|
|
| 198 |
- return false |
|
| 199 |
- } |
|
| 200 |
- for i := 0; i < len(a); i++ {
|
|
| 201 |
- if !comparePortBinding(&a[i], &b[i]) {
|
|
| 202 |
- return false |
|
| 203 |
- } |
|
| 204 |
- } |
|
| 205 |
- return true |
|
| 206 |
-} |
|
| 207 |
- |
|
| 208 |
-func getIPv4Data(t *testing.T, iface string) []driverapi.IPAMData {
|
|
| 209 |
- ipd := driverapi.IPAMData{AddressSpace: "full"}
|
|
| 210 |
- nw, err := netutils.FindAvailableNetwork(ipamutils.GetLocalScopeDefaultNetworks()) |
|
| 211 |
- if err != nil {
|
|
| 212 |
- t.Fatal(err) |
|
| 213 |
- } |
|
| 214 |
- ipd.Pool = nw |
|
| 215 |
- // Set network gateway to X.X.X.1 |
|
| 216 |
- ipd.Gateway = types.GetIPNetCopy(nw) |
|
| 217 |
- ipd.Gateway.IP[len(ipd.Gateway.IP)-1] = 1 |
|
| 218 |
- return []driverapi.IPAMData{ipd}
|
|
| 219 |
-} |
|
| 220 |
- |
|
| 221 |
-func TestCreateFullOptions(t *testing.T) {
|
|
| 222 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 223 |
- d := newDriver() |
|
| 224 |
- |
|
| 225 |
- config := &configuration{
|
|
| 226 |
- EnableIPForwarding: true, |
|
| 227 |
- EnableIPTables: true, |
|
| 228 |
- } |
|
| 229 |
- |
|
| 230 |
- // Test this scenario: Default gw address does not belong to |
|
| 231 |
- // container network and it's greater than bridge address |
|
| 232 |
- cnw, _ := types.ParseCIDR("172.16.122.0/24")
|
|
| 233 |
- bnw, _ := types.ParseCIDR("172.16.0.0/24")
|
|
| 234 |
- br, _ := types.ParseCIDR("172.16.0.1/16")
|
|
| 235 |
- defgw, _ := types.ParseCIDR("172.16.0.100/16")
|
|
| 236 |
- |
|
| 237 |
- genericOption := make(map[string]interface{})
|
|
| 238 |
- genericOption[netlabel.GenericData] = config |
|
| 239 |
- |
|
| 240 |
- if err := d.configure(genericOption); err != nil {
|
|
| 241 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 242 |
- } |
|
| 243 |
- |
|
| 244 |
- netOption := make(map[string]interface{})
|
|
| 245 |
- netOption[netlabel.EnableIPv6] = true |
|
| 246 |
- netOption[netlabel.GenericData] = &networkConfiguration{
|
|
| 247 |
- BridgeName: DefaultBridgeName, |
|
| 248 |
- } |
|
| 249 |
- |
|
| 250 |
- ipdList := []driverapi.IPAMData{
|
|
| 251 |
- {
|
|
| 252 |
- Pool: bnw, |
|
| 253 |
- Gateway: br, |
|
| 254 |
- AuxAddresses: map[string]*net.IPNet{DefaultGatewayV4AuxKey: defgw},
|
|
| 255 |
- }, |
|
| 256 |
- } |
|
| 257 |
- err := d.CreateNetwork("dummy", netOption, nil, ipdList, nil)
|
|
| 258 |
- if err != nil {
|
|
| 259 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 260 |
- } |
|
| 261 |
- |
|
| 262 |
- // Verify the IP address allocated for the endpoint belongs to the container network |
|
| 263 |
- epOptions := make(map[string]interface{})
|
|
| 264 |
- te := newTestEndpoint(cnw, 10) |
|
| 265 |
- err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
|
|
| 266 |
- if err != nil {
|
|
| 267 |
- t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 268 |
- } |
|
| 269 |
- |
|
| 270 |
- if !cnw.Contains(te.Interface().Address().IP) {
|
|
| 271 |
- t.Fatalf("endpoint got assigned address outside of container network(%s): %s", cnw.String(), te.Interface().Address())
|
|
| 272 |
- } |
|
| 273 |
-} |
|
| 274 |
- |
|
| 275 |
-func TestCreateNoConfig(t *testing.T) {
|
|
| 276 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 277 |
- d := newDriver() |
|
| 278 |
- |
|
| 279 |
- netconfig := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 280 |
- genericOption := make(map[string]interface{})
|
|
| 281 |
- genericOption[netlabel.GenericData] = netconfig |
|
| 282 |
- |
|
| 283 |
- if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 284 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 285 |
- } |
|
| 286 |
-} |
|
| 287 |
- |
|
| 288 |
-func TestCreateFullOptionsLabels(t *testing.T) {
|
|
| 289 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 290 |
- d := newDriver() |
|
| 291 |
- |
|
| 292 |
- config := &configuration{
|
|
| 293 |
- EnableIPForwarding: true, |
|
| 294 |
- } |
|
| 295 |
- genericOption := make(map[string]interface{})
|
|
| 296 |
- genericOption[netlabel.GenericData] = config |
|
| 297 |
- |
|
| 298 |
- if err := d.configure(genericOption); err != nil {
|
|
| 299 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 300 |
- } |
|
| 301 |
- |
|
| 302 |
- bndIPs := "127.0.0.1" |
|
| 303 |
- testHostIP := "1.2.3.4" |
|
| 304 |
- nwV6s := "2001:db8:2600:2700:2800::/80" |
|
| 305 |
- gwV6s := "2001:db8:2600:2700:2800::25/80" |
|
| 306 |
- nwV6, _ := types.ParseCIDR(nwV6s) |
|
| 307 |
- gwV6, _ := types.ParseCIDR(gwV6s) |
|
| 308 |
- |
|
| 309 |
- labels := map[string]string{
|
|
| 310 |
- BridgeName: DefaultBridgeName, |
|
| 311 |
- DefaultBridge: "true", |
|
| 312 |
- EnableICC: "true", |
|
| 313 |
- EnableIPMasquerade: "true", |
|
| 314 |
- DefaultBindingIP: bndIPs, |
|
| 315 |
- netlabel.HostIP: testHostIP, |
|
| 316 |
- } |
|
| 317 |
- |
|
| 318 |
- netOption := make(map[string]interface{})
|
|
| 319 |
- netOption[netlabel.EnableIPv6] = true |
|
| 320 |
- netOption[netlabel.GenericData] = labels |
|
| 321 |
- |
|
| 322 |
- ipdList := getIPv4Data(t, "") |
|
| 323 |
- ipd6List := []driverapi.IPAMData{
|
|
| 324 |
- {
|
|
| 325 |
- Pool: nwV6, |
|
| 326 |
- AuxAddresses: map[string]*net.IPNet{
|
|
| 327 |
- DefaultGatewayV6AuxKey: gwV6, |
|
| 328 |
- }, |
|
| 329 |
- }, |
|
| 330 |
- } |
|
| 331 |
- |
|
| 332 |
- err := d.CreateNetwork("dummy", netOption, nil, ipdList, ipd6List)
|
|
| 333 |
- if err != nil {
|
|
| 334 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 335 |
- } |
|
| 336 |
- |
|
| 337 |
- nw, ok := d.networks["dummy"] |
|
| 338 |
- if !ok {
|
|
| 339 |
- t.Fatal("Cannot find dummy network in bridge driver")
|
|
| 340 |
- } |
|
| 341 |
- |
|
| 342 |
- if nw.config.BridgeName != DefaultBridgeName {
|
|
| 343 |
- t.Fatal("incongruent name in bridge network")
|
|
| 344 |
- } |
|
| 345 |
- |
|
| 346 |
- if !nw.config.EnableIPv6 {
|
|
| 347 |
- t.Fatal("incongruent EnableIPv6 in bridge network")
|
|
| 348 |
- } |
|
| 349 |
- |
|
| 350 |
- if !nw.config.EnableICC {
|
|
| 351 |
- t.Fatal("incongruent EnableICC in bridge network")
|
|
| 352 |
- } |
|
| 353 |
- |
|
| 354 |
- if !nw.config.EnableIPMasquerade {
|
|
| 355 |
- t.Fatal("incongruent EnableIPMasquerade in bridge network")
|
|
| 356 |
- } |
|
| 357 |
- |
|
| 358 |
- bndIP := net.ParseIP(bndIPs) |
|
| 359 |
- if !bndIP.Equal(nw.config.DefaultBindingIP) {
|
|
| 360 |
- t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
|
|
| 361 |
- } |
|
| 362 |
- |
|
| 363 |
- hostIP := net.ParseIP(testHostIP) |
|
| 364 |
- if !hostIP.Equal(nw.config.HostIP) {
|
|
| 365 |
- t.Fatalf("Unexpected: %v", nw.config.HostIP)
|
|
| 366 |
- } |
|
| 367 |
- |
|
| 368 |
- if !types.CompareIPNet(nw.config.AddressIPv6, nwV6) {
|
|
| 369 |
- t.Fatalf("Unexpected: %v", nw.config.AddressIPv6)
|
|
| 370 |
- } |
|
| 371 |
- |
|
| 372 |
- if !gwV6.IP.Equal(nw.config.DefaultGatewayIPv6) {
|
|
| 373 |
- t.Fatalf("Unexpected: %v", nw.config.DefaultGatewayIPv6)
|
|
| 374 |
- } |
|
| 375 |
- |
|
| 376 |
- // In short here we are testing --fixed-cidr-v6 daemon option |
|
| 377 |
- // plus --mac-address run option |
|
| 378 |
- mac, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff")
|
|
| 379 |
- epOptions := map[string]interface{}{netlabel.MacAddress: mac}
|
|
| 380 |
- te := newTestEndpoint(ipdList[0].Pool, 20) |
|
| 381 |
- err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions)
|
|
| 382 |
- if err != nil {
|
|
| 383 |
- t.Fatal(err) |
|
| 384 |
- } |
|
| 385 |
- |
|
| 386 |
- if !nwV6.Contains(te.Interface().AddressIPv6().IP) {
|
|
| 387 |
- t.Fatalf("endpoint got assigned address outside of container network(%s): %s", nwV6.String(), te.Interface().AddressIPv6())
|
|
| 388 |
- } |
|
| 389 |
- if te.Interface().AddressIPv6().IP.String() != "2001:db8:2600:2700:2800:aabb:ccdd:eeff" {
|
|
| 390 |
- t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP)
|
|
| 391 |
- } |
|
| 392 |
-} |
|
| 393 |
- |
|
| 394 |
-func TestCreate(t *testing.T) {
|
|
| 395 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 396 |
- |
|
| 397 |
- d := newDriver() |
|
| 398 |
- |
|
| 399 |
- if err := d.configure(nil); err != nil {
|
|
| 400 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 401 |
- } |
|
| 402 |
- |
|
| 403 |
- netconfig := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 404 |
- genericOption := make(map[string]interface{})
|
|
| 405 |
- genericOption[netlabel.GenericData] = netconfig |
|
| 406 |
- |
|
| 407 |
- if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 408 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 409 |
- } |
|
| 410 |
- |
|
| 411 |
- err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil)
|
|
| 412 |
- if err == nil {
|
|
| 413 |
- t.Fatal("Expected bridge driver to refuse creation of second network with default name")
|
|
| 414 |
- } |
|
| 415 |
- if _, ok := err.(types.ForbiddenError); !ok {
|
|
| 416 |
- t.Fatal("Creation of second network with default name failed with unexpected error type")
|
|
| 417 |
- } |
|
| 418 |
-} |
|
| 419 |
- |
|
| 420 |
-func TestCreateFail(t *testing.T) {
|
|
| 421 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 422 |
- |
|
| 423 |
- d := newDriver() |
|
| 424 |
- |
|
| 425 |
- if err := d.configure(nil); err != nil {
|
|
| 426 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 427 |
- } |
|
| 428 |
- |
|
| 429 |
- netconfig := &networkConfiguration{BridgeName: "dummy0", DefaultBridge: true}
|
|
| 430 |
- genericOption := make(map[string]interface{})
|
|
| 431 |
- genericOption[netlabel.GenericData] = netconfig |
|
| 432 |
- |
|
| 433 |
- if err := d.CreateNetwork("dummy", genericOption, nil, getIPv4Data(t, ""), nil); err == nil {
|
|
| 434 |
- t.Fatal("Bridge creation was expected to fail")
|
|
| 435 |
- } |
|
| 436 |
-} |
|
| 437 |
- |
|
| 438 |
-func TestCreateMultipleNetworks(t *testing.T) {
|
|
| 439 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 440 |
- |
|
| 441 |
- d := newDriver() |
|
| 442 |
- |
|
| 443 |
- config := &configuration{
|
|
| 444 |
- EnableIPTables: true, |
|
| 445 |
- } |
|
| 446 |
- genericOption := make(map[string]interface{})
|
|
| 447 |
- genericOption[netlabel.GenericData] = config |
|
| 448 |
- |
|
| 449 |
- if err := d.configure(genericOption); err != nil {
|
|
| 450 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 451 |
- } |
|
| 452 |
- |
|
| 453 |
- config1 := &networkConfiguration{BridgeName: "net_test_1"}
|
|
| 454 |
- genericOption = make(map[string]interface{})
|
|
| 455 |
- genericOption[netlabel.GenericData] = config1 |
|
| 456 |
- if err := d.CreateNetwork("1", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 457 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 458 |
- } |
|
| 459 |
- |
|
| 460 |
- verifyV4INCEntries(d.networks, t) |
|
| 461 |
- |
|
| 462 |
- config2 := &networkConfiguration{BridgeName: "net_test_2"}
|
|
| 463 |
- genericOption[netlabel.GenericData] = config2 |
|
| 464 |
- if err := d.CreateNetwork("2", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 465 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 466 |
- } |
|
| 467 |
- |
|
| 468 |
- verifyV4INCEntries(d.networks, t) |
|
| 469 |
- |
|
| 470 |
- config3 := &networkConfiguration{BridgeName: "net_test_3"}
|
|
| 471 |
- genericOption[netlabel.GenericData] = config3 |
|
| 472 |
- if err := d.CreateNetwork("3", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 473 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 474 |
- } |
|
| 475 |
- |
|
| 476 |
- verifyV4INCEntries(d.networks, t) |
|
| 477 |
- |
|
| 478 |
- config4 := &networkConfiguration{BridgeName: "net_test_4"}
|
|
| 479 |
- genericOption[netlabel.GenericData] = config4 |
|
| 480 |
- if err := d.CreateNetwork("4", genericOption, nil, getIPv4Data(t, ""), nil); err != nil {
|
|
| 481 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 482 |
- } |
|
| 483 |
- |
|
| 484 |
- verifyV4INCEntries(d.networks, t) |
|
| 485 |
- |
|
| 486 |
- if err := d.DeleteNetwork("1"); err != nil {
|
|
| 487 |
- t.Log(err) |
|
| 488 |
- } |
|
| 489 |
- verifyV4INCEntries(d.networks, t) |
|
| 490 |
- |
|
| 491 |
- if err := d.DeleteNetwork("2"); err != nil {
|
|
| 492 |
- t.Log(err) |
|
| 493 |
- } |
|
| 494 |
- verifyV4INCEntries(d.networks, t) |
|
| 495 |
- |
|
| 496 |
- if err := d.DeleteNetwork("3"); err != nil {
|
|
| 497 |
- t.Log(err) |
|
| 498 |
- } |
|
| 499 |
- verifyV4INCEntries(d.networks, t) |
|
| 500 |
- |
|
| 501 |
- if err := d.DeleteNetwork("4"); err != nil {
|
|
| 502 |
- t.Log(err) |
|
| 503 |
- } |
|
| 504 |
- verifyV4INCEntries(d.networks, t) |
|
| 505 |
-} |
|
| 506 |
- |
|
| 507 |
-// Verify the network isolation rules are installed for each network |
|
| 508 |
-func verifyV4INCEntries(networks map[string]*bridgeNetwork, t *testing.T) {
|
|
| 509 |
- iptable := iptables.GetIptable(iptables.IPv4) |
|
| 510 |
- out1, err := iptable.Raw("-S", IsolationChain1)
|
|
| 511 |
- if err != nil {
|
|
| 512 |
- t.Fatal(err) |
|
| 513 |
- } |
|
| 514 |
- out2, err := iptable.Raw("-S", IsolationChain2)
|
|
| 515 |
- if err != nil {
|
|
| 516 |
- t.Fatal(err) |
|
| 517 |
- } |
|
| 518 |
- |
|
| 519 |
- for _, n := range networks {
|
|
| 520 |
- re := regexp.MustCompile(fmt.Sprintf("-i %s ! -o %s -j %s", n.config.BridgeName, n.config.BridgeName, IsolationChain2))
|
|
| 521 |
- matches := re.FindAllString(string(out1[:]), -1) |
|
| 522 |
- if len(matches) != 1 {
|
|
| 523 |
- t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out1[:]))
|
|
| 524 |
- } |
|
| 525 |
- re = regexp.MustCompile(fmt.Sprintf("-o %s -j DROP", n.config.BridgeName))
|
|
| 526 |
- matches = re.FindAllString(string(out2[:]), -1) |
|
| 527 |
- if len(matches) != 1 {
|
|
| 528 |
- t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out2[:]))
|
|
| 529 |
- } |
|
| 530 |
- } |
|
| 531 |
-} |
|
| 532 |
- |
|
| 533 |
-type testInterface struct {
|
|
| 534 |
- mac net.HardwareAddr |
|
| 535 |
- addr *net.IPNet |
|
| 536 |
- addrv6 *net.IPNet |
|
| 537 |
- srcName string |
|
| 538 |
- dstName string |
|
| 539 |
-} |
|
| 540 |
- |
|
| 541 |
-type testEndpoint struct {
|
|
| 542 |
- iface *testInterface |
|
| 543 |
- gw net.IP |
|
| 544 |
- gw6 net.IP |
|
| 545 |
- routes []types.StaticRoute |
|
| 546 |
-} |
|
| 547 |
- |
|
| 548 |
-func newTestEndpoint(nw *net.IPNet, ordinal byte) *testEndpoint {
|
|
| 549 |
- addr := types.GetIPNetCopy(nw) |
|
| 550 |
- addr.IP[len(addr.IP)-1] = ordinal |
|
| 551 |
- return &testEndpoint{iface: &testInterface{addr: addr}}
|
|
| 552 |
-} |
|
| 553 |
- |
|
| 554 |
-func (te *testEndpoint) Interface() driverapi.InterfaceInfo {
|
|
| 555 |
- if te.iface != nil {
|
|
| 556 |
- return te.iface |
|
| 557 |
- } |
|
| 558 |
- |
|
| 559 |
- return nil |
|
| 560 |
-} |
|
| 561 |
- |
|
| 562 |
-func (i *testInterface) MacAddress() net.HardwareAddr {
|
|
| 563 |
- return i.mac |
|
| 564 |
-} |
|
| 565 |
- |
|
| 566 |
-func (i *testInterface) Address() *net.IPNet {
|
|
| 567 |
- return i.addr |
|
| 568 |
-} |
|
| 569 |
- |
|
| 570 |
-func (i *testInterface) AddressIPv6() *net.IPNet {
|
|
| 571 |
- return i.addrv6 |
|
| 572 |
-} |
|
| 573 |
- |
|
| 574 |
-func (i *testInterface) SetMacAddress(mac net.HardwareAddr) error {
|
|
| 575 |
- if i.mac != nil {
|
|
| 576 |
- return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", i.mac, mac)
|
|
| 577 |
- } |
|
| 578 |
- if mac == nil {
|
|
| 579 |
- return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
|
|
| 580 |
- } |
|
| 581 |
- i.mac = types.GetMacCopy(mac) |
|
| 582 |
- return nil |
|
| 583 |
-} |
|
| 584 |
- |
|
| 585 |
-func (i *testInterface) SetIPAddress(address *net.IPNet) error {
|
|
| 586 |
- if address.IP == nil {
|
|
| 587 |
- return types.BadRequestErrorf("tried to set nil IP address to endpoint interface")
|
|
| 588 |
- } |
|
| 589 |
- if address.IP.To4() == nil {
|
|
| 590 |
- return setAddress(&i.addrv6, address) |
|
| 591 |
- } |
|
| 592 |
- return setAddress(&i.addr, address) |
|
| 593 |
-} |
|
| 594 |
- |
|
| 595 |
-func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error {
|
|
| 596 |
- if *ifaceAddr != nil {
|
|
| 597 |
- return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address)
|
|
| 598 |
- } |
|
| 599 |
- *ifaceAddr = types.GetIPNetCopy(address) |
|
| 600 |
- return nil |
|
| 601 |
-} |
|
| 602 |
- |
|
| 603 |
-func (i *testInterface) SetNames(srcName string, dstName string) error {
|
|
| 604 |
- i.srcName = srcName |
|
| 605 |
- i.dstName = dstName |
|
| 606 |
- return nil |
|
| 607 |
-} |
|
| 608 |
- |
|
| 609 |
-func (te *testEndpoint) InterfaceName() driverapi.InterfaceNameInfo {
|
|
| 610 |
- if te.iface != nil {
|
|
| 611 |
- return te.iface |
|
| 612 |
- } |
|
| 613 |
- |
|
| 614 |
- return nil |
|
| 615 |
-} |
|
| 616 |
- |
|
| 617 |
-func (te *testEndpoint) SetGateway(gw net.IP) error {
|
|
| 618 |
- te.gw = gw |
|
| 619 |
- return nil |
|
| 620 |
-} |
|
| 621 |
- |
|
| 622 |
-func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error {
|
|
| 623 |
- te.gw6 = gw6 |
|
| 624 |
- return nil |
|
| 625 |
-} |
|
| 626 |
- |
|
| 627 |
-func (te *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
|
|
| 628 |
- te.routes = append(te.routes, types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop})
|
|
| 629 |
- return nil |
|
| 630 |
-} |
|
| 631 |
- |
|
| 632 |
-func (te *testEndpoint) AddTableEntry(tableName string, key string, value []byte) error {
|
|
| 633 |
- return nil |
|
| 634 |
-} |
|
| 635 |
- |
|
| 636 |
-func (te *testEndpoint) DisableGatewayService() {}
|
|
| 637 |
- |
|
| 638 |
-func TestQueryEndpointInfo(t *testing.T) {
|
|
| 639 |
- testQueryEndpointInfo(t, true) |
|
| 640 |
-} |
|
| 641 |
- |
|
| 642 |
-func TestQueryEndpointInfoHairpin(t *testing.T) {
|
|
| 643 |
- testQueryEndpointInfo(t, false) |
|
| 644 |
-} |
|
| 645 |
- |
|
| 646 |
-func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
|
|
| 647 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 648 |
- d := newDriver() |
|
| 649 |
- d.portAllocator = portallocator.NewInstance() |
|
| 650 |
- |
|
| 651 |
- config := &configuration{
|
|
| 652 |
- EnableIPTables: true, |
|
| 653 |
- EnableUserlandProxy: ulPxyEnabled, |
|
| 654 |
- } |
|
| 655 |
- genericOption := make(map[string]interface{})
|
|
| 656 |
- genericOption[netlabel.GenericData] = config |
|
| 657 |
- |
|
| 658 |
- if err := d.configure(genericOption); err != nil {
|
|
| 659 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 660 |
- } |
|
| 661 |
- |
|
| 662 |
- netconfig := &networkConfiguration{
|
|
| 663 |
- BridgeName: DefaultBridgeName, |
|
| 664 |
- EnableICC: false, |
|
| 665 |
- } |
|
| 666 |
- genericOption = make(map[string]interface{})
|
|
| 667 |
- genericOption[netlabel.GenericData] = netconfig |
|
| 668 |
- |
|
| 669 |
- ipdList := getIPv4Data(t, "") |
|
| 670 |
- err := d.CreateNetwork("net1", genericOption, nil, ipdList, nil)
|
|
| 671 |
- if err != nil {
|
|
| 672 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 673 |
- } |
|
| 674 |
- |
|
| 675 |
- sbOptions := make(map[string]interface{})
|
|
| 676 |
- sbOptions[netlabel.PortMap] = getPortMapping() |
|
| 677 |
- |
|
| 678 |
- te := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 679 |
- err = d.CreateEndpoint("net1", "ep1", te.Interface(), nil)
|
|
| 680 |
- if err != nil {
|
|
| 681 |
- t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 682 |
- } |
|
| 683 |
- |
|
| 684 |
- err = d.Join("net1", "ep1", "sbox", te, sbOptions)
|
|
| 685 |
- if err != nil {
|
|
| 686 |
- t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 687 |
- } |
|
| 688 |
- |
|
| 689 |
- err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions)
|
|
| 690 |
- if err != nil {
|
|
| 691 |
- t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 692 |
- } |
|
| 693 |
- |
|
| 694 |
- network, ok := d.networks["net1"] |
|
| 695 |
- if !ok {
|
|
| 696 |
- t.Fatalf("Cannot find network %s inside driver", "net1")
|
|
| 697 |
- } |
|
| 698 |
- ep := network.endpoints["ep1"] |
|
| 699 |
- data, err := d.EndpointOperInfo(network.id, ep.id) |
|
| 700 |
- if err != nil {
|
|
| 701 |
- t.Fatalf("Failed to ask for endpoint operational data: %v", err)
|
|
| 702 |
- } |
|
| 703 |
- pmd, ok := data[netlabel.PortMap] |
|
| 704 |
- if !ok {
|
|
| 705 |
- t.Fatal("Endpoint operational data does not contain port mapping data")
|
|
| 706 |
- } |
|
| 707 |
- pm, ok := pmd.([]types.PortBinding) |
|
| 708 |
- if !ok {
|
|
| 709 |
- t.Fatal("Unexpected format for port mapping in endpoint operational data")
|
|
| 710 |
- } |
|
| 711 |
- if len(ep.portMapping) != len(pm) {
|
|
| 712 |
- t.Fatal("Incomplete data for port mapping in endpoint operational data")
|
|
| 713 |
- } |
|
| 714 |
- for i, pb := range ep.portMapping {
|
|
| 715 |
- if !comparePortBinding(&pb, &pm[i]) {
|
|
| 716 |
- t.Fatal("Unexpected data for port mapping in endpoint operational data")
|
|
| 717 |
- } |
|
| 718 |
- } |
|
| 719 |
- |
|
| 720 |
- err = d.RevokeExternalConnectivity("net1", "ep1")
|
|
| 721 |
- if err != nil {
|
|
| 722 |
- t.Fatal(err) |
|
| 723 |
- } |
|
| 724 |
- |
|
| 725 |
- // release host mapped ports |
|
| 726 |
- err = d.Leave("net1", "ep1")
|
|
| 727 |
- if err != nil {
|
|
| 728 |
- t.Fatal(err) |
|
| 729 |
- } |
|
| 730 |
-} |
|
| 731 |
- |
|
| 732 |
-func getExposedPorts() []types.TransportPort {
|
|
| 733 |
- return []types.TransportPort{
|
|
| 734 |
- {Proto: types.TCP, Port: uint16(5000)},
|
|
| 735 |
- {Proto: types.UDP, Port: uint16(400)},
|
|
| 736 |
- {Proto: types.TCP, Port: uint16(600)},
|
|
| 737 |
- } |
|
| 738 |
-} |
|
| 739 |
- |
|
| 740 |
-func getPortMapping() []types.PortBinding {
|
|
| 741 |
- return []types.PortBinding{
|
|
| 742 |
- {Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
|
|
| 743 |
- {Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
|
|
| 744 |
- {Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
|
|
| 745 |
- } |
|
| 746 |
-} |
|
| 747 |
- |
|
| 748 |
-func TestLinkContainers(t *testing.T) {
|
|
| 749 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 750 |
- |
|
| 751 |
- d := newDriver() |
|
| 752 |
- iptable := iptables.GetIptable(iptables.IPv4) |
|
| 753 |
- |
|
| 754 |
- config := &configuration{
|
|
| 755 |
- EnableIPTables: true, |
|
| 756 |
- } |
|
| 757 |
- genericOption := make(map[string]interface{})
|
|
| 758 |
- genericOption[netlabel.GenericData] = config |
|
| 759 |
- |
|
| 760 |
- if err := d.configure(genericOption); err != nil {
|
|
| 761 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 762 |
- } |
|
| 763 |
- |
|
| 764 |
- netconfig := &networkConfiguration{
|
|
| 765 |
- BridgeName: DefaultBridgeName, |
|
| 766 |
- EnableICC: false, |
|
| 767 |
- } |
|
| 768 |
- genericOption = make(map[string]interface{})
|
|
| 769 |
- genericOption[netlabel.GenericData] = netconfig |
|
| 770 |
- |
|
| 771 |
- ipdList := getIPv4Data(t, "") |
|
| 772 |
- err := d.CreateNetwork("net1", genericOption, nil, ipdList, nil)
|
|
| 773 |
- if err != nil {
|
|
| 774 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 775 |
- } |
|
| 776 |
- |
|
| 777 |
- te1 := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 778 |
- err = d.CreateEndpoint("net1", "ep1", te1.Interface(), nil)
|
|
| 779 |
- if err != nil {
|
|
| 780 |
- t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 781 |
- } |
|
| 782 |
- |
|
| 783 |
- exposedPorts := getExposedPorts() |
|
| 784 |
- sbOptions := make(map[string]interface{})
|
|
| 785 |
- sbOptions[netlabel.ExposedPorts] = exposedPorts |
|
| 786 |
- |
|
| 787 |
- err = d.Join("net1", "ep1", "sbox", te1, sbOptions)
|
|
| 788 |
- if err != nil {
|
|
| 789 |
- t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 790 |
- } |
|
| 791 |
- |
|
| 792 |
- err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions)
|
|
| 793 |
- if err != nil {
|
|
| 794 |
- t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 795 |
- } |
|
| 796 |
- |
|
| 797 |
- addr1 := te1.iface.addr |
|
| 798 |
- if addr1.IP.To4() == nil {
|
|
| 799 |
- t.Fatal("No Ipv4 address assigned to the endpoint: ep1")
|
|
| 800 |
- } |
|
| 801 |
- |
|
| 802 |
- te2 := newTestEndpoint(ipdList[0].Pool, 22) |
|
| 803 |
- err = d.CreateEndpoint("net1", "ep2", te2.Interface(), nil)
|
|
| 804 |
- if err != nil {
|
|
| 805 |
- t.Fatalf("Failed to create an endpoint : %s", err.Error())
|
|
| 806 |
- } |
|
| 807 |
- |
|
| 808 |
- addr2 := te2.iface.addr |
|
| 809 |
- if addr2.IP.To4() == nil {
|
|
| 810 |
- t.Fatal("No Ipv4 address assigned to the endpoint: ep2")
|
|
| 811 |
- } |
|
| 812 |
- |
|
| 813 |
- sbOptions = make(map[string]interface{})
|
|
| 814 |
- sbOptions[netlabel.GenericData] = options.Generic{
|
|
| 815 |
- "ChildEndpoints": []string{"ep1"},
|
|
| 816 |
- } |
|
| 817 |
- |
|
| 818 |
- err = d.Join("net1", "ep2", "", te2, sbOptions)
|
|
| 819 |
- if err != nil {
|
|
| 820 |
- t.Fatal("Failed to link ep1 and ep2")
|
|
| 821 |
- } |
|
| 822 |
- |
|
| 823 |
- err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions)
|
|
| 824 |
- if err != nil {
|
|
| 825 |
- t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 826 |
- } |
|
| 827 |
- |
|
| 828 |
- out, _ := iptable.Raw("-L", DockerChain)
|
|
| 829 |
- for _, pm := range exposedPorts {
|
|
| 830 |
- regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
|
|
| 831 |
- re := regexp.MustCompile(regex) |
|
| 832 |
- matches := re.FindAllString(string(out[:]), -1) |
|
| 833 |
- if len(matches) != 1 {
|
|
| 834 |
- t.Fatalf("IP Tables programming failed %s", string(out[:]))
|
|
| 835 |
- } |
|
| 836 |
- |
|
| 837 |
- regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
|
|
| 838 |
- matched, _ := regexp.MatchString(regex, string(out[:])) |
|
| 839 |
- if !matched {
|
|
| 840 |
- t.Fatalf("IP Tables programming failed %s", string(out[:]))
|
|
| 841 |
- } |
|
| 842 |
- } |
|
| 843 |
- |
|
| 844 |
- err = d.RevokeExternalConnectivity("net1", "ep2")
|
|
| 845 |
- if err != nil {
|
|
| 846 |
- t.Fatalf("Failed to revoke external connectivity: %v", err)
|
|
| 847 |
- } |
|
| 848 |
- |
|
| 849 |
- err = d.Leave("net1", "ep2")
|
|
| 850 |
- if err != nil {
|
|
| 851 |
- t.Fatal("Failed to unlink ep1 and ep2")
|
|
| 852 |
- } |
|
| 853 |
- |
|
| 854 |
- out, _ = iptable.Raw("-L", DockerChain)
|
|
| 855 |
- for _, pm := range exposedPorts {
|
|
| 856 |
- regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
|
|
| 857 |
- re := regexp.MustCompile(regex) |
|
| 858 |
- matches := re.FindAllString(string(out[:]), -1) |
|
| 859 |
- if len(matches) != 0 {
|
|
| 860 |
- t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:]))
|
|
| 861 |
- } |
|
| 862 |
- |
|
| 863 |
- regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
|
|
| 864 |
- matched, _ := regexp.MatchString(regex, string(out[:])) |
|
| 865 |
- if matched {
|
|
| 866 |
- t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:]))
|
|
| 867 |
- } |
|
| 868 |
- } |
|
| 869 |
- |
|
| 870 |
- // Error condition test with an invalid endpoint-id "ep4" |
|
| 871 |
- sbOptions = make(map[string]interface{})
|
|
| 872 |
- sbOptions[netlabel.GenericData] = options.Generic{
|
|
| 873 |
- "ChildEndpoints": []string{"ep1", "ep4"},
|
|
| 874 |
- } |
|
| 875 |
- |
|
| 876 |
- err = d.Join("net1", "ep2", "", te2, sbOptions)
|
|
| 877 |
- if err != nil {
|
|
| 878 |
- t.Fatal(err) |
|
| 879 |
- } |
|
| 880 |
- err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions)
|
|
| 881 |
- if err != nil {
|
|
| 882 |
- out, _ = iptable.Raw("-L", DockerChain)
|
|
| 883 |
- for _, pm := range exposedPorts {
|
|
| 884 |
- regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
|
|
| 885 |
- re := regexp.MustCompile(regex) |
|
| 886 |
- matches := re.FindAllString(string(out[:]), -1) |
|
| 887 |
- if len(matches) != 0 {
|
|
| 888 |
- t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:]))
|
|
| 889 |
- } |
|
| 890 |
- |
|
| 891 |
- regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
|
|
| 892 |
- matched, _ := regexp.MatchString(regex, string(out[:])) |
|
| 893 |
- if matched {
|
|
| 894 |
- t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:]))
|
|
| 895 |
- } |
|
| 896 |
- } |
|
| 897 |
- } else {
|
|
| 898 |
- t.Fatal("Expected Join to fail given link conditions are not satisfied")
|
|
| 899 |
- } |
|
| 900 |
-} |
|
| 901 |
- |
|
| 902 |
-func TestValidateConfig(t *testing.T) {
|
|
| 903 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 904 |
- |
|
| 905 |
- // Test mtu |
|
| 906 |
- c := networkConfiguration{Mtu: -2}
|
|
| 907 |
- err := c.Validate() |
|
| 908 |
- if err == nil {
|
|
| 909 |
- t.Fatal("Failed to detect invalid MTU number")
|
|
| 910 |
- } |
|
| 911 |
- |
|
| 912 |
- c.Mtu = 9000 |
|
| 913 |
- err = c.Validate() |
|
| 914 |
- if err != nil {
|
|
| 915 |
- t.Fatal("unexpected validation error on MTU number")
|
|
| 916 |
- } |
|
| 917 |
- |
|
| 918 |
- // Bridge network |
|
| 919 |
- _, network, _ := net.ParseCIDR("172.28.0.0/16")
|
|
| 920 |
- c = networkConfiguration{
|
|
| 921 |
- AddressIPv4: network, |
|
| 922 |
- } |
|
| 923 |
- |
|
| 924 |
- err = c.Validate() |
|
| 925 |
- if err != nil {
|
|
| 926 |
- t.Fatal(err) |
|
| 927 |
- } |
|
| 928 |
- |
|
| 929 |
- // Test v4 gw |
|
| 930 |
- c.DefaultGatewayIPv4 = net.ParseIP("172.27.30.234")
|
|
| 931 |
- err = c.Validate() |
|
| 932 |
- if err == nil {
|
|
| 933 |
- t.Fatal("Failed to detect invalid default gateway")
|
|
| 934 |
- } |
|
| 935 |
- |
|
| 936 |
- c.DefaultGatewayIPv4 = net.ParseIP("172.28.30.234")
|
|
| 937 |
- err = c.Validate() |
|
| 938 |
- if err != nil {
|
|
| 939 |
- t.Fatal("Unexpected validation error on default gateway")
|
|
| 940 |
- } |
|
| 941 |
- |
|
| 942 |
- // Test v6 gw |
|
| 943 |
- _, v6nw, _ := net.ParseCIDR("2001:db8:ae:b004::/64")
|
|
| 944 |
- c = networkConfiguration{
|
|
| 945 |
- EnableIPv6: true, |
|
| 946 |
- AddressIPv6: v6nw, |
|
| 947 |
- DefaultGatewayIPv6: net.ParseIP("2001:db8:ac:b004::bad:a55"),
|
|
| 948 |
- } |
|
| 949 |
- err = c.Validate() |
|
| 950 |
- if err == nil {
|
|
| 951 |
- t.Fatal("Failed to detect invalid v6 default gateway")
|
|
| 952 |
- } |
|
| 953 |
- |
|
| 954 |
- c.DefaultGatewayIPv6 = net.ParseIP("2001:db8:ae:b004::bad:a55")
|
|
| 955 |
- err = c.Validate() |
|
| 956 |
- if err != nil {
|
|
| 957 |
- t.Fatal("Unexpected validation error on v6 default gateway")
|
|
| 958 |
- } |
|
| 959 |
- |
|
| 960 |
- c.AddressIPv6 = nil |
|
| 961 |
- err = c.Validate() |
|
| 962 |
- if err == nil {
|
|
| 963 |
- t.Fatal("Failed to detect invalid v6 default gateway")
|
|
| 964 |
- } |
|
| 965 |
- |
|
| 966 |
- c.AddressIPv6 = nil |
|
| 967 |
- err = c.Validate() |
|
| 968 |
- if err == nil {
|
|
| 969 |
- t.Fatal("Failed to detect invalid v6 default gateway")
|
|
| 970 |
- } |
|
| 971 |
-} |
|
| 972 |
- |
|
| 973 |
-func TestSetDefaultGw(t *testing.T) {
|
|
| 974 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 975 |
- |
|
| 976 |
- d := newDriver() |
|
| 977 |
- |
|
| 978 |
- if err := d.configure(nil); err != nil {
|
|
| 979 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 980 |
- } |
|
| 981 |
- |
|
| 982 |
- _, subnetv6, _ := net.ParseCIDR("2001:db8:ea9:9abc:b0c4::/80")
|
|
| 983 |
- |
|
| 984 |
- ipdList := getIPv4Data(t, "") |
|
| 985 |
- gw4 := types.GetIPCopy(ipdList[0].Pool.IP).To4() |
|
| 986 |
- gw4[3] = 254 |
|
| 987 |
- gw6 := net.ParseIP("2001:db8:ea9:9abc:b0c4::254")
|
|
| 988 |
- |
|
| 989 |
- config := &networkConfiguration{
|
|
| 990 |
- BridgeName: DefaultBridgeName, |
|
| 991 |
- AddressIPv6: subnetv6, |
|
| 992 |
- DefaultGatewayIPv4: gw4, |
|
| 993 |
- DefaultGatewayIPv6: gw6, |
|
| 994 |
- } |
|
| 995 |
- |
|
| 996 |
- genericOption := make(map[string]interface{})
|
|
| 997 |
- genericOption[netlabel.EnableIPv6] = true |
|
| 998 |
- genericOption[netlabel.GenericData] = config |
|
| 999 |
- |
|
| 1000 |
- err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 1001 |
- if err != nil {
|
|
| 1002 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 1003 |
- } |
|
| 1004 |
- |
|
| 1005 |
- te := newTestEndpoint(ipdList[0].Pool, 10) |
|
| 1006 |
- err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
|
|
| 1007 |
- if err != nil {
|
|
| 1008 |
- t.Fatalf("Failed to create endpoint: %v", err)
|
|
| 1009 |
- } |
|
| 1010 |
- |
|
| 1011 |
- err = d.Join("dummy", "ep", "sbox", te, nil)
|
|
| 1012 |
- if err != nil {
|
|
| 1013 |
- t.Fatalf("Failed to join endpoint: %v", err)
|
|
| 1014 |
- } |
|
| 1015 |
- |
|
| 1016 |
- if !gw4.Equal(te.gw) {
|
|
| 1017 |
- t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw4, te.gw)
|
|
| 1018 |
- } |
|
| 1019 |
- |
|
| 1020 |
- if !gw6.Equal(te.gw6) {
|
|
| 1021 |
- t.Fatalf("Failed to configure default gateway. Expected %v. Found %v", gw6, te.gw6)
|
|
| 1022 |
- } |
|
| 1023 |
-} |
|
| 1024 |
- |
|
| 1025 |
-func TestCleanupIptableRules(t *testing.T) {
|
|
| 1026 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 1027 |
- bridgeChain := []iptables.ChainInfo{
|
|
| 1028 |
- {Name: DockerChain, Table: iptables.Nat},
|
|
| 1029 |
- {Name: DockerChain, Table: iptables.Filter},
|
|
| 1030 |
- {Name: IsolationChain1, Table: iptables.Filter},
|
|
| 1031 |
- } |
|
| 1032 |
- |
|
| 1033 |
- ipVersions := []iptables.IPVersion{iptables.IPv4, iptables.IPv6}
|
|
| 1034 |
- |
|
| 1035 |
- for _, version := range ipVersions {
|
|
| 1036 |
- if _, _, _, _, err := setupIPChains(configuration{EnableIPTables: true}, version); err != nil {
|
|
| 1037 |
- t.Fatalf("Error setting up ip chains for %s: %v", version, err)
|
|
| 1038 |
- } |
|
| 1039 |
- |
|
| 1040 |
- iptable := iptables.GetIptable(version) |
|
| 1041 |
- for _, chainInfo := range bridgeChain {
|
|
| 1042 |
- if !iptable.ExistChain(chainInfo.Name, chainInfo.Table) {
|
|
| 1043 |
- t.Fatalf("iptables version %s chain %s of %s table should have been created", version, chainInfo.Name, chainInfo.Table)
|
|
| 1044 |
- } |
|
| 1045 |
- } |
|
| 1046 |
- removeIPChains(version) |
|
| 1047 |
- for _, chainInfo := range bridgeChain {
|
|
| 1048 |
- if iptable.ExistChain(chainInfo.Name, chainInfo.Table) {
|
|
| 1049 |
- t.Fatalf("iptables version %s chain %s of %s table should have been deleted", version, chainInfo.Name, chainInfo.Table)
|
|
| 1050 |
- } |
|
| 1051 |
- } |
|
| 1052 |
- } |
|
| 1053 |
-} |
|
| 1054 |
- |
|
| 1055 |
-func TestCreateWithExistingBridge(t *testing.T) {
|
|
| 1056 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 1057 |
- d := newDriver() |
|
| 1058 |
- |
|
| 1059 |
- if err := d.configure(nil); err != nil {
|
|
| 1060 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 1061 |
- } |
|
| 1062 |
- |
|
| 1063 |
- brName := "br111" |
|
| 1064 |
- br := &netlink.Bridge{
|
|
| 1065 |
- LinkAttrs: netlink.LinkAttrs{
|
|
| 1066 |
- Name: brName, |
|
| 1067 |
- }, |
|
| 1068 |
- } |
|
| 1069 |
- if err := netlink.LinkAdd(br); err != nil {
|
|
| 1070 |
- t.Fatalf("Failed to create bridge interface: %v", err)
|
|
| 1071 |
- } |
|
| 1072 |
- defer netlink.LinkDel(br) |
|
| 1073 |
- if err := netlink.LinkSetUp(br); err != nil {
|
|
| 1074 |
- t.Fatalf("Failed to set bridge interface up: %v", err)
|
|
| 1075 |
- } |
|
| 1076 |
- |
|
| 1077 |
- ip := net.IP{192, 168, 122, 1}
|
|
| 1078 |
- addr := &netlink.Addr{IPNet: &net.IPNet{
|
|
| 1079 |
- IP: ip, |
|
| 1080 |
- Mask: net.IPv4Mask(255, 255, 255, 0), |
|
| 1081 |
- }} |
|
| 1082 |
- if err := netlink.AddrAdd(br, addr); err != nil {
|
|
| 1083 |
- t.Fatalf("Failed to add IP address to bridge: %v", err)
|
|
| 1084 |
- } |
|
| 1085 |
- |
|
| 1086 |
- netconfig := &networkConfiguration{BridgeName: brName}
|
|
| 1087 |
- genericOption := make(map[string]interface{})
|
|
| 1088 |
- genericOption[netlabel.GenericData] = netconfig |
|
| 1089 |
- |
|
| 1090 |
- ipv4Data := []driverapi.IPAMData{{
|
|
| 1091 |
- AddressSpace: "full", |
|
| 1092 |
- Pool: types.GetIPNetCopy(addr.IPNet), |
|
| 1093 |
- Gateway: types.GetIPNetCopy(addr.IPNet), |
|
| 1094 |
- }} |
|
| 1095 |
- // Set network gateway to X.X.X.1 |
|
| 1096 |
- ipv4Data[0].Gateway.IP[len(ipv4Data[0].Gateway.IP)-1] = 1 |
|
| 1097 |
- |
|
| 1098 |
- if err := d.CreateNetwork(brName, genericOption, nil, ipv4Data, nil); err != nil {
|
|
| 1099 |
- t.Fatalf("Failed to create bridge network: %v", err)
|
|
| 1100 |
- } |
|
| 1101 |
- |
|
| 1102 |
- nw, err := d.getNetwork(brName) |
|
| 1103 |
- if err != nil {
|
|
| 1104 |
- t.Fatalf("Failed to getNetwork(%s): %v", brName, err)
|
|
| 1105 |
- } |
|
| 1106 |
- |
|
| 1107 |
- addrs4, _, err := nw.bridge.addresses() |
|
| 1108 |
- if err != nil {
|
|
| 1109 |
- t.Fatalf("Failed to get the bridge network's address: %v", err)
|
|
| 1110 |
- } |
|
| 1111 |
- |
|
| 1112 |
- if !addrs4[0].IP.Equal(ip) {
|
|
| 1113 |
- t.Fatal("Creating bridge network with existing bridge interface unexpectedly modified the IP address of the bridge")
|
|
| 1114 |
- } |
|
| 1115 |
- |
|
| 1116 |
- if err := d.DeleteNetwork(brName); err != nil {
|
|
| 1117 |
- t.Fatalf("Failed to delete network %s: %v", brName, err)
|
|
| 1118 |
- } |
|
| 1119 |
- |
|
| 1120 |
- if _, err := netlink.LinkByName(brName); err != nil {
|
|
| 1121 |
- t.Fatal("Deleting bridge network that using existing bridge interface unexpectedly deleted the bridge interface")
|
|
| 1122 |
- } |
|
| 1123 |
-} |
|
| 1124 |
- |
|
| 1125 |
-func TestCreateParallel(t *testing.T) {
|
|
| 1126 |
- c := netnsutils.SetupTestOSContextEx(t) |
|
| 1127 |
- defer c.Cleanup(t) |
|
| 1128 |
- |
|
| 1129 |
- d := newDriver() |
|
| 1130 |
- d.portAllocator = portallocator.NewInstance() |
|
| 1131 |
- |
|
| 1132 |
- if err := d.configure(nil); err != nil {
|
|
| 1133 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 1134 |
- } |
|
| 1135 |
- |
|
| 1136 |
- ipV4Data := getIPv4Data(t, "docker0") |
|
| 1137 |
- |
|
| 1138 |
- ch := make(chan error, 100) |
|
| 1139 |
- for i := 0; i < 100; i++ {
|
|
| 1140 |
- name := "net" + strconv.Itoa(i) |
|
| 1141 |
- c.Go(t, func() {
|
|
| 1142 |
- config := &networkConfiguration{BridgeName: name}
|
|
| 1143 |
- genericOption := make(map[string]interface{})
|
|
| 1144 |
- genericOption[netlabel.GenericData] = config |
|
| 1145 |
- if err := d.CreateNetwork(name, genericOption, nil, ipV4Data, nil); err != nil {
|
|
| 1146 |
- ch <- fmt.Errorf("failed to create %s", name)
|
|
| 1147 |
- return |
|
| 1148 |
- } |
|
| 1149 |
- if err := d.CreateNetwork(name, genericOption, nil, ipV4Data, nil); err == nil {
|
|
| 1150 |
- ch <- fmt.Errorf("failed was able to create overlap %s", name)
|
|
| 1151 |
- return |
|
| 1152 |
- } |
|
| 1153 |
- ch <- nil |
|
| 1154 |
- }) |
|
| 1155 |
- } |
|
| 1156 |
- // wait for the go routines |
|
| 1157 |
- var success int |
|
| 1158 |
- for i := 0; i < 100; i++ {
|
|
| 1159 |
- val := <-ch |
|
| 1160 |
- if val == nil {
|
|
| 1161 |
- success++ |
|
| 1162 |
- } |
|
| 1163 |
- } |
|
| 1164 |
- if success != 1 {
|
|
| 1165 |
- t.Fatalf("Success should be 1 instead: %d", success)
|
|
| 1166 |
- } |
|
| 1167 |
-} |
| 1168 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,93 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "fmt" |
|
| 8 |
- "net" |
|
| 9 |
- |
|
| 10 |
- "github.com/containerd/containerd/log" |
|
| 11 |
- "github.com/vishvananda/netlink" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-const ( |
|
| 15 |
- // DefaultBridgeName is the default name for the bridge interface managed |
|
| 16 |
- // by the driver when unspecified by the caller. |
|
| 17 |
- DefaultBridgeName = "docker0" |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-// Interface models the bridge network device. |
|
| 21 |
-type bridgeInterface struct {
|
|
| 22 |
- Link netlink.Link |
|
| 23 |
- bridgeIPv4 *net.IPNet |
|
| 24 |
- bridgeIPv6 *net.IPNet |
|
| 25 |
- gatewayIPv4 net.IP |
|
| 26 |
- gatewayIPv6 net.IP |
|
| 27 |
- nlh *netlink.Handle |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-// newInterface creates a new bridge interface structure. It attempts to find |
|
| 31 |
-// an already existing device identified by the configuration BridgeName field, |
|
| 32 |
-// or the default bridge name when unspecified, but doesn't attempt to create |
|
| 33 |
-// one when missing |
|
| 34 |
-func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) {
|
|
| 35 |
- var err error |
|
| 36 |
- i := &bridgeInterface{nlh: nlh}
|
|
| 37 |
- |
|
| 38 |
- // Initialize the bridge name to the default if unspecified. |
|
| 39 |
- if config.BridgeName == "" {
|
|
| 40 |
- config.BridgeName = DefaultBridgeName |
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- // Attempt to find an existing bridge named with the specified name. |
|
| 44 |
- i.Link, err = nlh.LinkByName(config.BridgeName) |
|
| 45 |
- if err != nil {
|
|
| 46 |
- log.G(context.TODO()).Debugf("Did not find any interface with name %s: %v", config.BridgeName, err)
|
|
| 47 |
- } else if _, ok := i.Link.(*netlink.Bridge); !ok {
|
|
| 48 |
- return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name)
|
|
| 49 |
- } |
|
| 50 |
- return i, nil |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-// exists indicates if the existing bridge interface exists on the system. |
|
| 54 |
-func (i *bridgeInterface) exists() bool {
|
|
| 55 |
- return i.Link != nil |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-// addresses returns all IPv4 addresses and all IPv6 addresses for the bridge interface. |
|
| 59 |
-func (i *bridgeInterface) addresses() ([]netlink.Addr, []netlink.Addr, error) {
|
|
| 60 |
- if !i.exists() {
|
|
| 61 |
- // A nonexistent interface, by definition, cannot have any addresses. |
|
| 62 |
- return nil, nil, nil |
|
| 63 |
- } |
|
| 64 |
- v4addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V4) |
|
| 65 |
- if err != nil {
|
|
| 66 |
- return nil, nil, fmt.Errorf("Failed to retrieve V4 addresses: %v", err)
|
|
| 67 |
- } |
|
| 68 |
- |
|
| 69 |
- v6addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V6) |
|
| 70 |
- if err != nil {
|
|
| 71 |
- return nil, nil, fmt.Errorf("Failed to retrieve V6 addresses: %v", err)
|
|
| 72 |
- } |
|
| 73 |
- |
|
| 74 |
- if len(v4addr) == 0 {
|
|
| 75 |
- return nil, v6addr, nil |
|
| 76 |
- } |
|
| 77 |
- return v4addr, v6addr, nil |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-func (i *bridgeInterface) programIPv6Address() error {
|
|
| 81 |
- _, nlAddressList, err := i.addresses() |
|
| 82 |
- if err != nil {
|
|
| 83 |
- return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: fmt.Errorf("failed to retrieve address list: %v", err)}
|
|
| 84 |
- } |
|
| 85 |
- nlAddr := netlink.Addr{IPNet: i.bridgeIPv6}
|
|
| 86 |
- if findIPv6Address(nlAddr, nlAddressList) {
|
|
| 87 |
- return nil |
|
| 88 |
- } |
|
| 89 |
- if err := i.nlh.AddrAdd(i.Link, &nlAddr); err != nil {
|
|
| 90 |
- return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err}
|
|
| 91 |
- } |
|
| 92 |
- return nil |
|
| 93 |
-} |
| 94 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,91 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/containerd/containerd/log" |
|
| 8 |
+ "github.com/vishvananda/netlink" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+const ( |
|
| 12 |
+ // DefaultBridgeName is the default name for the bridge interface managed |
|
| 13 |
+ // by the driver when unspecified by the caller. |
|
| 14 |
+ DefaultBridgeName = "docker0" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+// Interface models the bridge network device. |
|
| 18 |
+type bridgeInterface struct {
|
|
| 19 |
+ Link netlink.Link |
|
| 20 |
+ bridgeIPv4 *net.IPNet |
|
| 21 |
+ bridgeIPv6 *net.IPNet |
|
| 22 |
+ gatewayIPv4 net.IP |
|
| 23 |
+ gatewayIPv6 net.IP |
|
| 24 |
+ nlh *netlink.Handle |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+// newInterface creates a new bridge interface structure. It attempts to find |
|
| 28 |
+// an already existing device identified by the configuration BridgeName field, |
|
| 29 |
+// or the default bridge name when unspecified, but doesn't attempt to create |
|
| 30 |
+// one when missing |
|
| 31 |
+func newInterface(nlh *netlink.Handle, config *networkConfiguration) (*bridgeInterface, error) {
|
|
| 32 |
+ var err error |
|
| 33 |
+ i := &bridgeInterface{nlh: nlh}
|
|
| 34 |
+ |
|
| 35 |
+ // Initialize the bridge name to the default if unspecified. |
|
| 36 |
+ if config.BridgeName == "" {
|
|
| 37 |
+ config.BridgeName = DefaultBridgeName |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ // Attempt to find an existing bridge named with the specified name. |
|
| 41 |
+ i.Link, err = nlh.LinkByName(config.BridgeName) |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ log.G(context.TODO()).Debugf("Did not find any interface with name %s: %v", config.BridgeName, err)
|
|
| 44 |
+ } else if _, ok := i.Link.(*netlink.Bridge); !ok {
|
|
| 45 |
+ return nil, fmt.Errorf("existing interface %s is not a bridge", i.Link.Attrs().Name)
|
|
| 46 |
+ } |
|
| 47 |
+ return i, nil |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// exists indicates if the existing bridge interface exists on the system. |
|
| 51 |
+func (i *bridgeInterface) exists() bool {
|
|
| 52 |
+ return i.Link != nil |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+// addresses returns all IPv4 addresses and all IPv6 addresses for the bridge interface. |
|
| 56 |
+func (i *bridgeInterface) addresses() ([]netlink.Addr, []netlink.Addr, error) {
|
|
| 57 |
+ if !i.exists() {
|
|
| 58 |
+ // A nonexistent interface, by definition, cannot have any addresses. |
|
| 59 |
+ return nil, nil, nil |
|
| 60 |
+ } |
|
| 61 |
+ v4addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V4) |
|
| 62 |
+ if err != nil {
|
|
| 63 |
+ return nil, nil, fmt.Errorf("Failed to retrieve V4 addresses: %v", err)
|
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ v6addr, err := i.nlh.AddrList(i.Link, netlink.FAMILY_V6) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return nil, nil, fmt.Errorf("Failed to retrieve V6 addresses: %v", err)
|
|
| 69 |
+ } |
|
| 70 |
+ |
|
| 71 |
+ if len(v4addr) == 0 {
|
|
| 72 |
+ return nil, v6addr, nil |
|
| 73 |
+ } |
|
| 74 |
+ return v4addr, v6addr, nil |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+func (i *bridgeInterface) programIPv6Address() error {
|
|
| 78 |
+ _, nlAddressList, err := i.addresses() |
|
| 79 |
+ if err != nil {
|
|
| 80 |
+ return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: fmt.Errorf("failed to retrieve address list: %v", err)}
|
|
| 81 |
+ } |
|
| 82 |
+ nlAddr := netlink.Addr{IPNet: i.bridgeIPv6}
|
|
| 83 |
+ if findIPv6Address(nlAddr, nlAddressList) {
|
|
| 84 |
+ return nil |
|
| 85 |
+ } |
|
| 86 |
+ if err := i.nlh.AddrAdd(i.Link, &nlAddr); err != nil {
|
|
| 87 |
+ return &IPv6AddrAddError{IP: i.bridgeIPv6, Err: err}
|
|
| 88 |
+ } |
|
| 89 |
+ return nil |
|
| 90 |
+} |
| 0 | 91 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,50 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 6 |
+ "github.com/vishvananda/netlink" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func TestInterfaceDefaultName(t *testing.T) {
|
|
| 10 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 11 |
+ |
|
| 12 |
+ nh, err := netlink.NewHandle() |
|
| 13 |
+ if err != nil {
|
|
| 14 |
+ t.Fatal(err) |
|
| 15 |
+ } |
|
| 16 |
+ config := &networkConfiguration{}
|
|
| 17 |
+ _, err = newInterface(nh, config) |
|
| 18 |
+ if err != nil {
|
|
| 19 |
+ t.Fatalf("newInterface() failed: %v", err)
|
|
| 20 |
+ } |
|
| 21 |
+ |
|
| 22 |
+ if config.BridgeName != DefaultBridgeName {
|
|
| 23 |
+ t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, config.BridgeName)
|
|
| 24 |
+ } |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func TestAddressesEmptyInterface(t *testing.T) {
|
|
| 28 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 29 |
+ |
|
| 30 |
+ nh, err := netlink.NewHandle() |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ t.Fatal(err) |
|
| 33 |
+ } |
|
| 34 |
+ inf, err := newInterface(nh, &networkConfiguration{})
|
|
| 35 |
+ if err != nil {
|
|
| 36 |
+ t.Fatalf("newInterface() failed: %v", err)
|
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ addrsv4, addrsv6, err := inf.addresses() |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ t.Fatalf("Failed to get addresses of default interface: %v", err)
|
|
| 42 |
+ } |
|
| 43 |
+ if len(addrsv4) != 0 {
|
|
| 44 |
+ t.Fatalf("Default interface has unexpected IPv4: %s", addrsv4)
|
|
| 45 |
+ } |
|
| 46 |
+ if len(addrsv6) != 0 {
|
|
| 47 |
+ t.Fatalf("Default interface has unexpected IPv6: %v", addrsv6)
|
|
| 48 |
+ } |
|
| 49 |
+} |
| 0 | 50 |
deleted file mode 100644 |
| ... | ... |
@@ -1,52 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "testing" |
|
| 7 |
- |
|
| 8 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 9 |
- "github.com/vishvananda/netlink" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-func TestInterfaceDefaultName(t *testing.T) {
|
|
| 13 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 14 |
- |
|
| 15 |
- nh, err := netlink.NewHandle() |
|
| 16 |
- if err != nil {
|
|
| 17 |
- t.Fatal(err) |
|
| 18 |
- } |
|
| 19 |
- config := &networkConfiguration{}
|
|
| 20 |
- _, err = newInterface(nh, config) |
|
| 21 |
- if err != nil {
|
|
| 22 |
- t.Fatalf("newInterface() failed: %v", err)
|
|
| 23 |
- } |
|
| 24 |
- |
|
| 25 |
- if config.BridgeName != DefaultBridgeName {
|
|
| 26 |
- t.Fatalf("Expected default interface name %q, got %q", DefaultBridgeName, config.BridgeName)
|
|
| 27 |
- } |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-func TestAddressesEmptyInterface(t *testing.T) {
|
|
| 31 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 32 |
- |
|
| 33 |
- nh, err := netlink.NewHandle() |
|
| 34 |
- if err != nil {
|
|
| 35 |
- t.Fatal(err) |
|
| 36 |
- } |
|
| 37 |
- inf, err := newInterface(nh, &networkConfiguration{})
|
|
| 38 |
- if err != nil {
|
|
| 39 |
- t.Fatalf("newInterface() failed: %v", err)
|
|
| 40 |
- } |
|
| 41 |
- |
|
| 42 |
- addrsv4, addrsv6, err := inf.addresses() |
|
| 43 |
- if err != nil {
|
|
| 44 |
- t.Fatalf("Failed to get addresses of default interface: %v", err)
|
|
| 45 |
- } |
|
| 46 |
- if len(addrsv4) != 0 {
|
|
| 47 |
- t.Fatalf("Default interface has unexpected IPv4: %s", addrsv4)
|
|
| 48 |
- } |
|
| 49 |
- if len(addrsv6) != 0 {
|
|
| 50 |
- t.Fatalf("Default interface has unexpected IPv6: %v", addrsv6)
|
|
| 51 |
- } |
|
| 52 |
-} |
| 53 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,220 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 6 |
+ "github.com/docker/docker/libnetwork/driverapi" |
|
| 7 |
+ "github.com/docker/docker/libnetwork/netlabel" |
|
| 8 |
+ "github.com/vishvananda/netlink" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+func TestLinkCreate(t *testing.T) {
|
|
| 12 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 13 |
+ d := newDriver() |
|
| 14 |
+ |
|
| 15 |
+ if err := d.configure(nil); err != nil {
|
|
| 16 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ mtu := 1490 |
|
| 20 |
+ config := &networkConfiguration{
|
|
| 21 |
+ BridgeName: DefaultBridgeName, |
|
| 22 |
+ Mtu: mtu, |
|
| 23 |
+ EnableIPv6: true, |
|
| 24 |
+ } |
|
| 25 |
+ genericOption := make(map[string]interface{})
|
|
| 26 |
+ genericOption[netlabel.GenericData] = config |
|
| 27 |
+ |
|
| 28 |
+ ipdList := getIPv4Data(t, "") |
|
| 29 |
+ err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ te := newTestEndpoint(ipdList[0].Pool, 10) |
|
| 35 |
+ err = d.CreateEndpoint("dummy", "", te.Interface(), nil)
|
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ if _, ok := err.(InvalidEndpointIDError); !ok {
|
|
| 38 |
+ t.Fatalf("Failed with a wrong error :%s", err.Error())
|
|
| 39 |
+ } |
|
| 40 |
+ } else {
|
|
| 41 |
+ t.Fatal("Failed to detect invalid config")
|
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ // Good endpoint creation |
|
| 45 |
+ err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
|
|
| 46 |
+ if err != nil {
|
|
| 47 |
+ t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ err = d.Join("dummy", "ep", "sbox", te, nil)
|
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ // Verify sbox endpoint interface inherited MTU value from bridge config |
|
| 56 |
+ sboxLnk, err := netlink.LinkByName(te.iface.srcName) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ t.Fatal(err) |
|
| 59 |
+ } |
|
| 60 |
+ if mtu != sboxLnk.Attrs().MTU {
|
|
| 61 |
+ t.Fatal("Sandbox endpoint interface did not inherit bridge interface MTU config")
|
|
| 62 |
+ } |
|
| 63 |
+ // TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName |
|
| 64 |
+ // then we could check the MTU on hostLnk as well. |
|
| 65 |
+ |
|
| 66 |
+ te1 := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 67 |
+ err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil)
|
|
| 68 |
+ if err == nil {
|
|
| 69 |
+ t.Fatal("Failed to detect duplicate endpoint id on same network")
|
|
| 70 |
+ } |
|
| 71 |
+ |
|
| 72 |
+ if te.iface.dstName == "" {
|
|
| 73 |
+ t.Fatal("Invalid Dstname returned")
|
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ _, err = netlink.LinkByName(te.iface.srcName) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ t.Fatalf("Could not find source link %s: %v", te.iface.srcName, err)
|
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ n, ok := d.networks["dummy"] |
|
| 82 |
+ if !ok {
|
|
| 83 |
+ t.Fatalf("Cannot find network %s inside driver", "dummy")
|
|
| 84 |
+ } |
|
| 85 |
+ ip := te.iface.addr.IP |
|
| 86 |
+ if !n.bridge.bridgeIPv4.Contains(ip) {
|
|
| 87 |
+ t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String())
|
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ ip6 := te.iface.addrv6.IP |
|
| 91 |
+ if !n.bridge.bridgeIPv6.Contains(ip6) {
|
|
| 92 |
+ t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String())
|
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ if !te.gw.Equal(n.bridge.bridgeIPv4.IP) {
|
|
| 96 |
+ t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(),
|
|
| 97 |
+ te.gw.String()) |
|
| 98 |
+ } |
|
| 99 |
+ |
|
| 100 |
+ if !te.gw6.Equal(n.bridge.bridgeIPv6.IP) {
|
|
| 101 |
+ t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(),
|
|
| 102 |
+ te.gw6.String()) |
|
| 103 |
+ } |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+func TestLinkCreateTwo(t *testing.T) {
|
|
| 107 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 108 |
+ d := newDriver() |
|
| 109 |
+ |
|
| 110 |
+ if err := d.configure(nil); err != nil {
|
|
| 111 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ config := &networkConfiguration{
|
|
| 115 |
+ BridgeName: DefaultBridgeName, |
|
| 116 |
+ EnableIPv6: true, |
|
| 117 |
+ } |
|
| 118 |
+ genericOption := make(map[string]interface{})
|
|
| 119 |
+ genericOption[netlabel.GenericData] = config |
|
| 120 |
+ |
|
| 121 |
+ ipdList := getIPv4Data(t, "") |
|
| 122 |
+ err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 123 |
+ if err != nil {
|
|
| 124 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 125 |
+ } |
|
| 126 |
+ |
|
| 127 |
+ te1 := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 128 |
+ err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil)
|
|
| 129 |
+ if err != nil {
|
|
| 130 |
+ t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 131 |
+ } |
|
| 132 |
+ |
|
| 133 |
+ te2 := newTestEndpoint(ipdList[0].Pool, 12) |
|
| 134 |
+ err = d.CreateEndpoint("dummy", "ep", te2.Interface(), nil)
|
|
| 135 |
+ if err != nil {
|
|
| 136 |
+ if _, ok := err.(driverapi.ErrEndpointExists); !ok {
|
|
| 137 |
+ t.Fatalf("Failed with a wrong error: %s", err.Error())
|
|
| 138 |
+ } |
|
| 139 |
+ } else {
|
|
| 140 |
+ t.Fatal("Expected to fail while trying to add same endpoint twice")
|
|
| 141 |
+ } |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+func TestLinkCreateNoEnableIPv6(t *testing.T) {
|
|
| 145 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 146 |
+ d := newDriver() |
|
| 147 |
+ |
|
| 148 |
+ if err := d.configure(nil); err != nil {
|
|
| 149 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ config := &networkConfiguration{
|
|
| 153 |
+ BridgeName: DefaultBridgeName, |
|
| 154 |
+ } |
|
| 155 |
+ genericOption := make(map[string]interface{})
|
|
| 156 |
+ genericOption[netlabel.GenericData] = config |
|
| 157 |
+ |
|
| 158 |
+ ipdList := getIPv4Data(t, "") |
|
| 159 |
+ err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 160 |
+ if err != nil {
|
|
| 161 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 162 |
+ } |
|
| 163 |
+ te := newTestEndpoint(ipdList[0].Pool, 30) |
|
| 164 |
+ err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
|
|
| 165 |
+ if err != nil {
|
|
| 166 |
+ t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ iface := te.iface |
|
| 170 |
+ if iface.addrv6 != nil && iface.addrv6.IP.To16() != nil {
|
|
| 171 |
+ t.Fatalf("Expected IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", iface.addrv6.String())
|
|
| 172 |
+ } |
|
| 173 |
+ |
|
| 174 |
+ if te.gw6.To16() != nil {
|
|
| 175 |
+ t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", te.gw6.String())
|
|
| 176 |
+ } |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+func TestLinkDelete(t *testing.T) {
|
|
| 180 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 181 |
+ d := newDriver() |
|
| 182 |
+ |
|
| 183 |
+ if err := d.configure(nil); err != nil {
|
|
| 184 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 185 |
+ } |
|
| 186 |
+ |
|
| 187 |
+ config := &networkConfiguration{
|
|
| 188 |
+ BridgeName: DefaultBridgeName, |
|
| 189 |
+ EnableIPv6: true, |
|
| 190 |
+ } |
|
| 191 |
+ genericOption := make(map[string]interface{})
|
|
| 192 |
+ genericOption[netlabel.GenericData] = config |
|
| 193 |
+ |
|
| 194 |
+ ipdList := getIPv4Data(t, "") |
|
| 195 |
+ err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 196 |
+ if err != nil {
|
|
| 197 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 198 |
+ } |
|
| 199 |
+ |
|
| 200 |
+ te := newTestEndpoint(ipdList[0].Pool, 30) |
|
| 201 |
+ err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
|
| 202 |
+ if err != nil {
|
|
| 203 |
+ t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 204 |
+ } |
|
| 205 |
+ |
|
| 206 |
+ err = d.DeleteEndpoint("dummy", "")
|
|
| 207 |
+ if err != nil {
|
|
| 208 |
+ if _, ok := err.(InvalidEndpointIDError); !ok {
|
|
| 209 |
+ t.Fatalf("Failed with a wrong error :%s", err.Error())
|
|
| 210 |
+ } |
|
| 211 |
+ } else {
|
|
| 212 |
+ t.Fatal("Failed to detect invalid config")
|
|
| 213 |
+ } |
|
| 214 |
+ |
|
| 215 |
+ err = d.DeleteEndpoint("dummy", "ep1")
|
|
| 216 |
+ if err != nil {
|
|
| 217 |
+ t.Fatal(err) |
|
| 218 |
+ } |
|
| 219 |
+} |
| 0 | 220 |
deleted file mode 100644 |
| ... | ... |
@@ -1,222 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "testing" |
|
| 7 |
- |
|
| 8 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 9 |
- "github.com/docker/docker/libnetwork/driverapi" |
|
| 10 |
- "github.com/docker/docker/libnetwork/netlabel" |
|
| 11 |
- "github.com/vishvananda/netlink" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func TestLinkCreate(t *testing.T) {
|
|
| 15 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 16 |
- d := newDriver() |
|
| 17 |
- |
|
| 18 |
- if err := d.configure(nil); err != nil {
|
|
| 19 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 20 |
- } |
|
| 21 |
- |
|
| 22 |
- mtu := 1490 |
|
| 23 |
- config := &networkConfiguration{
|
|
| 24 |
- BridgeName: DefaultBridgeName, |
|
| 25 |
- Mtu: mtu, |
|
| 26 |
- EnableIPv6: true, |
|
| 27 |
- } |
|
| 28 |
- genericOption := make(map[string]interface{})
|
|
| 29 |
- genericOption[netlabel.GenericData] = config |
|
| 30 |
- |
|
| 31 |
- ipdList := getIPv4Data(t, "") |
|
| 32 |
- err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 33 |
- if err != nil {
|
|
| 34 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- te := newTestEndpoint(ipdList[0].Pool, 10) |
|
| 38 |
- err = d.CreateEndpoint("dummy", "", te.Interface(), nil)
|
|
| 39 |
- if err != nil {
|
|
| 40 |
- if _, ok := err.(InvalidEndpointIDError); !ok {
|
|
| 41 |
- t.Fatalf("Failed with a wrong error :%s", err.Error())
|
|
| 42 |
- } |
|
| 43 |
- } else {
|
|
| 44 |
- t.Fatal("Failed to detect invalid config")
|
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- // Good endpoint creation |
|
| 48 |
- err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
|
|
| 49 |
- if err != nil {
|
|
| 50 |
- t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- err = d.Join("dummy", "ep", "sbox", te, nil)
|
|
| 54 |
- if err != nil {
|
|
| 55 |
- t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- // Verify sbox endpoint interface inherited MTU value from bridge config |
|
| 59 |
- sboxLnk, err := netlink.LinkByName(te.iface.srcName) |
|
| 60 |
- if err != nil {
|
|
| 61 |
- t.Fatal(err) |
|
| 62 |
- } |
|
| 63 |
- if mtu != sboxLnk.Attrs().MTU {
|
|
| 64 |
- t.Fatal("Sandbox endpoint interface did not inherit bridge interface MTU config")
|
|
| 65 |
- } |
|
| 66 |
- // TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName |
|
| 67 |
- // then we could check the MTU on hostLnk as well. |
|
| 68 |
- |
|
| 69 |
- te1 := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 70 |
- err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil)
|
|
| 71 |
- if err == nil {
|
|
| 72 |
- t.Fatal("Failed to detect duplicate endpoint id on same network")
|
|
| 73 |
- } |
|
| 74 |
- |
|
| 75 |
- if te.iface.dstName == "" {
|
|
| 76 |
- t.Fatal("Invalid Dstname returned")
|
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- _, err = netlink.LinkByName(te.iface.srcName) |
|
| 80 |
- if err != nil {
|
|
| 81 |
- t.Fatalf("Could not find source link %s: %v", te.iface.srcName, err)
|
|
| 82 |
- } |
|
| 83 |
- |
|
| 84 |
- n, ok := d.networks["dummy"] |
|
| 85 |
- if !ok {
|
|
| 86 |
- t.Fatalf("Cannot find network %s inside driver", "dummy")
|
|
| 87 |
- } |
|
| 88 |
- ip := te.iface.addr.IP |
|
| 89 |
- if !n.bridge.bridgeIPv4.Contains(ip) {
|
|
| 90 |
- t.Fatalf("IP %s is not a valid ip in the subnet %s", ip.String(), n.bridge.bridgeIPv4.String())
|
|
| 91 |
- } |
|
| 92 |
- |
|
| 93 |
- ip6 := te.iface.addrv6.IP |
|
| 94 |
- if !n.bridge.bridgeIPv6.Contains(ip6) {
|
|
| 95 |
- t.Fatalf("IP %s is not a valid ip in the subnet %s", ip6.String(), bridgeIPv6.String())
|
|
| 96 |
- } |
|
| 97 |
- |
|
| 98 |
- if !te.gw.Equal(n.bridge.bridgeIPv4.IP) {
|
|
| 99 |
- t.Fatalf("Invalid default gateway. Expected %s. Got %s", n.bridge.bridgeIPv4.IP.String(),
|
|
| 100 |
- te.gw.String()) |
|
| 101 |
- } |
|
| 102 |
- |
|
| 103 |
- if !te.gw6.Equal(n.bridge.bridgeIPv6.IP) {
|
|
| 104 |
- t.Fatalf("Invalid default gateway for IPv6. Expected %s. Got %s", n.bridge.bridgeIPv6.IP.String(),
|
|
| 105 |
- te.gw6.String()) |
|
| 106 |
- } |
|
| 107 |
-} |
|
| 108 |
- |
|
| 109 |
-func TestLinkCreateTwo(t *testing.T) {
|
|
| 110 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 111 |
- d := newDriver() |
|
| 112 |
- |
|
| 113 |
- if err := d.configure(nil); err != nil {
|
|
| 114 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 115 |
- } |
|
| 116 |
- |
|
| 117 |
- config := &networkConfiguration{
|
|
| 118 |
- BridgeName: DefaultBridgeName, |
|
| 119 |
- EnableIPv6: true, |
|
| 120 |
- } |
|
| 121 |
- genericOption := make(map[string]interface{})
|
|
| 122 |
- genericOption[netlabel.GenericData] = config |
|
| 123 |
- |
|
| 124 |
- ipdList := getIPv4Data(t, "") |
|
| 125 |
- err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 126 |
- if err != nil {
|
|
| 127 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 128 |
- } |
|
| 129 |
- |
|
| 130 |
- te1 := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 131 |
- err = d.CreateEndpoint("dummy", "ep", te1.Interface(), nil)
|
|
| 132 |
- if err != nil {
|
|
| 133 |
- t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 134 |
- } |
|
| 135 |
- |
|
| 136 |
- te2 := newTestEndpoint(ipdList[0].Pool, 12) |
|
| 137 |
- err = d.CreateEndpoint("dummy", "ep", te2.Interface(), nil)
|
|
| 138 |
- if err != nil {
|
|
| 139 |
- if _, ok := err.(driverapi.ErrEndpointExists); !ok {
|
|
| 140 |
- t.Fatalf("Failed with a wrong error: %s", err.Error())
|
|
| 141 |
- } |
|
| 142 |
- } else {
|
|
| 143 |
- t.Fatal("Expected to fail while trying to add same endpoint twice")
|
|
| 144 |
- } |
|
| 145 |
-} |
|
| 146 |
- |
|
| 147 |
-func TestLinkCreateNoEnableIPv6(t *testing.T) {
|
|
| 148 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 149 |
- d := newDriver() |
|
| 150 |
- |
|
| 151 |
- if err := d.configure(nil); err != nil {
|
|
| 152 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 153 |
- } |
|
| 154 |
- |
|
| 155 |
- config := &networkConfiguration{
|
|
| 156 |
- BridgeName: DefaultBridgeName, |
|
| 157 |
- } |
|
| 158 |
- genericOption := make(map[string]interface{})
|
|
| 159 |
- genericOption[netlabel.GenericData] = config |
|
| 160 |
- |
|
| 161 |
- ipdList := getIPv4Data(t, "") |
|
| 162 |
- err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 163 |
- if err != nil {
|
|
| 164 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 165 |
- } |
|
| 166 |
- te := newTestEndpoint(ipdList[0].Pool, 30) |
|
| 167 |
- err = d.CreateEndpoint("dummy", "ep", te.Interface(), nil)
|
|
| 168 |
- if err != nil {
|
|
| 169 |
- t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 170 |
- } |
|
| 171 |
- |
|
| 172 |
- iface := te.iface |
|
| 173 |
- if iface.addrv6 != nil && iface.addrv6.IP.To16() != nil {
|
|
| 174 |
- t.Fatalf("Expected IPv6 address to be nil when IPv6 is not enabled. Got IPv6 = %s", iface.addrv6.String())
|
|
| 175 |
- } |
|
| 176 |
- |
|
| 177 |
- if te.gw6.To16() != nil {
|
|
| 178 |
- t.Fatalf("Expected GatewayIPv6 to be nil when IPv6 is not enabled. Got GatewayIPv6 = %s", te.gw6.String())
|
|
| 179 |
- } |
|
| 180 |
-} |
|
| 181 |
- |
|
| 182 |
-func TestLinkDelete(t *testing.T) {
|
|
| 183 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 184 |
- d := newDriver() |
|
| 185 |
- |
|
| 186 |
- if err := d.configure(nil); err != nil {
|
|
| 187 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 188 |
- } |
|
| 189 |
- |
|
| 190 |
- config := &networkConfiguration{
|
|
| 191 |
- BridgeName: DefaultBridgeName, |
|
| 192 |
- EnableIPv6: true, |
|
| 193 |
- } |
|
| 194 |
- genericOption := make(map[string]interface{})
|
|
| 195 |
- genericOption[netlabel.GenericData] = config |
|
| 196 |
- |
|
| 197 |
- ipdList := getIPv4Data(t, "") |
|
| 198 |
- err := d.CreateNetwork("dummy", genericOption, nil, ipdList, nil)
|
|
| 199 |
- if err != nil {
|
|
| 200 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 201 |
- } |
|
| 202 |
- |
|
| 203 |
- te := newTestEndpoint(ipdList[0].Pool, 30) |
|
| 204 |
- err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
|
| 205 |
- if err != nil {
|
|
| 206 |
- t.Fatalf("Failed to create a link: %s", err.Error())
|
|
| 207 |
- } |
|
| 208 |
- |
|
| 209 |
- err = d.DeleteEndpoint("dummy", "")
|
|
| 210 |
- if err != nil {
|
|
| 211 |
- if _, ok := err.(InvalidEndpointIDError); !ok {
|
|
| 212 |
- t.Fatalf("Failed with a wrong error :%s", err.Error())
|
|
| 213 |
- } |
|
| 214 |
- } else {
|
|
| 215 |
- t.Fatal("Failed to detect invalid config")
|
|
| 216 |
- } |
|
| 217 |
- |
|
| 218 |
- err = d.DeleteEndpoint("dummy", "ep1")
|
|
| 219 |
- if err != nil {
|
|
| 220 |
- t.Fatal(err) |
|
| 221 |
- } |
|
| 222 |
-} |
| 223 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,246 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "bytes" |
|
| 7 |
- "context" |
|
| 8 |
- "errors" |
|
| 9 |
- "fmt" |
|
| 10 |
- "net" |
|
| 11 |
- "sync" |
|
| 12 |
- |
|
| 13 |
- "github.com/containerd/containerd/log" |
|
| 14 |
- "github.com/docker/docker/libnetwork/types" |
|
| 15 |
- "github.com/ishidawataru/sctp" |
|
| 16 |
-) |
|
| 17 |
- |
|
| 18 |
-func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
|
| 19 |
- if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
|
|
| 20 |
- return nil, nil |
|
| 21 |
- } |
|
| 22 |
- |
|
| 23 |
- defHostIP := net.IPv4zero // 0.0.0.0 |
|
| 24 |
- if reqDefBindIP != nil {
|
|
| 25 |
- defHostIP = reqDefBindIP |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- var containerIPv6 net.IP |
|
| 29 |
- if ep.addrv6 != nil {
|
|
| 30 |
- containerIPv6 = ep.addrv6.IP |
|
| 31 |
- } |
|
| 32 |
- |
|
| 33 |
- pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled) |
|
| 34 |
- if err != nil {
|
|
| 35 |
- return nil, err |
|
| 36 |
- } |
|
| 37 |
- return pb, nil |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
|
| 41 |
- bs := make([]types.PortBinding, 0, len(bindings)) |
|
| 42 |
- for _, c := range bindings {
|
|
| 43 |
- bIPv4 := c.GetCopy() |
|
| 44 |
- bIPv6 := c.GetCopy() |
|
| 45 |
- // Allocate IPv4 Port mappings |
|
| 46 |
- if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok {
|
|
| 47 |
- if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil {
|
|
| 48 |
- // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
| 49 |
- if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
|
| 50 |
- log.G(context.TODO()).Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr)
|
|
| 51 |
- } |
|
| 52 |
- return nil, err |
|
| 53 |
- } |
|
| 54 |
- bs = append(bs, bIPv4) |
|
| 55 |
- } |
|
| 56 |
- |
|
| 57 |
- // skip adding implicit v6 addr, when the kernel was booted with `ipv6.disable=1` |
|
| 58 |
- // https://github.com/moby/moby/issues/42288 |
|
| 59 |
- isV6Binding := c.HostIP != nil && c.HostIP.To4() == nil |
|
| 60 |
- if !isV6Binding && !IsV6Listenable() {
|
|
| 61 |
- continue |
|
| 62 |
- } |
|
| 63 |
- |
|
| 64 |
- // Allocate IPv6 Port mappings |
|
| 65 |
- // If the container has no IPv6 address, allow proxying host IPv6 traffic to it |
|
| 66 |
- // by setting up the binding with the IPv4 interface if the userland proxy is enabled |
|
| 67 |
- // This change was added to keep backward compatibility |
|
| 68 |
- containerIP := containerIPv6 |
|
| 69 |
- if ulPxyEnabled && (containerIPv6 == nil) {
|
|
| 70 |
- containerIP = containerIPv4 |
|
| 71 |
- } |
|
| 72 |
- if ok := n.validatePortBindingIPv6(&bIPv6, containerIP, defHostIP); ok {
|
|
| 73 |
- if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil {
|
|
| 74 |
- // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
| 75 |
- if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
|
| 76 |
- log.G(context.TODO()).Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr)
|
|
| 77 |
- } |
|
| 78 |
- return nil, err |
|
| 79 |
- } |
|
| 80 |
- bs = append(bs, bIPv6) |
|
| 81 |
- } |
|
| 82 |
- } |
|
| 83 |
- return bs, nil |
|
| 84 |
-} |
|
| 85 |
- |
|
| 86 |
-// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true |
|
| 87 |
-// if this is a valid IPv4 binding, else returns false |
|
| 88 |
-func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool {
|
|
| 89 |
- // Return early if there is a valid Host IP, but its not a IPv4 address |
|
| 90 |
- if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil {
|
|
| 91 |
- return false |
|
| 92 |
- } |
|
| 93 |
- // Adjust the host address in the operational binding |
|
| 94 |
- if len(bnd.HostIP) == 0 {
|
|
| 95 |
- // Return early if the default binding address is an IPv6 address |
|
| 96 |
- if defHostIP.To4() == nil {
|
|
| 97 |
- return false |
|
| 98 |
- } |
|
| 99 |
- bnd.HostIP = defHostIP |
|
| 100 |
- } |
|
| 101 |
- bnd.IP = containerIPv4 |
|
| 102 |
- return true |
|
| 103 |
-} |
|
| 104 |
- |
|
| 105 |
-// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true |
|
| 106 |
-// if this is a valid IPv6 binding, else returns false |
|
| 107 |
-func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIP, defHostIP net.IP) bool {
|
|
| 108 |
- // Return early if there is no container endpoint |
|
| 109 |
- if containerIP == nil {
|
|
| 110 |
- return false |
|
| 111 |
- } |
|
| 112 |
- // Return early if there is a valid Host IP, which is a IPv4 address |
|
| 113 |
- if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil {
|
|
| 114 |
- return false |
|
| 115 |
- } |
|
| 116 |
- |
|
| 117 |
- // Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0 |
|
| 118 |
- if len(bnd.HostIP) == 0 {
|
|
| 119 |
- if defHostIP.Equal(net.IPv4zero) {
|
|
| 120 |
- bnd.HostIP = net.IPv6zero |
|
| 121 |
- // If the default binding IP is an IPv6 address, use it |
|
| 122 |
- } else if defHostIP.To4() == nil {
|
|
| 123 |
- bnd.HostIP = defHostIP |
|
| 124 |
- // Return false if default binding ip is an IPv4 address |
|
| 125 |
- } else {
|
|
| 126 |
- return false |
|
| 127 |
- } |
|
| 128 |
- } |
|
| 129 |
- bnd.IP = containerIP |
|
| 130 |
- return true |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error {
|
|
| 134 |
- var ( |
|
| 135 |
- host net.Addr |
|
| 136 |
- err error |
|
| 137 |
- ) |
|
| 138 |
- |
|
| 139 |
- // Adjust HostPortEnd if this is not a range. |
|
| 140 |
- if bnd.HostPortEnd == 0 {
|
|
| 141 |
- bnd.HostPortEnd = bnd.HostPort |
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- // Construct the container side transport address |
|
| 145 |
- container, err := bnd.ContainerAddr() |
|
| 146 |
- if err != nil {
|
|
| 147 |
- return err |
|
| 148 |
- } |
|
| 149 |
- |
|
| 150 |
- portmapper := n.portMapper |
|
| 151 |
- |
|
| 152 |
- if bnd.HostIP.To4() == nil {
|
|
| 153 |
- portmapper = n.portMapperV6 |
|
| 154 |
- } |
|
| 155 |
- |
|
| 156 |
- // Try up to maxAllocatePortAttempts times to get a port that's not already allocated. |
|
| 157 |
- for i := 0; i < maxAllocatePortAttempts; i++ {
|
|
| 158 |
- if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
|
|
| 159 |
- break |
|
| 160 |
- } |
|
| 161 |
- // There is no point in immediately retrying to map an explicitly chosen port. |
|
| 162 |
- if bnd.HostPort != 0 {
|
|
| 163 |
- log.G(context.TODO()).Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
|
|
| 164 |
- break |
|
| 165 |
- } |
|
| 166 |
- log.G(context.TODO()).Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
|
|
| 167 |
- } |
|
| 168 |
- if err != nil {
|
|
| 169 |
- return err |
|
| 170 |
- } |
|
| 171 |
- |
|
| 172 |
- // Save the host port (regardless it was or not specified in the binding) |
|
| 173 |
- switch netAddr := host.(type) {
|
|
| 174 |
- case *net.TCPAddr: |
|
| 175 |
- bnd.HostPort = uint16(host.(*net.TCPAddr).Port) |
|
| 176 |
- return nil |
|
| 177 |
- case *net.UDPAddr: |
|
| 178 |
- bnd.HostPort = uint16(host.(*net.UDPAddr).Port) |
|
| 179 |
- return nil |
|
| 180 |
- case *sctp.SCTPAddr: |
|
| 181 |
- bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port) |
|
| 182 |
- return nil |
|
| 183 |
- default: |
|
| 184 |
- // For completeness |
|
| 185 |
- return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
|
|
| 186 |
- } |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
|
|
| 190 |
- return n.releasePortsInternal(ep.portMapping) |
|
| 191 |
-} |
|
| 192 |
- |
|
| 193 |
-func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error {
|
|
| 194 |
- var errorBuf bytes.Buffer |
|
| 195 |
- |
|
| 196 |
- // Attempt to release all port bindings, do not stop on failure |
|
| 197 |
- for _, m := range bindings {
|
|
| 198 |
- if err := n.releasePort(m); err != nil {
|
|
| 199 |
- errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
|
|
| 200 |
- } |
|
| 201 |
- } |
|
| 202 |
- |
|
| 203 |
- if errorBuf.Len() != 0 {
|
|
| 204 |
- return errors.New(errorBuf.String()) |
|
| 205 |
- } |
|
| 206 |
- return nil |
|
| 207 |
-} |
|
| 208 |
- |
|
| 209 |
-func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
|
|
| 210 |
- // Construct the host side transport address |
|
| 211 |
- host, err := bnd.HostAddr() |
|
| 212 |
- if err != nil {
|
|
| 213 |
- return err |
|
| 214 |
- } |
|
| 215 |
- |
|
| 216 |
- portmapper := n.portMapper |
|
| 217 |
- |
|
| 218 |
- if bnd.HostIP.To4() == nil {
|
|
| 219 |
- portmapper = n.portMapperV6 |
|
| 220 |
- } |
|
| 221 |
- |
|
| 222 |
- return portmapper.Unmap(host) |
|
| 223 |
-} |
|
| 224 |
- |
|
| 225 |
-var ( |
|
| 226 |
- v6ListenableCached bool |
|
| 227 |
- v6ListenableOnce sync.Once |
|
| 228 |
-) |
|
| 229 |
- |
|
| 230 |
-// IsV6Listenable returns true when `[::1]:0` is listenable. |
|
| 231 |
-// IsV6Listenable returns false mostly when the kernel was booted with `ipv6.disable=1` option. |
|
| 232 |
-func IsV6Listenable() bool {
|
|
| 233 |
- v6ListenableOnce.Do(func() {
|
|
| 234 |
- ln, err := net.Listen("tcp6", "[::1]:0")
|
|
| 235 |
- if err != nil {
|
|
| 236 |
- // When the kernel was booted with `ipv6.disable=1`, |
|
| 237 |
- // we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol" |
|
| 238 |
- // https://github.com/moby/moby/issues/42288 |
|
| 239 |
- log.G(context.TODO()).Debugf("port_mapping: v6Listenable=false (%v)", err)
|
|
| 240 |
- } else {
|
|
| 241 |
- v6ListenableCached = true |
|
| 242 |
- ln.Close() |
|
| 243 |
- } |
|
| 244 |
- }) |
|
| 245 |
- return v6ListenableCached |
|
| 246 |
-} |
| 247 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,244 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "context" |
|
| 5 |
+ "errors" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "net" |
|
| 8 |
+ "sync" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/containerd/containerd/log" |
|
| 11 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 12 |
+ "github.com/ishidawataru/sctp" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
|
| 16 |
+ if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil {
|
|
| 17 |
+ return nil, nil |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ defHostIP := net.IPv4zero // 0.0.0.0 |
|
| 21 |
+ if reqDefBindIP != nil {
|
|
| 22 |
+ defHostIP = reqDefBindIP |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ var containerIPv6 net.IP |
|
| 26 |
+ if ep.addrv6 != nil {
|
|
| 27 |
+ containerIPv6 = ep.addrv6.IP |
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ pb, err := n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, containerIPv6, defHostIP, ulPxyEnabled) |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ return nil, err |
|
| 33 |
+ } |
|
| 34 |
+ return pb, nil |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIPv4, containerIPv6, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
|
|
| 38 |
+ bs := make([]types.PortBinding, 0, len(bindings)) |
|
| 39 |
+ for _, c := range bindings {
|
|
| 40 |
+ bIPv4 := c.GetCopy() |
|
| 41 |
+ bIPv6 := c.GetCopy() |
|
| 42 |
+ // Allocate IPv4 Port mappings |
|
| 43 |
+ if ok := n.validatePortBindingIPv4(&bIPv4, containerIPv4, defHostIP); ok {
|
|
| 44 |
+ if err := n.allocatePort(&bIPv4, ulPxyEnabled); err != nil {
|
|
| 45 |
+ // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
| 46 |
+ if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
|
| 47 |
+ log.G(context.TODO()).Warnf("allocation failure for %v, failed to clear previously allocated ipv4 port bindings: %v", bIPv4, cuErr)
|
|
| 48 |
+ } |
|
| 49 |
+ return nil, err |
|
| 50 |
+ } |
|
| 51 |
+ bs = append(bs, bIPv4) |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ // skip adding implicit v6 addr, when the kernel was booted with `ipv6.disable=1` |
|
| 55 |
+ // https://github.com/moby/moby/issues/42288 |
|
| 56 |
+ isV6Binding := c.HostIP != nil && c.HostIP.To4() == nil |
|
| 57 |
+ if !isV6Binding && !IsV6Listenable() {
|
|
| 58 |
+ continue |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ // Allocate IPv6 Port mappings |
|
| 62 |
+ // If the container has no IPv6 address, allow proxying host IPv6 traffic to it |
|
| 63 |
+ // by setting up the binding with the IPv4 interface if the userland proxy is enabled |
|
| 64 |
+ // This change was added to keep backward compatibility |
|
| 65 |
+ containerIP := containerIPv6 |
|
| 66 |
+ if ulPxyEnabled && (containerIPv6 == nil) {
|
|
| 67 |
+ containerIP = containerIPv4 |
|
| 68 |
+ } |
|
| 69 |
+ if ok := n.validatePortBindingIPv6(&bIPv6, containerIP, defHostIP); ok {
|
|
| 70 |
+ if err := n.allocatePort(&bIPv6, ulPxyEnabled); err != nil {
|
|
| 71 |
+ // On allocation failure, release previously allocated ports. On cleanup error, just log a warning message |
|
| 72 |
+ if cuErr := n.releasePortsInternal(bs); cuErr != nil {
|
|
| 73 |
+ log.G(context.TODO()).Warnf("allocation failure for %v, failed to clear previously allocated ipv6 port bindings: %v", bIPv6, cuErr)
|
|
| 74 |
+ } |
|
| 75 |
+ return nil, err |
|
| 76 |
+ } |
|
| 77 |
+ bs = append(bs, bIPv6) |
|
| 78 |
+ } |
|
| 79 |
+ } |
|
| 80 |
+ return bs, nil |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// validatePortBindingIPv4 validates the port binding, populates the missing Host IP field and returns true |
|
| 84 |
+// if this is a valid IPv4 binding, else returns false |
|
| 85 |
+func (n *bridgeNetwork) validatePortBindingIPv4(bnd *types.PortBinding, containerIPv4, defHostIP net.IP) bool {
|
|
| 86 |
+ // Return early if there is a valid Host IP, but its not a IPv4 address |
|
| 87 |
+ if len(bnd.HostIP) > 0 && bnd.HostIP.To4() == nil {
|
|
| 88 |
+ return false |
|
| 89 |
+ } |
|
| 90 |
+ // Adjust the host address in the operational binding |
|
| 91 |
+ if len(bnd.HostIP) == 0 {
|
|
| 92 |
+ // Return early if the default binding address is an IPv6 address |
|
| 93 |
+ if defHostIP.To4() == nil {
|
|
| 94 |
+ return false |
|
| 95 |
+ } |
|
| 96 |
+ bnd.HostIP = defHostIP |
|
| 97 |
+ } |
|
| 98 |
+ bnd.IP = containerIPv4 |
|
| 99 |
+ return true |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+// validatePortBindingIPv6 validates the port binding, populates the missing Host IP field and returns true |
|
| 103 |
+// if this is a valid IPv6 binding, else returns false |
|
| 104 |
+func (n *bridgeNetwork) validatePortBindingIPv6(bnd *types.PortBinding, containerIP, defHostIP net.IP) bool {
|
|
| 105 |
+ // Return early if there is no container endpoint |
|
| 106 |
+ if containerIP == nil {
|
|
| 107 |
+ return false |
|
| 108 |
+ } |
|
| 109 |
+ // Return early if there is a valid Host IP, which is a IPv4 address |
|
| 110 |
+ if len(bnd.HostIP) > 0 && bnd.HostIP.To4() != nil {
|
|
| 111 |
+ return false |
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 114 |
+ // Setup a binding to "::" if Host IP is empty and the default binding IP is 0.0.0.0 |
|
| 115 |
+ if len(bnd.HostIP) == 0 {
|
|
| 116 |
+ if defHostIP.Equal(net.IPv4zero) {
|
|
| 117 |
+ bnd.HostIP = net.IPv6zero |
|
| 118 |
+ // If the default binding IP is an IPv6 address, use it |
|
| 119 |
+ } else if defHostIP.To4() == nil {
|
|
| 120 |
+ bnd.HostIP = defHostIP |
|
| 121 |
+ // Return false if default binding ip is an IPv4 address |
|
| 122 |
+ } else {
|
|
| 123 |
+ return false |
|
| 124 |
+ } |
|
| 125 |
+ } |
|
| 126 |
+ bnd.IP = containerIP |
|
| 127 |
+ return true |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+func (n *bridgeNetwork) allocatePort(bnd *types.PortBinding, ulPxyEnabled bool) error {
|
|
| 131 |
+ var ( |
|
| 132 |
+ host net.Addr |
|
| 133 |
+ err error |
|
| 134 |
+ ) |
|
| 135 |
+ |
|
| 136 |
+ // Adjust HostPortEnd if this is not a range. |
|
| 137 |
+ if bnd.HostPortEnd == 0 {
|
|
| 138 |
+ bnd.HostPortEnd = bnd.HostPort |
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ // Construct the container side transport address |
|
| 142 |
+ container, err := bnd.ContainerAddr() |
|
| 143 |
+ if err != nil {
|
|
| 144 |
+ return err |
|
| 145 |
+ } |
|
| 146 |
+ |
|
| 147 |
+ portmapper := n.portMapper |
|
| 148 |
+ |
|
| 149 |
+ if bnd.HostIP.To4() == nil {
|
|
| 150 |
+ portmapper = n.portMapperV6 |
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ // Try up to maxAllocatePortAttempts times to get a port that's not already allocated. |
|
| 154 |
+ for i := 0; i < maxAllocatePortAttempts; i++ {
|
|
| 155 |
+ if host, err = portmapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), ulPxyEnabled); err == nil {
|
|
| 156 |
+ break |
|
| 157 |
+ } |
|
| 158 |
+ // There is no point in immediately retrying to map an explicitly chosen port. |
|
| 159 |
+ if bnd.HostPort != 0 {
|
|
| 160 |
+ log.G(context.TODO()).Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
|
|
| 161 |
+ break |
|
| 162 |
+ } |
|
| 163 |
+ log.G(context.TODO()).Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
|
|
| 164 |
+ } |
|
| 165 |
+ if err != nil {
|
|
| 166 |
+ return err |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ // Save the host port (regardless it was or not specified in the binding) |
|
| 170 |
+ switch netAddr := host.(type) {
|
|
| 171 |
+ case *net.TCPAddr: |
|
| 172 |
+ bnd.HostPort = uint16(host.(*net.TCPAddr).Port) |
|
| 173 |
+ return nil |
|
| 174 |
+ case *net.UDPAddr: |
|
| 175 |
+ bnd.HostPort = uint16(host.(*net.UDPAddr).Port) |
|
| 176 |
+ return nil |
|
| 177 |
+ case *sctp.SCTPAddr: |
|
| 178 |
+ bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port) |
|
| 179 |
+ return nil |
|
| 180 |
+ default: |
|
| 181 |
+ // For completeness |
|
| 182 |
+ return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
|
|
| 183 |
+ } |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 186 |
+func (n *bridgeNetwork) releasePorts(ep *bridgeEndpoint) error {
|
|
| 187 |
+ return n.releasePortsInternal(ep.portMapping) |
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+func (n *bridgeNetwork) releasePortsInternal(bindings []types.PortBinding) error {
|
|
| 191 |
+ var errorBuf bytes.Buffer |
|
| 192 |
+ |
|
| 193 |
+ // Attempt to release all port bindings, do not stop on failure |
|
| 194 |
+ for _, m := range bindings {
|
|
| 195 |
+ if err := n.releasePort(m); err != nil {
|
|
| 196 |
+ errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
|
|
| 197 |
+ } |
|
| 198 |
+ } |
|
| 199 |
+ |
|
| 200 |
+ if errorBuf.Len() != 0 {
|
|
| 201 |
+ return errors.New(errorBuf.String()) |
|
| 202 |
+ } |
|
| 203 |
+ return nil |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+func (n *bridgeNetwork) releasePort(bnd types.PortBinding) error {
|
|
| 207 |
+ // Construct the host side transport address |
|
| 208 |
+ host, err := bnd.HostAddr() |
|
| 209 |
+ if err != nil {
|
|
| 210 |
+ return err |
|
| 211 |
+ } |
|
| 212 |
+ |
|
| 213 |
+ portmapper := n.portMapper |
|
| 214 |
+ |
|
| 215 |
+ if bnd.HostIP.To4() == nil {
|
|
| 216 |
+ portmapper = n.portMapperV6 |
|
| 217 |
+ } |
|
| 218 |
+ |
|
| 219 |
+ return portmapper.Unmap(host) |
|
| 220 |
+} |
|
| 221 |
+ |
|
| 222 |
+var ( |
|
| 223 |
+ v6ListenableCached bool |
|
| 224 |
+ v6ListenableOnce sync.Once |
|
| 225 |
+) |
|
| 226 |
+ |
|
| 227 |
+// IsV6Listenable returns true when `[::1]:0` is listenable. |
|
| 228 |
+// IsV6Listenable returns false mostly when the kernel was booted with `ipv6.disable=1` option. |
|
| 229 |
+func IsV6Listenable() bool {
|
|
| 230 |
+ v6ListenableOnce.Do(func() {
|
|
| 231 |
+ ln, err := net.Listen("tcp6", "[::1]:0")
|
|
| 232 |
+ if err != nil {
|
|
| 233 |
+ // When the kernel was booted with `ipv6.disable=1`, |
|
| 234 |
+ // we get err "listen tcp6 [::1]:0: socket: address family not supported by protocol" |
|
| 235 |
+ // https://github.com/moby/moby/issues/42288 |
|
| 236 |
+ log.G(context.TODO()).Debugf("port_mapping: v6Listenable=false (%v)", err)
|
|
| 237 |
+ } else {
|
|
| 238 |
+ v6ListenableCached = true |
|
| 239 |
+ ln.Close() |
|
| 240 |
+ } |
|
| 241 |
+ }) |
|
| 242 |
+ return v6ListenableCached |
|
| 243 |
+} |
| 0 | 244 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,173 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 6 |
+ "github.com/docker/docker/libnetwork/netlabel" |
|
| 7 |
+ "github.com/docker/docker/libnetwork/ns" |
|
| 8 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+func TestPortMappingConfig(t *testing.T) {
|
|
| 12 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 13 |
+ d := newDriver() |
|
| 14 |
+ |
|
| 15 |
+ config := &configuration{
|
|
| 16 |
+ EnableIPTables: true, |
|
| 17 |
+ } |
|
| 18 |
+ genericOption := make(map[string]interface{})
|
|
| 19 |
+ genericOption[netlabel.GenericData] = config |
|
| 20 |
+ |
|
| 21 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 22 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ binding1 := types.PortBinding{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)}
|
|
| 26 |
+ binding2 := types.PortBinding{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)}
|
|
| 27 |
+ binding3 := types.PortBinding{Proto: types.SCTP, Port: uint16(300), HostPort: uint16(65000)}
|
|
| 28 |
+ portBindings := []types.PortBinding{binding1, binding2, binding3}
|
|
| 29 |
+ |
|
| 30 |
+ sbOptions := make(map[string]interface{})
|
|
| 31 |
+ sbOptions[netlabel.PortMap] = portBindings |
|
| 32 |
+ |
|
| 33 |
+ netConfig := &networkConfiguration{
|
|
| 34 |
+ BridgeName: DefaultBridgeName, |
|
| 35 |
+ } |
|
| 36 |
+ netOptions := make(map[string]interface{})
|
|
| 37 |
+ netOptions[netlabel.GenericData] = netConfig |
|
| 38 |
+ |
|
| 39 |
+ ipdList := getIPv4Data(t, "") |
|
| 40 |
+ err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil)
|
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ te := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 46 |
+ err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ t.Fatalf("Failed to create the endpoint: %s", err.Error())
|
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil {
|
|
| 52 |
+ t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil {
|
|
| 56 |
+ t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ network, ok := d.networks["dummy"] |
|
| 60 |
+ if !ok {
|
|
| 61 |
+ t.Fatalf("Cannot find network %s inside driver", "dummy")
|
|
| 62 |
+ } |
|
| 63 |
+ ep := network.endpoints["ep1"] |
|
| 64 |
+ if len(ep.portMapping) != 3 {
|
|
| 65 |
+ t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
|
|
| 66 |
+ } |
|
| 67 |
+ if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port || |
|
| 68 |
+ ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port || |
|
| 69 |
+ ep.portMapping[2].Proto != binding3.Proto || ep.portMapping[2].Port != binding3.Port {
|
|
| 70 |
+ t.Fatal("bridgeEndpoint has incorrect port mapping values")
|
|
| 71 |
+ } |
|
| 72 |
+ if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 || |
|
| 73 |
+ ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 || |
|
| 74 |
+ ep.portMapping[2].HostIP == nil || ep.portMapping[2].HostPort == 0 {
|
|
| 75 |
+ t.Fatal("operational port mapping data not found on bridgeEndpoint")
|
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ // release host mapped ports |
|
| 79 |
+ err = d.Leave("dummy", "ep1")
|
|
| 80 |
+ if err != nil {
|
|
| 81 |
+ t.Fatal(err) |
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ err = d.RevokeExternalConnectivity("dummy", "ep1")
|
|
| 85 |
+ if err != nil {
|
|
| 86 |
+ t.Fatal(err) |
|
| 87 |
+ } |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+func TestPortMappingV6Config(t *testing.T) {
|
|
| 91 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 92 |
+ if err := loopbackUp(); err != nil {
|
|
| 93 |
+ t.Fatalf("Could not bring loopback iface up: %v", err)
|
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+ d := newDriver() |
|
| 97 |
+ |
|
| 98 |
+ config := &configuration{
|
|
| 99 |
+ EnableIPTables: true, |
|
| 100 |
+ EnableIP6Tables: true, |
|
| 101 |
+ } |
|
| 102 |
+ genericOption := make(map[string]interface{})
|
|
| 103 |
+ genericOption[netlabel.GenericData] = config |
|
| 104 |
+ |
|
| 105 |
+ if err := d.configure(genericOption); err != nil {
|
|
| 106 |
+ t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ portBindings := []types.PortBinding{
|
|
| 110 |
+ {Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)},
|
|
| 111 |
+ {Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)},
|
|
| 112 |
+ {Proto: types.SCTP, Port: uint16(500), HostPort: uint16(65000)},
|
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ sbOptions := make(map[string]interface{})
|
|
| 116 |
+ sbOptions[netlabel.PortMap] = portBindings |
|
| 117 |
+ netConfig := &networkConfiguration{
|
|
| 118 |
+ BridgeName: DefaultBridgeName, |
|
| 119 |
+ EnableIPv6: true, |
|
| 120 |
+ } |
|
| 121 |
+ netOptions := make(map[string]interface{})
|
|
| 122 |
+ netOptions[netlabel.GenericData] = netConfig |
|
| 123 |
+ |
|
| 124 |
+ ipdList := getIPv4Data(t, "") |
|
| 125 |
+ err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil)
|
|
| 126 |
+ if err != nil {
|
|
| 127 |
+ t.Fatalf("Failed to create bridge: %v", err)
|
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ te := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 131 |
+ err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
|
| 132 |
+ if err != nil {
|
|
| 133 |
+ t.Fatalf("Failed to create the endpoint: %s", err.Error())
|
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil {
|
|
| 137 |
+ t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil {
|
|
| 141 |
+ t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 142 |
+ } |
|
| 143 |
+ |
|
| 144 |
+ network, ok := d.networks["dummy"] |
|
| 145 |
+ if !ok {
|
|
| 146 |
+ t.Fatalf("Cannot find network %s inside driver", "dummy")
|
|
| 147 |
+ } |
|
| 148 |
+ ep := network.endpoints["ep1"] |
|
| 149 |
+ if len(ep.portMapping) != 6 {
|
|
| 150 |
+ t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
|
|
| 151 |
+ } |
|
| 152 |
+ |
|
| 153 |
+ // release host mapped ports |
|
| 154 |
+ err = d.Leave("dummy", "ep1")
|
|
| 155 |
+ if err != nil {
|
|
| 156 |
+ t.Fatal(err) |
|
| 157 |
+ } |
|
| 158 |
+ |
|
| 159 |
+ err = d.RevokeExternalConnectivity("dummy", "ep1")
|
|
| 160 |
+ if err != nil {
|
|
| 161 |
+ t.Fatal(err) |
|
| 162 |
+ } |
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+func loopbackUp() error {
|
|
| 166 |
+ nlHandle := ns.NlHandle() |
|
| 167 |
+ iface, err := nlHandle.LinkByName("lo")
|
|
| 168 |
+ if err != nil {
|
|
| 169 |
+ return err |
|
| 170 |
+ } |
|
| 171 |
+ return nlHandle.LinkSetUp(iface) |
|
| 172 |
+} |
| 0 | 173 |
deleted file mode 100644 |
| ... | ... |
@@ -1,175 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "testing" |
|
| 7 |
- |
|
| 8 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 9 |
- "github.com/docker/docker/libnetwork/netlabel" |
|
| 10 |
- "github.com/docker/docker/libnetwork/ns" |
|
| 11 |
- "github.com/docker/docker/libnetwork/types" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func TestPortMappingConfig(t *testing.T) {
|
|
| 15 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 16 |
- d := newDriver() |
|
| 17 |
- |
|
| 18 |
- config := &configuration{
|
|
| 19 |
- EnableIPTables: true, |
|
| 20 |
- } |
|
| 21 |
- genericOption := make(map[string]interface{})
|
|
| 22 |
- genericOption[netlabel.GenericData] = config |
|
| 23 |
- |
|
| 24 |
- if err := d.configure(genericOption); err != nil {
|
|
| 25 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- binding1 := types.PortBinding{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)}
|
|
| 29 |
- binding2 := types.PortBinding{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)}
|
|
| 30 |
- binding3 := types.PortBinding{Proto: types.SCTP, Port: uint16(300), HostPort: uint16(65000)}
|
|
| 31 |
- portBindings := []types.PortBinding{binding1, binding2, binding3}
|
|
| 32 |
- |
|
| 33 |
- sbOptions := make(map[string]interface{})
|
|
| 34 |
- sbOptions[netlabel.PortMap] = portBindings |
|
| 35 |
- |
|
| 36 |
- netConfig := &networkConfiguration{
|
|
| 37 |
- BridgeName: DefaultBridgeName, |
|
| 38 |
- } |
|
| 39 |
- netOptions := make(map[string]interface{})
|
|
| 40 |
- netOptions[netlabel.GenericData] = netConfig |
|
| 41 |
- |
|
| 42 |
- ipdList := getIPv4Data(t, "") |
|
| 43 |
- err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil)
|
|
| 44 |
- if err != nil {
|
|
| 45 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- te := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 49 |
- err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
|
| 50 |
- if err != nil {
|
|
| 51 |
- t.Fatalf("Failed to create the endpoint: %s", err.Error())
|
|
| 52 |
- } |
|
| 53 |
- |
|
| 54 |
- if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil {
|
|
| 55 |
- t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil {
|
|
| 59 |
- t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 60 |
- } |
|
| 61 |
- |
|
| 62 |
- network, ok := d.networks["dummy"] |
|
| 63 |
- if !ok {
|
|
| 64 |
- t.Fatalf("Cannot find network %s inside driver", "dummy")
|
|
| 65 |
- } |
|
| 66 |
- ep := network.endpoints["ep1"] |
|
| 67 |
- if len(ep.portMapping) != 3 {
|
|
| 68 |
- t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
|
|
| 69 |
- } |
|
| 70 |
- if ep.portMapping[0].Proto != binding1.Proto || ep.portMapping[0].Port != binding1.Port || |
|
| 71 |
- ep.portMapping[1].Proto != binding2.Proto || ep.portMapping[1].Port != binding2.Port || |
|
| 72 |
- ep.portMapping[2].Proto != binding3.Proto || ep.portMapping[2].Port != binding3.Port {
|
|
| 73 |
- t.Fatal("bridgeEndpoint has incorrect port mapping values")
|
|
| 74 |
- } |
|
| 75 |
- if ep.portMapping[0].HostIP == nil || ep.portMapping[0].HostPort == 0 || |
|
| 76 |
- ep.portMapping[1].HostIP == nil || ep.portMapping[1].HostPort == 0 || |
|
| 77 |
- ep.portMapping[2].HostIP == nil || ep.portMapping[2].HostPort == 0 {
|
|
| 78 |
- t.Fatal("operational port mapping data not found on bridgeEndpoint")
|
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- // release host mapped ports |
|
| 82 |
- err = d.Leave("dummy", "ep1")
|
|
| 83 |
- if err != nil {
|
|
| 84 |
- t.Fatal(err) |
|
| 85 |
- } |
|
| 86 |
- |
|
| 87 |
- err = d.RevokeExternalConnectivity("dummy", "ep1")
|
|
| 88 |
- if err != nil {
|
|
| 89 |
- t.Fatal(err) |
|
| 90 |
- } |
|
| 91 |
-} |
|
| 92 |
- |
|
| 93 |
-func TestPortMappingV6Config(t *testing.T) {
|
|
| 94 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 95 |
- if err := loopbackUp(); err != nil {
|
|
| 96 |
- t.Fatalf("Could not bring loopback iface up: %v", err)
|
|
| 97 |
- } |
|
| 98 |
- |
|
| 99 |
- d := newDriver() |
|
| 100 |
- |
|
| 101 |
- config := &configuration{
|
|
| 102 |
- EnableIPTables: true, |
|
| 103 |
- EnableIP6Tables: true, |
|
| 104 |
- } |
|
| 105 |
- genericOption := make(map[string]interface{})
|
|
| 106 |
- genericOption[netlabel.GenericData] = config |
|
| 107 |
- |
|
| 108 |
- if err := d.configure(genericOption); err != nil {
|
|
| 109 |
- t.Fatalf("Failed to setup driver config: %v", err)
|
|
| 110 |
- } |
|
| 111 |
- |
|
| 112 |
- portBindings := []types.PortBinding{
|
|
| 113 |
- {Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)},
|
|
| 114 |
- {Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)},
|
|
| 115 |
- {Proto: types.SCTP, Port: uint16(500), HostPort: uint16(65000)},
|
|
| 116 |
- } |
|
| 117 |
- |
|
| 118 |
- sbOptions := make(map[string]interface{})
|
|
| 119 |
- sbOptions[netlabel.PortMap] = portBindings |
|
| 120 |
- netConfig := &networkConfiguration{
|
|
| 121 |
- BridgeName: DefaultBridgeName, |
|
| 122 |
- EnableIPv6: true, |
|
| 123 |
- } |
|
| 124 |
- netOptions := make(map[string]interface{})
|
|
| 125 |
- netOptions[netlabel.GenericData] = netConfig |
|
| 126 |
- |
|
| 127 |
- ipdList := getIPv4Data(t, "") |
|
| 128 |
- err := d.CreateNetwork("dummy", netOptions, nil, ipdList, nil)
|
|
| 129 |
- if err != nil {
|
|
| 130 |
- t.Fatalf("Failed to create bridge: %v", err)
|
|
| 131 |
- } |
|
| 132 |
- |
|
| 133 |
- te := newTestEndpoint(ipdList[0].Pool, 11) |
|
| 134 |
- err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil)
|
|
| 135 |
- if err != nil {
|
|
| 136 |
- t.Fatalf("Failed to create the endpoint: %s", err.Error())
|
|
| 137 |
- } |
|
| 138 |
- |
|
| 139 |
- if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil {
|
|
| 140 |
- t.Fatalf("Failed to join the endpoint: %v", err)
|
|
| 141 |
- } |
|
| 142 |
- |
|
| 143 |
- if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil {
|
|
| 144 |
- t.Fatalf("Failed to program external connectivity: %v", err)
|
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- network, ok := d.networks["dummy"] |
|
| 148 |
- if !ok {
|
|
| 149 |
- t.Fatalf("Cannot find network %s inside driver", "dummy")
|
|
| 150 |
- } |
|
| 151 |
- ep := network.endpoints["ep1"] |
|
| 152 |
- if len(ep.portMapping) != 6 {
|
|
| 153 |
- t.Fatalf("Failed to store the port bindings into the sandbox info. Found: %v", ep.portMapping)
|
|
| 154 |
- } |
|
| 155 |
- |
|
| 156 |
- // release host mapped ports |
|
| 157 |
- err = d.Leave("dummy", "ep1")
|
|
| 158 |
- if err != nil {
|
|
| 159 |
- t.Fatal(err) |
|
| 160 |
- } |
|
| 161 |
- |
|
| 162 |
- err = d.RevokeExternalConnectivity("dummy", "ep1")
|
|
| 163 |
- if err != nil {
|
|
| 164 |
- t.Fatal(err) |
|
| 165 |
- } |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-func loopbackUp() error {
|
|
| 169 |
- nlHandle := ns.NlHandle() |
|
| 170 |
- iface, err := nlHandle.LinkByName("lo")
|
|
| 171 |
- if err != nil {
|
|
| 172 |
- return err |
|
| 173 |
- } |
|
| 174 |
- return nlHandle.LinkSetUp(iface) |
|
| 175 |
-} |
| 176 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,81 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "fmt" |
|
| 8 |
- "os" |
|
| 9 |
- "path/filepath" |
|
| 10 |
- |
|
| 11 |
- "github.com/containerd/containerd/log" |
|
| 12 |
- "github.com/docker/docker/libnetwork/netutils" |
|
| 13 |
- "github.com/vishvananda/netlink" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-// SetupDevice create a new bridge interface/ |
|
| 17 |
-func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 18 |
- // We only attempt to create the bridge when the requested device name is |
|
| 19 |
- // the default one. The default bridge name can be overridden with the |
|
| 20 |
- // DOCKER_TEST_CREATE_DEFAULT_BRIDGE env var. It should be used only for |
|
| 21 |
- // test purpose. |
|
| 22 |
- var defaultBridgeName string |
|
| 23 |
- if defaultBridgeName = os.Getenv("DOCKER_TEST_CREATE_DEFAULT_BRIDGE"); defaultBridgeName == "" {
|
|
| 24 |
- defaultBridgeName = DefaultBridgeName |
|
| 25 |
- } |
|
| 26 |
- if config.BridgeName != defaultBridgeName && config.DefaultBridge {
|
|
| 27 |
- return NonDefaultBridgeExistError(config.BridgeName) |
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- // Set the bridgeInterface netlink.Bridge. |
|
| 31 |
- i.Link = &netlink.Bridge{
|
|
| 32 |
- LinkAttrs: netlink.LinkAttrs{
|
|
| 33 |
- Name: config.BridgeName, |
|
| 34 |
- }, |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- // Set the bridge's MAC address. Requires kernel version 3.3 or up. |
|
| 38 |
- hwAddr := netutils.GenerateRandomMAC() |
|
| 39 |
- i.Link.Attrs().HardwareAddr = hwAddr |
|
| 40 |
- log.G(context.TODO()).Debugf("Setting bridge mac address to %s", hwAddr)
|
|
| 41 |
- |
|
| 42 |
- if err := i.nlh.LinkAdd(i.Link); err != nil {
|
|
| 43 |
- log.G(context.TODO()).WithError(err).Errorf("Failed to create bridge %s via netlink", config.BridgeName)
|
|
| 44 |
- return err |
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- return nil |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-func setupDefaultSysctl(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 51 |
- // Disable IPv6 router advertisements originating on the bridge |
|
| 52 |
- sysPath := filepath.Join("/proc/sys/net/ipv6/conf/", config.BridgeName, "accept_ra")
|
|
| 53 |
- if _, err := os.Stat(sysPath); err != nil {
|
|
| 54 |
- log.G(context.TODO()). |
|
| 55 |
- WithField("bridge", config.BridgeName).
|
|
| 56 |
- WithField("syspath", sysPath).
|
|
| 57 |
- Info("failed to read ipv6 net.ipv6.conf.<bridge>.accept_ra")
|
|
| 58 |
- return nil |
|
| 59 |
- } |
|
| 60 |
- if err := os.WriteFile(sysPath, []byte{'0', '\n'}, 0o644); err != nil {
|
|
| 61 |
- log.G(context.TODO()).WithError(err).Warn("unable to disable IPv6 router advertisement")
|
|
| 62 |
- } |
|
| 63 |
- return nil |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-// SetupDeviceUp ups the given bridge interface. |
|
| 67 |
-func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 68 |
- err := i.nlh.LinkSetUp(i.Link) |
|
| 69 |
- if err != nil {
|
|
| 70 |
- return fmt.Errorf("Failed to set link up for %s: %v", config.BridgeName, err)
|
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- // Attempt to update the bridge interface to refresh the flags status, |
|
| 74 |
- // ignoring any failure to do so. |
|
| 75 |
- if lnk, err := i.nlh.LinkByName(config.BridgeName); err == nil {
|
|
| 76 |
- i.Link = lnk |
|
| 77 |
- } else {
|
|
| 78 |
- log.G(context.TODO()).Warnf("Failed to retrieve link for interface (%s): %v", config.BridgeName, err)
|
|
| 79 |
- } |
|
| 80 |
- return nil |
|
| 81 |
-} |
| 82 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,79 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path/filepath" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/containerd/containerd/log" |
|
| 9 |
+ "github.com/docker/docker/libnetwork/netutils" |
|
| 10 |
+ "github.com/vishvananda/netlink" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+// SetupDevice create a new bridge interface/ |
|
| 14 |
+func setupDevice(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 15 |
+ // We only attempt to create the bridge when the requested device name is |
|
| 16 |
+ // the default one. The default bridge name can be overridden with the |
|
| 17 |
+ // DOCKER_TEST_CREATE_DEFAULT_BRIDGE env var. It should be used only for |
|
| 18 |
+ // test purpose. |
|
| 19 |
+ var defaultBridgeName string |
|
| 20 |
+ if defaultBridgeName = os.Getenv("DOCKER_TEST_CREATE_DEFAULT_BRIDGE"); defaultBridgeName == "" {
|
|
| 21 |
+ defaultBridgeName = DefaultBridgeName |
|
| 22 |
+ } |
|
| 23 |
+ if config.BridgeName != defaultBridgeName && config.DefaultBridge {
|
|
| 24 |
+ return NonDefaultBridgeExistError(config.BridgeName) |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ // Set the bridgeInterface netlink.Bridge. |
|
| 28 |
+ i.Link = &netlink.Bridge{
|
|
| 29 |
+ LinkAttrs: netlink.LinkAttrs{
|
|
| 30 |
+ Name: config.BridgeName, |
|
| 31 |
+ }, |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ // Set the bridge's MAC address. Requires kernel version 3.3 or up. |
|
| 35 |
+ hwAddr := netutils.GenerateRandomMAC() |
|
| 36 |
+ i.Link.Attrs().HardwareAddr = hwAddr |
|
| 37 |
+ log.G(context.TODO()).Debugf("Setting bridge mac address to %s", hwAddr)
|
|
| 38 |
+ |
|
| 39 |
+ if err := i.nlh.LinkAdd(i.Link); err != nil {
|
|
| 40 |
+ log.G(context.TODO()).WithError(err).Errorf("Failed to create bridge %s via netlink", config.BridgeName)
|
|
| 41 |
+ return err |
|
| 42 |
+ } |
|
| 43 |
+ |
|
| 44 |
+ return nil |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func setupDefaultSysctl(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 48 |
+ // Disable IPv6 router advertisements originating on the bridge |
|
| 49 |
+ sysPath := filepath.Join("/proc/sys/net/ipv6/conf/", config.BridgeName, "accept_ra")
|
|
| 50 |
+ if _, err := os.Stat(sysPath); err != nil {
|
|
| 51 |
+ log.G(context.TODO()). |
|
| 52 |
+ WithField("bridge", config.BridgeName).
|
|
| 53 |
+ WithField("syspath", sysPath).
|
|
| 54 |
+ Info("failed to read ipv6 net.ipv6.conf.<bridge>.accept_ra")
|
|
| 55 |
+ return nil |
|
| 56 |
+ } |
|
| 57 |
+ if err := os.WriteFile(sysPath, []byte{'0', '\n'}, 0o644); err != nil {
|
|
| 58 |
+ log.G(context.TODO()).WithError(err).Warn("unable to disable IPv6 router advertisement")
|
|
| 59 |
+ } |
|
| 60 |
+ return nil |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// SetupDeviceUp ups the given bridge interface. |
|
| 64 |
+func setupDeviceUp(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 65 |
+ err := i.nlh.LinkSetUp(i.Link) |
|
| 66 |
+ if err != nil {
|
|
| 67 |
+ return fmt.Errorf("Failed to set link up for %s: %v", config.BridgeName, err)
|
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ // Attempt to update the bridge interface to refresh the flags status, |
|
| 71 |
+ // ignoring any failure to do so. |
|
| 72 |
+ if lnk, err := i.nlh.LinkByName(config.BridgeName); err == nil {
|
|
| 73 |
+ i.Link = lnk |
|
| 74 |
+ } else {
|
|
| 75 |
+ log.G(context.TODO()).Warnf("Failed to retrieve link for interface (%s): %v", config.BridgeName, err)
|
|
| 76 |
+ } |
|
| 77 |
+ return nil |
|
| 78 |
+} |
| 0 | 79 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,94 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "net" |
|
| 5 |
+ "testing" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 8 |
+ "github.com/docker/docker/libnetwork/netutils" |
|
| 9 |
+ "github.com/vishvananda/netlink" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func TestSetupNewBridge(t *testing.T) {
|
|
| 13 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 14 |
+ |
|
| 15 |
+ nh, err := netlink.NewHandle() |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ t.Fatal(err) |
|
| 18 |
+ } |
|
| 19 |
+ defer nh.Close() |
|
| 20 |
+ |
|
| 21 |
+ config := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 22 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 23 |
+ |
|
| 24 |
+ if err := setupDevice(config, br); err != nil {
|
|
| 25 |
+ t.Fatalf("Bridge creation failed: %v", err)
|
|
| 26 |
+ } |
|
| 27 |
+ if br.Link == nil {
|
|
| 28 |
+ t.Fatal("bridgeInterface link is nil (expected valid link)")
|
|
| 29 |
+ } |
|
| 30 |
+ if _, err := nh.LinkByName(DefaultBridgeName); err != nil {
|
|
| 31 |
+ t.Fatalf("Failed to retrieve bridge device: %v", err)
|
|
| 32 |
+ } |
|
| 33 |
+ if br.Link.Attrs().Flags&net.FlagUp == net.FlagUp {
|
|
| 34 |
+ t.Fatal("bridgeInterface should be created down")
|
|
| 35 |
+ } |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func TestSetupNewNonDefaultBridge(t *testing.T) {
|
|
| 39 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 40 |
+ |
|
| 41 |
+ nh, err := netlink.NewHandle() |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ t.Fatal(err) |
|
| 44 |
+ } |
|
| 45 |
+ defer nh.Close() |
|
| 46 |
+ |
|
| 47 |
+ config := &networkConfiguration{BridgeName: "test0", DefaultBridge: true}
|
|
| 48 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 49 |
+ |
|
| 50 |
+ err = setupDevice(config, br) |
|
| 51 |
+ if err == nil {
|
|
| 52 |
+ t.Fatal(`Expected bridge creation failure with "non default name", succeeded`) |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ if _, ok := err.(NonDefaultBridgeExistError); !ok {
|
|
| 56 |
+ t.Fatalf("Did not fail with expected error. Actual error: %v", err)
|
|
| 57 |
+ } |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func TestSetupDeviceUp(t *testing.T) {
|
|
| 61 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 62 |
+ |
|
| 63 |
+ nh, err := netlink.NewHandle() |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ t.Fatal(err) |
|
| 66 |
+ } |
|
| 67 |
+ defer nh.Close() |
|
| 68 |
+ |
|
| 69 |
+ config := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 70 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 71 |
+ |
|
| 72 |
+ if err := setupDevice(config, br); err != nil {
|
|
| 73 |
+ t.Fatalf("Bridge creation failed: %v", err)
|
|
| 74 |
+ } |
|
| 75 |
+ if err := setupDeviceUp(config, br); err != nil {
|
|
| 76 |
+ t.Fatalf("Failed to up bridge device: %v", err)
|
|
| 77 |
+ } |
|
| 78 |
+ |
|
| 79 |
+ lnk, _ := nh.LinkByName(DefaultBridgeName) |
|
| 80 |
+ if lnk.Attrs().Flags&net.FlagUp != net.FlagUp {
|
|
| 81 |
+ t.Fatal("bridgeInterface should be up")
|
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func TestGenerateRandomMAC(t *testing.T) {
|
|
| 86 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 87 |
+ |
|
| 88 |
+ mac1 := netutils.GenerateRandomMAC() |
|
| 89 |
+ mac2 := netutils.GenerateRandomMAC() |
|
| 90 |
+ if bytes.Equal(mac1, mac2) {
|
|
| 91 |
+ t.Fatalf("Generated twice the same MAC address %v", mac1)
|
|
| 92 |
+ } |
|
| 93 |
+} |
| 0 | 94 |
deleted file mode 100644 |
| ... | ... |
@@ -1,96 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "bytes" |
|
| 7 |
- "net" |
|
| 8 |
- "testing" |
|
| 9 |
- |
|
| 10 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 11 |
- "github.com/docker/docker/libnetwork/netutils" |
|
| 12 |
- "github.com/vishvananda/netlink" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-func TestSetupNewBridge(t *testing.T) {
|
|
| 16 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 17 |
- |
|
| 18 |
- nh, err := netlink.NewHandle() |
|
| 19 |
- if err != nil {
|
|
| 20 |
- t.Fatal(err) |
|
| 21 |
- } |
|
| 22 |
- defer nh.Close() |
|
| 23 |
- |
|
| 24 |
- config := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 25 |
- br := &bridgeInterface{nlh: nh}
|
|
| 26 |
- |
|
| 27 |
- if err := setupDevice(config, br); err != nil {
|
|
| 28 |
- t.Fatalf("Bridge creation failed: %v", err)
|
|
| 29 |
- } |
|
| 30 |
- if br.Link == nil {
|
|
| 31 |
- t.Fatal("bridgeInterface link is nil (expected valid link)")
|
|
| 32 |
- } |
|
| 33 |
- if _, err := nh.LinkByName(DefaultBridgeName); err != nil {
|
|
| 34 |
- t.Fatalf("Failed to retrieve bridge device: %v", err)
|
|
| 35 |
- } |
|
| 36 |
- if br.Link.Attrs().Flags&net.FlagUp == net.FlagUp {
|
|
| 37 |
- t.Fatal("bridgeInterface should be created down")
|
|
| 38 |
- } |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-func TestSetupNewNonDefaultBridge(t *testing.T) {
|
|
| 42 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 43 |
- |
|
| 44 |
- nh, err := netlink.NewHandle() |
|
| 45 |
- if err != nil {
|
|
| 46 |
- t.Fatal(err) |
|
| 47 |
- } |
|
| 48 |
- defer nh.Close() |
|
| 49 |
- |
|
| 50 |
- config := &networkConfiguration{BridgeName: "test0", DefaultBridge: true}
|
|
| 51 |
- br := &bridgeInterface{nlh: nh}
|
|
| 52 |
- |
|
| 53 |
- err = setupDevice(config, br) |
|
| 54 |
- if err == nil {
|
|
| 55 |
- t.Fatal(`Expected bridge creation failure with "non default name", succeeded`) |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- if _, ok := err.(NonDefaultBridgeExistError); !ok {
|
|
| 59 |
- t.Fatalf("Did not fail with expected error. Actual error: %v", err)
|
|
| 60 |
- } |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-func TestSetupDeviceUp(t *testing.T) {
|
|
| 64 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 65 |
- |
|
| 66 |
- nh, err := netlink.NewHandle() |
|
| 67 |
- if err != nil {
|
|
| 68 |
- t.Fatal(err) |
|
| 69 |
- } |
|
| 70 |
- defer nh.Close() |
|
| 71 |
- |
|
| 72 |
- config := &networkConfiguration{BridgeName: DefaultBridgeName}
|
|
| 73 |
- br := &bridgeInterface{nlh: nh}
|
|
| 74 |
- |
|
| 75 |
- if err := setupDevice(config, br); err != nil {
|
|
| 76 |
- t.Fatalf("Bridge creation failed: %v", err)
|
|
| 77 |
- } |
|
| 78 |
- if err := setupDeviceUp(config, br); err != nil {
|
|
| 79 |
- t.Fatalf("Failed to up bridge device: %v", err)
|
|
| 80 |
- } |
|
| 81 |
- |
|
| 82 |
- lnk, _ := nh.LinkByName(DefaultBridgeName) |
|
| 83 |
- if lnk.Attrs().Flags&net.FlagUp != net.FlagUp {
|
|
| 84 |
- t.Fatal("bridgeInterface should be up")
|
|
| 85 |
- } |
|
| 86 |
-} |
|
| 87 |
- |
|
| 88 |
-func TestGenerateRandomMAC(t *testing.T) {
|
|
| 89 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 90 |
- |
|
| 91 |
- mac1 := netutils.GenerateRandomMAC() |
|
| 92 |
- mac2 := netutils.GenerateRandomMAC() |
|
| 93 |
- if bytes.Equal(mac1, mac2) {
|
|
| 94 |
- t.Fatalf("Generated twice the same MAC address %v", mac1)
|
|
| 95 |
- } |
|
| 96 |
-} |
| 97 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,470 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "net" |
|
| 10 |
- |
|
| 11 |
- "github.com/containerd/containerd/log" |
|
| 12 |
- "github.com/docker/docker/libnetwork/iptables" |
|
| 13 |
- "github.com/docker/docker/libnetwork/types" |
|
| 14 |
- "github.com/vishvananda/netlink" |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-// DockerChain: DOCKER iptable chain name |
|
| 18 |
-const ( |
|
| 19 |
- DockerChain = "DOCKER" |
|
| 20 |
- |
|
| 21 |
- // Isolation between bridge networks is achieved in two stages by means |
|
| 22 |
- // of the following two chains in the filter table. The first chain matches |
|
| 23 |
- // on the source interface being a bridge network's bridge and the |
|
| 24 |
- // destination being a different interface. A positive match leads to the |
|
| 25 |
- // second isolation chain. No match returns to the parent chain. The second |
|
| 26 |
- // isolation chain matches on destination interface being a bridge network's |
|
| 27 |
- // bridge. A positive match identifies a packet originated from one bridge |
|
| 28 |
- // network's bridge destined to another bridge network's bridge and will |
|
| 29 |
- // result in the packet being dropped. No match returns to the parent chain. |
|
| 30 |
- |
|
| 31 |
- IsolationChain1 = "DOCKER-ISOLATION-STAGE-1" |
|
| 32 |
- IsolationChain2 = "DOCKER-ISOLATION-STAGE-2" |
|
| 33 |
-) |
|
| 34 |
- |
|
| 35 |
-func setupIPChains(config configuration, version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
|
|
| 36 |
- // Sanity check. |
|
| 37 |
- if !config.EnableIPTables {
|
|
| 38 |
- return nil, nil, nil, nil, errors.New("cannot create new chains, EnableIPTable is disabled")
|
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- hairpinMode := !config.EnableUserlandProxy |
|
| 42 |
- |
|
| 43 |
- iptable := iptables.GetIptable(version) |
|
| 44 |
- |
|
| 45 |
- natChain, err := iptable.NewChain(DockerChain, iptables.Nat, hairpinMode) |
|
| 46 |
- if err != nil {
|
|
| 47 |
- return nil, nil, nil, nil, fmt.Errorf("failed to create NAT chain %s: %v", DockerChain, err)
|
|
| 48 |
- } |
|
| 49 |
- defer func() {
|
|
| 50 |
- if err != nil {
|
|
| 51 |
- if err := iptable.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
|
|
| 52 |
- log.G(context.TODO()).Warnf("failed on removing iptables NAT chain %s on cleanup: %v", DockerChain, err)
|
|
| 53 |
- } |
|
| 54 |
- } |
|
| 55 |
- }() |
|
| 56 |
- |
|
| 57 |
- filterChain, err := iptable.NewChain(DockerChain, iptables.Filter, false) |
|
| 58 |
- if err != nil {
|
|
| 59 |
- return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER chain %s: %v", DockerChain, err)
|
|
| 60 |
- } |
|
| 61 |
- defer func() {
|
|
| 62 |
- if err != nil {
|
|
| 63 |
- if err := iptable.RemoveExistingChain(DockerChain, iptables.Filter); err != nil {
|
|
| 64 |
- log.G(context.TODO()).Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", DockerChain, err)
|
|
| 65 |
- } |
|
| 66 |
- } |
|
| 67 |
- }() |
|
| 68 |
- |
|
| 69 |
- isolationChain1, err := iptable.NewChain(IsolationChain1, iptables.Filter, false) |
|
| 70 |
- if err != nil {
|
|
| 71 |
- return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
|
|
| 72 |
- } |
|
| 73 |
- defer func() {
|
|
| 74 |
- if err != nil {
|
|
| 75 |
- if err := iptable.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil {
|
|
| 76 |
- log.G(context.TODO()).Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain1, err)
|
|
| 77 |
- } |
|
| 78 |
- } |
|
| 79 |
- }() |
|
| 80 |
- |
|
| 81 |
- isolationChain2, err := iptable.NewChain(IsolationChain2, iptables.Filter, false) |
|
| 82 |
- if err != nil {
|
|
| 83 |
- return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
|
|
| 84 |
- } |
|
| 85 |
- defer func() {
|
|
| 86 |
- if err != nil {
|
|
| 87 |
- if err := iptable.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil {
|
|
| 88 |
- log.G(context.TODO()).Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain2, err)
|
|
| 89 |
- } |
|
| 90 |
- } |
|
| 91 |
- }() |
|
| 92 |
- |
|
| 93 |
- if err := iptable.AddReturnRule(IsolationChain1); err != nil {
|
|
| 94 |
- return nil, nil, nil, nil, err |
|
| 95 |
- } |
|
| 96 |
- |
|
| 97 |
- if err := iptable.AddReturnRule(IsolationChain2); err != nil {
|
|
| 98 |
- return nil, nil, nil, nil, err |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- return natChain, filterChain, isolationChain1, isolationChain2, nil |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-func (n *bridgeNetwork) setupIP4Tables(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 105 |
- d := n.driver |
|
| 106 |
- d.Lock() |
|
| 107 |
- driverConfig := d.config |
|
| 108 |
- d.Unlock() |
|
| 109 |
- |
|
| 110 |
- // Sanity check. |
|
| 111 |
- if !driverConfig.EnableIPTables {
|
|
| 112 |
- return errors.New("Cannot program chains, EnableIPTable is disabled")
|
|
| 113 |
- } |
|
| 114 |
- |
|
| 115 |
- maskedAddrv4 := &net.IPNet{
|
|
| 116 |
- IP: i.bridgeIPv4.IP.Mask(i.bridgeIPv4.Mask), |
|
| 117 |
- Mask: i.bridgeIPv4.Mask, |
|
| 118 |
- } |
|
| 119 |
- return n.setupIPTables(iptables.IPv4, maskedAddrv4, config, i) |
|
| 120 |
-} |
|
| 121 |
- |
|
| 122 |
-func (n *bridgeNetwork) setupIP6Tables(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 123 |
- d := n.driver |
|
| 124 |
- d.Lock() |
|
| 125 |
- driverConfig := d.config |
|
| 126 |
- d.Unlock() |
|
| 127 |
- |
|
| 128 |
- // Sanity check. |
|
| 129 |
- if !driverConfig.EnableIP6Tables {
|
|
| 130 |
- return errors.New("Cannot program chains, EnableIP6Tables is disabled")
|
|
| 131 |
- } |
|
| 132 |
- |
|
| 133 |
- maskedAddrv6 := &net.IPNet{
|
|
| 134 |
- IP: i.bridgeIPv6.IP.Mask(i.bridgeIPv6.Mask), |
|
| 135 |
- Mask: i.bridgeIPv6.Mask, |
|
| 136 |
- } |
|
| 137 |
- |
|
| 138 |
- return n.setupIPTables(iptables.IPv6, maskedAddrv6, config, i) |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *net.IPNet, config *networkConfiguration, i *bridgeInterface) error {
|
|
| 142 |
- var err error |
|
| 143 |
- |
|
| 144 |
- d := n.driver |
|
| 145 |
- d.Lock() |
|
| 146 |
- driverConfig := d.config |
|
| 147 |
- d.Unlock() |
|
| 148 |
- |
|
| 149 |
- // Pickup this configuration option from driver |
|
| 150 |
- hairpinMode := !driverConfig.EnableUserlandProxy |
|
| 151 |
- |
|
| 152 |
- iptable := iptables.GetIptable(ipVersion) |
|
| 153 |
- |
|
| 154 |
- if config.Internal {
|
|
| 155 |
- if err = setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, true); err != nil {
|
|
| 156 |
- return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
|
| 157 |
- } |
|
| 158 |
- n.registerIptCleanFunc(func() error {
|
|
| 159 |
- return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false) |
|
| 160 |
- }) |
|
| 161 |
- } else {
|
|
| 162 |
- if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
|
|
| 163 |
- return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
|
| 164 |
- } |
|
| 165 |
- n.registerIptCleanFunc(func() error {
|
|
| 166 |
- return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) |
|
| 167 |
- }) |
|
| 168 |
- natChain, filterChain, _, _, err := n.getDriverChains(ipVersion) |
|
| 169 |
- if err != nil {
|
|
| 170 |
- return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
|
|
| 171 |
- } |
|
| 172 |
- |
|
| 173 |
- err = iptable.ProgramChain(natChain, config.BridgeName, hairpinMode, true) |
|
| 174 |
- if err != nil {
|
|
| 175 |
- return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
|
|
| 176 |
- } |
|
| 177 |
- |
|
| 178 |
- err = iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, true) |
|
| 179 |
- if err != nil {
|
|
| 180 |
- return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
|
|
| 181 |
- } |
|
| 182 |
- |
|
| 183 |
- n.registerIptCleanFunc(func() error {
|
|
| 184 |
- return iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, false) |
|
| 185 |
- }) |
|
| 186 |
- |
|
| 187 |
- if ipVersion == iptables.IPv4 {
|
|
| 188 |
- n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName()) |
|
| 189 |
- } else {
|
|
| 190 |
- n.portMapperV6.SetIptablesChain(natChain, n.getNetworkBridgeName()) |
|
| 191 |
- } |
|
| 192 |
- } |
|
| 193 |
- |
|
| 194 |
- d.Lock() |
|
| 195 |
- err = iptable.EnsureJumpRule("FORWARD", IsolationChain1)
|
|
| 196 |
- d.Unlock() |
|
| 197 |
- return err |
|
| 198 |
-} |
|
| 199 |
- |
|
| 200 |
-type iptRule struct {
|
|
| 201 |
- table iptables.Table |
|
| 202 |
- chain string |
|
| 203 |
- preArgs []string |
|
| 204 |
- args []string |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 |
-func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr *net.IPNet, icc, ipmasq, hairpin, enable bool) error {
|
|
| 208 |
- var ( |
|
| 209 |
- address = addr.String() |
|
| 210 |
- skipDNAT = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
|
|
| 211 |
- outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
|
|
| 212 |
- natArgs []string |
|
| 213 |
- hpNatArgs []string |
|
| 214 |
- ) |
|
| 215 |
- // if hostIP is set use this address as the src-ip during SNAT |
|
| 216 |
- if hostIP != nil {
|
|
| 217 |
- hostAddr := hostIP.String() |
|
| 218 |
- natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
|
|
| 219 |
- hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
|
|
| 220 |
- // Else use MASQUERADE which picks the src-ip based on NH from the route table |
|
| 221 |
- } else {
|
|
| 222 |
- natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}
|
|
| 223 |
- hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}
|
|
| 224 |
- } |
|
| 225 |
- |
|
| 226 |
- natRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: natArgs}
|
|
| 227 |
- hpNatRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: hpNatArgs}
|
|
| 228 |
- |
|
| 229 |
- ipVer := iptables.IPv4 |
|
| 230 |
- if addr.IP.To4() == nil {
|
|
| 231 |
- ipVer = iptables.IPv6 |
|
| 232 |
- } |
|
| 233 |
- |
|
| 234 |
- // Set NAT. |
|
| 235 |
- if ipmasq {
|
|
| 236 |
- if err := programChainRule(ipVer, natRule, "NAT", enable); err != nil {
|
|
| 237 |
- return err |
|
| 238 |
- } |
|
| 239 |
- } |
|
| 240 |
- |
|
| 241 |
- if ipmasq && !hairpin {
|
|
| 242 |
- if err := programChainRule(ipVer, skipDNAT, "SKIP DNAT", enable); err != nil {
|
|
| 243 |
- return err |
|
| 244 |
- } |
|
| 245 |
- } |
|
| 246 |
- |
|
| 247 |
- // In hairpin mode, masquerade traffic from localhost. If hairpin is disabled or if we're tearing down |
|
| 248 |
- // that bridge, make sure the iptables rule isn't lying around. |
|
| 249 |
- if err := programChainRule(ipVer, hpNatRule, "MASQ LOCAL HOST", enable && hairpin); err != nil {
|
|
| 250 |
- return err |
|
| 251 |
- } |
|
| 252 |
- |
|
| 253 |
- // Set Inter Container Communication. |
|
| 254 |
- if err := setIcc(ipVer, bridgeIface, icc, enable); err != nil {
|
|
| 255 |
- return err |
|
| 256 |
- } |
|
| 257 |
- |
|
| 258 |
- // Set Accept on all non-intercontainer outgoing packets. |
|
| 259 |
- return programChainRule(ipVer, outRule, "ACCEPT NON_ICC OUTGOING", enable) |
|
| 260 |
-} |
|
| 261 |
- |
|
| 262 |
-func programChainRule(version iptables.IPVersion, rule iptRule, ruleDescr string, insert bool) error {
|
|
| 263 |
- iptable := iptables.GetIptable(version) |
|
| 264 |
- |
|
| 265 |
- var ( |
|
| 266 |
- prefix []string |
|
| 267 |
- operation string |
|
| 268 |
- condition bool |
|
| 269 |
- doesExist = iptable.Exists(rule.table, rule.chain, rule.args...) |
|
| 270 |
- ) |
|
| 271 |
- |
|
| 272 |
- if insert {
|
|
| 273 |
- condition = !doesExist |
|
| 274 |
- prefix = []string{"-I", rule.chain}
|
|
| 275 |
- operation = "enable" |
|
| 276 |
- } else {
|
|
| 277 |
- condition = doesExist |
|
| 278 |
- prefix = []string{"-D", rule.chain}
|
|
| 279 |
- operation = "disable" |
|
| 280 |
- } |
|
| 281 |
- if rule.preArgs != nil {
|
|
| 282 |
- prefix = append(rule.preArgs, prefix...) |
|
| 283 |
- } |
|
| 284 |
- |
|
| 285 |
- if condition {
|
|
| 286 |
- if err := iptable.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
|
|
| 287 |
- return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
|
|
| 288 |
- } |
|
| 289 |
- } |
|
| 290 |
- |
|
| 291 |
- return nil |
|
| 292 |
-} |
|
| 293 |
- |
|
| 294 |
-func setIcc(version iptables.IPVersion, bridgeIface string, iccEnable, insert bool) error {
|
|
| 295 |
- iptable := iptables.GetIptable(version) |
|
| 296 |
- var ( |
|
| 297 |
- table = iptables.Filter |
|
| 298 |
- chain = "FORWARD" |
|
| 299 |
- args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
|
|
| 300 |
- acceptArgs = append(args, "ACCEPT") |
|
| 301 |
- dropArgs = append(args, "DROP") |
|
| 302 |
- ) |
|
| 303 |
- |
|
| 304 |
- if insert {
|
|
| 305 |
- if !iccEnable {
|
|
| 306 |
- iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
|
|
| 307 |
- |
|
| 308 |
- if !iptable.Exists(table, chain, dropArgs...) {
|
|
| 309 |
- if err := iptable.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
|
|
| 310 |
- return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
|
|
| 311 |
- } |
|
| 312 |
- } |
|
| 313 |
- } else {
|
|
| 314 |
- iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
|
|
| 315 |
- |
|
| 316 |
- if !iptable.Exists(table, chain, acceptArgs...) {
|
|
| 317 |
- if err := iptable.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
|
|
| 318 |
- return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
|
|
| 319 |
- } |
|
| 320 |
- } |
|
| 321 |
- } |
|
| 322 |
- } else {
|
|
| 323 |
- // Remove any ICC rule. |
|
| 324 |
- if !iccEnable {
|
|
| 325 |
- if iptable.Exists(table, chain, dropArgs...) {
|
|
| 326 |
- iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
|
|
| 327 |
- } |
|
| 328 |
- } else {
|
|
| 329 |
- if iptable.Exists(table, chain, acceptArgs...) {
|
|
| 330 |
- iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
|
|
| 331 |
- } |
|
| 332 |
- } |
|
| 333 |
- } |
|
| 334 |
- |
|
| 335 |
- return nil |
|
| 336 |
-} |
|
| 337 |
- |
|
| 338 |
-// Control Inter Network Communication. Install[Remove] only if it is [not] present. |
|
| 339 |
-func setINC(version iptables.IPVersion, iface string, enable bool) error {
|
|
| 340 |
- iptable := iptables.GetIptable(version) |
|
| 341 |
- var ( |
|
| 342 |
- action = iptables.Insert |
|
| 343 |
- actionMsg = "add" |
|
| 344 |
- chains = []string{IsolationChain1, IsolationChain2}
|
|
| 345 |
- rules = [][]string{
|
|
| 346 |
- {"-i", iface, "!", "-o", iface, "-j", IsolationChain2},
|
|
| 347 |
- {"-o", iface, "-j", "DROP"},
|
|
| 348 |
- } |
|
| 349 |
- ) |
|
| 350 |
- |
|
| 351 |
- if !enable {
|
|
| 352 |
- action = iptables.Delete |
|
| 353 |
- actionMsg = "remove" |
|
| 354 |
- } |
|
| 355 |
- |
|
| 356 |
- for i, chain := range chains {
|
|
| 357 |
- if err := iptable.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil {
|
|
| 358 |
- msg := fmt.Sprintf("unable to %s inter-network communication rule: %v", actionMsg, err)
|
|
| 359 |
- if enable {
|
|
| 360 |
- if i == 1 {
|
|
| 361 |
- // Rollback the rule installed on first chain |
|
| 362 |
- if err2 := iptable.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil {
|
|
| 363 |
- log.G(context.TODO()).Warnf("Failed to rollback iptables rule after failure (%v): %v", err, err2)
|
|
| 364 |
- } |
|
| 365 |
- } |
|
| 366 |
- return fmt.Errorf(msg) |
|
| 367 |
- } |
|
| 368 |
- log.G(context.TODO()).Warn(msg) |
|
| 369 |
- } |
|
| 370 |
- } |
|
| 371 |
- |
|
| 372 |
- return nil |
|
| 373 |
-} |
|
| 374 |
- |
|
| 375 |
-// Obsolete chain from previous docker versions |
|
| 376 |
-const oldIsolationChain = "DOCKER-ISOLATION" |
|
| 377 |
- |
|
| 378 |
-func removeIPChains(version iptables.IPVersion) {
|
|
| 379 |
- ipt := iptables.GetIptable(version) |
|
| 380 |
- |
|
| 381 |
- // Remove obsolete rules from default chains |
|
| 382 |
- ipt.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain})
|
|
| 383 |
- |
|
| 384 |
- // Remove chains |
|
| 385 |
- for _, chainInfo := range []iptables.ChainInfo{
|
|
| 386 |
- {Name: DockerChain, Table: iptables.Nat, IPVersion: version},
|
|
| 387 |
- {Name: DockerChain, Table: iptables.Filter, IPVersion: version},
|
|
| 388 |
- {Name: IsolationChain1, Table: iptables.Filter, IPVersion: version},
|
|
| 389 |
- {Name: IsolationChain2, Table: iptables.Filter, IPVersion: version},
|
|
| 390 |
- {Name: oldIsolationChain, Table: iptables.Filter, IPVersion: version},
|
|
| 391 |
- } {
|
|
| 392 |
- if err := chainInfo.Remove(); err != nil {
|
|
| 393 |
- log.G(context.TODO()).Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
|
|
| 394 |
- } |
|
| 395 |
- } |
|
| 396 |
-} |
|
| 397 |
- |
|
| 398 |
-func setupInternalNetworkRules(bridgeIface string, addr *net.IPNet, icc, insert bool) error {
|
|
| 399 |
- var version iptables.IPVersion |
|
| 400 |
- var inDropRule, outDropRule iptRule |
|
| 401 |
- |
|
| 402 |
- if addr.IP.To4() != nil {
|
|
| 403 |
- version = iptables.IPv4 |
|
| 404 |
- inDropRule = iptRule{
|
|
| 405 |
- table: iptables.Filter, |
|
| 406 |
- chain: IsolationChain1, |
|
| 407 |
- args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"},
|
|
| 408 |
- } |
|
| 409 |
- outDropRule = iptRule{
|
|
| 410 |
- table: iptables.Filter, |
|
| 411 |
- chain: IsolationChain1, |
|
| 412 |
- args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"},
|
|
| 413 |
- } |
|
| 414 |
- } else {
|
|
| 415 |
- version = iptables.IPv6 |
|
| 416 |
- inDropRule = iptRule{
|
|
| 417 |
- table: iptables.Filter, |
|
| 418 |
- chain: IsolationChain1, |
|
| 419 |
- args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"},
|
|
| 420 |
- } |
|
| 421 |
- outDropRule = iptRule{
|
|
| 422 |
- table: iptables.Filter, |
|
| 423 |
- chain: IsolationChain1, |
|
| 424 |
- args: []string{"!", "-i", bridgeIface, "-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"},
|
|
| 425 |
- } |
|
| 426 |
- } |
|
| 427 |
- |
|
| 428 |
- if err := programChainRule(version, inDropRule, "DROP INCOMING", insert); err != nil {
|
|
| 429 |
- return err |
|
| 430 |
- } |
|
| 431 |
- if err := programChainRule(version, outDropRule, "DROP OUTGOING", insert); err != nil {
|
|
| 432 |
- return err |
|
| 433 |
- } |
|
| 434 |
- |
|
| 435 |
- // Set Inter Container Communication. |
|
| 436 |
- return setIcc(version, bridgeIface, icc, insert) |
|
| 437 |
-} |
|
| 438 |
- |
|
| 439 |
-// clearConntrackEntries flushes conntrack entries matching endpoint IP address |
|
| 440 |
-// or matching one of the exposed UDP port. |
|
| 441 |
-// In the first case, this could happen if packets were received by the host |
|
| 442 |
-// between userland proxy startup and iptables setup. |
|
| 443 |
-// In the latter case, this could happen if packets were received whereas there |
|
| 444 |
-// were nowhere to route them, as netfilter creates entries in such case. |
|
| 445 |
-// This is required because iptables NAT rules are evaluated by netfilter only |
|
| 446 |
-// when creating a new conntrack entry. When Docker latter adds NAT rules, |
|
| 447 |
-// netfilter ignore them for any packet matching a pre-existing conntrack entry. |
|
| 448 |
-// As such, we need to flush all those conntrack entries to make sure NAT rules |
|
| 449 |
-// are correctly applied to all packets. |
|
| 450 |
-// See: #8795, #44688 & #44742. |
|
| 451 |
-func clearConntrackEntries(nlh *netlink.Handle, ep *bridgeEndpoint) {
|
|
| 452 |
- var ipv4List []net.IP |
|
| 453 |
- var ipv6List []net.IP |
|
| 454 |
- var udpPorts []uint16 |
|
| 455 |
- |
|
| 456 |
- if ep.addr != nil {
|
|
| 457 |
- ipv4List = append(ipv4List, ep.addr.IP) |
|
| 458 |
- } |
|
| 459 |
- if ep.addrv6 != nil {
|
|
| 460 |
- ipv6List = append(ipv6List, ep.addrv6.IP) |
|
| 461 |
- } |
|
| 462 |
- for _, pb := range ep.portMapping {
|
|
| 463 |
- if pb.Proto == types.UDP {
|
|
| 464 |
- udpPorts = append(udpPorts, pb.HostPort) |
|
| 465 |
- } |
|
| 466 |
- } |
|
| 467 |
- |
|
| 468 |
- iptables.DeleteConntrackEntries(nlh, ipv4List, ipv6List) |
|
| 469 |
- iptables.DeleteConntrackEntriesByPort(nlh, types.UDP, udpPorts) |
|
| 470 |
-} |
| 471 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,468 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "net" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/containerd/containerd/log" |
|
| 9 |
+ "github.com/docker/docker/libnetwork/iptables" |
|
| 10 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 11 |
+ "github.com/vishvananda/netlink" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// DockerChain: DOCKER iptable chain name |
|
| 15 |
+const ( |
|
| 16 |
+ DockerChain = "DOCKER" |
|
| 17 |
+ |
|
| 18 |
+ // Isolation between bridge networks is achieved in two stages by means |
|
| 19 |
+ // of the following two chains in the filter table. The first chain matches |
|
| 20 |
+ // on the source interface being a bridge network's bridge and the |
|
| 21 |
+ // destination being a different interface. A positive match leads to the |
|
| 22 |
+ // second isolation chain. No match returns to the parent chain. The second |
|
| 23 |
+ // isolation chain matches on destination interface being a bridge network's |
|
| 24 |
+ // bridge. A positive match identifies a packet originated from one bridge |
|
| 25 |
+ // network's bridge destined to another bridge network's bridge and will |
|
| 26 |
+ // result in the packet being dropped. No match returns to the parent chain. |
|
| 27 |
+ |
|
| 28 |
+ IsolationChain1 = "DOCKER-ISOLATION-STAGE-1" |
|
| 29 |
+ IsolationChain2 = "DOCKER-ISOLATION-STAGE-2" |
|
| 30 |
+) |
|
| 31 |
+ |
|
| 32 |
+func setupIPChains(config configuration, version iptables.IPVersion) (*iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, *iptables.ChainInfo, error) {
|
|
| 33 |
+ // Sanity check. |
|
| 34 |
+ if !config.EnableIPTables {
|
|
| 35 |
+ return nil, nil, nil, nil, errors.New("cannot create new chains, EnableIPTable is disabled")
|
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ hairpinMode := !config.EnableUserlandProxy |
|
| 39 |
+ |
|
| 40 |
+ iptable := iptables.GetIptable(version) |
|
| 41 |
+ |
|
| 42 |
+ natChain, err := iptable.NewChain(DockerChain, iptables.Nat, hairpinMode) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return nil, nil, nil, nil, fmt.Errorf("failed to create NAT chain %s: %v", DockerChain, err)
|
|
| 45 |
+ } |
|
| 46 |
+ defer func() {
|
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ if err := iptable.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
|
|
| 49 |
+ log.G(context.TODO()).Warnf("failed on removing iptables NAT chain %s on cleanup: %v", DockerChain, err)
|
|
| 50 |
+ } |
|
| 51 |
+ } |
|
| 52 |
+ }() |
|
| 53 |
+ |
|
| 54 |
+ filterChain, err := iptable.NewChain(DockerChain, iptables.Filter, false) |
|
| 55 |
+ if err != nil {
|
|
| 56 |
+ return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER chain %s: %v", DockerChain, err)
|
|
| 57 |
+ } |
|
| 58 |
+ defer func() {
|
|
| 59 |
+ if err != nil {
|
|
| 60 |
+ if err := iptable.RemoveExistingChain(DockerChain, iptables.Filter); err != nil {
|
|
| 61 |
+ log.G(context.TODO()).Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", DockerChain, err)
|
|
| 62 |
+ } |
|
| 63 |
+ } |
|
| 64 |
+ }() |
|
| 65 |
+ |
|
| 66 |
+ isolationChain1, err := iptable.NewChain(IsolationChain1, iptables.Filter, false) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
|
|
| 69 |
+ } |
|
| 70 |
+ defer func() {
|
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ if err := iptable.RemoveExistingChain(IsolationChain1, iptables.Filter); err != nil {
|
|
| 73 |
+ log.G(context.TODO()).Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain1, err)
|
|
| 74 |
+ } |
|
| 75 |
+ } |
|
| 76 |
+ }() |
|
| 77 |
+ |
|
| 78 |
+ isolationChain2, err := iptable.NewChain(IsolationChain2, iptables.Filter, false) |
|
| 79 |
+ if err != nil {
|
|
| 80 |
+ return nil, nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
|
|
| 81 |
+ } |
|
| 82 |
+ defer func() {
|
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ if err := iptable.RemoveExistingChain(IsolationChain2, iptables.Filter); err != nil {
|
|
| 85 |
+ log.G(context.TODO()).Warnf("failed on removing iptables FILTER chain %s on cleanup: %v", IsolationChain2, err)
|
|
| 86 |
+ } |
|
| 87 |
+ } |
|
| 88 |
+ }() |
|
| 89 |
+ |
|
| 90 |
+ if err := iptable.AddReturnRule(IsolationChain1); err != nil {
|
|
| 91 |
+ return nil, nil, nil, nil, err |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ if err := iptable.AddReturnRule(IsolationChain2); err != nil {
|
|
| 95 |
+ return nil, nil, nil, nil, err |
|
| 96 |
+ } |
|
| 97 |
+ |
|
| 98 |
+ return natChain, filterChain, isolationChain1, isolationChain2, nil |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+func (n *bridgeNetwork) setupIP4Tables(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 102 |
+ d := n.driver |
|
| 103 |
+ d.Lock() |
|
| 104 |
+ driverConfig := d.config |
|
| 105 |
+ d.Unlock() |
|
| 106 |
+ |
|
| 107 |
+ // Sanity check. |
|
| 108 |
+ if !driverConfig.EnableIPTables {
|
|
| 109 |
+ return errors.New("Cannot program chains, EnableIPTable is disabled")
|
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ maskedAddrv4 := &net.IPNet{
|
|
| 113 |
+ IP: i.bridgeIPv4.IP.Mask(i.bridgeIPv4.Mask), |
|
| 114 |
+ Mask: i.bridgeIPv4.Mask, |
|
| 115 |
+ } |
|
| 116 |
+ return n.setupIPTables(iptables.IPv4, maskedAddrv4, config, i) |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+func (n *bridgeNetwork) setupIP6Tables(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 120 |
+ d := n.driver |
|
| 121 |
+ d.Lock() |
|
| 122 |
+ driverConfig := d.config |
|
| 123 |
+ d.Unlock() |
|
| 124 |
+ |
|
| 125 |
+ // Sanity check. |
|
| 126 |
+ if !driverConfig.EnableIP6Tables {
|
|
| 127 |
+ return errors.New("Cannot program chains, EnableIP6Tables is disabled")
|
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ maskedAddrv6 := &net.IPNet{
|
|
| 131 |
+ IP: i.bridgeIPv6.IP.Mask(i.bridgeIPv6.Mask), |
|
| 132 |
+ Mask: i.bridgeIPv6.Mask, |
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ return n.setupIPTables(iptables.IPv6, maskedAddrv6, config, i) |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+func (n *bridgeNetwork) setupIPTables(ipVersion iptables.IPVersion, maskedAddr *net.IPNet, config *networkConfiguration, i *bridgeInterface) error {
|
|
| 139 |
+ var err error |
|
| 140 |
+ |
|
| 141 |
+ d := n.driver |
|
| 142 |
+ d.Lock() |
|
| 143 |
+ driverConfig := d.config |
|
| 144 |
+ d.Unlock() |
|
| 145 |
+ |
|
| 146 |
+ // Pickup this configuration option from driver |
|
| 147 |
+ hairpinMode := !driverConfig.EnableUserlandProxy |
|
| 148 |
+ |
|
| 149 |
+ iptable := iptables.GetIptable(ipVersion) |
|
| 150 |
+ |
|
| 151 |
+ if config.Internal {
|
|
| 152 |
+ if err = setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, true); err != nil {
|
|
| 153 |
+ return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
|
| 154 |
+ } |
|
| 155 |
+ n.registerIptCleanFunc(func() error {
|
|
| 156 |
+ return setupInternalNetworkRules(config.BridgeName, maskedAddr, config.EnableICC, false) |
|
| 157 |
+ }) |
|
| 158 |
+ } else {
|
|
| 159 |
+ if err = setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, true); err != nil {
|
|
| 160 |
+ return fmt.Errorf("Failed to Setup IP tables: %s", err.Error())
|
|
| 161 |
+ } |
|
| 162 |
+ n.registerIptCleanFunc(func() error {
|
|
| 163 |
+ return setupIPTablesInternal(config.HostIP, config.BridgeName, maskedAddr, config.EnableICC, config.EnableIPMasquerade, hairpinMode, false) |
|
| 164 |
+ }) |
|
| 165 |
+ natChain, filterChain, _, _, err := n.getDriverChains(ipVersion) |
|
| 166 |
+ if err != nil {
|
|
| 167 |
+ return fmt.Errorf("Failed to setup IP tables, cannot acquire chain info %s", err.Error())
|
|
| 168 |
+ } |
|
| 169 |
+ |
|
| 170 |
+ err = iptable.ProgramChain(natChain, config.BridgeName, hairpinMode, true) |
|
| 171 |
+ if err != nil {
|
|
| 172 |
+ return fmt.Errorf("Failed to program NAT chain: %s", err.Error())
|
|
| 173 |
+ } |
|
| 174 |
+ |
|
| 175 |
+ err = iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, true) |
|
| 176 |
+ if err != nil {
|
|
| 177 |
+ return fmt.Errorf("Failed to program FILTER chain: %s", err.Error())
|
|
| 178 |
+ } |
|
| 179 |
+ |
|
| 180 |
+ n.registerIptCleanFunc(func() error {
|
|
| 181 |
+ return iptable.ProgramChain(filterChain, config.BridgeName, hairpinMode, false) |
|
| 182 |
+ }) |
|
| 183 |
+ |
|
| 184 |
+ if ipVersion == iptables.IPv4 {
|
|
| 185 |
+ n.portMapper.SetIptablesChain(natChain, n.getNetworkBridgeName()) |
|
| 186 |
+ } else {
|
|
| 187 |
+ n.portMapperV6.SetIptablesChain(natChain, n.getNetworkBridgeName()) |
|
| 188 |
+ } |
|
| 189 |
+ } |
|
| 190 |
+ |
|
| 191 |
+ d.Lock() |
|
| 192 |
+ err = iptable.EnsureJumpRule("FORWARD", IsolationChain1)
|
|
| 193 |
+ d.Unlock() |
|
| 194 |
+ return err |
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+type iptRule struct {
|
|
| 198 |
+ table iptables.Table |
|
| 199 |
+ chain string |
|
| 200 |
+ preArgs []string |
|
| 201 |
+ args []string |
|
| 202 |
+} |
|
| 203 |
+ |
|
| 204 |
+func setupIPTablesInternal(hostIP net.IP, bridgeIface string, addr *net.IPNet, icc, ipmasq, hairpin, enable bool) error {
|
|
| 205 |
+ var ( |
|
| 206 |
+ address = addr.String() |
|
| 207 |
+ skipDNAT = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
|
|
| 208 |
+ outRule = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
|
|
| 209 |
+ natArgs []string |
|
| 210 |
+ hpNatArgs []string |
|
| 211 |
+ ) |
|
| 212 |
+ // if hostIP is set use this address as the src-ip during SNAT |
|
| 213 |
+ if hostIP != nil {
|
|
| 214 |
+ hostAddr := hostIP.String() |
|
| 215 |
+ natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
|
|
| 216 |
+ hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "SNAT", "--to-source", hostAddr}
|
|
| 217 |
+ // Else use MASQUERADE which picks the src-ip based on NH from the route table |
|
| 218 |
+ } else {
|
|
| 219 |
+ natArgs = []string{"-s", address, "!", "-o", bridgeIface, "-j", "MASQUERADE"}
|
|
| 220 |
+ hpNatArgs = []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}
|
|
| 221 |
+ } |
|
| 222 |
+ |
|
| 223 |
+ natRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: natArgs}
|
|
| 224 |
+ hpNatRule := iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: hpNatArgs}
|
|
| 225 |
+ |
|
| 226 |
+ ipVer := iptables.IPv4 |
|
| 227 |
+ if addr.IP.To4() == nil {
|
|
| 228 |
+ ipVer = iptables.IPv6 |
|
| 229 |
+ } |
|
| 230 |
+ |
|
| 231 |
+ // Set NAT. |
|
| 232 |
+ if ipmasq {
|
|
| 233 |
+ if err := programChainRule(ipVer, natRule, "NAT", enable); err != nil {
|
|
| 234 |
+ return err |
|
| 235 |
+ } |
|
| 236 |
+ } |
|
| 237 |
+ |
|
| 238 |
+ if ipmasq && !hairpin {
|
|
| 239 |
+ if err := programChainRule(ipVer, skipDNAT, "SKIP DNAT", enable); err != nil {
|
|
| 240 |
+ return err |
|
| 241 |
+ } |
|
| 242 |
+ } |
|
| 243 |
+ |
|
| 244 |
+ // In hairpin mode, masquerade traffic from localhost. If hairpin is disabled or if we're tearing down |
|
| 245 |
+ // that bridge, make sure the iptables rule isn't lying around. |
|
| 246 |
+ if err := programChainRule(ipVer, hpNatRule, "MASQ LOCAL HOST", enable && hairpin); err != nil {
|
|
| 247 |
+ return err |
|
| 248 |
+ } |
|
| 249 |
+ |
|
| 250 |
+ // Set Inter Container Communication. |
|
| 251 |
+ if err := setIcc(ipVer, bridgeIface, icc, enable); err != nil {
|
|
| 252 |
+ return err |
|
| 253 |
+ } |
|
| 254 |
+ |
|
| 255 |
+ // Set Accept on all non-intercontainer outgoing packets. |
|
| 256 |
+ return programChainRule(ipVer, outRule, "ACCEPT NON_ICC OUTGOING", enable) |
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+func programChainRule(version iptables.IPVersion, rule iptRule, ruleDescr string, insert bool) error {
|
|
| 260 |
+ iptable := iptables.GetIptable(version) |
|
| 261 |
+ |
|
| 262 |
+ var ( |
|
| 263 |
+ prefix []string |
|
| 264 |
+ operation string |
|
| 265 |
+ condition bool |
|
| 266 |
+ doesExist = iptable.Exists(rule.table, rule.chain, rule.args...) |
|
| 267 |
+ ) |
|
| 268 |
+ |
|
| 269 |
+ if insert {
|
|
| 270 |
+ condition = !doesExist |
|
| 271 |
+ prefix = []string{"-I", rule.chain}
|
|
| 272 |
+ operation = "enable" |
|
| 273 |
+ } else {
|
|
| 274 |
+ condition = doesExist |
|
| 275 |
+ prefix = []string{"-D", rule.chain}
|
|
| 276 |
+ operation = "disable" |
|
| 277 |
+ } |
|
| 278 |
+ if rule.preArgs != nil {
|
|
| 279 |
+ prefix = append(rule.preArgs, prefix...) |
|
| 280 |
+ } |
|
| 281 |
+ |
|
| 282 |
+ if condition {
|
|
| 283 |
+ if err := iptable.RawCombinedOutput(append(prefix, rule.args...)...); err != nil {
|
|
| 284 |
+ return fmt.Errorf("Unable to %s %s rule: %s", operation, ruleDescr, err.Error())
|
|
| 285 |
+ } |
|
| 286 |
+ } |
|
| 287 |
+ |
|
| 288 |
+ return nil |
|
| 289 |
+} |
|
| 290 |
+ |
|
| 291 |
+func setIcc(version iptables.IPVersion, bridgeIface string, iccEnable, insert bool) error {
|
|
| 292 |
+ iptable := iptables.GetIptable(version) |
|
| 293 |
+ var ( |
|
| 294 |
+ table = iptables.Filter |
|
| 295 |
+ chain = "FORWARD" |
|
| 296 |
+ args = []string{"-i", bridgeIface, "-o", bridgeIface, "-j"}
|
|
| 297 |
+ acceptArgs = append(args, "ACCEPT") |
|
| 298 |
+ dropArgs = append(args, "DROP") |
|
| 299 |
+ ) |
|
| 300 |
+ |
|
| 301 |
+ if insert {
|
|
| 302 |
+ if !iccEnable {
|
|
| 303 |
+ iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
|
|
| 304 |
+ |
|
| 305 |
+ if !iptable.Exists(table, chain, dropArgs...) {
|
|
| 306 |
+ if err := iptable.RawCombinedOutput(append([]string{"-A", chain}, dropArgs...)...); err != nil {
|
|
| 307 |
+ return fmt.Errorf("Unable to prevent intercontainer communication: %s", err.Error())
|
|
| 308 |
+ } |
|
| 309 |
+ } |
|
| 310 |
+ } else {
|
|
| 311 |
+ iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
|
|
| 312 |
+ |
|
| 313 |
+ if !iptable.Exists(table, chain, acceptArgs...) {
|
|
| 314 |
+ if err := iptable.RawCombinedOutput(append([]string{"-I", chain}, acceptArgs...)...); err != nil {
|
|
| 315 |
+ return fmt.Errorf("Unable to allow intercontainer communication: %s", err.Error())
|
|
| 316 |
+ } |
|
| 317 |
+ } |
|
| 318 |
+ } |
|
| 319 |
+ } else {
|
|
| 320 |
+ // Remove any ICC rule. |
|
| 321 |
+ if !iccEnable {
|
|
| 322 |
+ if iptable.Exists(table, chain, dropArgs...) {
|
|
| 323 |
+ iptable.Raw(append([]string{"-D", chain}, dropArgs...)...)
|
|
| 324 |
+ } |
|
| 325 |
+ } else {
|
|
| 326 |
+ if iptable.Exists(table, chain, acceptArgs...) {
|
|
| 327 |
+ iptable.Raw(append([]string{"-D", chain}, acceptArgs...)...)
|
|
| 328 |
+ } |
|
| 329 |
+ } |
|
| 330 |
+ } |
|
| 331 |
+ |
|
| 332 |
+ return nil |
|
| 333 |
+} |
|
| 334 |
+ |
|
| 335 |
+// Control Inter Network Communication. Install[Remove] only if it is [not] present. |
|
| 336 |
+func setINC(version iptables.IPVersion, iface string, enable bool) error {
|
|
| 337 |
+ iptable := iptables.GetIptable(version) |
|
| 338 |
+ var ( |
|
| 339 |
+ action = iptables.Insert |
|
| 340 |
+ actionMsg = "add" |
|
| 341 |
+ chains = []string{IsolationChain1, IsolationChain2}
|
|
| 342 |
+ rules = [][]string{
|
|
| 343 |
+ {"-i", iface, "!", "-o", iface, "-j", IsolationChain2},
|
|
| 344 |
+ {"-o", iface, "-j", "DROP"},
|
|
| 345 |
+ } |
|
| 346 |
+ ) |
|
| 347 |
+ |
|
| 348 |
+ if !enable {
|
|
| 349 |
+ action = iptables.Delete |
|
| 350 |
+ actionMsg = "remove" |
|
| 351 |
+ } |
|
| 352 |
+ |
|
| 353 |
+ for i, chain := range chains {
|
|
| 354 |
+ if err := iptable.ProgramRule(iptables.Filter, chain, action, rules[i]); err != nil {
|
|
| 355 |
+ msg := fmt.Sprintf("unable to %s inter-network communication rule: %v", actionMsg, err)
|
|
| 356 |
+ if enable {
|
|
| 357 |
+ if i == 1 {
|
|
| 358 |
+ // Rollback the rule installed on first chain |
|
| 359 |
+ if err2 := iptable.ProgramRule(iptables.Filter, chains[0], iptables.Delete, rules[0]); err2 != nil {
|
|
| 360 |
+ log.G(context.TODO()).Warnf("Failed to rollback iptables rule after failure (%v): %v", err, err2)
|
|
| 361 |
+ } |
|
| 362 |
+ } |
|
| 363 |
+ return fmt.Errorf(msg) |
|
| 364 |
+ } |
|
| 365 |
+ log.G(context.TODO()).Warn(msg) |
|
| 366 |
+ } |
|
| 367 |
+ } |
|
| 368 |
+ |
|
| 369 |
+ return nil |
|
| 370 |
+} |
|
| 371 |
+ |
|
| 372 |
+// Obsolete chain from previous docker versions |
|
| 373 |
+const oldIsolationChain = "DOCKER-ISOLATION" |
|
| 374 |
+ |
|
| 375 |
+func removeIPChains(version iptables.IPVersion) {
|
|
| 376 |
+ ipt := iptables.GetIptable(version) |
|
| 377 |
+ |
|
| 378 |
+ // Remove obsolete rules from default chains |
|
| 379 |
+ ipt.ProgramRule(iptables.Filter, "FORWARD", iptables.Delete, []string{"-j", oldIsolationChain})
|
|
| 380 |
+ |
|
| 381 |
+ // Remove chains |
|
| 382 |
+ for _, chainInfo := range []iptables.ChainInfo{
|
|
| 383 |
+ {Name: DockerChain, Table: iptables.Nat, IPVersion: version},
|
|
| 384 |
+ {Name: DockerChain, Table: iptables.Filter, IPVersion: version},
|
|
| 385 |
+ {Name: IsolationChain1, Table: iptables.Filter, IPVersion: version},
|
|
| 386 |
+ {Name: IsolationChain2, Table: iptables.Filter, IPVersion: version},
|
|
| 387 |
+ {Name: oldIsolationChain, Table: iptables.Filter, IPVersion: version},
|
|
| 388 |
+ } {
|
|
| 389 |
+ if err := chainInfo.Remove(); err != nil {
|
|
| 390 |
+ log.G(context.TODO()).Warnf("Failed to remove existing iptables entries in table %s chain %s : %v", chainInfo.Table, chainInfo.Name, err)
|
|
| 391 |
+ } |
|
| 392 |
+ } |
|
| 393 |
+} |
|
| 394 |
+ |
|
| 395 |
+func setupInternalNetworkRules(bridgeIface string, addr *net.IPNet, icc, insert bool) error {
|
|
| 396 |
+ var version iptables.IPVersion |
|
| 397 |
+ var inDropRule, outDropRule iptRule |
|
| 398 |
+ |
|
| 399 |
+ if addr.IP.To4() != nil {
|
|
| 400 |
+ version = iptables.IPv4 |
|
| 401 |
+ inDropRule = iptRule{
|
|
| 402 |
+ table: iptables.Filter, |
|
| 403 |
+ chain: IsolationChain1, |
|
| 404 |
+ args: []string{"-i", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"},
|
|
| 405 |
+ } |
|
| 406 |
+ outDropRule = iptRule{
|
|
| 407 |
+ table: iptables.Filter, |
|
| 408 |
+ chain: IsolationChain1, |
|
| 409 |
+ args: []string{"-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"},
|
|
| 410 |
+ } |
|
| 411 |
+ } else {
|
|
| 412 |
+ version = iptables.IPv6 |
|
| 413 |
+ inDropRule = iptRule{
|
|
| 414 |
+ table: iptables.Filter, |
|
| 415 |
+ chain: IsolationChain1, |
|
| 416 |
+ args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "!", "-d", addr.String(), "-j", "DROP"},
|
|
| 417 |
+ } |
|
| 418 |
+ outDropRule = iptRule{
|
|
| 419 |
+ table: iptables.Filter, |
|
| 420 |
+ chain: IsolationChain1, |
|
| 421 |
+ args: []string{"!", "-i", bridgeIface, "-o", bridgeIface, "!", "-s", addr.String(), "-j", "DROP"},
|
|
| 422 |
+ } |
|
| 423 |
+ } |
|
| 424 |
+ |
|
| 425 |
+ if err := programChainRule(version, inDropRule, "DROP INCOMING", insert); err != nil {
|
|
| 426 |
+ return err |
|
| 427 |
+ } |
|
| 428 |
+ if err := programChainRule(version, outDropRule, "DROP OUTGOING", insert); err != nil {
|
|
| 429 |
+ return err |
|
| 430 |
+ } |
|
| 431 |
+ |
|
| 432 |
+ // Set Inter Container Communication. |
|
| 433 |
+ return setIcc(version, bridgeIface, icc, insert) |
|
| 434 |
+} |
|
| 435 |
+ |
|
| 436 |
+// clearConntrackEntries flushes conntrack entries matching endpoint IP address |
|
| 437 |
+// or matching one of the exposed UDP port. |
|
| 438 |
+// In the first case, this could happen if packets were received by the host |
|
| 439 |
+// between userland proxy startup and iptables setup. |
|
| 440 |
+// In the latter case, this could happen if packets were received whereas there |
|
| 441 |
+// were nowhere to route them, as netfilter creates entries in such case. |
|
| 442 |
+// This is required because iptables NAT rules are evaluated by netfilter only |
|
| 443 |
+// when creating a new conntrack entry. When Docker latter adds NAT rules, |
|
| 444 |
+// netfilter ignore them for any packet matching a pre-existing conntrack entry. |
|
| 445 |
+// As such, we need to flush all those conntrack entries to make sure NAT rules |
|
| 446 |
+// are correctly applied to all packets. |
|
| 447 |
+// See: #8795, #44688 & #44742. |
|
| 448 |
+func clearConntrackEntries(nlh *netlink.Handle, ep *bridgeEndpoint) {
|
|
| 449 |
+ var ipv4List []net.IP |
|
| 450 |
+ var ipv6List []net.IP |
|
| 451 |
+ var udpPorts []uint16 |
|
| 452 |
+ |
|
| 453 |
+ if ep.addr != nil {
|
|
| 454 |
+ ipv4List = append(ipv4List, ep.addr.IP) |
|
| 455 |
+ } |
|
| 456 |
+ if ep.addrv6 != nil {
|
|
| 457 |
+ ipv6List = append(ipv6List, ep.addrv6.IP) |
|
| 458 |
+ } |
|
| 459 |
+ for _, pb := range ep.portMapping {
|
|
| 460 |
+ if pb.Proto == types.UDP {
|
|
| 461 |
+ udpPorts = append(udpPorts, pb.HostPort) |
|
| 462 |
+ } |
|
| 463 |
+ } |
|
| 464 |
+ |
|
| 465 |
+ iptables.DeleteConntrackEntries(nlh, ipv4List, ipv6List) |
|
| 466 |
+ iptables.DeleteConntrackEntriesByPort(nlh, types.UDP, udpPorts) |
|
| 467 |
+} |
| 0 | 468 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,141 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "testing" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 7 |
+ "github.com/docker/docker/libnetwork/iptables" |
|
| 8 |
+ "github.com/docker/docker/libnetwork/portmapper" |
|
| 9 |
+ "github.com/vishvananda/netlink" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+const ( |
|
| 13 |
+ iptablesTestBridgeIP = "192.168.42.1" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+func TestProgramIPTable(t *testing.T) {
|
|
| 17 |
+ // Create a test bridge with a basic bridge configuration (name + IPv4). |
|
| 18 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 19 |
+ |
|
| 20 |
+ nh, err := netlink.NewHandle() |
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ t.Fatal(err) |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ createTestBridge(getBasicTestConfig(), &bridgeInterface{nlh: nh}, t)
|
|
| 26 |
+ |
|
| 27 |
+ // Store various iptables chain rules we care for. |
|
| 28 |
+ rules := []struct {
|
|
| 29 |
+ rule iptRule |
|
| 30 |
+ descr string |
|
| 31 |
+ }{
|
|
| 32 |
+ {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"},
|
|
| 33 |
+ {iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"},
|
|
| 34 |
+ {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"},
|
|
| 35 |
+ {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"},
|
|
| 36 |
+ {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"},
|
|
| 37 |
+ {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"},
|
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ // Assert the chain rules' insertion and removal. |
|
| 41 |
+ for _, c := range rules {
|
|
| 42 |
+ assertIPTableChainProgramming(c.rule, c.descr, t) |
|
| 43 |
+ } |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func TestSetupIPChains(t *testing.T) {
|
|
| 47 |
+ // Create a test bridge with a basic bridge configuration (name + IPv4). |
|
| 48 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 49 |
+ |
|
| 50 |
+ nh, err := netlink.NewHandle() |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ t.Fatal(err) |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ driverconfig := configuration{
|
|
| 56 |
+ EnableIPTables: true, |
|
| 57 |
+ } |
|
| 58 |
+ d := &driver{
|
|
| 59 |
+ config: driverconfig, |
|
| 60 |
+ } |
|
| 61 |
+ assertChainConfig(d, t) |
|
| 62 |
+ |
|
| 63 |
+ config := getBasicTestConfig() |
|
| 64 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 65 |
+ createTestBridge(config, br, t) |
|
| 66 |
+ |
|
| 67 |
+ assertBridgeConfig(config, br, d, t) |
|
| 68 |
+ |
|
| 69 |
+ config.EnableIPMasquerade = true |
|
| 70 |
+ assertBridgeConfig(config, br, d, t) |
|
| 71 |
+ |
|
| 72 |
+ config.EnableICC = true |
|
| 73 |
+ assertBridgeConfig(config, br, d, t) |
|
| 74 |
+ |
|
| 75 |
+ config.EnableIPMasquerade = false |
|
| 76 |
+ assertBridgeConfig(config, br, d, t) |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func getBasicTestConfig() *networkConfiguration {
|
|
| 80 |
+ config := &networkConfiguration{
|
|
| 81 |
+ BridgeName: DefaultBridgeName, |
|
| 82 |
+ AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
|
|
| 83 |
+ } |
|
| 84 |
+ return config |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *testing.T) {
|
|
| 88 |
+ if err := setupDevice(config, br); err != nil {
|
|
| 89 |
+ t.Fatalf("Failed to create the testing Bridge: %s", err.Error())
|
|
| 90 |
+ } |
|
| 91 |
+ if err := setupBridgeIPv4(config, br); err != nil {
|
|
| 92 |
+ t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
|
|
| 93 |
+ } |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+// Assert base function which pushes iptables chain rules on insertion and removal. |
|
| 97 |
+func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) {
|
|
| 98 |
+ // Add |
|
| 99 |
+ if err := programChainRule(iptables.IPv4, rule, descr, true); err != nil {
|
|
| 100 |
+ t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error())
|
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+ iptable := iptables.GetIptable(iptables.IPv4) |
|
| 104 |
+ if iptable.Exists(rule.table, rule.chain, rule.args...) == false {
|
|
| 105 |
+ t.Fatalf("Failed to effectively program iptable rule: %s", descr)
|
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ // Remove |
|
| 109 |
+ if err := programChainRule(iptables.IPv4, rule, descr, false); err != nil {
|
|
| 110 |
+ t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error())
|
|
| 111 |
+ } |
|
| 112 |
+ if iptable.Exists(rule.table, rule.chain, rule.args...) == true {
|
|
| 113 |
+ t.Fatalf("Failed to effectively remove iptable rule: %s", descr)
|
|
| 114 |
+ } |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// Assert function which create chains. |
|
| 118 |
+func assertChainConfig(d *driver, t *testing.T) {
|
|
| 119 |
+ var err error |
|
| 120 |
+ |
|
| 121 |
+ d.natChain, d.filterChain, d.isolationChain1, d.isolationChain2, err = setupIPChains(d.config, iptables.IPv4) |
|
| 122 |
+ if err != nil {
|
|
| 123 |
+ t.Fatal(err) |
|
| 124 |
+ } |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+// Assert function which pushes chains based on bridge config parameters. |
|
| 128 |
+func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) {
|
|
| 129 |
+ nw := bridgeNetwork{
|
|
| 130 |
+ portMapper: portmapper.New(""),
|
|
| 131 |
+ config: config, |
|
| 132 |
+ } |
|
| 133 |
+ nw.driver = d |
|
| 134 |
+ |
|
| 135 |
+ // Attempt programming of ip tables. |
|
| 136 |
+ err := nw.setupIP4Tables(config, br) |
|
| 137 |
+ if err != nil {
|
|
| 138 |
+ t.Fatalf("%v", err)
|
|
| 139 |
+ } |
|
| 140 |
+} |
| 0 | 141 |
deleted file mode 100644 |
| ... | ... |
@@ -1,143 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "net" |
|
| 7 |
- "testing" |
|
| 8 |
- |
|
| 9 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 10 |
- "github.com/docker/docker/libnetwork/iptables" |
|
| 11 |
- "github.com/docker/docker/libnetwork/portmapper" |
|
| 12 |
- "github.com/vishvananda/netlink" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-const ( |
|
| 16 |
- iptablesTestBridgeIP = "192.168.42.1" |
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-func TestProgramIPTable(t *testing.T) {
|
|
| 20 |
- // Create a test bridge with a basic bridge configuration (name + IPv4). |
|
| 21 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 22 |
- |
|
| 23 |
- nh, err := netlink.NewHandle() |
|
| 24 |
- if err != nil {
|
|
| 25 |
- t.Fatal(err) |
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- createTestBridge(getBasicTestConfig(), &bridgeInterface{nlh: nh}, t)
|
|
| 29 |
- |
|
| 30 |
- // Store various iptables chain rules we care for. |
|
| 31 |
- rules := []struct {
|
|
| 32 |
- rule iptRule |
|
| 33 |
- descr string |
|
| 34 |
- }{
|
|
| 35 |
- {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"},
|
|
| 36 |
- {iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"},
|
|
| 37 |
- {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"},
|
|
| 38 |
- {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"},
|
|
| 39 |
- {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"},
|
|
| 40 |
- {iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"},
|
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- // Assert the chain rules' insertion and removal. |
|
| 44 |
- for _, c := range rules {
|
|
| 45 |
- assertIPTableChainProgramming(c.rule, c.descr, t) |
|
| 46 |
- } |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-func TestSetupIPChains(t *testing.T) {
|
|
| 50 |
- // Create a test bridge with a basic bridge configuration (name + IPv4). |
|
| 51 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 52 |
- |
|
| 53 |
- nh, err := netlink.NewHandle() |
|
| 54 |
- if err != nil {
|
|
| 55 |
- t.Fatal(err) |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- driverconfig := configuration{
|
|
| 59 |
- EnableIPTables: true, |
|
| 60 |
- } |
|
| 61 |
- d := &driver{
|
|
| 62 |
- config: driverconfig, |
|
| 63 |
- } |
|
| 64 |
- assertChainConfig(d, t) |
|
| 65 |
- |
|
| 66 |
- config := getBasicTestConfig() |
|
| 67 |
- br := &bridgeInterface{nlh: nh}
|
|
| 68 |
- createTestBridge(config, br, t) |
|
| 69 |
- |
|
| 70 |
- assertBridgeConfig(config, br, d, t) |
|
| 71 |
- |
|
| 72 |
- config.EnableIPMasquerade = true |
|
| 73 |
- assertBridgeConfig(config, br, d, t) |
|
| 74 |
- |
|
| 75 |
- config.EnableICC = true |
|
| 76 |
- assertBridgeConfig(config, br, d, t) |
|
| 77 |
- |
|
| 78 |
- config.EnableIPMasquerade = false |
|
| 79 |
- assertBridgeConfig(config, br, d, t) |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func getBasicTestConfig() *networkConfiguration {
|
|
| 83 |
- config := &networkConfiguration{
|
|
| 84 |
- BridgeName: DefaultBridgeName, |
|
| 85 |
- AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
|
|
| 86 |
- } |
|
| 87 |
- return config |
|
| 88 |
-} |
|
| 89 |
- |
|
| 90 |
-func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *testing.T) {
|
|
| 91 |
- if err := setupDevice(config, br); err != nil {
|
|
| 92 |
- t.Fatalf("Failed to create the testing Bridge: %s", err.Error())
|
|
| 93 |
- } |
|
| 94 |
- if err := setupBridgeIPv4(config, br); err != nil {
|
|
| 95 |
- t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
|
|
| 96 |
- } |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-// Assert base function which pushes iptables chain rules on insertion and removal. |
|
| 100 |
-func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) {
|
|
| 101 |
- // Add |
|
| 102 |
- if err := programChainRule(iptables.IPv4, rule, descr, true); err != nil {
|
|
| 103 |
- t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error())
|
|
| 104 |
- } |
|
| 105 |
- |
|
| 106 |
- iptable := iptables.GetIptable(iptables.IPv4) |
|
| 107 |
- if iptable.Exists(rule.table, rule.chain, rule.args...) == false {
|
|
| 108 |
- t.Fatalf("Failed to effectively program iptable rule: %s", descr)
|
|
| 109 |
- } |
|
| 110 |
- |
|
| 111 |
- // Remove |
|
| 112 |
- if err := programChainRule(iptables.IPv4, rule, descr, false); err != nil {
|
|
| 113 |
- t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error())
|
|
| 114 |
- } |
|
| 115 |
- if iptable.Exists(rule.table, rule.chain, rule.args...) == true {
|
|
| 116 |
- t.Fatalf("Failed to effectively remove iptable rule: %s", descr)
|
|
| 117 |
- } |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-// Assert function which create chains. |
|
| 121 |
-func assertChainConfig(d *driver, t *testing.T) {
|
|
| 122 |
- var err error |
|
| 123 |
- |
|
| 124 |
- d.natChain, d.filterChain, d.isolationChain1, d.isolationChain2, err = setupIPChains(d.config, iptables.IPv4) |
|
| 125 |
- if err != nil {
|
|
| 126 |
- t.Fatal(err) |
|
| 127 |
- } |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-// Assert function which pushes chains based on bridge config parameters. |
|
| 131 |
-func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) {
|
|
| 132 |
- nw := bridgeNetwork{
|
|
| 133 |
- portMapper: portmapper.New(""),
|
|
| 134 |
- config: config, |
|
| 135 |
- } |
|
| 136 |
- nw.driver = d |
|
| 137 |
- |
|
| 138 |
- // Attempt programming of ip tables. |
|
| 139 |
- err := nw.setupIP4Tables(config, br) |
|
| 140 |
- if err != nil {
|
|
| 141 |
- t.Fatalf("%v", err)
|
|
| 142 |
- } |
|
| 143 |
-} |
| 144 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,85 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "net" |
|
| 10 |
- "os" |
|
| 11 |
- "path/filepath" |
|
| 12 |
- |
|
| 13 |
- "github.com/containerd/containerd/log" |
|
| 14 |
- "github.com/docker/docker/libnetwork/types" |
|
| 15 |
- "github.com/vishvananda/netlink" |
|
| 16 |
-) |
|
| 17 |
- |
|
| 18 |
-func selectIPv4Address(addresses []netlink.Addr, selector *net.IPNet) (netlink.Addr, error) {
|
|
| 19 |
- if len(addresses) == 0 {
|
|
| 20 |
- return netlink.Addr{}, errors.New("unable to select an address as the address pool is empty")
|
|
| 21 |
- } |
|
| 22 |
- if selector != nil {
|
|
| 23 |
- for _, addr := range addresses {
|
|
| 24 |
- if selector.Contains(addr.IP) {
|
|
| 25 |
- return addr, nil |
|
| 26 |
- } |
|
| 27 |
- } |
|
| 28 |
- } |
|
| 29 |
- return addresses[0], nil |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 33 |
- if !config.InhibitIPv4 {
|
|
| 34 |
- addrv4List, _, err := i.addresses() |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err)
|
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- addrv4, _ := selectIPv4Address(addrv4List, config.AddressIPv4) |
|
| 40 |
- |
|
| 41 |
- if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) {
|
|
| 42 |
- if addrv4.IPNet != nil {
|
|
| 43 |
- if err := i.nlh.AddrDel(i.Link, &addrv4); err != nil {
|
|
| 44 |
- return fmt.Errorf("failed to remove current ip address from bridge: %v", err)
|
|
| 45 |
- } |
|
| 46 |
- } |
|
| 47 |
- log.G(context.TODO()).Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4)
|
|
| 48 |
- if err := i.nlh.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 49 |
- return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err}
|
|
| 50 |
- } |
|
| 51 |
- } |
|
| 52 |
- } |
|
| 53 |
- |
|
| 54 |
- // Store bridge network and default gateway |
|
| 55 |
- i.bridgeIPv4 = config.AddressIPv4 |
|
| 56 |
- i.gatewayIPv4 = config.AddressIPv4.IP |
|
| 57 |
- |
|
| 58 |
- return nil |
|
| 59 |
-} |
|
| 60 |
- |
|
| 61 |
-func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 62 |
- if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) {
|
|
| 63 |
- return &ErrInvalidGateway{}
|
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- // Store requested default gateway |
|
| 67 |
- i.gatewayIPv4 = config.DefaultGatewayIPv4 |
|
| 68 |
- |
|
| 69 |
- return nil |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func setupLoopbackAddressesRouting(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 73 |
- sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet")
|
|
| 74 |
- ipv4LoRoutingData, err := os.ReadFile(sysPath) |
|
| 75 |
- if err != nil {
|
|
| 76 |
- return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err)
|
|
| 77 |
- } |
|
| 78 |
- // Enable loopback addresses routing only if it isn't already enabled |
|
| 79 |
- if ipv4LoRoutingData[0] != '1' {
|
|
| 80 |
- if err := os.WriteFile(sysPath, []byte{'1', '\n'}, 0o644); err != nil {
|
|
| 81 |
- return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err)
|
|
| 82 |
- } |
|
| 83 |
- } |
|
| 84 |
- return nil |
|
| 85 |
-} |
| 86 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,83 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "net" |
|
| 7 |
+ "os" |
|
| 8 |
+ "path/filepath" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/containerd/containerd/log" |
|
| 11 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 12 |
+ "github.com/vishvananda/netlink" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+func selectIPv4Address(addresses []netlink.Addr, selector *net.IPNet) (netlink.Addr, error) {
|
|
| 16 |
+ if len(addresses) == 0 {
|
|
| 17 |
+ return netlink.Addr{}, errors.New("unable to select an address as the address pool is empty")
|
|
| 18 |
+ } |
|
| 19 |
+ if selector != nil {
|
|
| 20 |
+ for _, addr := range addresses {
|
|
| 21 |
+ if selector.Contains(addr.IP) {
|
|
| 22 |
+ return addr, nil |
|
| 23 |
+ } |
|
| 24 |
+ } |
|
| 25 |
+ } |
|
| 26 |
+ return addresses[0], nil |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 30 |
+ if !config.InhibitIPv4 {
|
|
| 31 |
+ addrv4List, _, err := i.addresses() |
|
| 32 |
+ if err != nil {
|
|
| 33 |
+ return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err)
|
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ addrv4, _ := selectIPv4Address(addrv4List, config.AddressIPv4) |
|
| 37 |
+ |
|
| 38 |
+ if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) {
|
|
| 39 |
+ if addrv4.IPNet != nil {
|
|
| 40 |
+ if err := i.nlh.AddrDel(i.Link, &addrv4); err != nil {
|
|
| 41 |
+ return fmt.Errorf("failed to remove current ip address from bridge: %v", err)
|
|
| 42 |
+ } |
|
| 43 |
+ } |
|
| 44 |
+ log.G(context.TODO()).Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4)
|
|
| 45 |
+ if err := i.nlh.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 46 |
+ return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err}
|
|
| 47 |
+ } |
|
| 48 |
+ } |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ // Store bridge network and default gateway |
|
| 52 |
+ i.bridgeIPv4 = config.AddressIPv4 |
|
| 53 |
+ i.gatewayIPv4 = config.AddressIPv4.IP |
|
| 54 |
+ |
|
| 55 |
+ return nil |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 59 |
+ if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) {
|
|
| 60 |
+ return &ErrInvalidGateway{}
|
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ // Store requested default gateway |
|
| 64 |
+ i.gatewayIPv4 = config.DefaultGatewayIPv4 |
|
| 65 |
+ |
|
| 66 |
+ return nil |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+func setupLoopbackAddressesRouting(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 70 |
+ sysPath := filepath.Join("/proc/sys/net/ipv4/conf", config.BridgeName, "route_localnet")
|
|
| 71 |
+ ipv4LoRoutingData, err := os.ReadFile(sysPath) |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ return fmt.Errorf("Cannot read IPv4 local routing setup: %v", err)
|
|
| 74 |
+ } |
|
| 75 |
+ // Enable loopback addresses routing only if it isn't already enabled |
|
| 76 |
+ if ipv4LoRoutingData[0] != '1' {
|
|
| 77 |
+ if err := os.WriteFile(sysPath, []byte{'1', '\n'}, 0o644); err != nil {
|
|
| 78 |
+ return fmt.Errorf("Unable to enable local routing for hairpin mode: %v", err)
|
|
| 79 |
+ } |
|
| 80 |
+ } |
|
| 81 |
+ return nil |
|
| 82 |
+} |
| 0 | 83 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,88 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "testing" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 7 |
+ "github.com/vishvananda/netlink" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func setupTestInterface(t *testing.T, nh *netlink.Handle) (*networkConfiguration, *bridgeInterface) {
|
|
| 11 |
+ config := &networkConfiguration{
|
|
| 12 |
+ BridgeName: DefaultBridgeName, |
|
| 13 |
+ } |
|
| 14 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 15 |
+ |
|
| 16 |
+ if err := setupDevice(config, br); err != nil {
|
|
| 17 |
+ t.Fatalf("Bridge creation failed: %v", err)
|
|
| 18 |
+ } |
|
| 19 |
+ return config, br |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func TestSetupBridgeIPv4Fixed(t *testing.T) {
|
|
| 23 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 24 |
+ |
|
| 25 |
+ ip, netw, err := net.ParseCIDR("192.168.1.1/24")
|
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ t.Fatalf("Failed to parse bridge IPv4: %v", err)
|
|
| 28 |
+ } |
|
| 29 |
+ |
|
| 30 |
+ nh, err := netlink.NewHandle() |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ t.Fatal(err) |
|
| 33 |
+ } |
|
| 34 |
+ defer nh.Close() |
|
| 35 |
+ |
|
| 36 |
+ config, br := setupTestInterface(t, nh) |
|
| 37 |
+ config.AddressIPv4 = &net.IPNet{IP: ip, Mask: netw.Mask}
|
|
| 38 |
+ if err := setupBridgeIPv4(config, br); err != nil {
|
|
| 39 |
+ t.Fatalf("Failed to setup bridge IPv4: %v", err)
|
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ addrsv4, err := nh.AddrList(br.Link, netlink.FAMILY_V4) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ t.Fatalf("Failed to list device IPv4 addresses: %v", err)
|
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ var found bool |
|
| 48 |
+ for _, addr := range addrsv4 {
|
|
| 49 |
+ if config.AddressIPv4.String() == addr.IPNet.String() {
|
|
| 50 |
+ found = true |
|
| 51 |
+ break |
|
| 52 |
+ } |
|
| 53 |
+ } |
|
| 54 |
+ |
|
| 55 |
+ if !found {
|
|
| 56 |
+ t.Fatalf("Bridge device does not have requested IPv4 address %v", config.AddressIPv4)
|
|
| 57 |
+ } |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func TestSetupGatewayIPv4(t *testing.T) {
|
|
| 61 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 62 |
+ |
|
| 63 |
+ nh, err := netlink.NewHandle() |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ t.Fatal(err) |
|
| 66 |
+ } |
|
| 67 |
+ defer nh.Close() |
|
| 68 |
+ |
|
| 69 |
+ ip, nw, _ := net.ParseCIDR("192.168.0.24/16")
|
|
| 70 |
+ nw.IP = ip |
|
| 71 |
+ gw := net.ParseIP("192.168.2.254")
|
|
| 72 |
+ |
|
| 73 |
+ config := &networkConfiguration{
|
|
| 74 |
+ BridgeName: DefaultBridgeName, |
|
| 75 |
+ DefaultGatewayIPv4: gw, |
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ br := &bridgeInterface{bridgeIPv4: nw, nlh: nh}
|
|
| 79 |
+ |
|
| 80 |
+ if err := setupGatewayIPv4(config, br); err != nil {
|
|
| 81 |
+ t.Fatalf("Set Default Gateway failed: %v", err)
|
|
| 82 |
+ } |
|
| 83 |
+ |
|
| 84 |
+ if !gw.Equal(br.gatewayIPv4) {
|
|
| 85 |
+ t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv4)
|
|
| 86 |
+ } |
|
| 87 |
+} |
| 0 | 88 |
deleted file mode 100644 |
| ... | ... |
@@ -1,90 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "net" |
|
| 7 |
- "testing" |
|
| 8 |
- |
|
| 9 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 10 |
- "github.com/vishvananda/netlink" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-func setupTestInterface(t *testing.T, nh *netlink.Handle) (*networkConfiguration, *bridgeInterface) {
|
|
| 14 |
- config := &networkConfiguration{
|
|
| 15 |
- BridgeName: DefaultBridgeName, |
|
| 16 |
- } |
|
| 17 |
- br := &bridgeInterface{nlh: nh}
|
|
| 18 |
- |
|
| 19 |
- if err := setupDevice(config, br); err != nil {
|
|
| 20 |
- t.Fatalf("Bridge creation failed: %v", err)
|
|
| 21 |
- } |
|
| 22 |
- return config, br |
|
| 23 |
-} |
|
| 24 |
- |
|
| 25 |
-func TestSetupBridgeIPv4Fixed(t *testing.T) {
|
|
| 26 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 27 |
- |
|
| 28 |
- ip, netw, err := net.ParseCIDR("192.168.1.1/24")
|
|
| 29 |
- if err != nil {
|
|
| 30 |
- t.Fatalf("Failed to parse bridge IPv4: %v", err)
|
|
| 31 |
- } |
|
| 32 |
- |
|
| 33 |
- nh, err := netlink.NewHandle() |
|
| 34 |
- if err != nil {
|
|
| 35 |
- t.Fatal(err) |
|
| 36 |
- } |
|
| 37 |
- defer nh.Close() |
|
| 38 |
- |
|
| 39 |
- config, br := setupTestInterface(t, nh) |
|
| 40 |
- config.AddressIPv4 = &net.IPNet{IP: ip, Mask: netw.Mask}
|
|
| 41 |
- if err := setupBridgeIPv4(config, br); err != nil {
|
|
| 42 |
- t.Fatalf("Failed to setup bridge IPv4: %v", err)
|
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- addrsv4, err := nh.AddrList(br.Link, netlink.FAMILY_V4) |
|
| 46 |
- if err != nil {
|
|
| 47 |
- t.Fatalf("Failed to list device IPv4 addresses: %v", err)
|
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- var found bool |
|
| 51 |
- for _, addr := range addrsv4 {
|
|
| 52 |
- if config.AddressIPv4.String() == addr.IPNet.String() {
|
|
| 53 |
- found = true |
|
| 54 |
- break |
|
| 55 |
- } |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- if !found {
|
|
| 59 |
- t.Fatalf("Bridge device does not have requested IPv4 address %v", config.AddressIPv4)
|
|
| 60 |
- } |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-func TestSetupGatewayIPv4(t *testing.T) {
|
|
| 64 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 65 |
- |
|
| 66 |
- nh, err := netlink.NewHandle() |
|
| 67 |
- if err != nil {
|
|
| 68 |
- t.Fatal(err) |
|
| 69 |
- } |
|
| 70 |
- defer nh.Close() |
|
| 71 |
- |
|
| 72 |
- ip, nw, _ := net.ParseCIDR("192.168.0.24/16")
|
|
| 73 |
- nw.IP = ip |
|
| 74 |
- gw := net.ParseIP("192.168.2.254")
|
|
| 75 |
- |
|
| 76 |
- config := &networkConfiguration{
|
|
| 77 |
- BridgeName: DefaultBridgeName, |
|
| 78 |
- DefaultGatewayIPv4: gw, |
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- br := &bridgeInterface{bridgeIPv4: nw, nlh: nh}
|
|
| 82 |
- |
|
| 83 |
- if err := setupGatewayIPv4(config, br); err != nil {
|
|
| 84 |
- t.Fatalf("Set Default Gateway failed: %v", err)
|
|
| 85 |
- } |
|
| 86 |
- |
|
| 87 |
- if !gw.Equal(br.gatewayIPv4) {
|
|
| 88 |
- t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv4)
|
|
| 89 |
- } |
|
| 90 |
-} |
| 91 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,111 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "fmt" |
|
| 8 |
- "net" |
|
| 9 |
- "os" |
|
| 10 |
- |
|
| 11 |
- "github.com/containerd/containerd/log" |
|
| 12 |
- "github.com/vishvananda/netlink" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-// bridgeIPv6 is the default, link-local IPv6 address for the bridge (fe80::1/64) |
|
| 16 |
-var bridgeIPv6 = &net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)}
|
|
| 17 |
- |
|
| 18 |
-const ( |
|
| 19 |
- ipv6ForwardConfPerm = 0o644 |
|
| 20 |
- ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding" |
|
| 21 |
- ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding" |
|
| 22 |
-) |
|
| 23 |
- |
|
| 24 |
-func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 25 |
- procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6" |
|
| 26 |
- ipv6BridgeData, err := os.ReadFile(procFile) |
|
| 27 |
- if err != nil {
|
|
| 28 |
- return fmt.Errorf("Cannot read IPv6 setup for bridge %v: %v", config.BridgeName, err)
|
|
| 29 |
- } |
|
| 30 |
- // Enable IPv6 on the bridge only if it isn't already enabled |
|
| 31 |
- if ipv6BridgeData[0] != '0' {
|
|
| 32 |
- if err := os.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil {
|
|
| 33 |
- return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
|
| 34 |
- } |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- // Store bridge network and default gateway |
|
| 38 |
- i.bridgeIPv6 = bridgeIPv6 |
|
| 39 |
- i.gatewayIPv6 = i.bridgeIPv6.IP |
|
| 40 |
- |
|
| 41 |
- if err := i.programIPv6Address(); err != nil {
|
|
| 42 |
- return err |
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- if config.AddressIPv6 == nil {
|
|
| 46 |
- return nil |
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- // Store the user specified bridge network and network gateway and program it |
|
| 50 |
- i.bridgeIPv6 = config.AddressIPv6 |
|
| 51 |
- i.gatewayIPv6 = config.AddressIPv6.IP |
|
| 52 |
- |
|
| 53 |
- if err := i.programIPv6Address(); err != nil {
|
|
| 54 |
- return err |
|
| 55 |
- } |
|
| 56 |
- |
|
| 57 |
- // Setting route to global IPv6 subnet |
|
| 58 |
- log.G(context.TODO()).Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName)
|
|
| 59 |
- err = i.nlh.RouteAdd(&netlink.Route{
|
|
| 60 |
- Scope: netlink.SCOPE_UNIVERSE, |
|
| 61 |
- LinkIndex: i.Link.Attrs().Index, |
|
| 62 |
- Dst: config.AddressIPv6, |
|
| 63 |
- }) |
|
| 64 |
- if err != nil && !os.IsExist(err) {
|
|
| 65 |
- log.G(context.TODO()).Errorf("Could not add route to IPv6 network %s via device %s: %s", config.AddressIPv6.String(), config.BridgeName, err)
|
|
| 66 |
- } |
|
| 67 |
- |
|
| 68 |
- return nil |
|
| 69 |
-} |
|
| 70 |
- |
|
| 71 |
-func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 72 |
- if config.AddressIPv6 == nil {
|
|
| 73 |
- return &ErrInvalidContainerSubnet{}
|
|
| 74 |
- } |
|
| 75 |
- if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) {
|
|
| 76 |
- return &ErrInvalidGateway{}
|
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- // Store requested default gateway |
|
| 80 |
- i.gatewayIPv6 = config.DefaultGatewayIPv6 |
|
| 81 |
- |
|
| 82 |
- return nil |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-func setupIPv6Forwarding(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 86 |
- // Get current IPv6 default forwarding setup |
|
| 87 |
- ipv6ForwardDataDefault, err := os.ReadFile(ipv6ForwardConfDefault) |
|
| 88 |
- if err != nil {
|
|
| 89 |
- return fmt.Errorf("Cannot read IPv6 default forwarding setup: %v", err)
|
|
| 90 |
- } |
|
| 91 |
- // Enable IPv6 default forwarding only if it is not already enabled |
|
| 92 |
- if ipv6ForwardDataDefault[0] != '1' {
|
|
| 93 |
- if err := os.WriteFile(ipv6ForwardConfDefault, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
|
| 94 |
- log.G(context.TODO()).Warnf("Unable to enable IPv6 default forwarding: %v", err)
|
|
| 95 |
- } |
|
| 96 |
- } |
|
| 97 |
- |
|
| 98 |
- // Get current IPv6 all forwarding setup |
|
| 99 |
- ipv6ForwardDataAll, err := os.ReadFile(ipv6ForwardConfAll) |
|
| 100 |
- if err != nil {
|
|
| 101 |
- return fmt.Errorf("Cannot read IPv6 all forwarding setup: %v", err)
|
|
| 102 |
- } |
|
| 103 |
- // Enable IPv6 all forwarding only if it is not already enabled |
|
| 104 |
- if ipv6ForwardDataAll[0] != '1' {
|
|
| 105 |
- if err := os.WriteFile(ipv6ForwardConfAll, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
|
| 106 |
- log.G(context.TODO()).Warnf("Unable to enable IPv6 all forwarding: %v", err)
|
|
| 107 |
- } |
|
| 108 |
- } |
|
| 109 |
- |
|
| 110 |
- return nil |
|
| 111 |
-} |
| 112 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,109 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ "os" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/containerd/containerd/log" |
|
| 9 |
+ "github.com/vishvananda/netlink" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+// bridgeIPv6 is the default, link-local IPv6 address for the bridge (fe80::1/64) |
|
| 13 |
+var bridgeIPv6 = &net.IPNet{IP: net.ParseIP("fe80::1"), Mask: net.CIDRMask(64, 128)}
|
|
| 14 |
+ |
|
| 15 |
+const ( |
|
| 16 |
+ ipv6ForwardConfPerm = 0o644 |
|
| 17 |
+ ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding" |
|
| 18 |
+ ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding" |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 22 |
+ procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6" |
|
| 23 |
+ ipv6BridgeData, err := os.ReadFile(procFile) |
|
| 24 |
+ if err != nil {
|
|
| 25 |
+ return fmt.Errorf("Cannot read IPv6 setup for bridge %v: %v", config.BridgeName, err)
|
|
| 26 |
+ } |
|
| 27 |
+ // Enable IPv6 on the bridge only if it isn't already enabled |
|
| 28 |
+ if ipv6BridgeData[0] != '0' {
|
|
| 29 |
+ if err := os.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil {
|
|
| 30 |
+ return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
|
| 31 |
+ } |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ // Store bridge network and default gateway |
|
| 35 |
+ i.bridgeIPv6 = bridgeIPv6 |
|
| 36 |
+ i.gatewayIPv6 = i.bridgeIPv6.IP |
|
| 37 |
+ |
|
| 38 |
+ if err := i.programIPv6Address(); err != nil {
|
|
| 39 |
+ return err |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ if config.AddressIPv6 == nil {
|
|
| 43 |
+ return nil |
|
| 44 |
+ } |
|
| 45 |
+ |
|
| 46 |
+ // Store the user specified bridge network and network gateway and program it |
|
| 47 |
+ i.bridgeIPv6 = config.AddressIPv6 |
|
| 48 |
+ i.gatewayIPv6 = config.AddressIPv6.IP |
|
| 49 |
+ |
|
| 50 |
+ if err := i.programIPv6Address(); err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ |
|
| 54 |
+ // Setting route to global IPv6 subnet |
|
| 55 |
+ log.G(context.TODO()).Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName)
|
|
| 56 |
+ err = i.nlh.RouteAdd(&netlink.Route{
|
|
| 57 |
+ Scope: netlink.SCOPE_UNIVERSE, |
|
| 58 |
+ LinkIndex: i.Link.Attrs().Index, |
|
| 59 |
+ Dst: config.AddressIPv6, |
|
| 60 |
+ }) |
|
| 61 |
+ if err != nil && !os.IsExist(err) {
|
|
| 62 |
+ log.G(context.TODO()).Errorf("Could not add route to IPv6 network %s via device %s: %s", config.AddressIPv6.String(), config.BridgeName, err)
|
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ return nil |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 69 |
+ if config.AddressIPv6 == nil {
|
|
| 70 |
+ return &ErrInvalidContainerSubnet{}
|
|
| 71 |
+ } |
|
| 72 |
+ if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) {
|
|
| 73 |
+ return &ErrInvalidGateway{}
|
|
| 74 |
+ } |
|
| 75 |
+ |
|
| 76 |
+ // Store requested default gateway |
|
| 77 |
+ i.gatewayIPv6 = config.DefaultGatewayIPv6 |
|
| 78 |
+ |
|
| 79 |
+ return nil |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+func setupIPv6Forwarding(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 83 |
+ // Get current IPv6 default forwarding setup |
|
| 84 |
+ ipv6ForwardDataDefault, err := os.ReadFile(ipv6ForwardConfDefault) |
|
| 85 |
+ if err != nil {
|
|
| 86 |
+ return fmt.Errorf("Cannot read IPv6 default forwarding setup: %v", err)
|
|
| 87 |
+ } |
|
| 88 |
+ // Enable IPv6 default forwarding only if it is not already enabled |
|
| 89 |
+ if ipv6ForwardDataDefault[0] != '1' {
|
|
| 90 |
+ if err := os.WriteFile(ipv6ForwardConfDefault, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
|
| 91 |
+ log.G(context.TODO()).Warnf("Unable to enable IPv6 default forwarding: %v", err)
|
|
| 92 |
+ } |
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ // Get current IPv6 all forwarding setup |
|
| 96 |
+ ipv6ForwardDataAll, err := os.ReadFile(ipv6ForwardConfAll) |
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ return fmt.Errorf("Cannot read IPv6 all forwarding setup: %v", err)
|
|
| 99 |
+ } |
|
| 100 |
+ // Enable IPv6 all forwarding only if it is not already enabled |
|
| 101 |
+ if ipv6ForwardDataAll[0] != '1' {
|
|
| 102 |
+ if err := os.WriteFile(ipv6ForwardConfAll, []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
|
|
| 103 |
+ log.G(context.TODO()).Warnf("Unable to enable IPv6 all forwarding: %v", err)
|
|
| 104 |
+ } |
|
| 105 |
+ } |
|
| 106 |
+ |
|
| 107 |
+ return nil |
|
| 108 |
+} |
| 0 | 109 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,82 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ "os" |
|
| 7 |
+ "testing" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 10 |
+ "github.com/vishvananda/netlink" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func TestSetupIPv6(t *testing.T) {
|
|
| 14 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 15 |
+ |
|
| 16 |
+ nh, err := netlink.NewHandle() |
|
| 17 |
+ if err != nil {
|
|
| 18 |
+ t.Fatal(err) |
|
| 19 |
+ } |
|
| 20 |
+ defer nh.Close() |
|
| 21 |
+ |
|
| 22 |
+ config, br := setupTestInterface(t, nh) |
|
| 23 |
+ if err := setupBridgeIPv6(config, br); err != nil {
|
|
| 24 |
+ t.Fatalf("Failed to setup bridge IPv6: %v", err)
|
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ procSetting, err := os.ReadFile(fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", config.BridgeName))
|
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ t.Fatalf("Failed to read disable_ipv6 kernel setting: %v", err)
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ if expected := []byte("0\n"); !bytes.Equal(expected, procSetting) {
|
|
| 33 |
+ t.Fatalf("Invalid kernel setting disable_ipv6: expected %q, got %q", string(expected), string(procSetting))
|
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ addrsv6, err := nh.AddrList(br.Link, netlink.FAMILY_V6) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ t.Fatalf("Failed to list device IPv6 addresses: %v", err)
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ var found bool |
|
| 42 |
+ for _, addr := range addrsv6 {
|
|
| 43 |
+ if bridgeIPv6.String() == addr.IPNet.String() {
|
|
| 44 |
+ found = true |
|
| 45 |
+ break |
|
| 46 |
+ } |
|
| 47 |
+ } |
|
| 48 |
+ |
|
| 49 |
+ if !found {
|
|
| 50 |
+ t.Fatalf("Bridge device does not have requested IPv6 address %v", bridgeIPv6)
|
|
| 51 |
+ } |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func TestSetupGatewayIPv6(t *testing.T) {
|
|
| 55 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 56 |
+ |
|
| 57 |
+ _, nw, _ := net.ParseCIDR("2001:db8:ea9:9abc:ffff::/80")
|
|
| 58 |
+ gw := net.ParseIP("2001:db8:ea9:9abc:ffff::254")
|
|
| 59 |
+ |
|
| 60 |
+ config := &networkConfiguration{
|
|
| 61 |
+ BridgeName: DefaultBridgeName, |
|
| 62 |
+ AddressIPv6: nw, |
|
| 63 |
+ DefaultGatewayIPv6: gw, |
|
| 64 |
+ } |
|
| 65 |
+ |
|
| 66 |
+ nh, err := netlink.NewHandle() |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ t.Fatal(err) |
|
| 69 |
+ } |
|
| 70 |
+ defer nh.Close() |
|
| 71 |
+ |
|
| 72 |
+ br := &bridgeInterface{nlh: nh}
|
|
| 73 |
+ |
|
| 74 |
+ if err := setupGatewayIPv6(config, br); err != nil {
|
|
| 75 |
+ t.Fatalf("Set Default Gateway failed: %v", err)
|
|
| 76 |
+ } |
|
| 77 |
+ |
|
| 78 |
+ if !gw.Equal(br.gatewayIPv6) {
|
|
| 79 |
+ t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv6)
|
|
| 80 |
+ } |
|
| 81 |
+} |
| 0 | 82 |
deleted file mode 100644 |
| ... | ... |
@@ -1,84 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "bytes" |
|
| 7 |
- "fmt" |
|
| 8 |
- "net" |
|
| 9 |
- "os" |
|
| 10 |
- "testing" |
|
| 11 |
- |
|
| 12 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 13 |
- "github.com/vishvananda/netlink" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-func TestSetupIPv6(t *testing.T) {
|
|
| 17 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 18 |
- |
|
| 19 |
- nh, err := netlink.NewHandle() |
|
| 20 |
- if err != nil {
|
|
| 21 |
- t.Fatal(err) |
|
| 22 |
- } |
|
| 23 |
- defer nh.Close() |
|
| 24 |
- |
|
| 25 |
- config, br := setupTestInterface(t, nh) |
|
| 26 |
- if err := setupBridgeIPv6(config, br); err != nil {
|
|
| 27 |
- t.Fatalf("Failed to setup bridge IPv6: %v", err)
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- procSetting, err := os.ReadFile(fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", config.BridgeName))
|
|
| 31 |
- if err != nil {
|
|
| 32 |
- t.Fatalf("Failed to read disable_ipv6 kernel setting: %v", err)
|
|
| 33 |
- } |
|
| 34 |
- |
|
| 35 |
- if expected := []byte("0\n"); !bytes.Equal(expected, procSetting) {
|
|
| 36 |
- t.Fatalf("Invalid kernel setting disable_ipv6: expected %q, got %q", string(expected), string(procSetting))
|
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- addrsv6, err := nh.AddrList(br.Link, netlink.FAMILY_V6) |
|
| 40 |
- if err != nil {
|
|
| 41 |
- t.Fatalf("Failed to list device IPv6 addresses: %v", err)
|
|
| 42 |
- } |
|
| 43 |
- |
|
| 44 |
- var found bool |
|
| 45 |
- for _, addr := range addrsv6 {
|
|
| 46 |
- if bridgeIPv6.String() == addr.IPNet.String() {
|
|
| 47 |
- found = true |
|
| 48 |
- break |
|
| 49 |
- } |
|
| 50 |
- } |
|
| 51 |
- |
|
| 52 |
- if !found {
|
|
| 53 |
- t.Fatalf("Bridge device does not have requested IPv6 address %v", bridgeIPv6)
|
|
| 54 |
- } |
|
| 55 |
-} |
|
| 56 |
- |
|
| 57 |
-func TestSetupGatewayIPv6(t *testing.T) {
|
|
| 58 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 59 |
- |
|
| 60 |
- _, nw, _ := net.ParseCIDR("2001:db8:ea9:9abc:ffff::/80")
|
|
| 61 |
- gw := net.ParseIP("2001:db8:ea9:9abc:ffff::254")
|
|
| 62 |
- |
|
| 63 |
- config := &networkConfiguration{
|
|
| 64 |
- BridgeName: DefaultBridgeName, |
|
| 65 |
- AddressIPv6: nw, |
|
| 66 |
- DefaultGatewayIPv6: gw, |
|
| 67 |
- } |
|
| 68 |
- |
|
| 69 |
- nh, err := netlink.NewHandle() |
|
| 70 |
- if err != nil {
|
|
| 71 |
- t.Fatal(err) |
|
| 72 |
- } |
|
| 73 |
- defer nh.Close() |
|
| 74 |
- |
|
| 75 |
- br := &bridgeInterface{nlh: nh}
|
|
| 76 |
- |
|
| 77 |
- if err := setupGatewayIPv6(config, br); err != nil {
|
|
| 78 |
- t.Fatalf("Set Default Gateway failed: %v", err)
|
|
| 79 |
- } |
|
| 80 |
- |
|
| 81 |
- if !gw.Equal(br.gatewayIPv6) {
|
|
| 82 |
- t.Fatalf("Set Default Gateway failed. Expected %v, Found %v", gw, br.gatewayIPv6)
|
|
| 83 |
- } |
|
| 84 |
-} |
| 85 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,77 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "context" |
|
| 7 |
- "fmt" |
|
| 8 |
- "strings" |
|
| 9 |
- |
|
| 10 |
- "github.com/containerd/containerd/log" |
|
| 11 |
- "github.com/docker/docker/libnetwork/ns" |
|
| 12 |
- "github.com/docker/docker/libnetwork/types" |
|
| 13 |
- "github.com/vishvananda/netlink" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-func setupVerifyAndReconcile(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 17 |
- // Fetch a slice of IPv4 addresses and a slice of IPv6 addresses from the bridge. |
|
| 18 |
- addrsv4, addrsv6, err := i.addresses() |
|
| 19 |
- if err != nil {
|
|
| 20 |
- return fmt.Errorf("Failed to verify ip addresses: %v", err)
|
|
| 21 |
- } |
|
| 22 |
- |
|
| 23 |
- addrv4, _ := selectIPv4Address(addrsv4, config.AddressIPv4) |
|
| 24 |
- |
|
| 25 |
- // Verify that the bridge does have an IPv4 address. |
|
| 26 |
- if addrv4.IPNet == nil {
|
|
| 27 |
- return &ErrNoIPAddr{}
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- // Verify that the bridge IPv4 address matches the requested configuration. |
|
| 31 |
- if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) {
|
|
| 32 |
- return &IPv4AddrNoMatchError{IP: addrv4.IP, CfgIP: config.AddressIPv4.IP}
|
|
| 33 |
- } |
|
| 34 |
- |
|
| 35 |
- // Verify that one of the bridge IPv6 addresses matches the requested |
|
| 36 |
- // configuration. |
|
| 37 |
- if config.EnableIPv6 && !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) {
|
|
| 38 |
- return (*IPv6AddrNoMatchError)(bridgeIPv6) |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- // Release any residual IPv6 address that might be there because of older daemon instances |
|
| 42 |
- for _, addrv6 := range addrsv6 {
|
|
| 43 |
- addrv6 := addrv6 |
|
| 44 |
- if addrv6.IP.IsGlobalUnicast() && !types.CompareIPNet(addrv6.IPNet, i.bridgeIPv6) {
|
|
| 45 |
- if err := i.nlh.AddrDel(i.Link, &addrv6); err != nil {
|
|
| 46 |
- log.G(context.TODO()).Warnf("Failed to remove residual IPv6 address %s from bridge: %v", addrv6.IPNet, err)
|
|
| 47 |
- } |
|
| 48 |
- } |
|
| 49 |
- } |
|
| 50 |
- |
|
| 51 |
- return nil |
|
| 52 |
-} |
|
| 53 |
- |
|
| 54 |
-func findIPv6Address(addr netlink.Addr, addresses []netlink.Addr) bool {
|
|
| 55 |
- for _, addrv6 := range addresses {
|
|
| 56 |
- if addrv6.String() == addr.String() {
|
|
| 57 |
- return true |
|
| 58 |
- } |
|
| 59 |
- } |
|
| 60 |
- return false |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-func bridgeInterfaceExists(name string) (bool, error) {
|
|
| 64 |
- nlh := ns.NlHandle() |
|
| 65 |
- link, err := nlh.LinkByName(name) |
|
| 66 |
- if err != nil {
|
|
| 67 |
- if strings.Contains(err.Error(), "Link not found") {
|
|
| 68 |
- return false, nil |
|
| 69 |
- } |
|
| 70 |
- return false, fmt.Errorf("failed to check bridge interface existence: %v", err)
|
|
| 71 |
- } |
|
| 72 |
- |
|
| 73 |
- if link.Type() == "bridge" {
|
|
| 74 |
- return true, nil |
|
| 75 |
- } |
|
| 76 |
- return false, fmt.Errorf("existing interface %s is not a bridge", name)
|
|
| 77 |
-} |
| 78 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,75 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/containerd/containerd/log" |
|
| 8 |
+ "github.com/docker/docker/libnetwork/ns" |
|
| 9 |
+ "github.com/docker/docker/libnetwork/types" |
|
| 10 |
+ "github.com/vishvananda/netlink" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func setupVerifyAndReconcile(config *networkConfiguration, i *bridgeInterface) error {
|
|
| 14 |
+ // Fetch a slice of IPv4 addresses and a slice of IPv6 addresses from the bridge. |
|
| 15 |
+ addrsv4, addrsv6, err := i.addresses() |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ return fmt.Errorf("Failed to verify ip addresses: %v", err)
|
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ addrv4, _ := selectIPv4Address(addrsv4, config.AddressIPv4) |
|
| 21 |
+ |
|
| 22 |
+ // Verify that the bridge does have an IPv4 address. |
|
| 23 |
+ if addrv4.IPNet == nil {
|
|
| 24 |
+ return &ErrNoIPAddr{}
|
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ // Verify that the bridge IPv4 address matches the requested configuration. |
|
| 28 |
+ if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) {
|
|
| 29 |
+ return &IPv4AddrNoMatchError{IP: addrv4.IP, CfgIP: config.AddressIPv4.IP}
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ // Verify that one of the bridge IPv6 addresses matches the requested |
|
| 33 |
+ // configuration. |
|
| 34 |
+ if config.EnableIPv6 && !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) {
|
|
| 35 |
+ return (*IPv6AddrNoMatchError)(bridgeIPv6) |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ // Release any residual IPv6 address that might be there because of older daemon instances |
|
| 39 |
+ for _, addrv6 := range addrsv6 {
|
|
| 40 |
+ addrv6 := addrv6 |
|
| 41 |
+ if addrv6.IP.IsGlobalUnicast() && !types.CompareIPNet(addrv6.IPNet, i.bridgeIPv6) {
|
|
| 42 |
+ if err := i.nlh.AddrDel(i.Link, &addrv6); err != nil {
|
|
| 43 |
+ log.G(context.TODO()).Warnf("Failed to remove residual IPv6 address %s from bridge: %v", addrv6.IPNet, err)
|
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 46 |
+ } |
|
| 47 |
+ |
|
| 48 |
+ return nil |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+func findIPv6Address(addr netlink.Addr, addresses []netlink.Addr) bool {
|
|
| 52 |
+ for _, addrv6 := range addresses {
|
|
| 53 |
+ if addrv6.String() == addr.String() {
|
|
| 54 |
+ return true |
|
| 55 |
+ } |
|
| 56 |
+ } |
|
| 57 |
+ return false |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func bridgeInterfaceExists(name string) (bool, error) {
|
|
| 61 |
+ nlh := ns.NlHandle() |
|
| 62 |
+ link, err := nlh.LinkByName(name) |
|
| 63 |
+ if err != nil {
|
|
| 64 |
+ if strings.Contains(err.Error(), "Link not found") {
|
|
| 65 |
+ return false, nil |
|
| 66 |
+ } |
|
| 67 |
+ return false, fmt.Errorf("failed to check bridge interface existence: %v", err)
|
|
| 68 |
+ } |
|
| 69 |
+ |
|
| 70 |
+ if link.Type() == "bridge" {
|
|
| 71 |
+ return true, nil |
|
| 72 |
+ } |
|
| 73 |
+ return false, fmt.Errorf("existing interface %s is not a bridge", name)
|
|
| 74 |
+} |
| 0 | 75 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,114 @@ |
| 0 |
+package bridge |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net" |
|
| 4 |
+ "testing" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 7 |
+ "github.com/vishvananda/netlink" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func setupVerifyTest(t *testing.T) *bridgeInterface {
|
|
| 11 |
+ nh, err := netlink.NewHandle() |
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ t.Fatal(err) |
|
| 14 |
+ } |
|
| 15 |
+ inf := &bridgeInterface{nlh: nh}
|
|
| 16 |
+ |
|
| 17 |
+ br := netlink.Bridge{}
|
|
| 18 |
+ br.LinkAttrs.Name = "default0" |
|
| 19 |
+ if err := nh.LinkAdd(&br); err == nil {
|
|
| 20 |
+ inf.Link = &br |
|
| 21 |
+ } else {
|
|
| 22 |
+ t.Fatalf("Failed to create bridge interface: %v", err)
|
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ return inf |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func TestSetupVerify(t *testing.T) {
|
|
| 29 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 30 |
+ |
|
| 31 |
+ addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 32 |
+ inf := setupVerifyTest(t) |
|
| 33 |
+ config := &networkConfiguration{}
|
|
| 34 |
+ config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 35 |
+ |
|
| 36 |
+ if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 37 |
+ t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err)
|
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ if err := setupVerifyAndReconcile(config, inf); err != nil {
|
|
| 41 |
+ t.Fatalf("Address verification failed: %v", err)
|
|
| 42 |
+ } |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func TestSetupVerifyBad(t *testing.T) {
|
|
| 46 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 47 |
+ |
|
| 48 |
+ addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 49 |
+ inf := setupVerifyTest(t) |
|
| 50 |
+ config := &networkConfiguration{}
|
|
| 51 |
+ config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 52 |
+ |
|
| 53 |
+ ipnet := &net.IPNet{IP: net.IPv4(192, 168, 1, 2), Mask: addrv4.DefaultMask()}
|
|
| 54 |
+ if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: ipnet}); err != nil {
|
|
| 55 |
+ t.Fatalf("Failed to assign IPv4 %s to interface: %v", ipnet, err)
|
|
| 56 |
+ } |
|
| 57 |
+ |
|
| 58 |
+ if err := setupVerifyAndReconcile(config, inf); err == nil {
|
|
| 59 |
+ t.Fatal("Address verification was expected to fail")
|
|
| 60 |
+ } |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func TestSetupVerifyMissing(t *testing.T) {
|
|
| 64 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 65 |
+ |
|
| 66 |
+ addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 67 |
+ inf := setupVerifyTest(t) |
|
| 68 |
+ config := &networkConfiguration{}
|
|
| 69 |
+ config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 70 |
+ |
|
| 71 |
+ if err := setupVerifyAndReconcile(config, inf); err == nil {
|
|
| 72 |
+ t.Fatal("Address verification was expected to fail")
|
|
| 73 |
+ } |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func TestSetupVerifyIPv6(t *testing.T) {
|
|
| 77 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 78 |
+ |
|
| 79 |
+ addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 80 |
+ inf := setupVerifyTest(t) |
|
| 81 |
+ config := &networkConfiguration{}
|
|
| 82 |
+ config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 83 |
+ config.EnableIPv6 = true |
|
| 84 |
+ |
|
| 85 |
+ if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
|
|
| 86 |
+ t.Fatalf("Failed to assign IPv6 %s to interface: %v", bridgeIPv6, err)
|
|
| 87 |
+ } |
|
| 88 |
+ if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 89 |
+ t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err)
|
|
| 90 |
+ } |
|
| 91 |
+ |
|
| 92 |
+ if err := setupVerifyAndReconcile(config, inf); err != nil {
|
|
| 93 |
+ t.Fatalf("Address verification failed: %v", err)
|
|
| 94 |
+ } |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func TestSetupVerifyIPv6Missing(t *testing.T) {
|
|
| 98 |
+ defer netnsutils.SetupTestOSContext(t)() |
|
| 99 |
+ |
|
| 100 |
+ addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 101 |
+ inf := setupVerifyTest(t) |
|
| 102 |
+ config := &networkConfiguration{}
|
|
| 103 |
+ config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 104 |
+ config.EnableIPv6 = true |
|
| 105 |
+ |
|
| 106 |
+ if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 107 |
+ t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err)
|
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ if err := setupVerifyAndReconcile(config, inf); err == nil {
|
|
| 111 |
+ t.Fatal("Address verification was expected to fail")
|
|
| 112 |
+ } |
|
| 113 |
+} |
| 0 | 114 |
deleted file mode 100644 |
| ... | ... |
@@ -1,116 +0,0 @@ |
| 1 |
-//go:build linux |
|
| 2 |
- |
|
| 3 |
-package bridge |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "net" |
|
| 7 |
- "testing" |
|
| 8 |
- |
|
| 9 |
- "github.com/docker/docker/internal/testutils/netnsutils" |
|
| 10 |
- "github.com/vishvananda/netlink" |
|
| 11 |
-) |
|
| 12 |
- |
|
| 13 |
-func setupVerifyTest(t *testing.T) *bridgeInterface {
|
|
| 14 |
- nh, err := netlink.NewHandle() |
|
| 15 |
- if err != nil {
|
|
| 16 |
- t.Fatal(err) |
|
| 17 |
- } |
|
| 18 |
- inf := &bridgeInterface{nlh: nh}
|
|
| 19 |
- |
|
| 20 |
- br := netlink.Bridge{}
|
|
| 21 |
- br.LinkAttrs.Name = "default0" |
|
| 22 |
- if err := nh.LinkAdd(&br); err == nil {
|
|
| 23 |
- inf.Link = &br |
|
| 24 |
- } else {
|
|
| 25 |
- t.Fatalf("Failed to create bridge interface: %v", err)
|
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- return inf |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-func TestSetupVerify(t *testing.T) {
|
|
| 32 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 33 |
- |
|
| 34 |
- addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 35 |
- inf := setupVerifyTest(t) |
|
| 36 |
- config := &networkConfiguration{}
|
|
| 37 |
- config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 38 |
- |
|
| 39 |
- if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 40 |
- t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err)
|
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- if err := setupVerifyAndReconcile(config, inf); err != nil {
|
|
| 44 |
- t.Fatalf("Address verification failed: %v", err)
|
|
| 45 |
- } |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func TestSetupVerifyBad(t *testing.T) {
|
|
| 49 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 50 |
- |
|
| 51 |
- addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 52 |
- inf := setupVerifyTest(t) |
|
| 53 |
- config := &networkConfiguration{}
|
|
| 54 |
- config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 55 |
- |
|
| 56 |
- ipnet := &net.IPNet{IP: net.IPv4(192, 168, 1, 2), Mask: addrv4.DefaultMask()}
|
|
| 57 |
- if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: ipnet}); err != nil {
|
|
| 58 |
- t.Fatalf("Failed to assign IPv4 %s to interface: %v", ipnet, err)
|
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- if err := setupVerifyAndReconcile(config, inf); err == nil {
|
|
| 62 |
- t.Fatal("Address verification was expected to fail")
|
|
| 63 |
- } |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-func TestSetupVerifyMissing(t *testing.T) {
|
|
| 67 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 68 |
- |
|
| 69 |
- addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 70 |
- inf := setupVerifyTest(t) |
|
| 71 |
- config := &networkConfiguration{}
|
|
| 72 |
- config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 73 |
- |
|
| 74 |
- if err := setupVerifyAndReconcile(config, inf); err == nil {
|
|
| 75 |
- t.Fatal("Address verification was expected to fail")
|
|
| 76 |
- } |
|
| 77 |
-} |
|
| 78 |
- |
|
| 79 |
-func TestSetupVerifyIPv6(t *testing.T) {
|
|
| 80 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 81 |
- |
|
| 82 |
- addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 83 |
- inf := setupVerifyTest(t) |
|
| 84 |
- config := &networkConfiguration{}
|
|
| 85 |
- config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 86 |
- config.EnableIPv6 = true |
|
| 87 |
- |
|
| 88 |
- if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
|
|
| 89 |
- t.Fatalf("Failed to assign IPv6 %s to interface: %v", bridgeIPv6, err)
|
|
| 90 |
- } |
|
| 91 |
- if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 92 |
- t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err)
|
|
| 93 |
- } |
|
| 94 |
- |
|
| 95 |
- if err := setupVerifyAndReconcile(config, inf); err != nil {
|
|
| 96 |
- t.Fatalf("Address verification failed: %v", err)
|
|
| 97 |
- } |
|
| 98 |
-} |
|
| 99 |
- |
|
| 100 |
-func TestSetupVerifyIPv6Missing(t *testing.T) {
|
|
| 101 |
- defer netnsutils.SetupTestOSContext(t)() |
|
| 102 |
- |
|
| 103 |
- addrv4 := net.IPv4(192, 168, 1, 1) |
|
| 104 |
- inf := setupVerifyTest(t) |
|
| 105 |
- config := &networkConfiguration{}
|
|
| 106 |
- config.AddressIPv4 = &net.IPNet{IP: addrv4, Mask: addrv4.DefaultMask()}
|
|
| 107 |
- config.EnableIPv6 = true |
|
| 108 |
- |
|
| 109 |
- if err := netlink.AddrAdd(inf.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
|
| 110 |
- t.Fatalf("Failed to assign IPv4 %s to interface: %v", config.AddressIPv4, err)
|
|
| 111 |
- } |
|
| 112 |
- |
|
| 113 |
- if err := setupVerifyAndReconcile(config, inf); err == nil {
|
|
| 114 |
- t.Fatal("Address verification was expected to fail")
|
|
| 115 |
- } |
|
| 116 |
-} |