Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions nl/seg6local_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,39 @@ const (
SEG6_LOCAL_OIF
SEG6_LOCAL_BPF
SEG6_LOCAL_VRFTABLE
SEG6_LOCAL_COUNTERS
SEG6_LOCAL_FLAVORS
__SEG6_LOCAL_MAX
)
const (
SEG6_LOCAL_MAX = __SEG6_LOCAL_MAX
)

// seg6local flavors (SEG6_LOCAL_FLAVORS nested attributes)
const (
SEG6_LOCAL_FLV_UNSPEC = iota
SEG6_LOCAL_FLV_OPERATION
SEG6_LOCAL_FLV_LCBLOCK_BITS
SEG6_LOCAL_FLV_LCNODE_FN_BITS
__SEG6_LOCAL_FLV_MAX
)
const (
SEG6_LOCAL_FLV_MAX = __SEG6_LOCAL_FLV_MAX - 1
)

// seg6local flavor operations
const (
SEG6_LOCAL_FLV_OP_UNSPEC = iota
SEG6_LOCAL_FLV_OP_PSP
SEG6_LOCAL_FLV_OP_USP
SEG6_LOCAL_FLV_OP_USD
SEG6_LOCAL_FLV_OP_NEXT_CSID
__SEG6_LOCAL_FLV_OP_MAX
)
const (
SEG6_LOCAL_FLV_OP_MAX = __SEG6_LOCAL_FLV_OP_MAX - 1
)

// seg6local actions
const (
SEG6_LOCAL_ACTION_END = iota + 1 // 1
Expand Down Expand Up @@ -80,3 +107,17 @@ func SEG6LocalActionString(action int) string {
}
return "unknown"
}

func SEG6LocalFlavorOperationString(op int) string {
switch op {
case SEG6_LOCAL_FLV_OP_PSP:
return "PSP"
case SEG6_LOCAL_FLV_OP_USP:
return "USP"
case SEG6_LOCAL_FLV_OP_USD:
return "USD"
case SEG6_LOCAL_FLV_OP_NEXT_CSID:
return "NEXT-CSID"
}
return "unknown"
}
41 changes: 40 additions & 1 deletion route_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,16 @@ type SEG6LocalEncap struct {
Iif int
Oif int
bpf bpfObj
Flavors SEG6LocalFlavors // flavors (e.g. NEXT-CSID) applied to the action
}

// SEG6LocalFlavors carries the SEG6_LOCAL_FLAVORS sub-attributes: the
// flavor operation and, for NEXT-CSID, the locator block/node function
// bit lengths.
type SEG6LocalFlavors struct {
Operation uint32
LcBlockBits uint8
LcNodeFnBits uint8
}

