Skip to content

Commit

Permalink
Add AF_XDP support
Browse files Browse the repository at this point in the history
Signed-off-by: Artem Glazychev <artem.glazychev@xored.com>
  • Loading branch information
glazychev-art committed Aug 16, 2021
1 parent 4e1d77e commit 9aa9aa4
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 8 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ issues:
- path: internal/vppinit/vppinit.go
linters:
- funlen
text: "Function 'LinkToAfPacket'"
text: "Function 'LinkToSocket'"
- path: internal/tests/suite_combinatronics_test.go
linters:
- funlen
Expand Down
2 changes: 2 additions & 0 deletions internal/imports/imports_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions internal/tests/suite_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ func (f *ForwarderTestSuite) SetupSuite() {
log.FromContext(f.ctx).Infof("Creating test vpp Server (time since start: %s)", time.Since(starttime))
// ********************************************************************************
f.vppServerConn, f.vppServerRoot, f.vppServerErrCh = f.createVpp(f.ctx, "vpp-server")
_, err := vppinit.LinkToAfPacket(f.ctx, f.vppServerConn, net.ParseIP(serverIP))
_, err := vppinit.LinkToSocket(f.ctx, f.vppServerConn, net.ParseIP(serverIP), vppinit.AfXDP)
f.Require().NoError(err)

// ********************************************************************************
log.FromContext(f.ctx).Infof("Creating test vpp Client (time since start: %s)", time.Since(starttime))
// ********************************************************************************
f.vppClientConn, f.vppClientRoot, f.vppClientErrCh = f.createVpp(f.ctx, "vpp-client")
_, err = vppinit.LinkToAfPacket(f.ctx, f.vppClientConn, net.ParseIP(clientIP))
_, err = vppinit.LinkToSocket(f.ctx, f.vppClientConn, net.ParseIP(clientIP), vppinit.AfXDP)
f.Require().NoError(err)

// ********************************************************************************
Expand Down
96 changes: 92 additions & 4 deletions internal/vppinit/vppinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ import (
"context"
"fmt"
"net"
"strconv"
"strings"
"syscall"
"time"

"git.fd.io/govpp.git/api"
"github.com/edwarnicke/govpp/binapi/af_packet"
"github.com/edwarnicke/govpp/binapi/af_xdp"
"github.com/edwarnicke/govpp/binapi/fib_types"
interfaces "github.com/edwarnicke/govpp/binapi/interface"
"github.com/edwarnicke/govpp/binapi/interface_types"
Expand All @@ -39,6 +43,22 @@ import (
"github.com/networkservicemesh/sdk/pkg/tools/log"
)

// AfType represents socket address family
type AfType uint32

const (
// AfPacket - AF_PACKET
AfPacket AfType = 0
// AfXDP - AF_XDP
AfXDP AfType = 1
)

// Minimum required kernel version for AF_XDP
const (
afXdpMajorVer = 5
afXdpMinorVer = 4
)

// Func - vpp initialization function
type Func struct {
f func(ctx context.Context, vppConn api.Connection, tunnelIP net.IP) (net.IP, error)
Expand All @@ -53,7 +73,13 @@ func (f *Func) Execute(ctx context.Context, vppConn api.Connection, tunnelIP net
func (f *Func) Decode(value string) error {
switch value {
case "AF_PACKET":
f.f = LinkToAfPacket
f.f = func(ctx context.Context, vppConn api.Connection, tunnelIP net.IP) (net.IP, error) {
return LinkToSocket(ctx, vppConn, tunnelIP, AfPacket)
}
case "AF_XDP":
f.f = func(ctx context.Context, vppConn api.Connection, tunnelIP net.IP) (net.IP, error) {
return LinkToSocket(ctx, vppConn, tunnelIP, AfXDP)
}
case "NONE":
f.f = None
default:
Expand All @@ -75,10 +101,38 @@ func None(ctx context.Context, vppConn api.Connection, tunnelIP net.IP) (net.IP,
return tunnelIP, nil
}

// LinkToAfPacket - will link vpp via af_packet to the interface having the tunnelIP
// Get Linux kernel version
// Example: 5.11.0-25-generic -> 5.11
func getKernelVer() []int {
var uname syscall.Utsname
err := syscall.Uname(&uname)
if err != nil {
return nil
}

b := make([]byte, 0, len(uname.Release))
for _, v := range uname.Release {
if v == 0x00 {
break
}
b = append(b, byte(v))
}
ver := strings.Split(string(b), ".")
maj, err := strconv.Atoi(ver[0])
if err != nil {
return nil
}
min, err := strconv.Atoi(ver[1])
if err != nil {
return nil
}
return []int{maj, min}
}

// LinkToSocket - will link vpp via af_packet or af_xdp to the interface having the tunnelIP
// if tunnelIP is nil, it will find the interface for the default route and use that instead.
// It returns the resulting tunnelIP
func LinkToAfPacket(ctx context.Context, vppConn api.Connection, tunnelIP net.IP) (net.IP, error) {
func LinkToSocket(ctx context.Context, vppConn api.Connection, tunnelIP net.IP, family AfType) (net.IP, error) {
link, addrs, routes, err := linkAddrsRoutes(ctx, tunnelIP)
if err != nil {
return nil, err
Expand All @@ -87,7 +141,20 @@ func LinkToAfPacket(ctx context.Context, vppConn api.Connection, tunnelIP net.IP
return tunnelIP, nil
}

swIfIndex, err := createAfPacket(ctx, vppConn, link)
afFunc := createAfPacket
if family == AfXDP {
// Check if AF_XDP is supported
ver := getKernelVer()
if ver != nil {
if ver[0] < afXdpMajorVer && ver[1] < afXdpMinorVer {
log.FromContext(ctx).Warn("AF_XDP is not supported for this linux kernel version. AF_PACKET will be used")
} else {
afFunc = createAfXDP
}
}
}

swIfIndex, err := afFunc(ctx, vppConn, link)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -214,6 +281,27 @@ func createAfPacket(ctx context.Context, vppConn api.Connection, link netlink.Li
return afPacketCreateRsp.SwIfIndex, nil
}

func createAfXDP(ctx context.Context, vppConn api.Connection, link netlink.Link) (interface_types.InterfaceIndex, error) {
afXDPCreate := &af_xdp.AfXdpCreate{
HostIf: link.Attrs().Name,
}
now := time.Now()
afXDPCreateRsp, err := af_xdp.NewServiceClient(vppConn).AfXdpCreate(ctx, afXDPCreate)
if err != nil {
return 0, err
}
log.FromContext(ctx).
WithField("swIfIndex", afXDPCreateRsp.SwIfIndex).
WithField("hostIfName", afXDPCreate.HostIf).
WithField("duration", time.Since(now)).
WithField("vppapi", "afXDPCreateRsp").Debug("completed")

if err := setMtu(ctx, vppConn, link, afXDPCreateRsp.SwIfIndex); err != nil {
return 0, err
}
return afXDPCreateRsp.SwIfIndex, nil
}

func addIPNeighbor(ctx context.Context, vppConn api.Connection, swIfIndex interface_types.InterfaceIndex, linkIdx int) error {
neighList, err := netlink.NeighList(linkIdx, netlink.FAMILY_ALL)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type Config struct {
ListenOn url.URL `default:"unix:///listen.on.socket" desc:"url to listen on" split_words:"true"`
MaxTokenLifetime time.Duration `default:"10m" desc:"maximum lifetime of tokens" split_words:"true"`
VppAPISocket string `default:"" desc:"filename of socket to connect to existing VPP instance. If empty a VPP instance is run in forwarder" split_words:"true"`
VppInit vppinit.Func `default:"AF_PACKET" desc:"type of VPP initialization. Must be AF_PACKET or NONE" split_words:"true"`
VppInit vppinit.Func `default:"AF_XDP" desc:"type of VPP initialization. Must be AF_XDP, AF_PACKET or NONE" split_words:"true"`
LogLevel string `default:"INFO" desc:"Log level" split_words:"true"`
}

Expand Down

0 comments on commit 9aa9aa4

Please sign in to comment.