Skip to content

Commit

Permalink
Add support for mapping between tunnel id and vlan
Browse files Browse the repository at this point in the history
  • Loading branch information
zlaval committed Nov 22, 2024
1 parent 976bd8d commit 5d8045d
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 22 deletions.
159 changes: 138 additions & 21 deletions bridge_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,71 @@ import (
"golang.org/x/sys/unix"
)

// BridgeVlanTunnelShow gets vlanid-tunnelid mapping.
// Equivalent to: `bridge vlan tunnelshow`
//
// If the returned error is [ErrDumpInterrupted], results may be inconsistent
// or incomplete.
func BridgeVlanTunnelShow() ([]nl.TunnelInfo, error) {
return pkgHandle.BridgeVlanTunnelShow()
}

func (h *Handle) BridgeVlanTunnelShow() ([]nl.TunnelInfo, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_DUMP)
msg := nl.NewIfInfomsg(unix.AF_BRIDGE)
req.AddData(msg)
req.AddData(nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(uint32(nl.RTEXT_FILTER_BRVLAN))))

msgs, executeErr := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWLINK)
if executeErr != nil && !errors.Is(executeErr, ErrDumpInterrupted) {
return nil, executeErr
}
ret := make([]nl.TunnelInfo, 0)
for _, m := range msgs {
msg := nl.DeserializeIfInfomsg(m)

attrs, err := nl.ParseRouteAttr(m[msg.Len():])
if err != nil {
return nil, err
}
for _, attr := range attrs {
switch attr.Attr.Type {
case unix.IFLA_AF_SPEC:
nestedAttrs, err := nl.ParseRouteAttr(attr.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse nested attr %v", err)
}
for _, nestAttr := range nestedAttrs {
switch nestAttr.Attr.Type {
case nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO:
tunnelInfos, err := nl.ParseRouteAttr(nestAttr.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse nested attr %v", err)
}
var tunnelId uint32
var vid uint16
for _, tunnelInfo := range tunnelInfos {
switch tunnelInfo.Attr.Type {
case nl.IFLA_BRIDGE_VLAN_TUNNEL_ID:
tunnelId = native.Uint32(tunnelInfo.Value)
case nl.IFLA_BRIDGE_VLAN_TUNNEL_VID:
vid = native.Uint16(tunnelInfo.Value)
}
}
t := nl.TunnelInfo{
TunId: tunnelId,
Vid: vid,
}

ret = append(ret, t)
}
}
}
}
}
return ret, executeErr
}

// BridgeVlanList gets a map of device id to bridge vlan infos.
// Equivalent to: `bridge vlan show`
//
Expand Down Expand Up @@ -61,6 +126,38 @@ func (h *Handle) BridgeVlanList() (map[int32][]*nl.BridgeVlanInfo, error) {
return ret, executeErr
}

// BridgeVlanAddTunnelInfo adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID tunnel_info id TUNID [ self ] [ master ]`
func BridgeVlanAddTunnelInfo(link Link, vid uint16, tunid uint32, self, master bool) error {
return pkgHandle.BridgeVlanAddTunnelInfo(link, vid, 0, tunid, 0, self, master)
}

// BridgeVlanAddRangeTunnelInfoRange adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID-VIDEND tunnel_info id VIN-VINEND [ self ] [ master ]`
func BridgeVlanAddRangeTunnelInfoRange(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return pkgHandle.BridgeVlanAddTunnelInfo(link, vid, vidEnd, tunid, tunidEnd, self, master)
}

func (h *Handle) BridgeVlanAddTunnelInfo(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, vidEnd, tunid, tunidEnd, false, false, self, master)
}

// BridgeVlanDelTunnelInfo adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID tunnel_info id TUNID [ self ] [ master ]`
func BridgeVlanDelTunnelInfo(link Link, vid uint16, tunid uint32, self, master bool) error {
return pkgHandle.BridgeVlanDelTunnelInfo(link, vid, 0, tunid, 0, self, master)
}

// BridgeVlanDelRangeTunnelInfoRange adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID-VIDEND tunnel_info id VIN-VINEND [ self ] [ master ]`
func BridgeVlanDelRangeTunnelInfoRange(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return pkgHandle.BridgeVlanDelTunnelInfo(link, vid, vidEnd, tunid, tunidEnd, self, master)
}

func (h *Handle) BridgeVlanDelTunnelInfo(link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, vidEnd, tunid, tunidEnd, false, false, self, master)
}

// BridgeVlanAdd adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
Expand All @@ -70,7 +167,7 @@ func BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) err
// BridgeVlanAdd adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanAdd(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, 0, pvid, untagged, self, master)
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, 0, 0, 0, pvid, untagged, self, master)
}

