Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added support for Foo-over-UDP netlink calls
Signed-off-by: Reinier Schoof <reinier@skoef.nl>
- Loading branch information
Showing
4 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |