Skip to content

Commit 3755884

Browse files
committed
Enabled hairpin mode for bridged external interface
1 parent 87642a3 commit 3755884

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

Diff for: netlink/link.go

+65
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,68 @@ func SetLinkAddress(ifName string, hwAddress net.HardwareAddr) error {
314314

315315
return s.sendAndWaitForAck(req)
316316
}
317+
318+
// SetLinkPromisc sets the promiscuous mode of a network interface.
319+
func SetLinkPromisc(ifName string, on bool) error {
320+
s, err := getSocket()
321+
if err != nil {
322+
return err
323+
}
324+
325+
iface, err := net.InterfaceByName(ifName)
326+
if err != nil {
327+
return err
328+
}
329+
330+
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
331+
332+
ifInfo := newIfInfoMsg()
333+
ifInfo.Type = unix.RTM_SETLINK
334+
ifInfo.Index = int32(iface.Index)
335+
336+
if on {
337+
ifInfo.Flags = unix.IFF_PROMISC
338+
ifInfo.Change = unix.IFF_PROMISC
339+
} else {
340+
ifInfo.Flags = 0 & ^unix.IFF_PROMISC
341+
ifInfo.Change = unix.IFF_PROMISC
342+
}
343+
344+
req.addPayload(ifInfo)
345+
346+
return s.sendAndWaitForAck(req)
347+
}
348+
349+
// SetLinkHairpin sets the hairpin (reflective relay) mode of a bridged interface.
350+
func SetLinkHairpin(bridgeName string, on bool) error {
351+
s, err := getSocket()
352+
if err != nil {
353+
return err
354+
}
355+
356+
iface, err := net.InterfaceByName(bridgeName)
357+
if err != nil {
358+
return err
359+
}
360+
361+
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
362+
363+
ifInfo := newIfInfoMsg()
364+
ifInfo.Family = unix.AF_BRIDGE
365+
ifInfo.Type = unix.RTM_SETLINK
366+
ifInfo.Index = int32(iface.Index)
367+
ifInfo.Flags = unix.NLM_F_REQUEST
368+
ifInfo.Change = DEFAULT_CHANGE
369+
req.addPayload(ifInfo)
370+
371+
hairpin := []byte{0}
372+
if on {
373+
hairpin[0] = byte(1)
374+
}
375+
376+
attrProtInfo := newAttribute(unix.IFLA_PROTINFO|unix.NLA_F_NESTED, nil)
377+
attrProtInfo.addNested(newAttribute(IFLA_BRPORT_MODE, hairpin))
378+
req.addPayload(attrProtInfo)
379+
380+
return s.sendAndWaitForAck(req)
381+
}

Diff for: netlink/netlink_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,71 @@ func TestSetLinkState(t *testing.T) {
164164
t.Errorf("DeleteLink failed: %+v", err)
165165
}
166166
}
167+
168+
// TestSetLinkPromisc tests setting the promiscuous mode of a network interface.
169+
func TestSetLinkPromisc(t *testing.T) {
170+
_, err := addDummyInterface(ifName)
171+
if err != nil {
172+
t.Errorf("addDummyInterface failed: %v", err)
173+
}
174+
175+
err = SetLinkPromisc(ifName, true)
176+
if err != nil {
177+
t.Errorf("SetLinkPromisc on failed: %+v", err)
178+
}
179+
180+
err = SetLinkPromisc(ifName, false)
181+
if err != nil {
182+
t.Errorf("SetLinkPromisc off failed: %+v", err)
183+
}
184+
185+
err = DeleteLink(ifName)
186+
if err != nil {
187+
t.Errorf("DeleteLink failed: %+v", err)
188+
}
189+
}
190+
191+
// TestSetHairpinMode tests setting the hairpin mode of a bridged interface.
192+
func TestSetLinkHairpin(t *testing.T) {
193+
link := BridgeLink{
194+
LinkInfo: LinkInfo{
195+
Type: LINK_TYPE_BRIDGE,
196+
Name: ifName,
197+
},
198+
}
199+
200+
err := AddLink(&link)
201+
if err != nil {
202+
t.Errorf("AddLink failed: %+v", err)
203+
}
204+
205+
_, err = addDummyInterface(ifName2)
206+
if err != nil {
207+
t.Errorf("addDummyInterface failed: %v", err)
208+
}
209+
210+
err = SetLinkMaster(ifName2, ifName)
211+
if err != nil {
212+
t.Errorf("SetLinkMaster failed: %+v", err)
213+
}
214+
215+
err = SetLinkHairpin(ifName2, true)
216+
if err != nil {
217+
t.Errorf("SetLinkHairpin on failed: %+v", err)
218+
}
219+
220+
err = SetLinkHairpin(ifName2, false)
221+
if err != nil {
222+
t.Errorf("SetLinkHairpin off failed: %+v", err)
223+
}
224+
225+
err = DeleteLink(ifName2)
226+
if err != nil {
227+
t.Errorf("DeleteLink failed: %+v", err)
228+
}
229+
230+
err = DeleteLink(ifName)
231+
if err != nil {
232+
t.Errorf("DeleteLink failed: %+v", err)
233+
}
234+
}

Diff for: netlink/protocol.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const (
1717
IFLA_INFO_DATA = 2
1818
IFLA_NET_NS_FD = 28
1919
IFLA_IPVLAN_MODE = 1
20+
IFLA_BRPORT_MODE = 4
2021
VETH_INFO_PEER = 1
2122
DEFAULT_CHANGE = 0xFFFFFFFF
2223
)

Diff for: network/network_linux.go

+7
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ func (nm *networkManager) connectExternalInterface(extIf *externalInterface, nwI
268268
goto cleanup
269269
}
270270

271+
// External interface hairpin on.
272+
log.Printf("[net] Setting link %v hairpin on.", hostIf.Name)
273+
err = netlink.SetLinkHairpin(hostIf.Name, true)
274+
if err != nil {
275+
goto cleanup
276+
}
277+
271278
// Bridge up.
272279
log.Printf("[net] Setting link %v state up.", bridgeName)
273280
err = netlink.SetLinkState(bridgeName, true)

0 commit comments

Comments
 (0)