support netkit

netkit device is merged to Linux upstream:
https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git/commit/?id=22360fad5889cbefe1eca695b0cc0273ab280b56

this PR add support to manage a netkit device in Golang

authored-by: tangchen <tangchen.1@bytedance.com>
This commit is contained in:
tc 2023-11-21 21:14:30 +08:00 committed by Alessandro Boch
parent 95ab6696a5
commit f1333cd79a
4 changed files with 276 additions and 9 deletions

40
link.go
View File

@ -357,6 +357,46 @@ func (tuntap *Tuntap) Type() string {
return "tuntap"
}
type NetkitMode uint32
const (
NETKIT_MODE_L2 NetkitMode = iota
NETKIT_MODE_L3
)
type NetkitPolicy int
const (
NETKIT_POLICY_FORWARD NetkitPolicy = 0
NETKIT_POLICY_BLACKHOLE NetkitPolicy = 2
)
func (n *Netkit) IsPrimary() bool {
return n.isPrimary
}
// SetPeerAttrs will not take effect if trying to modify an existing netkit device
func (n *Netkit) SetPeerAttrs(Attrs *LinkAttrs) {
n.peerLinkAttrs = *Attrs
}
type Netkit struct {
LinkAttrs
Mode NetkitMode
Policy NetkitPolicy
PeerPolicy NetkitPolicy
isPrimary bool
peerLinkAttrs LinkAttrs
}
func (n *Netkit) Attrs() *LinkAttrs {
return &n.LinkAttrs
}
func (n *Netkit) Type() string {
return "netkit"
}
// Veth devices must specify PeerName on create
type Veth struct {
LinkAttrs

View File

@ -1634,6 +1634,10 @@ func (h *Handle) linkModify(link Link, flags int) error {
if link.VlanProtocol != VLAN_PROTOCOL_UNKNOWN {
data.AddRtAttr(nl.IFLA_VLAN_PROTOCOL, htons(uint16(link.VlanProtocol)))
}
case *Netkit:
if err := addNetkitAttrs(link, linkInfo, flags); err != nil {
return err
}
case *Veth:
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
peer := data.AddRtAttr(nl.VETH_INFO_PEER, nil)
@ -1947,6 +1951,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
link = &Bridge{}
case "vlan":
link = &Vlan{}
case "netkit":
link = &Netkit{}
case "veth":
link = &Veth{}
case "wireguard":
@ -2004,6 +2010,8 @@ func LinkDeserialize(hdr *unix.NlMsghdr, m []byte) (Link, error) {
return nil, err
}
switch linkType {
case "netkit":
parseNetkitData(link, data)
case "vlan":
parseVlanData(link, data)
case "vxlan":
@ -2544,6 +2552,80 @@ func (h *Handle) LinkSetGroup(link Link, group int) error {
return err
}
func addNetkitAttrs(nk *Netkit, linkInfo *nl.RtAttr, flag int) error {
if nk.peerLinkAttrs.HardwareAddr != nil || nk.HardwareAddr != nil {
return fmt.Errorf("netkit doesn't support setting Ethernet")
}
data := linkInfo.AddRtAttr(nl.IFLA_INFO_DATA, nil)
// Kernel will return error if trying to change the mode of an existing netkit device
data.AddRtAttr(nl.IFLA_NETKIT_MODE, nl.Uint32Attr(uint32(nk.Mode)))
data.AddRtAttr(nl.IFLA_NETKIT_POLICY, nl.Uint32Attr(uint32(nk.Policy)))
data.AddRtAttr(nl.IFLA_NETKIT_PEER_POLICY, nl.Uint32Attr(uint32(nk.PeerPolicy)))
if (flag & unix.NLM_F_EXCL) == 0 {
// Modifying peer link attributes will not take effect
return nil
}
peer := data.AddRtAttr(nl.IFLA_NETKIT_PEER_INFO, nil)
msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
if nk.peerLinkAttrs.Flags&net.FlagUp != 0 {
msg.Change = unix.IFF_UP
msg.Flags = unix.IFF_UP
}
if nk.peerLinkAttrs.Index != 0 {
msg.Index = int32(nk.peerLinkAttrs.Index)
}
peer.AddChild(msg)
if nk.peerLinkAttrs.Name != "" {
peer.AddRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(nk.peerLinkAttrs.Name))
}
if nk.peerLinkAttrs.MTU > 0 {
peer.AddRtAttr(unix.IFLA_MTU, nl.Uint32Attr(uint32(nk.peerLinkAttrs.MTU)))
}
if nk.peerLinkAttrs.GSOMaxSegs > 0 {
peer.AddRtAttr(unix.IFLA_GSO_MAX_SEGS, nl.Uint32Attr(nk.peerLinkAttrs.GSOMaxSegs))
}
if nk.peerLinkAttrs.GSOMaxSize > 0 {
peer.AddRtAttr(unix.IFLA_GSO_MAX_SIZE, nl.Uint32Attr(nk.peerLinkAttrs.GSOMaxSize))
}
if nk.peerLinkAttrs.GSOIPv4MaxSize > 0 {
peer.AddRtAttr(unix.IFLA_GSO_IPV4_MAX_SIZE, nl.Uint32Attr(nk.peerLinkAttrs.GSOIPv4MaxSize))
}
if nk.peerLinkAttrs.GROIPv4MaxSize > 0 {
peer.AddRtAttr(unix.IFLA_GRO_IPV4_MAX_SIZE, nl.Uint32Attr(nk.peerLinkAttrs.GROIPv4MaxSize))
}
if nk.peerLinkAttrs.Namespace != nil {
switch ns := nk.peerLinkAttrs.Namespace.(type) {
case NsPid:
peer.AddRtAttr(unix.IFLA_NET_NS_PID, nl.Uint32Attr(uint32(ns)))
case NsFd:
peer.AddRtAttr(unix.IFLA_NET_NS_FD, nl.Uint32Attr(uint32(ns)))
}
}
return nil
}
func parseNetkitData(link Link, data []syscall.NetlinkRouteAttr) {
netkit := link.(*Netkit)
for _, datum := range data {
switch datum.Attr.Type {
case nl.IFLA_NETKIT_PRIMARY:
isPrimary := datum.Value[0:1][0]
if isPrimary != 0 {
netkit.isPrimary = true
}
case nl.IFLA_NETKIT_MODE:
netkit.Mode = NetkitMode(native.Uint32(datum.Value[0:4]))
case nl.IFLA_NETKIT_POLICY:
netkit.Policy = NetkitPolicy(native.Uint32(datum.Value[0:4]))
case nl.IFLA_NETKIT_PEER_POLICY:
netkit.PeerPolicy = NetkitPolicy(native.Uint32(datum.Value[0:4]))
}
}
}
func parseVlanData(link Link, data []syscall.NetlinkRouteAttr) {
vlan := link.(*Vlan)
for _, datum := range data {

View File

@ -67,6 +67,44 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}
if resultPrimary, ok := result.(*Netkit); ok {
if inputPrimary, ok := link.(*Netkit); ok {
if resultPrimary.Policy != inputPrimary.Policy {
t.Fatalf("Policy is %d, should be %d", int(resultPrimary.Policy), int(inputPrimary.Policy))
}
if resultPrimary.PeerPolicy != inputPrimary.PeerPolicy {
t.Fatalf("Peer Policy is %d, should be %d", int(resultPrimary.PeerPolicy), int(inputPrimary.PeerPolicy))
}
if resultPrimary.Mode != inputPrimary.Mode {
t.Fatalf("Mode is %d, should be %d", int(resultPrimary.Mode), int(inputPrimary.Mode))
}
if inputPrimary.peerLinkAttrs.Name != "" {
var resultPeer *Netkit
pLink, err := LinkByName(inputPrimary.peerLinkAttrs.Name)
if err != nil {
t.Fatalf("Failed to get Peer netkit %s", inputPrimary.peerLinkAttrs.Name)
}
if resultPeer, ok = pLink.(*Netkit); !ok {
t.Fatalf("Peer %s is incorrect type", inputPrimary.peerLinkAttrs.Name)
}
if resultPrimary.PeerPolicy != resultPeer.Policy {
t.Fatalf("Peer Policy from primary is %d, should be %d", int(resultPrimary.PeerPolicy), int(resultPeer.Policy))
}
if resultPeer.PeerPolicy != resultPrimary.Policy {
t.Fatalf("PeerPolicy from peer is %d, should be %d", int(resultPeer.PeerPolicy), int(resultPrimary.Policy))
}
if resultPrimary.Mode != resultPeer.Mode {
t.Fatalf("Peer Mode from primary is %d, should be %d", int(resultPrimary.Mode), int(resultPeer.Mode))
}
if resultPrimary.IsPrimary() == resultPeer.IsPrimary() {
t.Fatalf("Both primary and peer device has the same value in IsPrimary() %t", resultPrimary.IsPrimary())
}
}
}
}
if veth, ok := result.(*Veth); ok {
if rBase.TxQLen != base.TxQLen {
t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen)
@ -108,15 +146,19 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}
}
} else {
// recent kernels set the parent index for veths in the response
if rBase.ParentIndex == 0 && base.ParentIndex != 0 {
t.Fatalf("Created link doesn't have parent %d but it should", base.ParentIndex)
} else if rBase.ParentIndex != 0 && base.ParentIndex == 0 {
t.Fatalf("Created link has parent %d but it shouldn't", rBase.ParentIndex)
} else if rBase.ParentIndex != 0 && base.ParentIndex != 0 {
if rBase.ParentIndex != base.ParentIndex {
t.Fatalf("Link.ParentIndex doesn't match %d != %d", rBase.ParentIndex, base.ParentIndex)
}
if _, ok := result.(*Veth); !ok {
if _, ok := result.(*Netkit); !ok {
// recent kernels set the parent index for veths/netkit in the response
if rBase.ParentIndex == 0 && base.ParentIndex != 0 {
t.Fatalf("Created link doesn't have parent %d but it should", base.ParentIndex)
} else if rBase.ParentIndex != 0 && base.ParentIndex == 0 {
t.Fatalf("Created link has parent %d but it shouldn't", rBase.ParentIndex)
} else if rBase.ParentIndex != 0 && base.ParentIndex != 0 {
if rBase.ParentIndex != base.ParentIndex {
t.Fatalf("Link.ParentIndex doesn't match %d != %d", rBase.ParentIndex, base.ParentIndex)
}
}
}
}
@ -868,6 +910,99 @@ func TestLinkAddDelMacvtap(t *testing.T) {
}
}
func TestNetkitPeerNs(t *testing.T) {
minKernelRequired(t, 6, 7)
tearDown := setUpNetlinkTest(t)
defer tearDown()
basens, err := netns.Get()
if err != nil {
t.Fatal("Failed to get basens")
}
defer basens.Close()
nsOne, err := netns.New()
if err != nil {
t.Fatal("Failed to create nsOne")
}
defer nsOne.Close()
nsTwo, err := netns.New()
if err != nil {
t.Fatal("Failed to create nsTwo")
}
defer nsTwo.Close()
netkit := &Netkit{
LinkAttrs: LinkAttrs{
Name: "foo",
Namespace: NsFd(basens),
},
Mode: NETKIT_MODE_L2,
Policy: NETKIT_POLICY_FORWARD,
PeerPolicy: NETKIT_POLICY_BLACKHOLE,
}
peerAttr := &LinkAttrs{
Name: "bar",
Namespace: NsFd(nsOne),
}
netkit.SetPeerAttrs(peerAttr)
if err := LinkAdd(netkit); err != nil {
t.Fatal(err)
}
_, err = LinkByName("bar")
if err == nil {
t.Fatal("netkit link bar is in nsTwo")
}
_, err = LinkByName("foo")
if err == nil {
t.Fatal("netkit link foo is in nsTwo")
}
err = netns.Set(basens)
if err != nil {
t.Fatal("Failed to set basens")
}
_, err = LinkByName("foo")
if err != nil {
t.Fatal("netkit link foo is not in basens")
}
err = netns.Set(nsOne)
if err != nil {
t.Fatal("Failed to set nsOne")
}
_, err = LinkByName("bar")
if err != nil {
t.Fatal("netkit link bar is not in nsOne")
}
}
func TestLinkAddDelNetkit(t *testing.T) {
minKernelRequired(t, 6, 7)
tearDown := setUpNetlinkTest(t)
defer tearDown()
netkit := &Netkit{
LinkAttrs: LinkAttrs{
Name: "foo",
},
Mode: NETKIT_MODE_L2,
Policy: NETKIT_POLICY_FORWARD,
PeerPolicy: NETKIT_POLICY_BLACKHOLE,
}
peerAttr := &LinkAttrs{
Name: "bar",
}
netkit.SetPeerAttrs(peerAttr)
testLinkAddDel(t, netkit)
}
func TestLinkAddDelVeth(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()

View File

@ -31,6 +31,16 @@ const (
IFLA_VLAN_MAX = IFLA_VLAN_PROTOCOL
)
const (
IFLA_NETKIT_UNSPEC = iota
IFLA_NETKIT_PEER_INFO
IFLA_NETKIT_PRIMARY
IFLA_NETKIT_POLICY
IFLA_NETKIT_PEER_POLICY
IFLA_NETKIT_MODE
IFLA_NETKIT_MAX = IFLA_NETKIT_MODE
)
const (
VETH_INFO_UNSPEC = iota
VETH_INFO_PEER