// BridgeVlanAddRange adds a new vlan filter entry
Expand All @@ -82,7 +179,7 @@ func BridgeVlanAddRange(link Link, vid, vidEnd uint16, pvid, untagged, self, mas
// BridgeVlanAddRange adds a new vlan filter entry
// Equivalent to: `bridge vlan add dev DEV vid VID-VIDEND [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanAddRange(link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, vidEnd, pvid, untagged, self, master)
return h.bridgeVlanModify(unix.RTM_SETLINK, link, vid, vidEnd, 0, 0, pvid, untagged, self, master)
}

// BridgeVlanDel adds a new vlan filter entry
Expand All @@ -94,7 +191,7 @@ func BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) err
// BridgeVlanDel adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanDel(link Link, vid uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, 0, pvid, untagged, self, master)
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, 0, 0, 0, pvid, untagged, self, master)
}

// BridgeVlanDelRange adds a new vlan filter entry
Expand All @@ -106,10 +203,10 @@ func BridgeVlanDelRange(link Link, vid, vidEnd uint16, pvid, untagged, self, mas
// BridgeVlanDelRange adds a new vlan filter entry
// Equivalent to: `bridge vlan del dev DEV vid VID-VIDEND [ pvid ] [ untagged ] [ self ] [ master ]`
func (h *Handle) BridgeVlanDelRange(link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error {
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, vidEnd, pvid, untagged, self, master)
return h.bridgeVlanModify(unix.RTM_DELLINK, link, vid, vidEnd, 0, 0, pvid, untagged, self, master)
}

func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, pvid, untagged, self, master bool) error {
func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, tunid, tunidEnd uint32, pvid, untagged, self, master bool) error {
base := link.Attrs()
h.ensureIndex(base)
req := h.newNetlinkRequest(cmd, unix.NLM_F_ACK)
Expand All @@ -129,25 +226,45 @@ func (h *Handle) bridgeVlanModify(cmd int, link Link, vid, vidEnd uint16, pvid,
if flags > 0 {
br.AddRtAttr(nl.IFLA_BRIDGE_FLAGS, nl.Uint16Attr(flags))
}
vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
if pvid {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID
}
if untagged {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED
}

if vidEnd != 0 {
vlanEndInfo := &nl.BridgeVlanInfo{Vid: vidEnd}
vlanEndInfo.Flags = vlanInfo.Flags
if tunid != 0 {
if tunidEnd != 0 {
tiStart := br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO, nil)
tiStart.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_ID, nl.Uint32Attr(tunid))
tiStart.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_VID, nl.Uint16Attr(vid))
tiStart.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, nl.Uint16Attr(nl.BRIDGE_VLAN_INFO_RANGE_BEGIN))

vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_RANGE_BEGIN
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
tiEnd := br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO, nil)
tiEnd.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_ID, nl.Uint32Attr(tunidEnd))
tiEnd.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_VID, nl.Uint16Attr(vidEnd))
tiEnd.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, nl.Uint16Attr(nl.BRIDGE_VLAN_INFO_RANGE_END))
} else {
ti := br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_INFO, nil)
ti.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_ID, nl.Uint32Attr(tunid))
ti.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_VID, nl.Uint16Attr(vid))
ti.AddRtAttr(nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS, nl.Uint16Attr(0))
}
} else {
vlanInfo := &nl.BridgeVlanInfo{Vid: vid}
if pvid {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_PVID
}
if untagged {
vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_UNTAGGED
}

vlanEndInfo.Flags |= nl.BRIDGE_VLAN_INFO_RANGE_END
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanEndInfo.Serialize())
} else {
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
if vidEnd != 0 {
vlanEndInfo := &nl.BridgeVlanInfo{Vid: vidEnd}
vlanEndInfo.Flags = vlanInfo.Flags

vlanInfo.Flags |= nl.BRIDGE_VLAN_INFO_RANGE_BEGIN
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())

vlanEndInfo.Flags |= nl.BRIDGE_VLAN_INFO_RANGE_END
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanEndInfo.Serialize())
} else {
br.AddRtAttr(nl.IFLA_BRIDGE_VLAN_INFO, vlanInfo.Serialize())
}
}

req.AddData(br)
Expand Down
124 changes: 124 additions & 0 deletions bridge_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,130 @@ func TestBridgeVlan(t *testing.T) {
}
}

