mirror of
https://github.com/vishvananda/netlink.git
synced 2025-09-26 20:01:13 +08:00
added support for Foo-over-UDP netlink calls
Signed-off-by: Reinier Schoof <reinier@skoef.nl>
This commit is contained in:

committed by
Alessandro Boch

parent
aa48b8cff0
commit
bdf753e87c
64
fou.go
Normal file
64
fou.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrAttrHeaderTruncated is returned when a netlink attribute's header is
|
||||
// truncated.
|
||||
ErrAttrHeaderTruncated = errors.New("attribute header truncated")
|
||||
// ErrAttrBodyTruncated is returned when a netlink attribute's body is
|
||||
// truncated.
|
||||
ErrAttrBodyTruncated = errors.New("attribute body truncated")
|
||||
)
|
||||
|
||||
type Fou struct {
|
||||
Family int
|
||||
Port int
|
||||
Protocol int
|
||||
EncapType int
|
||||
}
|
||||
|
||||
func deserializeFouMsg(msg []byte) (Fou, error) {
|
||||
// we'll skip to byte 4 to first attribute
|
||||
msg = msg[3:]
|
||||
var shift int
|
||||
fou := Fou{}
|
||||
|
||||
for {
|
||||
// attribute header is at least 16 bits
|
||||
if len(msg) < 4 {
|
||||
return fou, ErrAttrHeaderTruncated
|
||||
}
|
||||
|
||||
lgt := int(binary.BigEndian.Uint16(msg[0:2]))
|
||||
if len(msg) < lgt+4 {
|
||||
return fou, ErrAttrBodyTruncated
|
||||
}
|
||||
attr := binary.BigEndian.Uint16(msg[2:4])
|
||||
|
||||
shift = lgt + 3
|
||||
switch attr {
|
||||
case FOU_ATTR_AF:
|
||||
fou.Family = int(msg[5])
|
||||
case FOU_ATTR_PORT:
|
||||
fou.Port = int(binary.BigEndian.Uint16(msg[5:7]))
|
||||
// port is 2 bytes
|
||||
shift = lgt + 2
|
||||
case FOU_ATTR_IPPROTO:
|
||||
fou.Protocol = int(msg[5])
|
||||
case FOU_ATTR_TYPE:
|
||||
fou.EncapType = int(msg[5])
|
||||
}
|
||||
|
||||
msg = msg[shift:]
|
||||
|
||||
if len(msg) < 4 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return fou, nil
|
||||
}
|
173
fou_linux.go
Normal file
173
fou_linux.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// +build linux
|
||||
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
FOU_GENL_NAME = "fou"
|
||||
)
|
||||
|
||||
const (
|
||||
FOU_CMD_UNSPEC uint8 = iota
|
||||
FOU_CMD_ADD
|
||||
FOU_CMD_DEL
|
||||
FOU_CMD_GET
|
||||
FOU_CMD_MAX = FOU_CMD_GET
|
||||
)
|
||||
|
||||
const (
|
||||
FOU_ATTR_UNSPEC = iota
|
||||
FOU_ATTR_PORT
|
||||
FOU_ATTR_AF
|
||||
FOU_ATTR_IPPROTO
|
||||
FOU_ATTR_TYPE
|
||||
FOU_ATTR_REMCSUM_NOPARTIAL
|
||||
FOU_ATTR_MAX = FOU_ATTR_REMCSUM_NOPARTIAL
|
||||
)
|
||||
|
||||
const (
|
||||
FOU_ENCAP_UNSPEC = iota
|
||||
FOU_ENCAP_DIRECT
|
||||
FOU_ENCAP_GUE
|
||||
FOU_ENCAP_MAX = FOU_ENCAP_GUE
|
||||
)
|
||||
|
||||
var fouFamilyId int
|
||||
|
||||
func FouFamilyId() (int, error) {
|
||||
if fouFamilyId != 0 {
|
||||
return fouFamilyId, nil
|
||||
}
|
||||
|
||||
fam, err := GenlFamilyGet(FOU_GENL_NAME)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
fouFamilyId = int(fam.ID)
|
||||
return fouFamilyId, nil
|
||||
}
|
||||
|
||||
func FouAdd(f Fou) error {
|
||||
return pkgHandle.FouAdd(f)
|
||||
}
|
||||
|
||||
func (h *Handle) FouAdd(f Fou) error {
|
||||
fam_id, err := FouFamilyId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// setting ip protocol conflicts with encapsulation type GUE
|
||||
if f.EncapType == FOU_ENCAP_GUE && f.Protocol != 0 {
|
||||
return errors.New("GUE encapsulation doesn't specify an IP protocol")
|
||||
}
|
||||
|
||||
req := h.newNetlinkRequest(fam_id, unix.NLM_F_ACK)
|
||||
|
||||
// int to byte for port
|
||||
bp := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(bp[0:2], uint16(f.Port))
|
||||
|
||||
attrs := []*nl.RtAttr{
|
||||
nl.NewRtAttr(FOU_ATTR_PORT, bp),
|
||||
nl.NewRtAttr(FOU_ATTR_TYPE, []byte{uint8(f.EncapType)}),
|
||||
nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(f.Family)}),
|
||||
nl.NewRtAttr(FOU_ATTR_IPPROTO, []byte{uint8(f.Protocol)}),
|
||||
}
|
||||
raw := []byte{FOU_CMD_ADD, 1, 0, 0}
|
||||
for _, a := range attrs {
|
||||
raw = append(raw, a.Serialize()...)
|
||||
}
|
||||
|
||||
req.AddRawData(raw)
|
||||
|
||||
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func FouDel(f Fou) error {
|
||||
return pkgHandle.FouDel(f)
|
||||
}
|
||||
|
||||
func (h *Handle) FouDel(f Fou) error {
|
||||
fam_id, err := FouFamilyId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := h.newNetlinkRequest(fam_id, unix.NLM_F_ACK)
|
||||
|
||||
// int to byte for port
|
||||
bp := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(bp[0:2], uint16(f.Port))
|
||||
|
||||
attrs := []*nl.RtAttr{
|
||||
nl.NewRtAttr(FOU_ATTR_PORT, bp),
|
||||
nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(f.Family)}),
|
||||
}
|
||||
raw := []byte{FOU_CMD_DEL, 1, 0, 0}
|
||||
for _, a := range attrs {
|
||||
raw = append(raw, a.Serialize()...)
|
||||
}
|
||||
|
||||
req.AddRawData(raw)
|
||||
|
||||
_, err = req.Execute(unix.NETLINK_GENERIC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func FouList(fam int) ([]Fou, error) {
|
||||
return pkgHandle.FouList(fam)
|
||||
}
|
||||
|
||||
func (h *Handle) FouList(fam int) ([]Fou, error) {
|
||||
fam_id, err := FouFamilyId()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := h.newNetlinkRequest(fam_id, unix.NLM_F_DUMP)
|
||||
|
||||
attrs := []*nl.RtAttr{
|
||||
nl.NewRtAttr(FOU_ATTR_AF, []byte{uint8(fam)}),
|
||||
}
|
||||
raw := []byte{FOU_CMD_GET, 1, 0, 0}
|
||||
for _, a := range attrs {
|
||||
raw = append(raw, a.Serialize()...)
|
||||
}
|
||||
|
||||
req.AddRawData(raw)
|
||||
|
||||
msgs, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fous := make([]Fou, 0, len(msgs))
|
||||
for _, m := range msgs {
|
||||
f, err := deserializeFouMsg(m)
|
||||
if err != nil {
|
||||
return fous, err
|
||||
}
|
||||
|
||||
fous = append(fous, f)
|
||||
}
|
||||
|
||||
return fous, nil
|
||||
}
|
107
fou_test.go
Normal file
107
fou_test.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package netlink
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFouDeserializeMsg(t *testing.T) {
|
||||
var msg []byte
|
||||
|
||||
// deserialize a valid message
|
||||
msg = []byte{3, 1, 0, 0, 5, 0, 2, 0, 2, 0, 0, 0, 6, 0, 1, 0, 21, 179, 0, 0, 5, 0, 3, 0, 4, 0, 0, 0, 5, 0, 4, 0, 1, 0, 0, 0}
|
||||
if fou, err := deserializeFouMsg(msg); err != nil {
|
||||
t.Error(err.Error())
|
||||
} else {
|
||||
|
||||
// check if message was deserialized correctly
|
||||
if fou.Family != FAMILY_V4 {
|
||||
t.Errorf("expected family %d, got %d", FAMILY_V4, fou.Family)
|
||||
}
|
||||
|
||||
if fou.Port != 5555 {
|
||||
t.Errorf("expected port 5555, got %d", fou.Port)
|
||||
}
|
||||
|
||||
if fou.Protocol != 4 { // ipip
|
||||
t.Errorf("expected protocol 4, got %d", fou.Protocol)
|
||||
}
|
||||
|
||||
if fou.EncapType != FOU_ENCAP_DIRECT {
|
||||
t.Errorf("expected encap type %d, got %d", FOU_ENCAP_DIRECT, fou.EncapType)
|
||||
}
|
||||
}
|
||||
|
||||
// deserialize truncated attribute header
|
||||
msg = []byte{3, 1, 0, 0, 5, 0}
|
||||
if _, err := deserializeFouMsg(msg); err == nil {
|
||||
t.Error("expected attribute header truncated error")
|
||||
} else if err != ErrAttrHeaderTruncated {
|
||||
t.Errorf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
// deserialize truncated attribute header
|
||||
msg = []byte{3, 1, 0, 0, 5, 0, 2, 0, 2, 0, 0}
|
||||
if _, err := deserializeFouMsg(msg); err == nil {
|
||||
t.Error("expected attribute body truncated error")
|
||||
} else if err != ErrAttrBodyTruncated {
|
||||
t.Errorf("unexpected error: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFouAddDel(t *testing.T) {
|
||||
// foo-over-udp was merged in 3.18 so skip these tests if the kernel is too old
|
||||
minKernelRequired(t, 3, 18)
|
||||
|
||||
// the fou module is usually not compiled in the kernel so we'll load it
|
||||
tearDown := setUpNetlinkTestWithKModule(t, "fou")
|
||||
defer tearDown()
|
||||
|
||||
fou := Fou{
|
||||
Port: 5555,
|
||||
Family: FAMILY_V4,
|
||||
Protocol: 4, // ipip
|
||||
EncapType: FOU_ENCAP_DIRECT,
|
||||
}
|
||||
|
||||
if err := FouAdd(fou); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
list, err := FouList(FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 1 {
|
||||
t.Fatalf("expected 1 fou, got %d", len(list))
|
||||
}
|
||||
|
||||
if list[0].Port != fou.Port {
|
||||
t.Errorf("expected port %d, got %d", fou.Port, list[0].Port)
|
||||
}
|
||||
|
||||
if list[0].Family != fou.Family {
|
||||
t.Errorf("expected family %d, got %d", fou.Family, list[0].Family)
|
||||
}
|
||||
|
||||
if list[0].Protocol != fou.Protocol {
|
||||
t.Errorf("expected protocol %d, got %d", fou.Protocol, list[0].Protocol)
|
||||
}
|
||||
|
||||
if list[0].EncapType != fou.EncapType {
|
||||
t.Errorf("expected encaptype %d, got %d", fou.EncapType, list[0].EncapType)
|
||||
}
|
||||
|
||||
if err := FouDel(Fou{Port: fou.Port, Family: fou.Family}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
list, err = FouList(FAMILY_V4)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(list) != 0 {
|
||||
t.Fatalf("expected 0 fou, got %d", len(list))
|
||||
}
|
||||
}
|
15
fou_unspecified.go
Normal file
15
fou_unspecified.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build !linux
|
||||
|
||||
package netlink
|
||||
|
||||
func FouAdd(f Fou) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func FouDel(f Fou) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func FouList(fam int) ([]Fou, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
Reference in New Issue
Block a user