func (d *Dispatcher) InspectVCH(vch *vm.VirtualMachine, conf *config.VirtualContainerHostConfigSpec) error { defer trace.End(trace.Begin(conf.Name)) state, err := vch.PowerState(d.ctx) if err != nil { log.Errorf("Failed to get VM power state, service might not be available at this moment.") } if state != types.VirtualMachinePowerStatePoweredOn { err = errors.Errorf("VCH is not powered on, state %s", state) log.Errorf("%s", err) return err } clientIP := conf.ExecutorConfig.Networks["client"].Assigned.IP externalIP := conf.ExecutorConfig.Networks["external"].Assigned.IP if ip.IsUnspecifiedIP(clientIP) { err = errors.Errorf("No client IP address assigned") log.Errorf("%s", err) return err } if ip.IsUnspecifiedIP(externalIP) { err = errors.Errorf("No external IP address assigned") log.Errorf("%s", err) return err } d.HostIP = clientIP.String() log.Debugf("IP address for client interface: %s", d.HostIP) if !conf.HostCertificate.IsNil() { d.VICAdminProto = "https" d.DockerPort = fmt.Sprintf("%d", opts.DefaultTLSHTTPPort) } else { d.VICAdminProto = "http" d.DockerPort = fmt.Sprintf("%d", opts.DefaultHTTPPort) } // try looking up preferred name, irrespective of CAs if cert, err := conf.HostCertificate.X509Certificate(); err == nil { name, _ := viableHostAddress([]net.IP{clientIP}, cert, conf.CertificateAuthorities) if name != "" { log.Debugf("Retrieved proposed name from host certificate: %q", name) log.Debugf("Assigning first name from set: %s", name) if name != d.HostIP { log.Infof("Using address from host certificate over allocated IP: %s", d.HostIP) // reassign d.HostIP = name } } else { log.Warnf("Unable to identify address acceptable to host certificate") } } else { log.Debugf("Failed to load host cert: %s", err) } d.ShowVCH(conf, "", "", "", "") return nil }
func (c *client) isCompletePacket(p *dhcp.Packet) bool { complete := !ip.IsUnspecifiedIP(p.YourIP()) && !ip.IsUnspecifiedIP(p.ServerIP()) if !complete { return false } for _, param := range c.params { switch dhcp4.OptionCode(param) { case dhcp4.OptionSubnetMask: ones, bits := p.SubnetMask().Size() if ones == 0 || bits == 0 { return false } case dhcp4.OptionRouter: if ip.IsUnspecifiedIP(p.Gateway()) { return false } case dhcp4.OptionDomainNameServer: if len(p.DNS()) == 0 { return false } } } if p.LeaseTime().Seconds() == 0 { return false } return true }
func (d *Dispatcher) InspectVCH(vch *vm.VirtualMachine, conf *config.VirtualContainerHostConfigSpec) error { defer trace.End(trace.Begin(conf.Name)) state, err := vch.PowerState(d.ctx) if err != nil { log.Errorf("Failed to get VM power state, service might not be avaialble at this moment.") } if state != types.VirtualMachinePowerStatePoweredOn { err = errors.Errorf("VCH is not powered on, state %s", state) log.Errorf("%s", err) return err } if ip.IsUnspecifiedIP(conf.ExecutorConfig.Networks["client"].Assigned.IP) { err = errors.Errorf("No client IP address assigned") log.Errorf("%s", err) return err } d.HostIP = conf.ExecutorConfig.Networks["client"].Assigned.IP.String() log.Debug("IP address for client interface: %s", d.HostIP) if !conf.HostCertificate.IsNil() { d.VICAdminProto = "https" d.DockerPort = fmt.Sprintf("%d", opts.DefaultTLSHTTPPort) } else { d.VICAdminProto = "http" d.DockerPort = fmt.Sprintf("%d", opts.DefaultHTTPPort) } d.ShowVCH(conf, "", "") return nil }
func updateDefaultRoute(t Netlink, link netlink.Link, endpoint *NetworkEndpoint) error { // Add routes if !endpoint.Network.Default || ip.IsUnspecifiedIP(endpoint.Network.Gateway.IP) { log.Debugf("not setting route for network: default=%v gateway=%s", endpoint.Network.Default, endpoint.Network.Gateway.IP) return nil } _, defaultNet, _ := net.ParseCIDR("0.0.0.0/0") // delete default route first if err := t.RouteDel(&netlink.Route{LinkIndex: link.Attrs().Index, Dst: defaultNet}); err != nil { if errno, ok := err.(syscall.Errno); !ok || errno != syscall.ESRCH { return fmt.Errorf("could not update default route: %s", err) } } log.Infof("Setting default gateway to %s", endpoint.Network.Gateway.IP) route := &netlink.Route{LinkIndex: link.Attrs().Index, Dst: defaultNet, Gw: endpoint.Network.Gateway.IP} if err := t.RouteAdd(route); err != nil { detail := fmt.Sprintf("failed to add gateway route for endpoint %s: %s", endpoint.Network.Name, err) return errors.New(detail) } log.Infof("updated default route to %s interface, gateway: %s", endpoint.Network.Name, endpoint.Network.Gateway.IP) return nil }
func toScopeConfig(scope *network.Scope) *models.ScopeConfig { subnet := "" if !ip.IsUnspecifiedIP(scope.Subnet().IP) { subnet = scope.Subnet().String() } gateway := "" if !scope.Gateway().IsUnspecified() { gateway = scope.Gateway().String() } id := scope.ID().String() sc := &models.ScopeConfig{ ID: &id, Name: scope.Name(), ScopeType: scope.Type(), IPAM: scope.IPAM().Pools(), Subnet: &subnet, Gateway: &gateway, } if len(sc.IPAM) == 0 && len(subnet) != 0 { // use subnet as pool sc.IPAM = []string{subnet} } eps := scope.Endpoints() sc.Endpoints = make([]*models.EndpointConfig, len(eps)) for i, e := range eps { sc.Endpoints[i] = toEndpointConfig(e) } return sc }
func isCompletePacket(p *dhcp.Packet) bool { complete := !ip.IsUnspecifiedIP(p.Gateway()) && !ip.IsUnspecifiedIP(p.YourIP()) && !ip.IsUnspecifiedIP(p.ServerIP()) if !complete { return false } ones, bits := p.SubnetMask().Size() if ones == 0 || bits == 0 { return false } if p.LeaseTime().Seconds() == 0 { return false } return true }
func newEndpoint(container *Container, scope *Scope, eip *net.IP, pciSlot *int32) *Endpoint { e := &Endpoint{ container: container, scope: scope, ip: net.IPv4(0, 0, 0, 0), static: false, ports: make(map[Port]interface{}), aliases: make(map[string][]alias), } if eip != nil && !ip.IsUnspecifiedIP(*eip) { e.ip = *eip e.static = true } return e }
func (t *BaseOperations) updateNameservers(endpoint *NetworkEndpoint) error { // Add nameservers // This is incredibly trivial for now - should be updated to a less messy approach if len(endpoint.Network.Nameservers) > 0 { Sys.ResolvConf.AddNameservers(endpoint.Network.Nameservers...) log.Infof("Added nameservers: %+v", endpoint.Network.Nameservers) } else if !ip.IsUnspecifiedIP(endpoint.Network.Gateway.IP) { Sys.ResolvConf.AddNameservers(endpoint.Network.Gateway.IP) log.Infof("Added nameserver: %s", endpoint.Network.Gateway.IP) } if err := Sys.ResolvConf.Save(); err != nil { return err } return nil }
func getDynamicIP(t Netlink, link netlink.Link, endpoint *NetworkEndpoint) (client.Client, error) { var ack *dhcp.Packet var err error // use dhcp to acquire address dc, err := client.NewClient(link.Attrs().Index, link.Attrs().HardwareAddr) if err != nil { return nil, err } params := []byte{byte(dhcp4.OptionSubnetMask)} if ip.IsUnspecifiedIP(endpoint.Network.Gateway.IP) { params = append(params, byte(dhcp4.OptionRouter)) } if len(endpoint.Network.Nameservers) == 0 { params = append(params, byte(dhcp4.OptionDomainNameServer)) } dc.SetParameterRequestList(params...) err = dc.Request() if err != nil { log.Errorf("error sending dhcp request: %s", err) return nil, err } ack = dc.LastAck() if ack.YourIP() == nil || ack.SubnetMask() == nil { err = fmt.Errorf("dhcp assigned nil ip or subnet mask") log.Error(err) return nil, err } log.Infof("DHCP response: IP=%s, SubnetMask=%s, Gateway=%s, DNS=%s, Lease Time=%s", ack.YourIP(), ack.SubnetMask(), ack.Gateway(), ack.DNS(), ack.LeaseTime()) defer func() { if err != nil && ack != nil { dc.Release() } }() return dc, nil }
func toEndpointConfig(e *network.Endpoint) *models.EndpointConfig { addr := "" if !ip.IsUnspecifiedIP(e.IP()) { addr = e.IP().String() } ports := e.Ports() ecports := make([]string, len(ports)) for i, p := range e.Ports() { ecports[i] = p.String() } return &models.EndpointConfig{ Address: addr, Container: e.ID().String(), ID: e.ID().String(), Name: e.Name(), Scope: e.Scope().Name(), Ports: ecports, } }
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 toScopeConfig(scope *network.Scope) *models.ScopeConfig { subnet := "" if !ip.IsUnspecifiedIP(scope.Subnet().IP) { subnet = scope.Subnet().String() } gateway := "" if !scope.Gateway().IsUnspecified() { gateway = scope.Gateway().String() } id := scope.ID().String() sc := &models.ScopeConfig{ ID: &id, Name: scope.Name(), ScopeType: scope.Type(), Subnet: &subnet, Gateway: &gateway, } var pools []string for _, p := range scope.Pools() { pools = append(pools, p.String()) } sc.IPAM = pools if len(sc.IPAM) == 0 { sc.IPAM = []string{subnet} } eps := scope.Endpoints() sc.Endpoints = make([]*models.EndpointConfig, len(eps)) for i, e := range eps { sc.Endpoints[i] = toEndpointConfig(e) } return sc }
func (s *Scope) reserveEndpointIP(e *Endpoint) error { if s.isDynamic() { return nil } // reserve an ip address var err error for _, p := range s.spaces { if !ip.IsUnspecifiedIP(e.ip) { if err = p.ReserveIP4(e.ip); err == nil { return nil } } else { var eip net.IP if eip, err = p.ReserveNextIP4(); err == nil { e.ip = eip return nil } } } return err }
// AddContainer add a container to the specified scope, optionally specifying an ip address // for the container in the scope func (c *Context) AddContainer(h *exec.Handle, options *AddContainerOptions) error { defer trace.End(trace.Begin("")) c.Lock() defer c.Unlock() if h == nil { return fmt.Errorf("handle is required") } var err error s, err := c.resolveScope(options.Scope) if err != nil { return err } if h.ExecConfig.Networks != nil { if _, ok := h.ExecConfig.Networks[s.Name()]; ok { // already part of this scope return nil } // check if container is already part of an "external" scope; // only one "external" scope per container is allowed if s.Type() == constants.ExternalScopeType { for name := range h.ExecConfig.Networks { sc, _ := c.resolveScope(name) if sc.Type() == constants.ExternalScopeType { return fmt.Errorf("container can only be added to at most one mapped network") } } } } // figure out if we need to add a new NIC // if there is already a NIC connected to a // bridge network and we are adding the container // to a bridge network, we just reuse that // NIC var pciSlot int32 if s.Type() == constants.BridgeScopeType { for _, ne := range h.ExecConfig.Networks { sc, err := c.resolveScope(ne.Network.Name) if err != nil { return err } if sc.Type() != constants.BridgeScopeType { continue } if ne.ID != "" { pciSlot = atoiOrZero(ne.ID) if pciSlot != 0 { break } } } } if pciSlot == 0 { d, err := addEthernetCard(h, s) if err != nil { return err } pciSlot = spec.VirtualDeviceSlotNumber(d) } if h.ExecConfig.Networks == nil { h.ExecConfig.Networks = make(map[string]*executor.NetworkEndpoint) } ne := &executor.NetworkEndpoint{ Common: executor.Common{ ID: strconv.Itoa(int(pciSlot)), // Name: this would cause NIC renaming if uncommented }, Network: executor.ContainerNetwork{ Common: executor.Common{ Name: s.Name(), }, Aliases: options.Aliases, Type: s.Type(), }, Ports: options.Ports, } pools := s.Pools() ne.Network.Pools = make([]ip.Range, len(pools)) for i, p := range pools { ne.Network.Pools[i] = *p } ne.Static = false if options.IP != nil && !ip.IsUnspecifiedIP(*options.IP) { ne.Static = true ne.IP = &net.IPNet{ IP: *options.IP, Mask: s.Subnet().Mask, } } h.ExecConfig.Networks[s.Name()] = ne return nil }
func (c *Context) bindContainer(h *exec.Handle) ([]*Endpoint, error) { con, err := c.container(h) if con != nil { return con.Endpoints(), nil // already bound } if _, ok := err.(ResourceNotFoundError); !ok { return nil, err } con = &Container{ id: uid.Parse(h.ExecConfig.ID), name: h.ExecConfig.Name, } defaultMarked := false aliases := make(map[string]*Container) var endpoints []*Endpoint for _, ne := range h.ExecConfig.Networks { var s *Scope s, ok := c.scopes[ne.Network.Name] if !ok { return nil, &ResourceNotFoundError{} } defer func() { if err == nil { return } s.RemoveContainer(con) }() var eip *net.IP if ne.Static { eip = &ne.IP.IP } else if !ip.IsUnspecifiedIP(ne.Assigned.IP) { // for VCH restart, we need to reserve // the IP of the running container // // this may be a DHCP assigned IP, however, the // addContainer call below will ignore reserving // an IP if the scope is "dynamic" eip = &ne.Assigned.IP } e := newEndpoint(con, s, eip, nil) e.static = ne.Static if err = s.AddContainer(con, e); err != nil { return nil, err } ports, _, err := nat.ParsePortSpecs(ne.Ports) if err != nil { return nil, err } for p := range ports { var port Port if port, err = ParsePort(string(p)); err != nil { return nil, err } if err = e.addPort(port); err != nil { return nil, err } } if !ip.IsUnspecifiedIP(e.IP()) { ne.IP = &net.IPNet{ IP: e.IP(), Mask: e.Scope().Subnet().Mask, } } ne.Network.Gateway = net.IPNet{IP: e.Gateway(), Mask: e.Subnet().Mask} ne.Network.Nameservers = make([]net.IP, len(s.dns)) copy(ne.Network.Nameservers, s.dns) // mark the external network as default if !defaultMarked && e.Scope().Type() == constants.ExternalScopeType { defaultMarked = true ne.Network.Default = true } // dns lookup aliases aliases[fmt.Sprintf("%s:%s", s.Name(), con.name)] = con aliases[fmt.Sprintf("%s:%s", s.Name(), con.id.Truncate())] = con // container specific aliases for _, a := range ne.Network.Aliases { log.Debugf("adding alias %s", a) l := strings.Split(a, ":") if len(l) != 2 { err = fmt.Errorf("Parsing network alias %s failed", a) return nil, err } who, what := l[0], l[1] if who == "" { who = con.name } if a, exists := e.addAlias(who, what); a != badAlias && !exists { whoc := con // if the alias is not for this container, then // find it in the container collection if who != con.name { whoc = c.containers[who] } // whoc may be nil here, which means that the aliased // container is not bound yet; this is OK, and will be // fixed up when "who" is bound if whoc != nil { aliases[a.scopedName()] = whoc } } } // fix up the aliases to this container // from other containers for _, e := range s.Endpoints() { if e.Container() == con { continue } for _, a := range e.getAliases(con.name) { aliases[a.scopedName()] = con } } endpoints = append(endpoints, e) } // verify all the aliases to be added do not conflict with // existing container keys for a := range aliases { if _, ok := c.containers[a]; ok { return nil, fmt.Errorf("duplicate alias %s for container %s", a, con.ID()) } } // FIXME: if there was no external network to mark as default, // then just pick the first network to mark as default if !defaultMarked { defaultMarked = true for _, ne := range h.ExecConfig.Networks { ne.Network.Default = true break } } // long id c.containers[con.id.String()] = con // short id c.containers[con.id.Truncate().String()] = con // name c.containers[con.name] = con // aliases for k, v := range aliases { log.Debugf("adding alias %s -> %s", k, v.Name()) c.containers[k] = v } return endpoints, 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 }
// ensureApplianceInitializes checks if the appliance component processes are launched correctly func (d *Dispatcher) ensureApplianceInitializes(conf *config.VirtualContainerHostConfigSpec) error { defer trace.End(trace.Begin("")) if d.appliance == nil { return errors.New("cannot validate appliance due to missing VM reference") } log.Infof("Waiting for IP information") d.waitForKey("guestinfo..init.networks|client.ip.IP") ctxerr := d.ctx.Err() if ctxerr == nil { log.Info("Waiting for major appliance components to launch") log.Debug("waiting for vicadmin to start") d.waitForKey("guestinfo..init.sessions|vicadmin.started") log.Debug("waiting for docker personality to start") d.waitForKey("guestinfo..init.sessions|docker-personality.started") log.Debug("waiting for port layer to start") d.waitForKey("guestinfo..init.sessions|port-layer.started") } // at this point either everything has succeeded or we're going into diagnostics, ignore error // as we're only using it for IP in the success case updateErr := d.applianceConfiguration(conf) // TODO: we should call to the general vic-machine inspect implementation here for more detail // but instead... if !ip.IsUnspecifiedIP(conf.ExecutorConfig.Networks["client"].Assigned.IP) { d.HostIP = conf.ExecutorConfig.Networks["client"].Assigned.IP.String() log.Debug("Obtained IP address for client interface: %q", d.HostIP) return nil } // it's possible we timed out... get updated info having adjusted context to allow it // keeping it short ctxerr = d.ctx.Err() d.ctx, _ = context.WithTimeout(context.Background(), 10*time.Second) err := d.applianceConfiguration(conf) if err != nil { return fmt.Errorf("unable to retrieve updated configuration from appliance for diagnostics: %s", err) } if ctxerr == context.DeadlineExceeded { log.Info("Failed to retrieve IP for client interface") log.Info(" State of all interfaces:") // if we timed out, then report status - if cancelled this doesn't need reporting for name, net := range conf.ExecutorConfig.Networks { addr := net.Assigned.String() if ip.IsUnspecifiedIP(net.Assigned.IP) { addr = "waiting for IP" } log.Infof(" %q IP: %q", name, addr) } // if we timed out, then report status - if cancelled this doesn't need reporting log.Info(" State of components:") for name, session := range conf.ExecutorConfig.Sessions { status := "waiting to launch" if session.Started == "true" { status = "started successfully" } else if session.Started != "" { status = session.Started } log.Infof(" %q: %q", name, status) } return errors.New("timed out waiting for IP address information from appliance") } return fmt.Errorf("could not obtain IP address information from appliance: %s", updateErr) }
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 apply(nl Netlink, t *BaseOperations, endpoint *NetworkEndpoint) error { if endpoint.applied { log.Infof("skipping applying config for network %s as it has been applied already", endpoint.Network.Name) return nil // already applied } // Locate interface slot, err := strconv.Atoi(endpoint.ID) if err != nil { return fmt.Errorf("endpoint ID must be a base10 numeric pci slot identifier: %s", err) } defer func() { if err == nil { log.Infof("successfully applied config for network %s", endpoint.Network.Name) endpoint.applied = true } }() var link netlink.Link link, err = nl.LinkBySlot(int32(slot)) if err != nil { return fmt.Errorf("unable to acquire reference to link %s: %s", endpoint.ID, err) } // rename the link if needed link, err = renameLink(nl, link, int32(slot), endpoint) if err != nil { return fmt.Errorf("unable to reacquire link %s after rename pass: %s", endpoint.ID, err) } var dc client.Client defer func() { if err != nil && dc != nil { dc.Release() } }() var newIP *net.IPNet if endpoint.IsDynamic() && endpoint.DHCP == nil { if e, ok := t.dynEndpoints[endpoint.ID]; ok { // endpoint shares NIC, copy over DHCP endpoint.DHCP = e[0].DHCP } } log.Debugf("%+v", endpoint) if endpoint.IsDynamic() { if endpoint.DHCP == nil { dc, err = getDynamicIP(nl, link, endpoint) if err != nil { return err } ack := dc.LastAck() endpoint.DHCP = &DHCPInfo{ Assigned: net.IPNet{IP: ack.YourIP(), Mask: ack.SubnetMask()}, Nameservers: ack.DNS(), Gateway: net.IPNet{IP: ack.Gateway(), Mask: ack.SubnetMask()}, } } newIP = &endpoint.DHCP.Assigned } else { newIP = endpoint.IP if newIP.IP.Equal(net.IPv4zero) { // managed externally return nil } } var old *net.IPNet if !ip.IsUnspecifiedIP(endpoint.Assigned.IP) { old = &endpoint.Assigned } if err = linkAddrUpdate(old, newIP, nl, link); err != nil { return err } updateEndpoint(newIP, endpoint) if err = updateDefaultRoute(nl, link, endpoint); err != nil { return err } if err = t.updateHosts(endpoint); err != nil { return err } Sys.ResolvConf.RemoveNameservers(endpoint.Network.Nameservers...) if err = t.updateNameservers(endpoint); err != nil { return err } if endpoint.IsDynamic() { eps := t.dynEndpoints[endpoint.ID] found := false for _, e := range eps { if e == endpoint { found = true break } } if !found { eps = append(eps, endpoint) t.dynEndpoints[endpoint.ID] = eps } } // add renew/release loop if necessary if dc != nil { if _, ok := t.dhcpLoops[endpoint.ID]; !ok { stop := make(chan struct{}) if err != nil { log.Errorf("could not make DHCP client id for link %s: %s", link.Attrs().Name, err) } else { t.dhcpLoops[endpoint.ID] = stop go t.dhcpLoop(stop, endpoint, dc) } } } return nil }
func apply(nl Netlink, t *BaseOperations, endpoint *NetworkEndpoint) error { // Locate interface slot, err := strconv.Atoi(endpoint.ID) if err != nil { detail := fmt.Sprintf("endpoint ID must be a base10 numeric pci slot identifier: %s", err) return errors.New(detail) } link, err := nl.LinkBySlot(int32(slot)) if err != nil { detail := fmt.Sprintf("unable to acquire reference to link %s: %s", endpoint.ID, err) return errors.New(detail) } // rename the link if needed link, err = renameLink(nl, link, int32(slot), endpoint) if err != nil { detail := fmt.Sprintf("unable to reacquire link %s after rename pass: %s", endpoint.ID, err) return errors.New(detail) } var ack *dhcp.Packet defer func() { if err != nil && ack != nil { t.dhcpClient.Release(ack) } }() var newIP *net.IPNet if endpoint.IsDynamic() && endpoint.DHCP == nil { if e, ok := t.dynEndpoints[endpoint.ID]; ok { // endpoint shares NIC, copy over DHCP endpoint.DHCP = e[0].DHCP } } log.Debugf("%+v", endpoint) if endpoint.IsDynamic() { if endpoint.DHCP == nil { ack, err = getDynamicIP(nl, link, t.dhcpClient) if err != nil { return err } endpoint.DHCP = &DHCPInfo{ Assigned: net.IPNet{IP: ack.YourIP(), Mask: ack.SubnetMask()}, Nameservers: ack.DNS(), Gateway: net.IPNet{IP: ack.Gateway(), Mask: ack.SubnetMask()}, } } newIP = &endpoint.DHCP.Assigned } else { newIP = endpoint.Static if newIP.IP.IsUnspecified() { // managed externally return nil } } var old *net.IPNet if !ip.IsUnspecifiedIP(endpoint.Assigned.IP) { old = &endpoint.Assigned } if err = linkAddrUpdate(old, newIP, nl, link); err != nil { return err } updateEndpoint(newIP, endpoint) if err = updateDefaultRoute(nl, link, endpoint); err != nil { return err } if err = t.updateHosts(endpoint); err != nil { return err } t.resolvConf.RemoveNameservers(endpoint.Network.Nameservers...) if err = t.updateNameservers(endpoint); err != nil { return err } if endpoint.IsDynamic() { eps := t.dynEndpoints[endpoint.ID] found := false for _, e := range eps { if e == endpoint { found = true break } } if !found { eps = append(eps, endpoint) t.dynEndpoints[endpoint.ID] = eps } } // add renew/release loop if necessary if ack != nil { if _, ok := t.dhcpLoops[endpoint.ID]; !ok { stop := make(chan bool) id, err := client.NewID(link.Attrs().Index, link.Attrs().HardwareAddr) if err != nil { log.Errorf("could not make DHCP client id for link %s: %s", link.Attrs().Name, err) } else { go t.dhcpLoop(stop, endpoint, ack, id) t.dhcpLoops[endpoint.ID] = stop } } } return nil }
func (e *NetworkEndpoint) IsDynamic() bool { return !e.Static && (e.IP == nil || ip.IsUnspecifiedIP(e.IP.IP)) }
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) } } } }