Skip to content

Commit b026e38

Browse files
committed
Support NDP proxying on bridge networks
- 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]>
1 parent a540b41 commit b026e38

File tree

5 files changed

+105
-0
lines changed

5 files changed

+105
-0
lines changed

Diff for: drivers/bridge/bridge.go

+46
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type networkConfiguration struct {
5858
BridgeName string
5959
EnableIPv6 bool
6060
EnableIPMasquerade bool
61+
NDPProxyInterface string
6162
EnableICC bool
6263
Mtu int
6364
DefaultBindingIP net.IP
@@ -215,6 +216,8 @@ func (c *networkConfiguration) fromLabels(labels map[string]string) error {
215216
if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil {
216217
return parseErr(label, value, err.Error())
217218
}
219+
case NDPProxyInterface:
220+
c.NDPProxyInterface = value
218221
case EnableICC:
219222
if c.EnableICC, err = strconv.ParseBool(value); err != nil {
220223
return parseErr(label, value, err.Error())
@@ -680,6 +683,7 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
680683
bridgeSetup.queueStep(setupBridgeIPv4)
681684

682685
enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil
686+
enableNDPProxying := config.NDPProxyInterface != "" && config.AddressIPv6 != nil
683687

684688
// Conditionally queue setup steps depending on configuration values.
685689
for _, step := range []struct {
@@ -699,6 +703,9 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
699703
// Enable IPv6 Forwarding
700704
{enableIPv6Forwarding, setupIPv6Forwarding},
701705

706+
// Enable NDP Proxying
707+
{enableNDPProxying, setupNDPProxying},
708+
702709
// Setup Loopback Adresses Routing
703710
{!d.config.EnableUserlandProxy, setupLoopbackAdressesRouting},
704711

@@ -1019,6 +1026,27 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
10191026
}
10201027
}
10211028

1029+
// Add a neighbor proxy if using NDP proxying
1030+
if config.NDPProxyInterface != "" && config.EnableIPv6 {
1031+
link, err := d.nlh.LinkByName(config.NDPProxyInterface)
1032+
if err != nil {
1033+
return err
1034+
}
1035+
neighbor := netlink.Neigh{
1036+
LinkIndex: link.Attrs().Index,
1037+
Family: netlink.FAMILY_V6,
1038+
State: netlink.NUD_PERMANENT,
1039+
Type: netlink.NDA_UNSPEC,
1040+
Flags: netlink.NTF_PROXY,
1041+
IP: endpoint.addrv6.IP,
1042+
HardwareAddr: endpoint.macAddress,
1043+
}
1044+
if err := d.nlh.NeighAdd(&neighbor); err != nil {
1045+
logrus.Warnf("could not add the neighbor proxy: %v", err)
1046+
return err
1047+
}
1048+
}
1049+
10221050
if err = d.storeUpdate(endpoint); err != nil {
10231051
return fmt.Errorf("failed to save bridge endpoint %s to store: %v", endpoint.id[0:7], err)
10241052
}
@@ -1077,6 +1105,24 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
10771105
}
10781106
}()
10791107

