Pārlūkot izejas kodu

Merge pull request #1228 from aboch/ll

Allow to program Endpoint link-local addresses
Madhu Venugopal 9 gadi atpakaļ
vecāks
revīzija
4c1ed2010b

+ 15 - 1
libnetwork/endpoint.go

@@ -852,11 +852,25 @@ func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
 	}
 }
 
+var (
+	linkLocalMask     = net.CIDRMask(16, 32)
+	linkLocalMaskIPv6 = net.CIDRMask(64, 128)
+)
+
 // CreateOptionIpam function returns an option setter for the ipam configuration for this endpoint
-func CreateOptionIpam(ipV4, ipV6 net.IP, ipamOptions map[string]string) EndpointOption {
+func CreateOptionIpam(ipV4, ipV6 net.IP, llIPs []net.IP, ipamOptions map[string]string) EndpointOption {
 	return func(ep *endpoint) {
 		ep.prefAddress = ipV4
 		ep.prefAddressV6 = ipV6
+		if len(llIPs) != 0 {
+			for _, ip := range llIPs {
+				nw := &net.IPNet{IP: ip, Mask: linkLocalMask}
+				if ip.To4() == nil {
+					nw.Mask = linkLocalMaskIPv6
+				}
+				ep.iface.llAddrs = append(ep.iface.llAddrs, nw)
+			}
+		}
 		ep.ipamOptions = ipamOptions
 	}
 }

+ 32 - 1
libnetwork/endpoint_info.go

@@ -43,12 +43,16 @@ type InterfaceInfo interface {
 
 	// AddressIPv6 returns the IPv6 address assigned to the endpoint.
 	AddressIPv6() *net.IPNet
+
+	// LinkLocalAddresses returns the list of link-local (IPv4/IPv6) addresses assigned to the endpoint.
+	LinkLocalAddresses() []*net.IPNet
 }
 
 type endpointInterface struct {
 	mac       net.HardwareAddr
 	addr      *net.IPNet
 	addrv6    *net.IPNet
+	llAddrs   []*net.IPNet
 	srcName   string
 	dstPrefix string
 	routes    []*net.IPNet
@@ -67,6 +71,13 @@ func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
 	if epi.addrv6 != nil {
 		epMap["addrv6"] = epi.addrv6.String()
 	}
+	if len(epi.llAddrs) != 0 {
+		list := make([]string, 0, len(epi.llAddrs))
+		for _, ll := range epi.llAddrs {
+			list = append(list, ll.String())
+		}
+		epMap["llAddrs"] = list
+	}
 	epMap["srcName"] = epi.srcName
 	epMap["dstPrefix"] = epi.dstPrefix
 	var routes []string
@@ -102,7 +113,17 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) error {
 			return types.InternalErrorf("failed to decode endpoint interface ipv6 address after json unmarshal: %v", err)
 		}
 	}
-
+	if v, ok := epMap["llAddrs"]; ok {
+		list := v.([]string)
+		epi.llAddrs = make([]*net.IPNet, 0, len(list))
+		for _, llS := range list {
+			ll, err := types.ParseCIDR(llS)
+			if err != nil {
+				return types.InternalErrorf("failed to decode endpoint interface link-local address (%s) after json unmarshal: %v", llS, err)
+			}
+			epi.llAddrs = append(epi.llAddrs, ll)
+		}
+	}
 	epi.srcName = epMap["srcName"].(string)
 	epi.dstPrefix = epMap["dstPrefix"].(string)
 
@@ -131,6 +152,12 @@ func (epi *endpointInterface) CopyTo(dstEpi *endpointInterface) error {
 	dstEpi.dstPrefix = epi.dstPrefix
 	dstEpi.v4PoolID = epi.v4PoolID
 	dstEpi.v6PoolID = epi.v6PoolID
+	if len(epi.llAddrs) != 0 {
+		dstEpi.llAddrs = make([]*net.IPNet, 0, len(epi.llAddrs))
+		for _, ll := range epi.llAddrs {
+			dstEpi.llAddrs = append(dstEpi.llAddrs, ll)
+		}
+	}
 
 	for _, route := range epi.routes {
 		dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route))
@@ -266,6 +293,10 @@ func (epi *endpointInterface) AddressIPv6() *net.IPNet {
 	return types.GetIPNetCopy(epi.addrv6)
 }
 
