Skip to content

Commit

Permalink
Support NDP proxying on bridge networks
Browse files Browse the repository at this point in the history
- Proxying is enabled via the network label com.docker.network.bridge.ndp_proxy_interface=<interface>

fixes #1159

Signed-off-by: Zvi "CtrlZvi" Effron <[email protected]>
  • Loading branch information
CtrlZvi committed Aug 1, 2016
1 parent 9dd5990 commit cc9c97c
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
46 changes: 46 additions & 0 deletions drivers/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type networkConfiguration struct {
BridgeName string
EnableIPv6 bool
EnableIPMasquerade bool
NDPProxyInterface string
EnableICC bool
Mtu int
DefaultBindingIP net.IP
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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 {
Expand All @@ -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},

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions drivers/bridge/bridge_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 23 additions & 0 deletions drivers/bridge/bridge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -269,13 +270,15 @@ 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,
DefaultBridge: "true",
EnableICC: "true",
EnableIPMasquerade: "true",
DefaultBindingIP: bndIPs,
NDPProxyInterface: ndpPxyIface,
}

netOption := make(map[string]interface{})
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions drivers/bridge/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
31 changes: 31 additions & 0 deletions drivers/bridge/setup_ipv6.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}

0 comments on commit cc9c97c

Please sign in to comment.