add netlink/rule (#139)

* add netlink/rule

Signed-off-by: Florian Lehner <dev@der-flo.net>

* Add some fuzzing corpus

Signed-off-by: Jeroen Simonetti <jeroen@simonetti.nl>

Co-authored-by: Jeroen Simonetti <jeroen@simonetti.nl>
This commit is contained in:
Florian Lehner 2022-04-12 09:00:30 +02:00 committed by GitHub
parent d380b50506
commit a833fb5b68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
132 changed files with 698 additions and 1 deletions

View File

@ -17,6 +17,7 @@ type Conn struct {
Address *AddressService
Route *RouteService
Neigh *NeighService
Rule *RuleService
}
var _ conn = &netlink.Conn{}
@ -54,6 +55,7 @@ func newConn(c conn) *Conn {
rtc.Address = &AddressService{c: rtc}
rtc.Route = &RouteService{c: rtc}
rtc.Neigh = &NeighService{c: rtc}
rtc.Rule = &RuleService{c: rtc}
return rtc
}
@ -179,6 +181,8 @@ func unpackMessages(msgs []netlink.Message) ([]Message, error) {
m = &RouteMessage{}
case unix.RTM_GETNEIGH, unix.RTM_NEWNEIGH, unix.RTM_DELNEIGH:
m = &NeighMessage{}
case unix.RTM_GETRULE, unix.RTM_NEWRULE, unix.RTM_DELRULE:
m = &RuleMessage{}
default:
continue
}

27
example_rule_list_test.go Normal file
View File

@ -0,0 +1,27 @@
package rtnetlink_test
import (
"log"
"github.com/jsimonetti/rtnetlink"
)
// List all rules
func Example_listRule() {
// Dial a connection to the rtnetlink socket
conn, err := rtnetlink.Dial(nil)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
// Request a list of rules
rules, err := conn.Rule.List()
if err != nil {
log.Fatal(err)
}
for _, rule := range rules {
log.Printf("%+v", rule)
}
}

14
fuzz.go
View File

@ -58,3 +58,17 @@ func FuzzNeighMessage(data []byte) int {
return 1
}
// FuzzRuleMessage will fuzz a RuleMessage
func FuzzRuleMessage(data []byte) int {
m := &RuleMessage{}
if err := (m).UnmarshalBinary(data); err != nil {
return 0
}
if _, err := m.MarshalBinary(); err != nil {
panic(err)
}
return 1
}

2
go.mod
View File

@ -6,5 +6,5 @@ require (
github.com/cilium/ebpf v0.8.1
github.com/google/go-cmp v0.5.7
github.com/mdlayher/netlink v1.6.0
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f
)

2
go.sum
View File

@ -34,6 +34,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@ -108,4 +108,32 @@ const (
RT_SCOPE_UNIVERSE = linux.RT_SCOPE_UNIVERSE
RT_SCOPE_HOST = linux.RT_SCOPE_HOST
RT_SCOPE_LINK = linux.RT_SCOPE_LINK
RTM_NEWRULE = linux.RTM_NEWRULE
RTM_GETRULE = linux.RTM_GETRULE
RTM_DELRULE = linux.RTM_DELRULE
FRA_UNSPEC = linux.FRA_UNSPEC
FRA_DST = linux.FRA_DST
FRA_SRC = linux.FRA_SRC
FRA_IIFNAME = linux.FRA_IIFNAME
FRA_GOTO = linux.FRA_GOTO
FRA_UNUSED2 = linux.FRA_UNUSED2
FRA_PRIORITY = linux.FRA_PRIORITY
FRA_UNUSED3 = linux.FRA_UNUSED3
FRA_UNUSED4 = linux.FRA_UNUSED4
FRA_UNUSED5 = linux.FRA_UNUSED5
FRA_FWMARK = linux.FRA_FWMARK
FRA_FLOW = linux.FRA_FLOW
FRA_TUN_ID = linux.FRA_TUN_ID
FRA_SUPPRESS_IFGROUP = linux.FRA_SUPPRESS_IFGROUP
FRA_SUPPRESS_PREFIXLEN = linux.FRA_SUPPRESS_PREFIXLEN
FRA_TABLE = linux.FRA_TABLE
FRA_FWMASK = linux.FRA_FWMASK
FRA_OIFNAME = linux.FRA_OIFNAME
FRA_PAD = linux.FRA_PAD
FRA_L3MDEV = linux.FRA_L3MDEV
FRA_UID_RANGE = linux.FRA_UID_RANGE
FRA_PROTOCOL = linux.FRA_PROTOCOL
FRA_IP_PROTO = linux.FRA_IP_PROTO
FRA_SPORT_RANGE = linux.FRA_SPORT_RANGE
FRA_DPORT_RANGE = linux.FRA_DPORT_RANGE
)

View File

@ -104,4 +104,32 @@ const (
RT_SCOPE_UNIVERSE = 0x0
RT_SCOPE_HOST = 0xfe
RT_SCOPE_LINK = 0xfd
RTM_NEWRULE = 0x20
RTM_GETRULE = 0x22
RTM_DELRULE = 0x21
FRA_UNSPEC = 0x0
FRA_DST = 0x1
FRA_SRC = 0x2
FRA_IIFNAME = 0x3
FRA_GOTO = 0x4
FRA_UNUSED2 = 0x5
FRA_PRIORITY = 0x6
FRA_UNUSED3 = 0x7
FRA_UNUSED4 = 0x8
FRA_UNUSED5 = 0x9
FRA_FWMARK = 0xa
FRA_FLOW = 0xb
FRA_TUN_ID = 0xc
FRA_SUPPRESS_IFGROUP = 0xd
FRA_SUPPRESS_PREFIXLEN = 0xe
FRA_TABLE = 0xf
FRA_FWMASK = 0x10
FRA_OIFNAME = 0x11
FRA_PAD = 0x12
FRA_L3MDEV = 0x13
FRA_UID_RANGE = 0x14
FRA_PROTOCOL = 0x15
FRA_IP_PROTO = 0x16
FRA_SPORT_RANGE = 0x17
FRA_DPORT_RANGE = 0x18
)

394
rule.go Normal file
View File

@ -0,0 +1,394 @@
package rtnetlink
import (
"bytes"
"encoding/binary"
"errors"
"net"
"github.com/jsimonetti/rtnetlink/internal/unix"
"github.com/mdlayher/netlink"
)
var (
// errInvalidRuleMessage is returned when a RuleMessage is malformed.
errInvalidRuleMessage = errors.New("rtnetlink RuleMessage is invalid or too short")
// errInvalidRuleAttribute is returned when a RuleMessage contains an unknown attribute.
errInvalidRuleAttribute = errors.New("rtnetlink RuleMessage contains an unknown Attribute")
)
var _ Message = &RuleMessage{}
// A RuleMessage is a route netlink link message.
type RuleMessage struct {
// Address family
Family uint8
// Length of destination prefix
DstLength uint8
// Length of source prefix
SrcLength uint8
// Rule TOS
TOS uint8
// Routing table identifier
Table uint8
// Rule action
Action uint8
// Rule flags
Flags uint32
// Attributes List
Attributes *RuleAttributes
}
// MarshalBinary marshals a LinkMessage into a byte slice.
func (m *RuleMessage) MarshalBinary() ([]byte, error) {
b := make([]byte, 12)
// fib_rule_hdr
b[0] = m.Family
b[1] = m.DstLength
b[2] = m.SrcLength
b[3] = m.TOS
b[4] = m.Table
b[7] = m.Action
nativeEndian.PutUint32(b[8:12], m.Flags)
if m.Attributes != nil {
ae := netlink.NewAttributeEncoder()
ae.ByteOrder = nativeEndian
err := m.Attributes.encode(ae)
if err != nil {
return nil, err
}
a, err := ae.Encode()
if err != nil {
return nil, err
}
return append(b, a...), nil
}
return b, nil
}
// UnmarshalBinary unmarshals the contents of a byte slice into a LinkMessage.
func (m *RuleMessage) UnmarshalBinary(b []byte) error {
l := len(b)
if l < 12 {
return errInvalidRuleMessage
}
m.Family = b[0]
m.DstLength = b[1]
m.SrcLength = b[2]
m.TOS = b[3]
m.Table = b[4]
// b[5] and b[6] are reserved fields
m.Action = b[7]
m.Flags = nativeEndian.Uint32(b[8:12])
if l > 12 {
m.Attributes = &RuleAttributes{}
ad, err := netlink.NewAttributeDecoder(b[12:])
if err != nil {
return err
}
ad.ByteOrder = nativeEndian
return m.Attributes.decode(ad)
}
return nil
}
// rtMessage is an empty method to sattisfy the Message interface.
func (*RuleMessage) rtMessage() {}
// RuleService is used to retrieve rtnetlink family information.
type RuleService struct {
c *Conn
}
func (r *RuleService) execute(m Message, family uint16, flags netlink.HeaderFlags) ([]RuleMessage, error) {
msgs, err := r.c.Execute(m, family, flags)
rules := make([]RuleMessage, len(msgs))
for i := range msgs {
rules[i] = *msgs[i].(*RuleMessage)
}
return rules, err
}
// Add new rule
func (r *RuleService) Add(req *RuleMessage) error {
flags := netlink.Request | netlink.Create | netlink.Acknowledge | netlink.Excl
_, err := r.c.Execute(req, unix.RTM_NEWRULE, flags)
return err
}
// Replace or add new rule
func (r *RuleService) Replace(req *RuleMessage) error {
flags := netlink.Request | netlink.Create | netlink.Replace | netlink.Acknowledge
_, err := r.c.Execute(req, unix.RTM_NEWRULE, flags)
return err
}
// Delete existing rule
func (r *RuleService) Delete(req *RuleMessage) error {
flags := netlink.Request | netlink.Acknowledge
_, err := r.c.Execute(req, unix.RTM_DELRULE, flags)
return err
}
// Get Rule(s)
func (r *RuleService) Get(req *RuleMessage) ([]RuleMessage, error) {
flags := netlink.Request | netlink.DumpFiltered
return r.execute(req, unix.RTM_GETRULE, flags)
}
// List all rules
func (r *RuleService) List() ([]RuleMessage, error) {
flags := netlink.Request | netlink.Dump
return r.execute(&RuleMessage{}, unix.RTM_GETRULE, flags)
}
// RuleAttributes contains all attributes for a rule.
type RuleAttributes struct {
Src, Dst *net.IP
IIFName, OIFName *string
Goto *uint32
Priority *uint32
FwMark, FwMask *uint32
SrcRealm *uint16
DstRealm *uint16
TunID *uint64
Table *uint32
L3MDev *uint8
Protocol *uint8
IPProto *uint8
SuppressPrefixLen *uint32
SuppressIFGroup *uint32
UIDRange *RuleUIDRange
SPortRange *RulePortRange
DPortRange *RulePortRange
}
// unmarshalBinary unmarshals the contents of a byte slice into a RuleMessage.
func (r *RuleAttributes) decode(ad *netlink.AttributeDecoder) error {
for ad.Next() {
switch ad.Type() {
case unix.FRA_UNSPEC:
// unused
continue
case unix.FRA_DST:
r.Dst = &net.IP{}
ad.Do(decodeIP(r.Dst))
case unix.FRA_SRC:
r.Src = &net.IP{}
ad.Do(decodeIP(r.Src))
case unix.FRA_IIFNAME:
v := ad.String()
r.IIFName = &v
case unix.FRA_GOTO:
v := ad.Uint32()
r.Goto = &v
case unix.FRA_UNUSED2:
// unused
continue
case unix.FRA_PRIORITY:
v := ad.Uint32()
r.Priority = &v
case unix.FRA_UNUSED3:
// unused
continue
case unix.FRA_UNUSED4:
// unused
continue
case unix.FRA_UNUSED5:
// unused
continue
case unix.FRA_FWMARK:
v := ad.Uint32()
r.FwMark = &v
case unix.FRA_FLOW:
dst32 := ad.Uint32()
src32 := uint32(dst32 >> 16)
src32 &= 0xFFFF
dst32 &= 0xFFFF
src16 := uint16(src32)
dst16 := uint16(dst32)
r.SrcRealm = &src16
r.DstRealm = &dst16
case unix.FRA_TUN_ID:
v := ad.Uint64()
r.TunID = &v
case unix.FRA_SUPPRESS_IFGROUP:
v := ad.Uint32()
r.SuppressIFGroup = &v
case unix.FRA_SUPPRESS_PREFIXLEN:
v := ad.Uint32()
r.SuppressPrefixLen = &v
case unix.FRA_TABLE:
v := ad.Uint32()
r.Table = &v
case unix.FRA_FWMASK:
v := ad.Uint32()
r.FwMask = &v
case unix.FRA_OIFNAME:
v := ad.String()
r.OIFName = &v
case unix.FRA_PAD:
// unused
continue
case unix.FRA_L3MDEV:
v := ad.Uint8()
r.L3MDev = &v
case unix.FRA_UID_RANGE:
r.UIDRange = &RuleUIDRange{}
err := r.UIDRange.unmarshalBinary(ad.Bytes())
if err != nil {
return err
}
case unix.FRA_PROTOCOL:
v := ad.Uint8()
r.Protocol = &v
case unix.FRA_IP_PROTO:
v := ad.Uint8()
r.IPProto = &v
case unix.FRA_SPORT_RANGE:
r.SPortRange = &RulePortRange{}
err := r.SPortRange.unmarshalBinary(ad.Bytes())
if err != nil {
return err
}
case unix.FRA_DPORT_RANGE:
r.DPortRange = &RulePortRange{}
err := r.DPortRange.unmarshalBinary(ad.Bytes())
if err != nil {
return err
}
default:
return errInvalidRuleAttribute
}
}
return ad.Err()
}
// MarshalBinary marshals a RuleAttributes into a byte slice.
func (r *RuleAttributes) encode(ae *netlink.AttributeEncoder) error {
if r.Table != nil {
ae.Uint32(unix.FRA_TABLE, *r.Table)
}
if r.Protocol != nil {
ae.Uint8(unix.FRA_PROTOCOL, *r.Protocol)
}
if r.Src != nil {
ae.Do(unix.FRA_SRC, encodeIP(*r.Src))
}
if r.Dst != nil {
ae.Do(unix.FRA_DST, encodeIP(*r.Dst))
}
if r.IIFName != nil {
ae.String(unix.FRA_IIFNAME, *r.IIFName)
}
if r.OIFName != nil {
ae.String(unix.FRA_OIFNAME, *r.OIFName)
}
if r.Goto != nil {
ae.Uint32(unix.FRA_GOTO, *r.Goto)
}
if r.Priority != nil {
ae.Uint32(unix.FRA_PRIORITY, *r.Priority)
}
if r.FwMark != nil {
ae.Uint32(unix.FRA_FWMARK, *r.FwMark)
}
if r.FwMask != nil {
ae.Uint32(unix.FRA_FWMASK, *r.FwMask)
}
if r.DstRealm != nil {
value := uint32(*r.DstRealm)
if r.SrcRealm != nil {
value |= (uint32(*r.SrcRealm&0xFFFF) << 16)
}
ae.Uint32(unix.FRA_FLOW, value)
}
if r.TunID != nil {
ae.Uint64(unix.FRA_TUN_ID, *r.TunID)
}
if r.L3MDev != nil {
ae.Uint8(unix.FRA_L3MDEV, *r.L3MDev)
}
if r.IPProto != nil {
ae.Uint8(unix.FRA_IP_PROTO, *r.IPProto)
}
if r.SuppressIFGroup != nil {
ae.Uint32(unix.FRA_SUPPRESS_IFGROUP, *r.SuppressIFGroup)
}
if r.SuppressPrefixLen != nil {
ae.Uint32(unix.FRA_SUPPRESS_PREFIXLEN, *r.SuppressPrefixLen)
}
if r.UIDRange != nil {
data, err := marshalRuleUIDRange(*r.UIDRange)
if err != nil {
return err
}
ae.Bytes(unix.FRA_UID_RANGE, data)
}
if r.SPortRange != nil {
data, err := marshalRulePortRange(*r.SPortRange)
if err != nil {
return err
}
ae.Bytes(unix.FRA_SPORT_RANGE, data)
}
if r.DPortRange != nil {
data, err := marshalRulePortRange(*r.DPortRange)
if err != nil {
return err
}
ae.Bytes(unix.FRA_DPORT_RANGE, data)
}
return nil
}
// RulePortRange defines start and end ports for a rule
type RulePortRange struct {
Start, End uint16
}
func (r *RulePortRange) unmarshalBinary(data []byte) error {
b := bytes.NewReader(data)
return binary.Read(b, nativeEndian, r)
}
func marshalRulePortRange(s RulePortRange) ([]byte, error) {
var buf bytes.Buffer
err := binary.Write(&buf, nativeEndian, s)
return buf.Bytes(), err
}
// RuleUIDRange defines the start and end for UID matches
type RuleUIDRange struct {
Start, End uint16
}
func (r *RuleUIDRange) unmarshalBinary(data []byte) error {
b := bytes.NewReader(data)
return binary.Read(b, nativeEndian, r)
}
func marshalRuleUIDRange(s RuleUIDRange) ([]byte, error) {
var buf bytes.Buffer
err := binary.Write(&buf, nativeEndian, s)
return buf.Bytes(), err
}

200
rule_test.go Normal file
View File

@ -0,0 +1,200 @@
package rtnetlink
import (
"bytes"
"errors"
"net"
"reflect"
"testing"
)
func TestRuleMessage(t *testing.T) {
skipBigEndian(t)
tests := map[string]struct {
m Message
b []byte
marshalErr error
unmarshalErr error
}{
"empty": {
m: &RuleMessage{},
b: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
"no attributes": {
m: &RuleMessage{
Family: 1,
DstLength: 2,
SrcLength: 3,
TOS: 4,
Table: 5,
Action: 6,
Flags: 7,
},
b: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 0x00},
},
"with attributes": {
m: &RuleMessage{
Family: 7,
DstLength: 6,
SrcLength: 5,
TOS: 4,
Table: 3,
Action: 2,
Flags: 1,
Attributes: &RuleAttributes{
Src: netIPPtr(net.ParseIP("8.8.8.8")),
Dst: netIPPtr(net.ParseIP("1.1.1.1")),
IIFName: strPtr("eth0"),
OIFName: strPtr("br0"),
Goto: uint32Ptr(1),
Priority: uint32Ptr(2),
FwMark: uint32Ptr(3),
FwMask: uint32Ptr(5),
L3MDev: uint8Ptr(7),
DstRealm: uint16Ptr(11),
SrcRealm: uint16Ptr(13),
TunID: uint64Ptr(17),
Protocol: uint8Ptr(19),
IPProto: uint8Ptr(23),
Table: uint32Ptr(29),
SuppressPrefixLen: uint32Ptr(31),
SuppressIFGroup: uint32Ptr(37),
UIDRange: &RuleUIDRange{
Start: 22,
End: 25,
},
SPortRange: &RulePortRange{
Start: 23,
End: 26,
},
DPortRange: &RulePortRange{
Start: 24,
End: 27,
},
},
},
b: []byte{
0x07, 0x06, 0x05, 0x04, 0x03, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00,
0x0f, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, 0x13, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01,
0x01, 0x01, 0x09, 0x00, 0x03, 0x00, 0x65, 0x74, 0x68, 0x30, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x11, 0x00, 0x62, 0x72, 0x30, 0x00, 0x08, 0x00, 0x04, 0x00, 0x01, 0x00,
0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00,
0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00,
0x0b, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x07, 0x00, 0x00, 0x00, 0x05, 0x00,
0x16, 0x00, 0x17, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0x25, 0x00, 0x00, 0x00,
0x08, 0x00, 0x0e, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x16, 0x00,
0x19, 0x00, 0x08, 0x00, 0x17, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x08, 0x00, 0x18, 0x00,
0x18, 0x00, 0x1b, 0x00,
},
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
var b []byte
t.Run("marshal", func(t *testing.T) {
var marshalErr error
b, marshalErr = tt.m.MarshalBinary()
if !errors.Is(marshalErr, tt.marshalErr) {
t.Fatalf("Expected error '%v' but got '%v'", tt.marshalErr, marshalErr)
}
})
t.Run("compare bytes", func(t *testing.T) {
if want, got := tt.b, b; !bytes.Equal(want, got) {
t.Fatalf("unexpected Message bytes:\n- want: [%# x]\n- got: [%# x]", want, got)
}
})
m := &RuleMessage{}
t.Run("unmarshal", func(t *testing.T) {
unmarshalErr := (m).UnmarshalBinary(b)
if !errors.Is(unmarshalErr, tt.unmarshalErr) {
t.Fatalf("Expected error '%v' but got '%v'", tt.unmarshalErr, unmarshalErr)
}
})
t.Run("compare messages", func(t *testing.T) {
if !reflect.DeepEqual(tt.m, m) {
t.Fatalf("unexpected Message:\n- want: %#v\n- got: %#v", tt.m, m)
}
})
})
}
t.Run("invalid length", func(t *testing.T) {
m := &RuleMessage{}
unmarshalErr := (m).UnmarshalBinary([]byte{0x00, 0x01, 0x2, 0x03})
if !errors.Is(unmarshalErr, errInvalidRuleMessage) {
t.Fatalf("Expected 'errInvalidRuleMessage' but got '%v'", unmarshalErr)
}
})
t.Run("skipped attributes", func(t *testing.T) {
m := &RuleMessage{}
unmarshalErr := (m).UnmarshalBinary([]byte{
0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x04, 0x00, 0x05, 0x00, 0x04, 0x00, 0x07, 0x00, 0x04, 0x00, 0x08, 0x00,
0x04, 0x00, 0x09, 0x00, 0x04, 0x00, 0x12, 0x00,
})
if !errors.Is(unmarshalErr, nil) {
t.Fatalf("Expected no error but got '%v'", unmarshalErr)
}
expected := &RuleMessage{
Family: 1,
TOS: 2,
Table: 3,
Action: 4,
Flags: 5,
Attributes: &RuleAttributes{},
}
if !reflect.DeepEqual(expected, m) {
t.Fatalf("unexpected Message:\n- want: %#v\n- got: %#v", expected, m)
}
})
t.Run("invalid attribute", func(t *testing.T) {
m := &RuleMessage{}
unmarshalErr := (m).UnmarshalBinary([]byte{
0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x04, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00,
0x2a, 0x00,
})
if !errors.Is(unmarshalErr, errInvalidRuleAttribute) {
t.Fatalf("Expected 'errInvalidRuleAttribute' error but got '%v'", unmarshalErr)
}
})
}
func uint64Ptr(v uint64) *uint64 {
return &v
}
func uint32Ptr(v uint32) *uint32 {
return &v
}
func uint16Ptr(v uint16) *uint16 {
return &v
}
func uint8Ptr(v uint8) *uint8 {
return &v
}
func netIPPtr(v net.IP) *net.IP {
if ip4 := v.To4(); ip4 != nil {
// By default net.IP returns the 16 byte representation.
// But netlink requires us to provide only four bytes
// for legacy IPs.
return &ip4
}
return &v
}
func strPtr(v string) *string {
return &v
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More