1108+
// Try removal of neighbor proxy. Discard error: it is a best effort.
1109+
// Also make sure defer does not see this error either.
1110+
if n.config.NDPProxyInterface != "" && n.config.EnableIPv6 {
1111+
link, err := d.nlh.LinkByName(n.config.NDPProxyInterface)
1112+
if err == nil {
1113+
neighbor := netlink.Neigh{
1114+
LinkIndex: link.Attrs().Index,
1115+
Family: netlink.FAMILY_V6,
1116+
State: netlink.NUD_PERMANENT,
1117+
Type: netlink.NDA_UNSPEC,
1118+
Flags: netlink.NTF_PROXY,
1119+
IP: ep.addrv6.IP,
1120+
HardwareAddr: ep.macAddress,
1121+
}
1122+
d.nlh.NeighDel(&neighbor)
1123+
}
1124+
}
1125+
10801126
// Try removal of link. Discard error: it is a best effort.
10811127
// Also make sure defer does not see this error either.
10821128
if link, err := d.nlh.LinkByName(ep.srcName); err == nil {

Diff for: drivers/bridge/bridge_store.go

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) {
136136
nMap["BridgeName"] = ncfg.BridgeName
137137
nMap["EnableIPv6"] = ncfg.EnableIPv6
138138
nMap["EnableIPMasquerade"] = ncfg.EnableIPMasquerade
139+
nMap["NDPProxyInterface"] = ncfg.NDPProxyInterface
139140
nMap["EnableICC"] = ncfg.EnableICC
140141
nMap["Mtu"] = ncfg.Mtu
141142
nMap["Internal"] = ncfg.Internal
@@ -185,6 +186,7 @@ func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error {
185186
ncfg.BridgeName = nMap["BridgeName"].(string)
186187
ncfg.EnableIPv6 = nMap["EnableIPv6"].(bool)
187188
ncfg.EnableIPMasquerade = nMap["EnableIPMasquerade"].(bool)
189+
ncfg.NDPProxyInterface = nMap["NDPProxyInterface"].(string)
188190
ncfg.EnableICC = nMap["EnableICC"].(bool)
189191
ncfg.Mtu = int(nMap["Mtu"].(float64))
190192
if v, ok := nMap["Internal"]; ok {

Diff for: drivers/bridge/bridge_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/docker/libnetwork/options"
1717
"github.com/docker/libnetwork/testutils"
1818
"github.com/docker/libnetwork/types"
19+
"github.com/vishvananda/netlink"
1920
)
2021

2122
func init() {
@@ -269,13 +270,15 @@ func TestCreateFullOptionsLabels(t *testing.T) {
269270
gwV6s := "2001:db8:2600:2700:2800::25/80"
270271
nwV6, _ := types.ParseCIDR(nwV6s)
271272
gwV6, _ := types.ParseCIDR(gwV6s)
273+
ndpPxyIface := "lo"
272274

273275
labels := map[string]string{
274276
BridgeName: DefaultBridgeName,
275277
DefaultBridge: "true",
276278
EnableICC: "true",
277279
EnableIPMasquerade: "true",
278280
DefaultBindingIP: bndIPs,
281+
NDPProxyInterface: ndpPxyIface,
279282
}
280283

281284
netOption := make(map[string]interface{})
@@ -318,6 +321,10 @@ func TestCreateFullOptionsLabels(t *testing.T) {
318321
t.Fatalf("incongruent EnableIPMasquerade in bridge network")
319322
}
320323

324+
if nw.config.NDPProxyInterface != ndpPxyIface {
325+
t.Fatalf("incongruend NDPProxyInterface in bridge network")
326+
}
327+
321328
bndIP := net.ParseIP(bndIPs)
322329
if !bndIP.Equal(nw.config.DefaultBindingIP) {
323330
t.Fatalf("Unexpected: %v", nw.config.DefaultBindingIP)
@@ -347,6 +354,22 @@ func TestCreateFullOptionsLabels(t *testing.T) {
347354
if te.Interface().AddressIPv6().IP.String() != "2001:db8:2600:2700:2800:aabb:ccdd:eeff" {
348355
t.Fatalf("Unexpected endpoint IPv6 address: %v", te.Interface().AddressIPv6().IP)
349356
}
357+
358+
// Check that the neighbor proxy was created by trying to delete it,
359+
// because netlink.NeighList currently can't list proxies.
360+
link, _ := d.nlh.LinkByName(ndpPxyIface)
361+
err = d.nlh.NeighDel(&netlink.Neigh{
362+
LinkIndex: link.Attrs().Index,
363+
Family: netlink.FAMILY_V6,
364+
State: netlink.NUD_PERMANENT,
365+
Type: netlink.NDA_UNSPEC,
366+
Flags: netlink.NTF_PROXY,
367+
IP: te.Interface().AddressIPv6().IP,
368+
HardwareAddr: te.Interface().MacAddress(),
369+
})
370+
if err != nil {
371+
t.Fatalf("Cannot delete neighbor proxy, suggesting it wasn't created: %v", err)
372+
}
350373
}
351374

352375
func TestCreate(t *testing.T) {

Diff for: drivers/bridge/labels.go

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const (
77
// EnableIPMasquerade label for bridge driver
88
EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade"
99

10+
// NDPProxyInterface label for bridge driver
11+
NDPProxyInterface = "com.docker.network.bridge.ndp_proxy_interface"
12+
1013
// EnableICC label
1114
EnableICC = "com.docker.network.bridge.enable_icc"
1215

Diff for: drivers/bridge/setup_ipv6.go

+31
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const (
1818
ipv6ForwardConfPerm = 0644
1919
ipv6ForwardConfDefault = "/proc/sys/net/ipv6/conf/default/forwarding"
2020
ipv6ForwardConfAll = "/proc/sys/net/ipv6/conf/all/forwarding"
21+
ndpProxyConfPerm = 0644
22+
ndpProxyConfDefault = "/proc/sys/net/ipv6/conf/default/proxy_ndp"
23+
ndpProxyConfAll = "/proc/sys/net/ipv6/conf/all/proxy_ndp"
2124
)
2225

2326
func init() {
@@ -117,3 +120,31 @@ func setupIPv6Forwarding(config *networkConfiguration, i *bridgeInterface) error
117120

118121
return nil
119122
}
123+
124+
func setupNDPProxying(config *networkConfiguration, i *bridgeInterface) error {
125+
// Get current NDP default proxying setup
126+
ndpProxyDataDefault, err := ioutil.ReadFile(ndpProxyConfDefault)
127+
if err != nil {
128+
return fmt.Errorf("Cannot read NDP default proxying setup: %v", err)
129+
}
130+
// Enable NDP default proxying only if it is not already enabled
131+
if ndpProxyDataDefault[0] != '1' {
132+
if err := ioutil.WriteFile(ndpProxyConfDefault, []byte{'1', '\n'}, ndpProxyConfPerm); err != nil {
133+
logrus.Warnf("Unable to enable NDP default proxying: %v", err)
134+
}
135+
}
136+
137+
// Get current NDP all proxying setup
138+
ndpProxyDataAll, err := ioutil.ReadFile(ndpProxyConfAll)
139+
if err != nil {
140+
return fmt.Errorf("Cannot read NDP all proxying setup: %v", err)
141+
}
142+
// Enable NDP all proxying only if it is not already enabled
143+
if ndpProxyDataAll[0] != '1' {
144+
if err := ioutil.WriteFile(ndpProxyConfAll, []byte{'1', '\n'}, ndpProxyConfPerm); err != nil {
145+
logrus.Warnf("Unable to enable NDP all proxying: %v", err)
146+
}
147+
}
148+
149+
return nil
150+
}

0 commit comments

Comments
 (0)