func (e *SEG6LocalEncap) SetProg(progFd int, progName string) error {
Expand Down Expand Up @@ -343,6 +353,24 @@ func (e *SEG6LocalEncap) Decode(buf []byte) error {
}
e.bpf = bpfobj
e.Flags[nl.SEG6_LOCAL_BPF] = true
case nl.SEG6_LOCAL_FLAVORS:
var flvAttrs []syscall.NetlinkRouteAttr
flvAttrs, err = nl.ParseRouteAttr(attr.Value)
flavors := SEG6LocalFlavors{}
for _, flvAttr := range flvAttrs {
switch flvAttr.Attr.Type {
case nl.SEG6_LOCAL_FLV_OPERATION:
flavors.Operation = native.Uint32(flvAttr.Value)
case nl.SEG6_LOCAL_FLV_LCBLOCK_BITS:
flavors.LcBlockBits = flvAttr.Value[0]
case nl.SEG6_LOCAL_FLV_LCNODE_FN_BITS:
flavors.LcNodeFnBits = flvAttr.Value[0]
default:
err = fmt.Errorf("seg6local flavors decode: unknown attribute: Type %d", flvAttr.Attr)
}
}
e.Flavors = flavors
e.Flags[nl.SEG6_LOCAL_FLAVORS] = true
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
return err
Expand Down Expand Up @@ -423,6 +451,13 @@ func (e *SEG6LocalEncap) Encode() ([]byte, error) {
}
res = append(res, attr.Serialize()...)
}
if e.Flags[nl.SEG6_LOCAL_FLAVORS] {
attr := nl.NewRtAttr(nl.SEG6_LOCAL_FLAVORS, []byte{})
attr.AddRtAttr(nl.SEG6_LOCAL_FLV_OPERATION, nl.Uint32Attr(e.Flavors.Operation))
attr.AddRtAttr(nl.SEG6_LOCAL_FLV_LCBLOCK_BITS, nl.Uint8Attr(e.Flavors.LcBlockBits))
attr.AddRtAttr(nl.SEG6_LOCAL_FLV_LCNODE_FN_BITS, nl.Uint8Attr(e.Flavors.LcNodeFnBits))
res = append(res, attr.Serialize()...)
}
return res, err
}
func (e *SEG6LocalEncap) String() string {
Expand Down Expand Up @@ -470,6 +505,10 @@ func (e *SEG6LocalEncap) String() string {
if e.Flags[nl.SEG6_LOCAL_BPF] {
strs = append(strs, fmt.Sprintf("bpf %s[%d]", e.bpf.progName, e.bpf.progFd))
}
if e.Flags[nl.SEG6_LOCAL_FLAVORS] {
strs = append(strs, fmt.Sprintf("flavors %s lblen %d nflen %d",
nl.SEG6LocalFlavorOperationString(int(e.Flavors.Operation)), e.Flavors.LcBlockBits, e.Flavors.LcNodeFnBits))
}
return strings.Join(strs, " ")
}
func (e *SEG6LocalEncap) Equal(x Encap) bool {
Expand Down Expand Up @@ -501,7 +540,7 @@ func (e *SEG6LocalEncap) Equal(x Encap) bool {
if !e.InAddr.Equal(o.InAddr) || !e.In6Addr.Equal(o.In6Addr) {
return false
}
if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif || e.bpf != o.bpf || e.VrfTable != o.VrfTable {
if e.Action != o.Action || e.Table != o.Table || e.Iif != o.Iif || e.Oif != o.Oif || e.bpf != o.bpf || e.VrfTable != o.VrfTable || e.Flavors != o.Flavors {
return false
}
return true
Expand Down
98 changes: 98 additions & 0 deletions route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,104 @@ func TestSEG6LocalEqual(t *testing.T) {
}
}
}

func TestSEG6LocalEncapFlavors(t *testing.T) {
var flagsEnd [nl.SEG6_LOCAL_MAX]bool
flagsEnd[nl.SEG6_LOCAL_ACTION] = true

var flagsEndDt46 [nl.SEG6_LOCAL_MAX]bool
flagsEndDt46[nl.SEG6_LOCAL_ACTION] = true
flagsEndDt46[nl.SEG6_LOCAL_VRFTABLE] = true

var flagsEndFlavors [nl.SEG6_LOCAL_MAX]bool
flagsEndFlavors[nl.SEG6_LOCAL_ACTION] = true
flagsEndFlavors[nl.SEG6_LOCAL_FLAVORS] = true

testCases := []struct {
name string
encap *SEG6LocalEncap
wantString string
}{
{
name: "bare End action without flavors",
encap: &SEG6LocalEncap{
Flags: flagsEnd,
Action: nl.SEG6_LOCAL_ACTION_END,
},
wantString: "action End",
},
{
name: "End.DT46 with vrftable only, no flavors",
encap: &SEG6LocalEncap{
Flags: flagsEndDt46,
Action: nl.SEG6_LOCAL_ACTION_END_DT46,
VrfTable: 50,
},
// SEG6LocalActionString has no case for END_DT46; this documents
// existing behavior, unrelated to flavors.
wantString: "action unknown vrftable 50",
},
{
name: "End with NEXT-CSID flavor, lblen 32 nflen 16",
encap: &SEG6LocalEncap{
Flags: flagsEndFlavors,
Action: nl.SEG6_LOCAL_ACTION_END,
Flavors: SEG6LocalFlavors{
Operation: nl.SEG6_LOCAL_FLV_OP_NEXT_CSID,
LcBlockBits: 32,
LcNodeFnBits: 16,
},
},
wantString: "action End flavors NEXT-CSID lblen 32 nflen 16",
},
{
name: "End with NEXT-CSID flavor, lblen 48 nflen 0",
encap: &SEG6LocalEncap{
Flags: flagsEndFlavors,
Action: nl.SEG6_LOCAL_ACTION_END,
Flavors: SEG6LocalFlavors{
Operation: nl.SEG6_LOCAL_FLV_OP_NEXT_CSID,
LcBlockBits: 48,
LcNodeFnBits: 0,
},
},
wantString: "action End flavors NEXT-CSID lblen 48 nflen 0",
},
{
// The kernel is the source of truth for which operation values are
// valid; Decode/Encode round-trip the raw value without validating it.
name: "End with an unsupported flavor operation",
encap: &SEG6LocalEncap{
Flags: flagsEndFlavors,
Action: nl.SEG6_LOCAL_ACTION_END,
Flavors: SEG6LocalFlavors{
Operation: 99,
},
},
wantString: "action End flavors unknown lblen 0 nflen 0",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
buf, err := tc.encap.Encode()
if err != nil {
t.Fatal(err)
}
decoded := &SEG6LocalEncap{}
if err := decoded.Decode(buf); err != nil {
t.Fatal(err)
}
if !decoded.Equal(tc.encap) {
t.Fatalf("Decode(Encode(encap)) = %v, want %v", decoded, tc.encap)
}
if got := tc.encap.String(); got != tc.wantString {
t.Fatalf("String() = %q, want %q", got, tc.wantString)
}
})
}
}

func TestSEG6RouteAddDel(t *testing.T) {
// add/del routes with LWTUNNEL_SEG6 to/from interface.
// Test both seg6 modes: encap (IPv4) & inline (IPv6).
Expand Down
Loading