func TestBridgeVlanTunnelInfo(t *testing.T) {
minKernelRequired(t, 4, 11)
tearDown := setUpNetlinkTest(t)
defer tearDown()

if err := remountSysfs(); err != nil {
t.Fatal(err)
}
bridgeName := "br0"
vxlanName := "vxlan0"

// ip link add br0 type bridge
bridge := &Bridge{LinkAttrs: LinkAttrs{Name: bridgeName}}
if err := LinkAdd(bridge); err != nil {
t.Fatal(err)
}

// ip link add vxlan0 type vxlan dstport 4789 nolearning external local 10.0.1.1
vxlan := &Vxlan{
// local
SrcAddr: []byte("10.0.1.1"),
Learning: false,
// external
FlowBased: true,
// dstport
Port: 4789,
LinkAttrs: LinkAttrs{Name: vxlanName},
}
if err := LinkAdd(vxlan); err != nil {
t.Fatal(err)
}

//ip link set dev vxlan0 master br0
if err := LinkSetMaster(vxlan, bridge); err != nil {
t.Fatal(err)
}

// ip link set br0 type bridge vlan_filtering 1
if err := BridgeSetVlanFiltering(bridge, true); err != nil {
t.Fatal(err)
}

// bridge link set dev vxlan0 vlan_tunnel on
if err := LinkSetVlanTunnel(vxlan, true); err != nil {
t.Fatal(err)
}

p, err := LinkGetProtinfo(vxlan)
if err != nil {
t.Fatal(err)
}
if !p.VlanTunnel {
t.Fatal("vlan tunnel should be enabled on vxlan device")
}

// bridge vlan add vid 10 dev vxlan0
if err := BridgeVlanAdd(vxlan, 10, false, false, false, false); err != nil {
t.Fatal(err)
}
//bridge vlan add vid 11 dev vxlan0
if err := BridgeVlanAdd(vxlan, 11, false, false, false, false); err != nil {
t.Fatal(err)
}

// bridge vlan add dev vxlan0 vid 10 tunnel_info id 20
if err := BridgeVlanAddTunnelInfo(vxlan, 10, 20, false, false); err != nil {
t.Fatal(err)
}

tis, err := BridgeVlanTunnelShow()
if err != nil {
t.Fatal(err)
}

if len(tis) != 1 {
t.Fatal("only one tunnel info")
}
ti := tis[0]
if ti.TunId != 20 || ti.Vid != 10 {
t.Fatal("unexpected result")
}

// bridge vlan del dev vxlan0 vid 10 tunnel_info id 20
if err := BridgeVlanDelTunnelInfo(vxlan, 10, 20, false, false); err != nil {
t.Fatal(err)
}

tis, err = BridgeVlanTunnelShow()
if err != nil {
t.Fatal(err)
}

if len(tis) != 0 {
t.Fatal("tunnel info should have been deleted")
}

// bridge vlan add dev vxlan0 vid 10-11 tunnel_info id 20-21
if err := BridgeVlanAddRangeTunnelInfoRange(vxlan, 10, 11, 20, 21, false, false); err != nil {
t.Fatal(err)
}

tis, err = BridgeVlanTunnelShow()
if err != nil {
t.Fatal(err)
}
if len(tis) != 2 {
t.Fatal("two tunnel info")
}

// bridge vlan del dev vxlan0 vid 10-11 tunnel_info id 20-21
if err := BridgeVlanDelRangeTunnelInfoRange(vxlan, 10, 11, 20, 21, false, false); err != nil {
t.Fatal(err)
}

tis, err = BridgeVlanTunnelShow()
if err != nil {
t.Fatal(err)
}

if len(tis) != 0 {
t.Fatal("tunnel info should have been deleted")
}
}

func TestBridgeGroupFwdMask(t *testing.T) {
minKernelRequired(t, 4, 15) //minimal release for per-port group_fwd_mask
tearDown := setUpNetlinkTest(t)
Expand Down
8 changes: 8 additions & 0 deletions link_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2540,6 +2540,14 @@ func (h *Handle) LinkSetLearning(link Link, mode bool) error {
return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_LEARNING)
}

func LinkSetVlanTunnel(link Link, mode bool) error {
return pkgHandle.LinkSetVlanTunnel(link, mode)
}

func (h *Handle) LinkSetVlanTunnel(link Link, mode bool) error {
return h.setProtinfoAttr(link, mode, nl.IFLA_BRPORT_VLAN_TUNNEL)
}

func LinkSetRootBlock(link Link, mode bool) error {
return pkgHandle.LinkSetRootBlock(link, mode)
}
Expand Down
13 changes: 13 additions & 0 deletions nl/bridge_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ const (
IFLA_BRIDGE_FLAGS = iota
IFLA_BRIDGE_MODE
IFLA_BRIDGE_VLAN_INFO
IFLA_BRIDGE_VLAN_TUNNEL_INFO
)

const (
IFLA_BRIDGE_VLAN_TUNNEL_UNSPEC = iota
IFLA_BRIDGE_VLAN_TUNNEL_ID
IFLA_BRIDGE_VLAN_TUNNEL_VID
IFLA_BRIDGE_VLAN_TUNNEL_FLAGS
)

const (
Expand All @@ -41,6 +49,11 @@ const (
// __u16 vid;
// };

type TunnelInfo struct {
TunId uint32
Vid uint16
}

type BridgeVlanInfo struct {
Flags uint16
Vid uint16
Expand Down
2 changes: 1 addition & 1 deletion nl/nl_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ func (a *RtAttr) AddChild(attr NetlinkRequestData) {

func (a *RtAttr) Len() int {
if len(a.children) == 0 {
return (unix.SizeofRtAttr + len(a.Data))
return unix.SizeofRtAttr + len(a.Data)
}

l := 0
Expand Down
Loading

0 comments on commit 5d8045d

Please sign in to comment.