Skip to content

Commit

Permalink
Add Driver Interface for Enhanced Data Encoding
Browse files Browse the repository at this point in the history
This commit implements significant changes by introducing a Driver interface, fundamentally altering the LinkInfo struct.
The Data and SlaveData fields are replaced in a non-backward-compatible manner to support driver-specific data encoding.
This update addresses the previous limitation where these fields were only byte slices, lacking the capability for detailed data encoding.

LinkNetkit is implemented as a Driver interface.
During the decoding process, data identified as "netkit" is now explicitly converted into a LinkNetkit struct.
Conversely, data not matching recognized types is assigned to a LinkUndefined struct.

Important: This update introduces breaking changes and will require a major version update of the library.

Signed-off-by: Birol Bilgin <[email protected]>
  • Loading branch information
brlbil committed Apr 3, 2024
1 parent e96eaa2 commit 7e4eabe
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 11 deletions.
12 changes: 12 additions & 0 deletions internal/unix/types_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ const (
IFLA_INFO_SLAVE_KIND = linux.IFLA_INFO_SLAVE_KIND
IFLA_INFO_DATA = linux.IFLA_INFO_DATA
IFLA_INFO_SLAVE_DATA = linux.IFLA_INFO_SLAVE_DATA
IFLA_NETKIT_UNSPEC = linux.IFLA_NETKIT_UNSPEC
IFLA_NETKIT_PEER_INFO = linux.IFLA_NETKIT_PEER_INFO
IFLA_NETKIT_PRIMARY = linux.IFLA_NETKIT_PRIMARY
IFLA_NETKIT_POLICY = linux.IFLA_NETKIT_POLICY
IFLA_NETKIT_PEER_POLICY = linux.IFLA_NETKIT_PEER_POLICY
IFLA_NETKIT_MODE = linux.IFLA_NETKIT_MODE
IFLA_XDP = linux.IFLA_XDP
IFLA_XDP_FD = linux.IFLA_XDP_FD
IFLA_XDP_ATTACHED = linux.IFLA_XDP_ATTACHED
Expand Down Expand Up @@ -147,4 +153,10 @@ const (
FRA_IP_PROTO = linux.FRA_IP_PROTO
FRA_SPORT_RANGE = linux.FRA_SPORT_RANGE
FRA_DPORT_RANGE = linux.FRA_DPORT_RANGE
NETKIT_NEXT = linux.NETKIT_NEXT
NETKIT_PASS = linux.NETKIT_PASS
NETKIT_DROP = linux.NETKIT_DROP
NETKIT_REDIRECT = linux.NETKIT_REDIRECT
NETKIT_L2 = linux.NETKIT_L2
NETKIT_L3 = linux.NETKIT_L3
)
12 changes: 12 additions & 0 deletions internal/unix/types_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ const (
IFLA_INFO_SLAVE_KIND = 0x4
IFLA_INFO_DATA = 0x2
IFLA_INFO_SLAVE_DATA = 0x5
IFLA_NETKIT_UNSPEC = 0x0
IFLA_NETKIT_PEER_INFO = 0x1
IFLA_NETKIT_PRIMARY = 0x2
IFLA_NETKIT_POLICY = 0x3
IFLA_NETKIT_PEER_POLICY = 0x4
IFLA_NETKIT_MODE = 0x5
IFLA_XDP = 0x2b
IFLA_XDP_FD = 0x1
IFLA_XDP_ATTACHED = 0x2
Expand Down Expand Up @@ -143,4 +149,10 @@ const (
FRA_IP_PROTO = 0x16
FRA_SPORT_RANGE = 0x17
FRA_DPORT_RANGE = 0x18
NETKIT_NEXT = -0x1
NETKIT_PASS = 0x0
NETKIT_DROP = 0x2
NETKIT_REDIRECT = 0x7
NETKIT_L2 = 0x0
NETKIT_L3 = 0x1
)
48 changes: 41 additions & 7 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,9 +550,9 @@ func (a *LinkStats64) unmarshalBinary(b []byte) error {
// LinkInfo contains data for specific network types
type LinkInfo struct {
Kind string // Driver name
Data []byte // Driver specific configuration stored as nested Netlink messages
Data Driver // Driver specific configuration stored as nested Netlink messages
SlaveKind string // Slave driver name
SlaveData []byte // Slave driver specific configuration
SlaveData Driver // Slave driver specific configuration
}

func (i *LinkInfo) decode(ad *netlink.AttributeDecoder) error {
Expand All @@ -563,9 +563,19 @@ func (i *LinkInfo) decode(ad *netlink.AttributeDecoder) error {
case unix.IFLA_INFO_SLAVE_KIND:
i.SlaveKind = ad.String()
case unix.IFLA_INFO_DATA:
i.Data = ad.Bytes()
i.Data = getDriver(i.Kind)
if lu, ok := i.Data.(*LinkData); ok {
lu.Data = ad.Bytes()
} else {
ad.Nested(i.Data.decode)
}
case unix.IFLA_INFO_SLAVE_DATA:
i.SlaveData = ad.Bytes()
i.SlaveData = getDriver(i.SlaveKind)
if lu, ok := i.SlaveData.(*LinkData); ok {
lu.Data = ad.Bytes()
} else {
ad.Nested(i.SlaveData.decode)
}
}
}

Expand All @@ -574,11 +584,35 @@ func (i *LinkInfo) decode(ad *netlink.AttributeDecoder) error {

func (i *LinkInfo) encode(ae *netlink.AttributeEncoder) error {
ae.String(unix.IFLA_INFO_KIND, i.Kind)
ae.Bytes(unix.IFLA_INFO_DATA, i.Data)
if i.Data != nil {
if lu, ok := i.Data.(*LinkData); ok {
ae.Bytes(unix.IFLA_INFO_DATA, lu.Data)
} else {
ae.Do(unix.IFLA_INFO_DATA, func() ([]byte, error) {
nae := netlink.NewAttributeEncoder()
nae.ByteOrder = ae.ByteOrder
if err := i.encode(nae); err != nil {
return nil, err
}
return nae.Encode()
})
}
}

if len(i.SlaveData) > 0 {
if i.SlaveData != nil {
ae.String(unix.IFLA_INFO_SLAVE_KIND, i.SlaveKind)
ae.Bytes(unix.IFLA_INFO_SLAVE_DATA, i.SlaveData)
if lu, ok := i.Data.(*LinkData); ok {
ae.Bytes(unix.IFLA_INFO_SLAVE_DATA, lu.Data)
} else {
ae.Do(unix.IFLA_INFO_SLAVE_DATA, func() ([]byte, error) {
nae := netlink.NewAttributeEncoder()
nae.ByteOrder = ae.ByteOrder
if err := i.SlaveData.encode(nae); err != nil {
return nil, err
}
return nae.Encode()
})
}
}

return nil
Expand Down
88 changes: 88 additions & 0 deletions link_driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package rtnetlink

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

type Driver interface {
encode(*netlink.AttributeEncoder) error
decode(*netlink.AttributeDecoder) error
rtDriver()
}

func getDriver(kind string) Driver {
switch kind {
case "netkit":
return &LinkNetkit{}
default:
return &LinkData{}
}
}

// LinkNetkit holds netkit link specific information
type LinkNetkit struct {
Mode uint32
Policy int32
PeerPolicy int32
Primary bool
PeerInfo *LinkMessage
}

var _ Driver = &LinkNetkit{}

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

func (n *LinkNetkit) encode(ae *netlink.AttributeEncoder) error {
ae.Uint32(unix.IFLA_NETKIT_MODE, n.Mode)
ae.Int32(unix.IFLA_NETKIT_POLICY, n.Policy)
ae.Int32(unix.IFLA_NETKIT_PEER_POLICY, n.PeerPolicy)
if n.PeerInfo != nil {
n.PeerInfo.Family = unix.AF_UNSPEC
// we need to set the netkit.peerInfo.Flags and .Change according to primary link
// however we do not have this information here
b, err := n.PeerInfo.MarshalBinary()
if err != nil {
return err
}
ae.Bytes(unix.IFLA_NETKIT_PEER_INFO, b)
}
return nil
}

// rtDriver is an empty method to sattisfy the Driver interface.
func (n *LinkNetkit) rtDriver() {}

// LinkData is retured when the Driver kind for the LinkInfo Data is not defined
type LinkData struct {
Data []byte
}

var _ Driver = &LinkData{}

// dencode is an empty method to sattisfy the Driver interface.
func (u *LinkData) decode(ad *netlink.AttributeDecoder) error {
return nil
}

// encode is an empty method to sattisfy the Driver interface.
func (u *LinkData) encode(ae *netlink.AttributeEncoder) error {
return nil
}

// rtDriver is an empty method to sattisfy the Driver interface.
func (u *LinkData) rtDriver() {}
8 changes: 4 additions & 4 deletions link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ func TestLinkMessageMarshalBinary(t *testing.T) {
Name: "lo",
Info: &LinkInfo{
Kind: "data",
Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
Data: &LinkData{Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}},
SlaveKind: "foo",
SlaveData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
SlaveData: &LinkData{Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}},
},
},
},
Expand Down Expand Up @@ -371,9 +371,9 @@ func TestLinkMessageUnmarshalBinary(t *testing.T) {
Name: "lo",
Info: &LinkInfo{
Kind: "data",
Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
Data: &LinkData{Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}},
SlaveKind: "foo",
SlaveData: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
SlaveData: &LinkData{Data: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}},
},
},
},
Expand Down

0 comments on commit 7e4eabe

Please sign in to comment.