Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for mapping between tunnel id and vlan #1032

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 156 additions & 21 deletions bridge_linux.go
Original file line number Diff line number Diff line change
@@ -8,6 +8,89 @@ 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
var flag 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)
case nl.IFLA_BRIDGE_VLAN_TUNNEL_FLAGS:
flag = native.Uint16(tunnelInfo.Value)
}
}

if flag == nl.BRIDGE_VLAN_INFO_RANGE_END {
lastTi := ret[len(ret)-1]
vni := lastTi.TunId + 1
for i := lastTi.Vid + 1; i < vid; i++ {
t := nl.TunnelInfo{
TunId: vni,
Vid: i,
}
ret = append(ret, t)
vni++
}
}

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`
//
@@ -61,6 +144,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 {
@@ -70,7 +185,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
@@ -82,7 +197,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
@@ -94,7 +209,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
@@ -106,10 +221,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)
@@ -129,25 +244,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)
125 changes: 125 additions & 0 deletions bridge_linux_test.go
Original file line number Diff line number Diff line change
@@ -79,6 +79,131 @@ 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)
5 changes: 5 additions & 0 deletions handle_unspecified.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !linux
// +build !linux

package netlink
@@ -183,6 +184,10 @@ func (h *Handle) LinkSetGROIPv4MaxSize(link Link, maxSize int) error {
return ErrNotImplemented
}

func (h *Handle) LinkSetIP6AddrGenMode(link Link, mode int) error {
return ErrNotImplemented
}

func (h *Handle) setProtinfoAttr(link Link, mode bool, attr int) error {
return ErrNotImplemented
}
Loading