+func (epi *endpointInterface) LinkLocalAddresses() []*net.IPNet {
+	return epi.llAddrs
+}
+
 func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error {
 	epi.srcName = srcName
 	epi.dstPrefix = dstPrefix

+ 6 - 0
libnetwork/network.go

@@ -837,6 +837,12 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
 
 	ep.processOptions(options...)
 
+	for _, llIPNet := range ep.Iface().LinkLocalAddresses() {
+		if !llIPNet.IP.IsLinkLocalUnicast() {
+			return nil, types.BadRequestErrorf("invalid link local IP address: %v", llIPNet.IP)
+		}
+	}
+
 	if opt, ok := ep.generic[netlabel.MacAddress]; ok {
 		if mac, ok := opt.(net.HardwareAddr); ok {
 			ep.iface.mac = mac

+ 19 - 0
libnetwork/osl/interface_linux.go

@@ -24,6 +24,7 @@ type nwIface struct {
 	mac         net.HardwareAddr
 	address     *net.IPNet
 	addressIPv6 *net.IPNet
+	llAddrs     []*net.IPNet
 	routes      []*net.IPNet
 	bridge      bool
 	ns          *networkNamespace
@@ -86,6 +87,13 @@ func (i *nwIface) AddressIPv6() *net.IPNet {
 	return types.GetIPNetCopy(i.addressIPv6)
 }
 
+func (i *nwIface) LinkLocalAddresses() []*net.IPNet {
+	i.Lock()
+	defer i.Unlock()
+
+	return i.llAddrs
+}
+
 func (i *nwIface) Routes() []*net.IPNet {
 	i.Lock()
 	defer i.Unlock()
@@ -316,6 +324,7 @@ func configureInterface(iface netlink.Link, i *nwIface) error {
 		{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %v", ifaceName, i.Address())},
 		{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %v", ifaceName, i.AddressIPv6())},
 		{setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())},
+		{setInterfaceLinkLocalIPs, fmt.Sprintf("error setting interface %q link local IPs to %v", ifaceName, i.LinkLocalAddresses())},
 	}
 
 	for _, config := range ifaceConfigurators {
@@ -359,6 +368,16 @@ func setInterfaceIPv6(iface netlink.Link, i *nwIface) error {
 	return netlink.AddrAdd(iface, ipAddr)
 }
 
+func setInterfaceLinkLocalIPs(iface netlink.Link, i *nwIface) error {
+	for _, llIP := range i.LinkLocalAddresses() {
+		ipAddr := &netlink.Addr{IPNet: llIP}
+		if err := netlink.AddrAdd(iface, ipAddr); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func setInterfaceName(iface netlink.Link, i *nwIface) error {
 	return netlink.LinkSetName(iface, i.DstName())
 }

+ 6 - 0
libnetwork/osl/options_linux.go

@@ -60,6 +60,12 @@ func (n *networkNamespace) AddressIPv6(addr *net.IPNet) IfaceOption {
 	}
 }
 
+func (n *networkNamespace) LinkLocalAddresses(list []*net.IPNet) IfaceOption {
+	return func(i *nwIface) {
+		i.llAddrs = list
+	}
+}
+
 func (n *networkNamespace) Routes(routes []*net.IPNet) IfaceOption {
 	return func(i *nwIface) {
 		i.routes = routes

+ 6 - 0
libnetwork/osl/sandbox.go

@@ -85,6 +85,9 @@ type IfaceOptionSetter interface {
 	// Address returns an option setter to set IPv6 address.
 	AddressIPv6(*net.IPNet) IfaceOption
 
+	// LinkLocalAddresses returns an option setter to set the link-local IP addresses.
+	LinkLocalAddresses([]*net.IPNet) IfaceOption
+
 	// Master returns an option setter to set the master interface if any for this
 	// interface. The master interface name should refer to the srcname of a
 	// previously added interface of type bridge.
@@ -138,6 +141,9 @@ type Interface interface {
 	// IPv6 address for the interface.
 	AddressIPv6() *net.IPNet
 
+	// LinkLocalAddresses returns the link-local IP addresses assigned to the interface.
+	LinkLocalAddresses() []*net.IPNet
+
 	// IP routes for the interface.
 	Routes() []*net.IPNet
 

+ 3 - 0
libnetwork/sandbox.go

@@ -722,6 +722,9 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
 		if i.addrv6 != nil && i.addrv6.IP.To16() != nil {
 			ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6))
 		}
+		if len(i.llAddrs) != 0 {
+			ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().LinkLocalAddresses(i.llAddrs))
+		}
 		if i.mac != nil {
 			ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().MacAddress(i.mac))
 		}