Skip to content

Commit

Permalink
add m_skbmod
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Lehner <dev@der-flo.net>
  • Loading branch information
florianl committed Dec 31, 2024
1 parent 01191c2 commit fa96917
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 0 deletions.
10 changes: 10 additions & 0 deletions m_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type Action struct {
TunnelKey *TunnelKey
MPLS *MPLS
SkbEdit *SkbEdit
SkbMod *SkbMod
}

func unmarshalActions(data []byte, actions *[]*Action) error {
Expand Down Expand Up @@ -243,6 +244,10 @@ func marshalAction(cmd int, info *Action, actOption uint16) ([]byte, error) {
data, err := marshalSkbEdit(info.SkbEdit)
multiError = concatError(multiError, err)
options = append(options, tcOption{Interpretation: vtBytes, Type: actOption, Data: data})
case "skbmod":
data, err := marshalSkbMod(info.SkbMod)
multiError = concatError(multiError, err)
options = append(options, tcOption{Interpretation: vtBytes, Type: actOption, Data: data})
default:
return []byte{}, fmt.Errorf("unknown kind '%s'", info.Kind)
}
Expand Down Expand Up @@ -362,6 +367,11 @@ func extractActOptions(data []byte, act *Action, kind string) error {
err = unmarshalSkbEdit(data, info)
multiError = concatError(multiError, err)
act.SkbEdit = info
case "skbmod":
info := &SkbMod{}
err = unmarshalSkbMod(data, info)
multiError = concatError(multiError, err)
act.SkbMod = info
default:
return fmt.Errorf("extractActOptions(): unsupported kind: %s", kind)

Expand Down
4 changes: 4 additions & 0 deletions m_action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ func TestAction(t *testing.T) {
Kind: "skbedit",
SkbEdit: &SkbEdit{Priority: uint32Ptr(42)},
}},
"skbmod": {val: Action{
Kind: "skbmod",
SkbMod: &SkbMod{EType: uint16Ptr(73)},
}},
}

for name, testcase := range tests {
Expand Down
102 changes: 102 additions & 0 deletions m_skbmod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package tc

import (
"fmt"
"net"

"github.com/mdlayher/netlink"
)

const (
tcaSkbModUnspec = iota
tcaSkbModTm
tcaSkbModParms
tcaSkbModDMac
tcaSkbModSMac
tcaSkbModEType
tcaSkbModPad
)

// SkbMod contains attribute of thet SkbMod discipline
type SkbMod struct {
Tm *Tcft
Parms *SkbModParms
DMac *net.HardwareAddr
SMac *net.HardwareAddr
EType *uint16
}

// SkbModParms from include/uapi/linux/tc_act/tc_skbmod.h
type SkbModParms struct {
Index uint32
Capab uint32
Action uint32
RefCnt uint32
BindCnt uint32
Flags uint64
}

func marshalSkbMod(info *SkbMod) ([]byte, error) {
options := []tcOption{}
if info == nil {
return []byte{}, fmt.Errorf("skbmod: %w", ErrNoArg)
}
// TODO: improve logic and check combinations
if info.Tm != nil {
return []byte{}, ErrNoArgAlter
}

if info.Parms != nil {
data, err := marshalStruct(info.Parms)
if err != nil {
return []byte{}, err
}
options = append(options, tcOption{Interpretation: vtBytes, Type: tcaSkbModParms, Data: data})
}
if info.DMac != nil {
options = append(options, tcOption{Interpretation: vtBytes, Type: tcaSkbModDMac, Data: hardwareAddrToBytes(*info.DMac)})
}
if info.SMac != nil {
options = append(options, tcOption{Interpretation: vtBytes, Type: tcaSkbModSMac, Data: hardwareAddrToBytes(*info.SMac)})
}
if info.EType != nil {
options = append(options, tcOption{Interpretation: vtUint16, Type: tcaSkbModEType, Data: *info.EType})
}

return marshalAttributes(options)
}

func unmarshalSkbMod(data []byte, info *SkbMod) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
var multiError error
for ad.Next() {
switch ad.Type() {
case tcaSkbModParms:
parms := &SkbModParms{}
err = unmarshalStruct(ad.Bytes(), parms)
multiError = concatError(multiError, err)
info.Parms = parms
case tcaSkbModTm:
tcft := &Tcft{}
err = unmarshalStruct(ad.Bytes(), tcft)
multiError = concatError(multiError, err)
info.Tm = tcft
case tcaSkbModDMac:
mac := bytesToHardwareAddr(ad.Bytes())
info.DMac = &mac
case tcaSkbModSMac:
mac := bytesToHardwareAddr(ad.Bytes())
info.SMac = &mac
case tcaSkbModEType:
info.EType = uint16Ptr(ad.Uint16())
case tcaSkbModPad:
// padding does not contain data, we just skip it
default:
return fmt.Errorf("unmarshalSkbMod()\t%d\n\t%v", ad.Type(), ad.Bytes())
}
}
return concatError(multiError, ad.Err())
}
59 changes: 59 additions & 0 deletions m_skbmod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tc

import (
"errors"
"net"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestSkbMod(t *testing.T) {
srcMac, _ := net.ParseMAC("00:00:5e:00:53:01")
dstMac, _ := net.ParseMAC("00:00:5e:00:53:02")
tests := map[string]struct {
val SkbMod
err1 error
err2 error
}{
"simple": {val: SkbMod{
Parms: &SkbModParms{Index: 42},
SMac: &srcMac,
DMac: &dstMac,
EType: uint16Ptr(13)}},
}

for name, testcase := range tests {
t.Run(name, func(t *testing.T) {
data, err1 := marshalSkbMod(&testcase.val)
if err1 != nil {
if errors.Is(err1, testcase.err1) {
return
}
t.Fatalf("Unexpected error: %v", err1)
}

newData, tm := injectTcft(t, data, tcaSkbModTm)
newData = injectAttribute(t, newData, []byte{}, tcaSkbModPad)
val := SkbMod{}
err2 := unmarshalSkbMod(newData, &val)
if err2 != nil {
if errors.Is(err2, testcase.err2) {
return
}
t.Fatalf("Unexpected error: %v", err2)

}
testcase.val.Tm = tm
if diff := cmp.Diff(val, testcase.val); diff != "" {
t.Fatalf("Defact missmatch (want +got):\n%s", diff)
}
})
}
t.Run("nil", func(t *testing.T) {
_, err := marshalSkbMod(nil)
if !errors.Is(err, ErrNoArg) {
t.Fatalf("unexpected error: %v", err)
}
})
}

0 comments on commit fa96917

Please sign in to comment.