Skip to content

Commit

Permalink
Add Netkit and Veth drivers
Browse files Browse the repository at this point in the history
This commit adds Netkit and Veth drivers.

Signed-off-by: Birol Bilgin <birolbilgin@gmail.com>
  • Loading branch information
brlbil committed May 9, 2024
1 parent 5687cef commit 22f75eb
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 0 deletions.
2 changes: 2 additions & 0 deletions driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ func init() {
for _, drv := range []rtnetlink.LinkDriver{
&Bond{},
&BondSlave{},
&Netkit{},
&Veth{},
} {
_ = rtnetlink.RegisterDriver(drv)
}
Expand Down
119 changes: 119 additions & 0 deletions driver/netkit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package driver

import (
"errors"
"fmt"

"github.com/jsimonetti/rtnetlink"
"github.com/jsimonetti/rtnetlink/internal/unix"
"github.com/mdlayher/netlink"
)

// NetkitMode specifies netkit operation mode
type NetkitMode uint32

func (n NetkitMode) String() string {
switch n {
case NetkitModeL2:
return "layer2"
case NetkitModeL3:
return "layer3"
default:
return fmt.Sprintf("unknown NetkitMode value (%d)", n)
}
}

const (
// Netkit operates on layer2
NetkitModeL2 NetkitMode = unix.NETKIT_L2

// Netkit operates on layer3, this is the default mode
NetkitModeL3 NetkitMode = unix.NETKIT_L3
)

// NetkitPolicy specifies default packet policy when no eBPF program is attached
type NetkitPolicy int32

func (n NetkitPolicy) String() string {
switch n {
case NetkitPolicyPass:
return "forward"
case NetkitPolicyDrop:
return "blackhole"
default:
return fmt.Sprintf("unknown NetkitPolicy value (%d)", n)
}
}

const (
// Default policy to forwards packets
NetkitPolicyPass NetkitPolicy = unix.NETKIT_PASS

// Default policy to drops packets
NetkitPolicyDrop NetkitPolicy = unix.NETKIT_DROP
)

// Netkit implements LinkDriverVerifier for the netkit driver
type Netkit struct {
Mode *NetkitMode // Specifies driver operation mode
Policy *NetkitPolicy // Specifies default policy
PeerPolicy *NetkitPolicy // Specifies default peer policy
Primary bool // Shows primary link
PeerInfo *rtnetlink.LinkMessage // Specifies peer link information
}

var _ rtnetlink.LinkDriverVerifier = &Netkit{}

func (n *Netkit) New() rtnetlink.LinkDriver {
return &Netkit{}
}

func (n *Netkit) Verify(msg *rtnetlink.LinkMessage) error {
if msg.Attributes.Address != nil || (n.PeerInfo != nil && n.PeerInfo.Attributes != nil && n.PeerInfo.Attributes.Address != nil) {
return errors.New("netkit does not support setting Ethernet address")
}
return nil
}

func (n *Netkit) Decode(ad *netlink.AttributeDecoder) error {
for ad.Next() {
switch ad.Type() {
case unix.IFLA_NETKIT_MODE:
v := NetkitMode(ad.Uint32())
n.Mode = &v
case unix.IFLA_NETKIT_POLICY:
v := NetkitPolicy(ad.Int32())
n.Policy = &v
case unix.IFLA_NETKIT_PEER_POLICY:
v := NetkitPolicy(ad.Int32())
n.PeerPolicy = &v
case unix.IFLA_NETKIT_PRIMARY:
n.Primary = ad.Uint8() != 0
}
}
return nil
}

func (n *Netkit) Encode(ae *netlink.AttributeEncoder) error {
if n.Mode != nil {
ae.Uint32(unix.IFLA_NETKIT_MODE, uint32(*n.Mode))
}
if n.Policy != nil {
ae.Int32(unix.IFLA_NETKIT_POLICY, int32(*n.Policy))
}
if n.PeerPolicy != nil {
ae.Int32(unix.IFLA_NETKIT_PEER_POLICY, int32(*n.PeerPolicy))
}
if n.PeerInfo != nil {
b, err := n.PeerInfo.MarshalBinary()
if err != nil {
return err
}
ae.Bytes(unix.IFLA_NETKIT_PEER_INFO, b)
}
return nil
}

func (n *Netkit) Kind() string {
return "netkit"
}
155 changes: 155 additions & 0 deletions driver/netkit_live_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//go:build integration
// +build integration

package driver

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/jsimonetti/rtnetlink"
"github.com/mdlayher/netlink"
)

