func (c *Context) newBridgeScope(id uid.UID, name string, subnet *net.IPNet, gateway net.IP, dns []net.IP, pools []string) (newScope *Scope, err error) { defer trace.End(trace.Begin("")) bnPG, ok := c.config.PortGroups[c.config.BridgeNetwork] if !ok || bnPG == nil { return nil, fmt.Errorf("bridge network not set") } if ip.IsUnspecifiedSubnet(subnet) { // get the next available subnet from the default bridge pool var err error subnet, err = c.defaultBridgePool.NextIP4Net(c.defaultBridgeMask) if err != nil { return nil, err } } s, err := c.newScopeCommon(id, name, constants.BridgeScopeType, subnet, gateway, dns, pools, bnPG) if err != nil { return nil, err } // add the gateway address to the bridge interface if err = c.config.BridgeLink.AddrAdd(net.IPNet{IP: s.Gateway(), Mask: s.Subnet().Mask}); err != nil { if errno, ok := err.(syscall.Errno); !ok || errno != syscall.EEXIST { log.Warnf("failed to add gateway address %s to bridge interface: %s", s.Gateway(), err) } } return s, nil }
func (c *Context) newExternalScope(id uid.UID, name string, subnet *net.IPNet, gateway net.IP, dns []net.IP, ipam *IPAM) (*Scope, error) { // ipam cannot be specified without gateway and subnet if ipam != nil && len(ipam.pools) > 0 { if ip.IsUnspecifiedSubnet(subnet) || gateway.IsUnspecified() { return nil, fmt.Errorf("ipam cannot be specified without gateway and subnet for external network") } } if !ip.IsUnspecifiedSubnet(subnet) { // cannot overlap with the default bridge pool if c.defaultBridgePool.Network.Contains(subnet.IP) || c.defaultBridgePool.Network.Contains(highestIP4(subnet)) { return nil, fmt.Errorf("external network cannot overlap with default bridge network") } } n := Config.ContainerNetworks[name] if n == nil { return nil, fmt.Errorf("no network info for external scope %s", name) } return c.newScopeCommon(id, name, ExternalScopeType, subnet, gateway, dns, ipam, n.PortGroup) }
func (c *Context) newExternalScope(id uid.UID, name string, subnet *net.IPNet, gateway net.IP, dns []net.IP, pools []string) (*Scope, error) { defer trace.End(trace.Begin("")) // ipam cannot be specified without gateway and subnet if len(pools) > 0 { if ip.IsUnspecifiedSubnet(subnet) || gateway.IsUnspecified() { return nil, fmt.Errorf("ipam cannot be specified without gateway and subnet for external network") } } if !ip.IsUnspecifiedSubnet(subnet) { // cannot overlap with the default bridge pool if c.defaultBridgePool.Network.Contains(subnet.IP) || c.defaultBridgePool.Network.Contains(highestIP4(subnet)) { return nil, fmt.Errorf("external network cannot overlap with default bridge network") } } pg := c.config.PortGroups[name] if pg == nil { return nil, fmt.Errorf("no network info for external scope %s", name) } return c.newScopeCommon(id, name, constants.ExternalScopeType, subnet, gateway, dns, pools, pg) }
func reserveGateway(gateway net.IP, subnet *net.IPNet, spaces []*AddressSpace) (net.IP, error) { defer trace.End(trace.Begin("")) if ip.IsUnspecifiedSubnet(subnet) { return nil, fmt.Errorf("cannot reserve gateway for nil subnet") } if !ip.IsUnspecifiedIP(gateway) { // verify gateway is routable address if !ip.IsRoutableIP(gateway, subnet) { return nil, fmt.Errorf("gateway address %s is not routable on network %s", gateway, subnet) } // optionally reserve it in one of the pools for _, p := range spaces { if err := p.ReserveIP4(gateway); err == nil { break } } return gateway, nil } // gateway is not specified, pick one from the available pools if len(spaces) > 0 { var err error if gateway, err = spaces[0].ReserveNextIP4(); err != nil { return nil, err } if !ip.IsRoutableIP(gateway, subnet) { return nil, fmt.Errorf("gateway address %s is not routable on network %s", gateway, subnet) } return gateway, nil } return nil, fmt.Errorf("could not reserve gateway address for network %s", subnet) }
func (s *Scope) Refresh(h *exec.Handle) error { s.Lock() defer s.Unlock() if !s.isDynamic() { return nil } ne := h.ExecConfig.Networks[s.name] if ip.IsUnspecifiedSubnet(&ne.Network.Gateway) { return fmt.Errorf("updating container %s: gateway not present for scope %s", h.ExecConfig.ID, s.name) } gw, snet, err := net.ParseCIDR(ne.Network.Gateway.String()) if err != nil { return err } s.gateway = gw s.subnet = new(net.IPNet) *s.subnet = *snet return nil }
func (c *Context) addScope(s *Scope) error { defer trace.End(trace.Begin("")) if _, ok := c.scopes[s.name]; ok { return DuplicateResourceError{} } var err error var defaultPool bool var allzeros, allones net.IP var space *AddressSpace spaces := s.spaces subnet := s.subnet gateway := s.gateway // cleanup defer func() { if err == nil || space == nil || !defaultPool { return } for _, p := range spaces { // release DNS IPs for _, d := range s.dns { p.ReleaseIP4(d) } // release gateway if !ip.IsUnspecifiedIP(gateway) { p.ReleaseIP4(gateway) } // release all-ones and all-zeros addresses if !ip.IsUnspecifiedIP(allzeros) { p.ReleaseIP4(allzeros) } if !ip.IsUnspecifiedIP(allones) { p.ReleaseIP4(allones) } } c.defaultBridgePool.ReleaseIP4Range(space) }() // subnet may not be specified, e.g. for "external" networks if !ip.IsUnspecifiedSubnet(subnet) { // allocate the subnet space, defaultPool, err = c.reserveSubnet(subnet) if err != nil { return err } subnet = space.Network spaces, err = reservePools(space, spaces) if err != nil { return err } // reserve all-ones and all-zeros addresses, which are not routable and so // should not be handed out allones = ip.AllOnesAddr(subnet) allzeros = ip.AllZerosAddr(subnet) for _, p := range spaces { p.ReserveIP4(allones) p.ReserveIP4(allzeros) // reserve DNS IPs for _, d := range s.dns { if d.Equal(gateway) { continue // gateway will be reserved later } p.ReserveIP4(d) } } if gateway, err = reserveGateway(gateway, subnet, spaces); err != nil { return err } s.gateway = gateway s.spaces = spaces s.subnet = subnet } c.scopes[s.name] = s return nil }
func (v *Validator) network(ctx context.Context, input *data.Data, conf *config.VirtualContainerHostConfigSpec) { defer trace.End(trace.Begin("")) // External net // external network is default for appliance err := v.addNetworkHelper(ctx, conf, input.ExternalNetworkName, "external", "external", true) if err != nil { v.NoteIssue(fmt.Errorf("Error checking network for --external-network: %s", err)) v.suggestNetwork("--external-network", true) } // Bridge network should be different than all other networks v.checkNetworkConflict(input.BridgeNetworkName, input.ExternalNetworkName, "external") // Client net if input.ClientNetworkName == "" { input.ClientNetworkName = input.ExternalNetworkName } err = v.addNetworkHelper(ctx, conf, input.ClientNetworkName, "client", "client", false) if err != nil { v.NoteIssue(fmt.Errorf("Error checking network for --client-network: %s", err)) v.suggestNetwork("--client-network", true) } v.checkNetworkConflict(input.BridgeNetworkName, input.ClientNetworkName, "client") // Management net if input.ManagementNetworkName == "" { input.ManagementNetworkName = input.ClientNetworkName } err = v.addNetworkHelper(ctx, conf, input.ManagementNetworkName, "", "management", false) if err != nil { v.NoteIssue(fmt.Errorf("Error checking network for --management-network: %s", err)) v.suggestNetwork("--management-network", true) } v.checkNetworkConflict(input.BridgeNetworkName, input.ManagementNetworkName, "management") // Bridge net - // vCenter: must exist and must be a DPG // ESX: doesn't need to exist - we will create with default value // // for now we're hardcoded to "bridge" for the container host name conf.BridgeNetwork = "bridge" endpointMoref, err := v.dpgHelper(ctx, input.BridgeNetworkName) var bridgeID, netMoid string if err != nil { bridgeID = "" netMoid = "" } else { bridgeID = endpointMoref.String() netMoid = endpointMoref.String() } checkBridgeVDS := true if err != nil { if _, ok := err.(*find.NotFoundError); !ok || v.IsVC() { v.NoteIssue(fmt.Errorf("An existing distributed port group must be specified for bridge network on vCenter: %s", err)) v.suggestNetwork("--bridge-network", false) checkBridgeVDS = false // prevent duplicate error output } // this allows the dispatcher to create the network with corresponding name // if BridgeNetworkName doesn't already exist then we set the ContainerNetwork // ID to the name, but leaving the NetworkEndpoint moref as "" netMoid = input.BridgeNetworkName } bridgeNet := &executor.NetworkEndpoint{ Common: executor.Common{ Name: "bridge", ID: bridgeID, }, Static: &net.IPNet{IP: net.IPv4zero}, // static but managed externally Network: executor.ContainerNetwork{ Common: executor.Common{ Name: "bridge", ID: netMoid, }, }, } // we need to have the bridge network identified as an available container network conf.AddContainerNetwork(&bridgeNet.Network) // we also need to have the appliance attached to the bridge network to allow // port forwarding conf.AddNetwork(bridgeNet) err = v.checkVDSMembership(ctx, endpointMoref, input.BridgeNetworkName) if err != nil && checkBridgeVDS { v.NoteIssue(fmt.Errorf("Unable to check hosts in vDS for %q: %s", input.BridgeNetworkName, err)) } // add mapped networks (from --container-network) // these should be a distributed port groups in vCenter suggestedMapped := false // only suggest mapped nets once for name, net := range input.MappedNetworks { checkMappedVDS := true // "bridge" is reserved if name == "bridge" { v.NoteIssue(fmt.Errorf("Cannot use reserved name \"bridge\" for container network")) continue } gw := input.MappedNetworksGateways[name] pools := input.MappedNetworksIPRanges[name] dns := input.MappedNetworksDNS[name] if len(pools) != 0 && ip.IsUnspecifiedSubnet(&gw) { v.NoteIssue(fmt.Errorf("IP range specified without gateway for container network %q", name)) continue } if !ip.IsUnspecifiedSubnet(&gw) && !ip.IsRoutableIP(gw.IP, &gw) { v.NoteIssue(fmt.Errorf("Gateway %s is not a routable address", gw.IP)) continue } err = nil // verify ip ranges are within subnet, // and don't overlap with each other for i, r := range pools { if !gw.Contains(r.FirstIP) || !gw.Contains(r.LastIP) { err = fmt.Errorf("IP range %q is not in subnet %q", r, gw) break } for _, r2 := range pools[i+1:] { if r2.Overlaps(r) { err = fmt.Errorf("Overlapping ip ranges: %q %q", r2, r) break } } if err != nil { break } } if err != nil { v.NoteIssue(err) continue } moref, err := v.dpgHelper(ctx, net) if err != nil { v.NoteIssue(fmt.Errorf("Error adding container network %q: %s", name, err)) checkMappedVDS = false if !suggestedMapped { v.suggestNetwork("--container-network", true) suggestedMapped = true } } mappedNet := &executor.ContainerNetwork{ Common: executor.Common{ Name: name, ID: moref.String(), }, Gateway: gw, Nameservers: dns, Pools: pools, } if input.BridgeNetworkName == net { v.NoteIssue(errors.Errorf("the bridge network must not be shared with another network role - %q also mapped as container network %q", input.BridgeNetworkName, name)) } err = v.checkVDSMembership(ctx, moref, net) if err != nil && checkMappedVDS { v.NoteIssue(fmt.Errorf("Unable to check hosts in vDS for %q: %s", net, err)) } conf.AddContainerNetwork(mappedNet) } }
func (c *Context) newScopeCommon(id uid.UID, name, scopeType string, subnet *net.IPNet, gateway net.IP, dns []net.IP, ipam *IPAM, network object.NetworkReference) (*Scope, error) { var err error var space *AddressSpace var defaultPool bool var allzeros, allones net.IP // cleanup defer func() { if err == nil || space == nil || !defaultPool { return } for _, p := range ipam.spaces { // release DNS IPs for _, d := range dns { p.ReleaseIP4(d) } // release gateway if !ip.IsUnspecifiedIP(gateway) { p.ReleaseIP4(gateway) } // release all-ones and all-zeros addresses if !ip.IsUnspecifiedIP(allzeros) { p.ReleaseIP4(allzeros) } if !ip.IsUnspecifiedIP(allones) { p.ReleaseIP4(allones) } } c.defaultBridgePool.ReleaseIP4Range(space) }() // subnet may not be specified, e.g. for "external" networks if !ip.IsUnspecifiedSubnet(subnet) { // allocate the subnet space, defaultPool, err = c.reserveSubnet(subnet) if err != nil { return nil, err } subnet = space.Network ipam.spaces, err = reservePools(space, ipam) if err != nil { return nil, err } // reserve all-ones and all-zeros addresses, which are not routable and so // should not be handed out allones = ip.AllOnesAddr(subnet) allzeros = ip.AllZerosAddr(subnet) for _, p := range ipam.spaces { p.ReserveIP4(allones) p.ReserveIP4(allzeros) // reserve DNS IPs for _, d := range dns { if d.Equal(gateway) { continue // gateway will be reserved later } p.ReserveIP4(d) } } if gateway, err = reserveGateway(gateway, subnet, ipam); err != nil { return nil, err } } newScope := &Scope{ id: id, name: name, subnet: *subnet, gateway: gateway, ipam: ipam, containers: make(map[uid.UID]*Container), scopeType: scopeType, space: space, dns: dns, builtin: false, network: network, } c.scopes[name] = newScope return newScope, nil }
func TestMapExternalNetworks(t *testing.T) { ctx, err := NewContext(net.IPNet{IP: net.IPv4(172, 16, 0, 0), Mask: net.CIDRMask(12, 32)}, net.CIDRMask(16, 32)) if err != nil { t.Fatalf("NewContext() => (nil, %s), want (ctx, nil)", err) } // check if external networks were loaded for n, nn := range Config.ContainerNetworks { scopes, err := ctx.Scopes(&n) if err != nil || len(scopes) != 1 { t.Fatalf("external network %s was not loaded", n) } s := scopes[0] pools := s.IPAM().Pools() if !ip.IsUnspecifiedIP(nn.Gateway.IP) { subnet := &net.IPNet{IP: nn.Gateway.IP.Mask(nn.Gateway.Mask), Mask: nn.Gateway.Mask} if ip.IsUnspecifiedSubnet(s.Subnet()) || !s.Subnet().IP.Equal(subnet.IP) || !bytes.Equal(s.Subnet().Mask, subnet.Mask) { t.Fatalf("external network %s was loaded with wrong subnet, got: %s, want: %s", n, s.Subnet(), subnet) } if ip.IsUnspecifiedIP(s.Gateway()) || !s.Gateway().Equal(nn.Gateway.IP) { t.Fatalf("external network %s was loaded with wrong gateway, got: %s, want: %s", n, s.Gateway(), nn.Gateway.IP) } if len(nn.Pools) == 0 { // only one pool corresponding to the subnet if len(pools) != 1 || pools[0] != subnet.String() { t.Fatalf("external network %s was loaded with wrong pool, got: %+v, want %+v", n, pools, []*net.IPNet{subnet}) } } } for _, d := range nn.Nameservers { found := false for _, d2 := range s.DNS() { if d2.Equal(d) { found = true break } } if !found { t.Fatalf("external network %s was loaded with wrong nameservers, got: %+v, want: %+v", n, s.DNS(), nn.Nameservers) } } for _, p := range nn.Pools { found := false for _, p2 := range pools { if p2 == p.String() { found = true break } } if !found { t.Fatalf("external network %s was loaded with wrong pools, got: %+v, want: %+v", n, s.IPAM().Pools(), nn.Pools) } } } }