diff --git a/drivers/bridge/bridge.go b/drivers/bridge/bridge.go index d2b1d24829..b67c5cbe85 100644 --- a/drivers/bridge/bridge.go +++ b/drivers/bridge/bridge.go @@ -58,6 +58,7 @@ type networkConfiguration struct { BridgeName string EnableIPv6 bool EnableIPMasquerade bool + NDPProxyInterface string EnableICC bool Mtu int DefaultBindingIP net.IP @@ -215,6 +216,8 @@ func (c *networkConfiguration) fromLabels(labels map[string]string) error { if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil { return parseErr(label, value, err.Error()) } + case NDPProxyInterface: + c.NDPProxyInterface = value case EnableICC: if c.EnableICC, err = strconv.ParseBool(value); err != nil { return parseErr(label, value, err.Error()) @@ -680,6 +683,7 @@ func (d *driver) createNetwork(config *networkConfiguration) error { bridgeSetup.queueStep(setupBridgeIPv4) enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil + enableNDPProxying := config.NDPProxyInterface != "" && config.AddressIPv6 != nil // Conditionally queue setup steps depending on configuration values. for _, step := range []struct { @@ -699,6 +703,9 @@ func (d *driver) createNetwork(config *networkConfiguration) error { // Enable IPv6 Forwarding {enableIPv6Forwarding, setupIPv6Forwarding}, + // Enable NDP Proxying + {enableNDPProxying, setupNDPProxying}, + // Setup Loopback Adresses Routing {!d.config.EnableUserlandProxy, setupLoopbackAdressesRouting}, @@ -1019,6 +1026,27 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } } + // Add a neighbor proxy if using NDP proxying + if config.NDPProxyInterface != "" && config.EnableIPv6 { + link, err := d.nlh.LinkByName(config.NDPProxyInterface) + if err != nil { + return err + } + neighbor := netlink.Neigh{ + LinkIndex: link.Attrs().Index, + Family: netlink.FAMILY_V6, + State: netlink.NUD_PERMANENT, + Type: netlink.NDA_UNSPEC, + Flags: netlink.NTF_PROXY, + IP: endpoint.addrv6.IP, + HardwareAddr: endpoint.macAddress, + } + if err := d.nlh.NeighAdd(&neighbor); err != nil { + logrus.Warnf("could not add the neighbor proxy: %v", err) + return err + } + } + if err = d.storeUpdate(endpoint); err != nil { return fmt.Errorf("failed to save bridge endpoint %s to store: %v", endpoint.id[0:7], err) } @@ -1077,6 +1105,24 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } }() + // Try removal of neighbor proxy. Discard error: it is a best effort. + // Also make sure defer does not see this error either. + if n.config.NDPProxyInterface != "" && n.config.EnableIPv6 { + link, err := d.nlh.LinkByName(n.config.NDPProxyInterface) + if err == nil { + neighbor := netlink.Neigh{ + LinkIndex: link.Attrs().Index, + Family: netlink.FAMILY_V6, + State: netlink.NUD_PERMANENT, + Type: netlink.NDA_UNSPEC, + Flags: netlink.NTF_PROXY, + IP: ep.addrv6.IP, + HardwareAddr: ep.macAddress, + } + d.nlh.NeighDel(&neighbor) + } + } + // Try removal of link. Discard error: it is a best effort. // Also make sure defer does not see this error either. if link, err := d.nlh.LinkByName(ep.srcName); err == nil { diff --git a/drivers/bridge/bridge_store.go b/drivers/bridge/bridge_store.go index 41c54b00f1..7a1fb46845 100644 --- a/drivers/bridge/bridge_store.go +++ b/drivers/bridge/bridge_store.go @@ -136,6 +136,7 @@ func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) { nMap["BridgeName"] = ncfg.BridgeName nMap["EnableIPv6"] = ncfg.EnableIPv6 nMap["EnableIPMasquerade"] = ncfg.EnableIPMasquerade + nMap["NDPProxyInterface"] = ncfg.NDPProxyInterface nMap["EnableICC"] = ncfg.EnableICC nMap["Mtu"] = ncfg.Mtu nMap["Internal"] = ncfg.Internal @@ -185,6 +186,9 @@ func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error { ncfg.BridgeName = nMap["BridgeName"].(string) ncfg.EnableIPv6 = nMap["EnableIPv6"].(bool) ncfg.EnableIPMasquerade = nMap["EnableIPMasquerade"].(bool) + if v, ok := nMap["NDPProxyInterface"]; ok { + ncfg.NDPProxyInterface = v.(string) + } ncfg.EnableICC = nMap["EnableICC"].(bool) ncfg.Mtu = int(nMap["Mtu"].(float64)) if v, ok := nMap["Internal"]; ok { diff --git a/drivers/bridge/bridge_test.go b/drivers/bridge/bridge_test.go index 6c53b162c9..17fd7df681 100644 --- a/drivers/bridge/bridge_test.go +++ b/drivers/bridge/bridge_test.go @@ -16,6 +16,7 @@ import ( "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/testutils" "github.com/docker/libnetwork/types" + "github.com/vishvananda/netlink" ) func init() { @@ -269,6 +270,7 @@ func TestCreateFullOptionsLabels(t *testing.T) { gwV6s := "2001:db8:2600:2700:2800::25/80" nwV6, _ := types.ParseCIDR(nwV6s) gwV6, _ := types.ParseCIDR(gwV6s) + ndpPxyIface := "lo" labels := map[string]string{ BridgeName: DefaultBridgeName, @@ -276,6 +278,7 @@ func TestCreateFullOptionsLabels(t *testing.T) { EnableICC: "true", EnableIPMasquerade: "true", DefaultBindingIP: bndIPs, + NDPProxyInterface: ndpPxyIface, } netOption := make(map[string]interface{}) @@ -318,6 +321,10 @@ func TestCreateFullOptionsLabels(t *testing.T) { t.Fatalf("incongruent EnableIPMasquerade in bridge network") } + if nw.config.NDPProxyInterface != ndpPxyIface { + t.Fatalf("incongruend NDPProxyInterface in bridge network") + } + bndIP := net.ParseIP(bndIPs) if !bndIP.Equal(nw.config.DefaultBindingIP) { t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP) @@ -347,6 +354,22 @@ func TestCreateFullOptionsLabels(t *testing.T) { if te.Interface().AddressIPv6().IP.String() != "2001:db8:2600:2700:2800:aabb:ccdd:eeff" { t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP) } + + // Check that the neighbor proxy was created by trying to delete it, + // because netlink.NeighList currently can't list proxies. + link, _ := d.nlh.LinkByName(ndpPxyIface) + err = d.nlh.NeighDel(&netlink.Neigh{ + LinkIndex: link.Attrs().Index, + Family: netlink.FAMILY_V6, + State: netlink.NUD_PERMANENT, + Type: netlink.NDA_UNSPEC, + Flags: netlink.NTF_PROXY, + IP: te.Interface().AddressIPv6().IP, + HardwareAddr: te.Interface().MacAddress(), + }) + if err != nil { + t.Fatalf("Cannot delete neighbor proxy, suggesting it wasn't created: %v", err) + } } func TestCreate(t *testing.T) { diff --git a/drivers/bridge/labels.go b/drivers/bridge/labels.go index 7447bd3f93..39bdd56d45 100644 --- a/drivers/bridge/labels.go +++ b/drivers/bridge/labels.go @@ -7,6 +7,9 @@ const ( // EnableIPMasquerade label for bridge driver EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade" + // NDPProxyInterface label for bridge driver + NDPProxyInterface = "com.docker.network.bridge.ndp_proxy_interface" + // EnableICC label EnableICC = "com.docker.network.bridge.enable_icc" diff --git a/drivers/bridge/setup_ipv6.go b/drivers/bridge/setup_ipv6.go index ee3d753ac1..e92bf1cd24 100644 --- a/drivers/bridge/setup_ipv6.go +++ b/drivers/bridge/setup_ipv6.go @@ -18,6 +18,9 @@ const ( ipv6ForwardConfPerm = 0644 ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding" ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding" + ndpProxyConfPerm = 0644 + ndpProxyConfDefault = "/proc/sys/net/ipv6/conf/default/proxy_ndp" + ndpProxyConfAll = "/proc/sys/net/ipv6/conf/all/proxy_ndp" ) func init() { @@ -117,3 +120,31 @@ func setupIPv6Forwarding(config *networkConfiguration, i *bridgeInterface) error return nil } + +func setupNDPProxying(config *networkConfiguration, i *bridgeInterface) error { + // Get current NDP default proxying setup + ndpProxyDataDefault, err := ioutil.ReadFile(ndpProxyConfDefault) + if err != nil { + return fmt.Errorf("Cannot read NDP default proxying setup: %v", err) + } + // Enable NDP default proxying only if it is not already enabled + if ndpProxyDataDefault[0] != '1' { + if err := ioutil.WriteFile(ndpProxyConfDefault, []byte{'1', '\n'}, ndpProxyConfPerm); err != nil { + logrus.Warnf("Unable to enable NDP default proxying: %v", err) + } + } + + // Get current NDP all proxying setup + ndpProxyDataAll, err := ioutil.ReadFile(ndpProxyConfAll) + if err != nil { + return fmt.Errorf("Cannot read NDP all proxying setup: %v", err) + } + // Enable NDP all proxying only if it is not already enabled + if ndpProxyDataAll[0] != '1' { + if err := ioutil.WriteFile(ndpProxyConfAll, []byte{'1', '\n'}, ndpProxyConfPerm); err != nil { + logrus.Warnf("Unable to enable NDP all proxying: %v", err) + } + } + + return nil +}