func TestNetkit(t *testing.T) {
kernelMinReq(t, 6, 7)

// establish a netlink connection
conn, err := rtnetlink.Dial(nil)
if err != nil {
t.Fatalf("failed to establish netlink socket: %v", err)
}
defer conn.Close()

// create netns
nkns, clean, err := createNS("nkns1")
if err != nil {
t.Fatal(err)
}
defer clean()

// establish a netlink connection with netns
connNS, err := rtnetlink.Dial(&netlink.Config{NetNS: int(nkns.Value())})
if err != nil {
t.Fatalf("failed to establish netlink socket to ns nkns: %v", err)
}
defer connNS.Close()

const (
ifIndex = 1011
ifPeerIndex = 1012
)

modeL2 := NetkitModeL2
modeL3 := NetkitModeL3
polPass := NetkitPolicyPass
polDrop := NetkitPolicyDrop

tests := []struct {
name string
linkName string
pconn *rtnetlink.Conn
driver *Netkit
primary *Netkit
peer *Netkit
}{
{
name: "with empty link names both in default ns",
linkName: "",
pconn: conn,
driver: &Netkit{
PeerInfo: &rtnetlink.LinkMessage{
Index: ifPeerIndex,
},
},
primary: &Netkit{
Mode: &modeL3,
Policy: &polPass,
PeerPolicy: &polPass,
Primary: true,
},
peer: &Netkit{
Mode: &modeL3,
Policy: &polPass,
PeerPolicy: &polPass,
},
},
{
name: "with names both in default ns",
linkName: "nkp",
pconn: conn,
driver: &Netkit{
Mode: &modeL2,
PeerInfo: &rtnetlink.LinkMessage{
Index: ifPeerIndex,
Attributes: &rtnetlink.LinkAttributes{
Name: "nke",
},
},
},
primary: &Netkit{
Mode: &modeL2,
Policy: &polPass,
PeerPolicy: &polPass,
Primary: true,
},
peer: &Netkit{
Mode: &modeL2,
Policy: &polPass,
PeerPolicy: &polPass,
},
},
{
name: "with one is in other ns",
linkName: "nkp",
pconn: connNS,
driver: &Netkit{
Policy: &polPass,
PeerPolicy: &polDrop,
PeerInfo: &rtnetlink.LinkMessage{
Index: ifPeerIndex,
Attributes: &rtnetlink.LinkAttributes{
Name: "nke",
NetNS: nkns,
},
},
},
primary: &Netkit{
Mode: &modeL3,
Policy: &polPass,
PeerPolicy: &polDrop,
Primary: true,
},
peer: &Netkit{
Mode: &modeL3,
Policy: &polDrop,
PeerPolicy: &polPass,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := setupInterface(conn, tt.linkName, ifIndex, 0, tt.driver); err != nil {
t.Fatalf("failed to setup netkit interface: %v", err)
}
defer conn.Link.Delete(ifIndex)

msg, err := getInterface(conn, ifIndex)
if err != nil {
t.Fatalf("failed to get primary netkit interface: %v", err)
}
if diff := cmp.Diff(tt.primary, msg.Attributes.Info.Data); diff != "" {
t.Error(diff)
}

msg, err = getInterface(tt.pconn, ifPeerIndex)
if err != nil {
t.Fatalf("failed to get peer netkit interface: %v", err)
}
if diff := cmp.Diff(tt.peer, msg.Attributes.Info.Data); diff != "" {
t.Error(diff)
}
})
}
}
51 changes: 51 additions & 0 deletions driver/veth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package driver

import (
"fmt"

"github.com/jsimonetti/rtnetlink"
"github.com/mdlayher/netlink"
)

const veth_info_peer = 0x1

// Veth implements LinkDriverVerifier for the veth driver
type Veth struct {
PeerInfo *rtnetlink.LinkMessage // Specifies peer link information
}

var _ rtnetlink.LinkDriverVerifier = &Veth{}

func (v *Veth) New() rtnetlink.LinkDriver {
return &Veth{}
}

func (v *Veth) Encode(ae *netlink.AttributeEncoder) error {
b, err := v.PeerInfo.MarshalBinary()
if err != nil {
return err
}
ae.Bytes(veth_info_peer, b)

return nil
}

func (v *Veth) Decode(ad *netlink.AttributeDecoder) error {
return nil
}

func (*Veth) Kind() string {
return "veth"
}

const (
eth_min_mtu = 68 // Min IPv4 MTU per RFC791
eth_max_mtu = 65535 // 65535, same as IP_MAX_MTU
)

func (v *Veth) Verify(msg *rtnetlink.LinkMessage) error {
if msg.Attributes != nil && msg.Attributes.MTU > 0 && (msg.Attributes.MTU < eth_min_mtu || msg.Attributes.MTU > eth_max_mtu) {
return fmt.Errorf("invalid MTU value %d, must be between %d %d", msg.Attributes.MTU, eth_min_mtu, eth_max_mtu)
}
return nil
}
Loading

0 comments on commit 22f75eb

Please sign in to comment.