From 74761f31f414f706f51fc901840105d543b43fbb Mon Sep 17 00:00:00 2001 From: moharedd Date: Wed, 12 Aug 2026 09:26:48 +0000 Subject: [PATCH 1/2] Added UDP IPV4 Encapsulation Header as per requirement --- fluent/fluent.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/fluent/fluent.go b/fluent/fluent.go index f57c114..d47db65 100644 --- a/fluent/fluent.go +++ b/fluent/fluent.go @@ -944,6 +944,11 @@ type udpv6EncapHeader struct { pb *aftpb.Afts_NextHop_EncapHeader } +// udpv4EncapHeader represents a UDP encapsulation header. +type udpv4EncapHeader struct { + pb *aftpb.Afts_NextHop_EncapHeader +} + const ( _ Header = iota // IPinIP specifies that the header to be decpsulated is an IPv4 header, and is typically @@ -951,6 +956,8 @@ const ( IPinIP // MPLS specifies that the header to be decapsulated is an MPLS header. MPLS + // UDPV4 specifies that the header to be decapsulated is a UDPv4 header. + UDPV4 // UDPV6 specifies that the header to be decapsulated is a UDPv6 header. UDPV6 ) @@ -960,6 +967,7 @@ const ( var encapMap = map[Header]enums.OpenconfigAftTypesEncapsulationHeaderType{ IPinIP: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_IPV4, MPLS: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_MPLS, + UDPV4: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_UDPV4, UDPV6: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_UDPV6, } @@ -1069,6 +1077,57 @@ func (eh *udpv6EncapHeader) EncapProto() *aftpb.Afts_NextHop_EncapHeader { return eh.pb } +// UDPV4EncapHeader returns a builder that can be used to build up a UDPv4 encapsulation header. +func UDPV4EncapHeader() *udpv4EncapHeader { + return &udpv4EncapHeader{ + pb: &aftpb.Afts_NextHop_EncapHeader{ + Type: encapMap[UDPV4], + UdpV4: &aftpb.Afts_NextHop_EncapHeader_UdpV4{}, + }, + } +} + +// WithDSCP specifies the DSCP value to be used for the UDPv4 header. +func (eh *udpv4EncapHeader) WithDSCP(dscp uint64) *udpv4EncapHeader { + eh.pb.UdpV4.Dscp = &wpb.UintValue{Value: dscp} + return eh +} + +// WithDstIP specifies the destination IP to be used for the UDPv4 header. +func (eh *udpv4EncapHeader) WithDstIP(ip string) *udpv4EncapHeader { + eh.pb.UdpV4.DstIp = &wpb.StringValue{Value: ip} + return eh +} + +// WithDstUDPPort specifies the destination UDP port to be used for the UDPv4 header. +func (eh *udpv4EncapHeader) WithDstUDPPort(port uint64) *udpv4EncapHeader { + eh.pb.UdpV4.DstUdpPort = &wpb.UintValue{Value: port} + return eh +} + +// WithIPTTL specifies the IP TTL to be used for the UDPv4 header. +func (eh *udpv4EncapHeader) WithIPTTL(ttl uint64) *udpv4EncapHeader { + eh.pb.UdpV4.IpTtl = &wpb.UintValue{Value: ttl} + return eh +} + +// WithSrcIP specifies the source IP to be used for the UDPv4 header. +func (eh *udpv4EncapHeader) WithSrcIP(ip string) *udpv4EncapHeader { + eh.pb.UdpV4.SrcIp = &wpb.StringValue{Value: ip} + return eh +} + +// WithSrcUDPPort specifies the source UDP port to be used for the UDPv4 header. +func (eh *udpv4EncapHeader) WithSrcUDPPort(port uint64) *udpv4EncapHeader { + eh.pb.UdpV4.SrcUdpPort = &wpb.UintValue{Value: port} + return eh +} + +// EncapProto returns the built-up protobuf of the udpv4EncapHeader. +func (eh *udpv4EncapHeader) EncapProto() *aftpb.Afts_NextHop_EncapHeader { + return eh.pb +} + // WithElectionID specifies an explicit election ID that is to be used hen the next hop // is programmed in an AFTOperation. The electionID is a uint128 made up of concatenating // the low and high uint64 values provided. From cf2026950c384570a26d22afb5e70feff3ed116c Mon Sep 17 00:00:00 2001 From: moharedd Date: Mon, 31 Aug 2026 09:35:19 +0000 Subject: [PATCH 2/2] Fixed review comments --- fluent/fluent.go | 2 +- fluent/fluent_test.go | 85 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/fluent/fluent.go b/fluent/fluent.go index d47db65..5d76415 100644 --- a/fluent/fluent.go +++ b/fluent/fluent.go @@ -939,7 +939,7 @@ type mplsEncapHeader struct { pb *aftpb.Afts_NextHop_EncapHeader } -// UDPEncapHeader represents a UDP encapsulation header. +// udpv6EncapHeader represents a UDP encapsulation header. type udpv6EncapHeader struct { pb *aftpb.Afts_NextHop_EncapHeader } diff --git a/fluent/fluent_test.go b/fluent/fluent_test.go index 9865f34..ce0c852 100644 --- a/fluent/fluent_test.go +++ b/fluent/fluent_test.go @@ -556,6 +556,91 @@ func TestEntry(t *testing.T) { }, }, }, + }, { + desc: "next-hop mpls-over-udpv4", + in: NextHopEntry(). + WithNetworkInstance("DEFAULT"). + WithIndex(1). + AddEncapHeader( + MPLSEncapHeader().WithLabels(100, 200), + UDPV4EncapHeader().WithDSCP(10).WithDstIP("192.0.2.1").WithDstUDPPort(5678).WithIPTTL(32).WithSrcIP("192.0.2.2").WithSrcUDPPort(8765), + ), + wantOpProto: &spb.AFTOperation{ + NetworkInstance: "DEFAULT", + Entry: &spb.AFTOperation_NextHop{ + NextHop: &aftpb.Afts_NextHopKey{ + Index: 1, + NextHop: &aftpb.Afts_NextHop{ + EncapHeader: []*aftpb.Afts_NextHop_EncapHeaderKey{ + { + Index: 1, + EncapHeader: &aftpb.Afts_NextHop_EncapHeader{ + Type: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_MPLS, + Mpls: &aftpb.Afts_NextHop_EncapHeader_Mpls{ + MplsLabelStack: []*aftpb.Afts_NextHop_EncapHeader_Mpls_MplsLabelStackUnion{ + {MplsLabelStackUint64: uint64(100)}, + {MplsLabelStackUint64: uint64(200)}, + }, + }, + }, + }, + { + Index: 2, + EncapHeader: &aftpb.Afts_NextHop_EncapHeader{ + Type: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_UDPV4, + UdpV4: &aftpb.Afts_NextHop_EncapHeader_UdpV4{ + Dscp: &wpb.UintValue{Value: uint64(10)}, + DstIp: &wpb.StringValue{Value: "192.0.2.1"}, + DstUdpPort: &wpb.UintValue{Value: uint64(5678)}, + IpTtl: &wpb.UintValue{Value: uint64(32)}, + SrcIp: &wpb.StringValue{Value: "192.0.2.2"}, + SrcUdpPort: &wpb.UintValue{Value: uint64(8765)}, + }, + }, + }, + }, + }, + }, + }, + }, + wantEntryProto: &spb.AFTEntry{ + NetworkInstance: "DEFAULT", + Entry: &spb.AFTEntry_NextHop{ + NextHop: &aftpb.Afts_NextHopKey{ + Index: 1, + NextHop: &aftpb.Afts_NextHop{ + EncapHeader: []*aftpb.Afts_NextHop_EncapHeaderKey{ + { + Index: 1, + EncapHeader: &aftpb.Afts_NextHop_EncapHeader{ + Type: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_MPLS, + Mpls: &aftpb.Afts_NextHop_EncapHeader_Mpls{ + MplsLabelStack: []*aftpb.Afts_NextHop_EncapHeader_Mpls_MplsLabelStackUnion{ + {MplsLabelStackUint64: uint64(100)}, + {MplsLabelStackUint64: uint64(200)}, + }, + }, + }, + }, + { + Index: 2, + EncapHeader: &aftpb.Afts_NextHop_EncapHeader{ + Type: enums.OpenconfigAftTypesEncapsulationHeaderType_OPENCONFIGAFTTYPESENCAPSULATIONHEADERTYPE_UDPV4, + UdpV4: &aftpb.Afts_NextHop_EncapHeader_UdpV4{ + Dscp: &wpb.UintValue{Value: uint64(10)}, + DstIp: &wpb.StringValue{Value: "192.0.2.1"}, + DstUdpPort: &wpb.UintValue{Value: uint64(5678)}, + IpTtl: &wpb.UintValue{Value: uint64(32)}, + SrcIp: &wpb.StringValue{Value: "192.0.2.2"}, + SrcUdpPort: &wpb.UintValue{Value: uint64(8765)}, + }, + }, + }, + }, + }, + }, + }, + }, }} for _, tt := range tests {