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 netkit #930

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
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
40 changes: 40 additions & 0 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 82 additions & 0 deletions link_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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 {
Expand Down
153 changes: 144 additions & 9 deletions link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}
}
}
}
Expand Down Expand Up @@ -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()
Expand Down
10 changes: 10 additions & 0 deletions nl/link_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading