From b8655c29806cb0ffe8a20266529e0479809af62a Mon Sep 17 00:00:00 2001 From: Teja Tamboli Date: Fri, 12 Jun 2026 17:35:43 +0000 Subject: [PATCH] Add support for handling hop-by-hop packets in Lucius --- dataplane/forwarding/fwdconfig/table.go | 13 ++++++-- dataplane/saiserver/hostif_test.go | 9 +++--- dataplane/saiserver/switch.go | 32 ++++++++++++++++++- .../dataplane/mplsoudp/mplsoverudp_test.go | 18 ++++++----- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/dataplane/forwarding/fwdconfig/table.go b/dataplane/forwarding/fwdconfig/table.go index 8dfa5aed5..dc68ec80d 100644 --- a/dataplane/forwarding/fwdconfig/table.go +++ b/dataplane/forwarding/fwdconfig/table.go @@ -203,7 +203,8 @@ func (b ActionEntryBuilder) set(ed *fwdpb.EntryDesc) { // FlowEntryBuilder builds flow table entries. type FlowEntryBuilder struct { - fields []*PacketFieldMaskedBytesBuilder + fields []*PacketFieldMaskedBytesBuilder + priority uint32 } // FlowEntry creates a new flow entry builder. @@ -213,8 +214,16 @@ func FlowEntry(fields ...*PacketFieldMaskedBytesBuilder) *FlowEntryBuilder { } } +// WithPriority sets the priority of the flow entry. +func (eeb *FlowEntryBuilder) WithPriority(priority uint32) *FlowEntryBuilder { + eeb.priority = priority + return eeb +} + func (eeb FlowEntryBuilder) set(ed *fwdpb.EntryDesc) { - flow := &fwdpb.FlowEntryDesc{} + flow := &fwdpb.FlowEntryDesc{ + Priority: eeb.priority, + } for _, b := range eeb.fields { flow.Fields = append(flow.Fields, b.Build()) } diff --git a/dataplane/saiserver/hostif_test.go b/dataplane/saiserver/hostif_test.go index f5b4bd2e4..7e01e671a 100644 --- a/dataplane/saiserver/hostif_test.go +++ b/dataplane/saiserver/hostif_test.go @@ -349,10 +349,11 @@ func createPacket(t testing.TB, nid uint64) fwdpacket.Packet { EthernetType: layers.EthernetTypeIPv6, } ip := &layers.IPv6{ - Version: 6, - SrcIP: net.ParseIP("2003::9"), - DstIP: net.ParseIP("2003::10"), - HopLimit: 255, + Version: 6, + SrcIP: net.ParseIP("2003::9"), + DstIP: net.ParseIP("2003::10"), + HopLimit: 255, + NextHeader: layers.IPProtocolUDP, } payload := gopacket.Payload([]byte("hello world")) buf := gopacket.NewSerializeBuffer() diff --git a/dataplane/saiserver/switch.go b/dataplane/saiserver/switch.go index 9bede9005..7ada9f3d1 100644 --- a/dataplane/saiserver/switch.go +++ b/dataplane/saiserver/switch.go @@ -992,7 +992,7 @@ func (sw *saiSwitch) createInvalidPacketFilter(ctx context.Context) error { } req := fwdconfig.TableEntryAddRequest(sw.dataplane.ID(), table). AppendEntry( - fwdconfig.EntryDesc(fwdconfig.FlowEntry(fwdconfig.PacketFieldMaskedBytes(field).WithBytes(prefix.IP, prefix.Mask))), + fwdconfig.EntryDesc(fwdconfig.FlowEntry(fwdconfig.PacketFieldMaskedBytes(field).WithBytes(prefix.IP, prefix.Mask)).WithPriority(20)), fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_BIT_WRITE, fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_ACTION).WithBitOp(1, 0).WithValue([]byte{0}), ).Build() if _, err := sw.dataplane.TableEntryAdd(ctx, req); err != nil { @@ -1000,6 +1000,36 @@ func (sw *saiSwitch) createInvalidPacketFilter(ctx context.Context) error { } } } + + if table == invalidIngressV6Table { + // Drop Hop-by-Hop options with unicast destination if not already trapped (PACKET_ACTION == 1). + // Rule 1: Multicast (dst starts with 0xFF) -> Continue + reqHopByHopMC := fwdconfig.TableEntryAddRequest(sw.dataplane.ID(), table). + AppendEntry( + fwdconfig.EntryDesc(fwdconfig.FlowEntry( + fwdconfig.PacketFieldMaskedBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_PROTO).WithBytes([]byte{0}, []byte{0xFF}), + fwdconfig.PacketFieldMaskedBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_ADDR_DST).WithBytes([]byte{0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, []byte{0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), + fwdconfig.PacketFieldMaskedBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_ACTION).WithBytes([]byte{1}, []byte{0xFF}), + ).WithPriority(10)), + fwdconfig.ContinueAction(), + ).Build() + if _, err := sw.dataplane.TableEntryAdd(ctx, reqHopByHopMC); err != nil { + return err + } + + // Rule 2: Unicast (any other dst) -> Drop + reqHopByHopUC := fwdconfig.TableEntryAddRequest(sw.dataplane.ID(), table). + AppendEntry( + fwdconfig.EntryDesc(fwdconfig.FlowEntry( + fwdconfig.PacketFieldMaskedBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_IP_PROTO).WithBytes([]byte{0}, []byte{0xFF}), + fwdconfig.PacketFieldMaskedBytes(fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_ACTION).WithBytes([]byte{1}, []byte{0xFF}), + ).WithPriority(5)), + fwdconfig.UpdateAction(fwdpb.UpdateType_UPDATE_TYPE_BIT_WRITE, fwdpb.PacketFieldNum_PACKET_FIELD_NUM_PACKET_ACTION).WithBitOp(1, 0).WithValue([]byte{0}), + ).Build() + if _, err := sw.dataplane.TableEntryAdd(ctx, reqHopByHopUC); err != nil { + return err + } + } // Before the TTL is decremented and after the packets may be punted, drop packet with TTL == 1 or TTL == 0. req := fwdconfig.TableEntryAddRequest(sw.dataplane.ID(), table). AppendEntry( diff --git a/integration_tests/dataplane/mplsoudp/mplsoverudp_test.go b/integration_tests/dataplane/mplsoudp/mplsoverudp_test.go index d24f9f07a..c905c9397 100644 --- a/integration_tests/dataplane/mplsoudp/mplsoverudp_test.go +++ b/integration_tests/dataplane/mplsoudp/mplsoverudp_test.go @@ -206,10 +206,11 @@ func TestMPLSoverUDP(t *testing.T) { } ip := &layers.IPv6{ - Version: 6, - SrcIP: net.ParseIP("2003::9"), - DstIP: net.ParseIP("2003::10"), - HopLimit: 255, + Version: 6, + NextHeader: layers.IPProtocolNoNextHeader, + SrcIP: net.ParseIP("2003::9"), + DstIP: net.ParseIP("2003::10"), + HopLimit: 255, } payload := gopacket.Payload([]byte("hello world")) @@ -251,10 +252,11 @@ func TestMPLSoverUDP(t *testing.T) { Label: 100, } wantInnerIP := &layers.IPv6{ - Version: 6, - SrcIP: net.ParseIP("2003::9"), - DstIP: net.ParseIP("2003::10"), - HopLimit: 255, + Version: 6, + NextHeader: layers.IPProtocolNoNextHeader, + SrcIP: net.ParseIP("2003::9"), + DstIP: net.ParseIP("2003::10"), + HopLimit: 255, } buf = gopacket.NewSerializeBuffer() if err := gopacket.SerializeLayers(buf, gopacket.SerializeOptions{FixLengths: true}, wantMPLS, wantInnerIP, payload); err != nil {