Skip to content

Commit

Permalink
Merge branch 'main' into sflow1-2-1
Browse files Browse the repository at this point in the history
  • Loading branch information
sgirishj authored Sep 20, 2024
2 parents 3550ed1 + 6658614 commit dfa8cb5
Show file tree
Hide file tree
Showing 7 changed files with 376 additions and 6 deletions.
324 changes: 324 additions & 0 deletions feature/gribi/otg_tests/mpls_in_udp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
# TE-18.1 gRIBI MPLS in UDP Encapsulation and Decapsulation

Create AFT entries using gRIBI to match on next hop group in a
network-instance and encapsulate the matching packets in MPLS in UDP.

Create a policy routing configuration using gNMI to decapsulate MPLS
in UDP packets which are sent to a loopback address and apply to
the DUT.

The MPLS in UDP encapsulation is expected to follow
[rfc7510](https://datatracker.ietf.org/doc/html/rfc7510#section-3),
but relaxing the requirement for a well-known destination UDP port. gRIBI is
expected to be able to set the destination UDP port.

## Topology

* [`featureprofiles/topologies/atedut_2.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/atedut_2.testbed)

## Test setup

TODO: Complete test environment setup steps

inner_ipv6_dst_A = "2001:aa:bb::1/128"
inner_ipv6_dst_B = "2001:aa:bb::2/128"
inner_ipv6_default = "::/0"

ipv4_inner_dst_A = "10.5.1.1/32"
ipv4_inner_dst_B = "10.5.1.2/32"
ipv4_inner_default = "0.0.0.0/0"

outer_ipv6_src = "2001:f:a:1::0"
outer_ipv6_dst_A = "2001:f:c:e::1"
outer_ipv6_dst_B = "2001:f:c:e::2"
outer_ipv6_dst_def = "2001:1:1:1::0"
outer_dst_udp_port = "6635"
outer_dscp = "26"
outer_ip-ttl = "64"

## Procedure

### TE-18.1.1 Match and Encapsulate using gRIBI aft modify

#### gRIBI RPC content

The gRIBI client should send this proto message to the DUT to create AFT
entries. See [OC AFT Encap PR in progress](https://github.com/openconfig/public/pull/1153)
for the new OC AFT model nodes needed for this.

TODO: The
[gRIBI v1 protobuf defintions](https://github.com/openconfig/gribi/blob/master/v1/proto/README.md)
will be generated from the afts tree.

```proto
network_instances: {
network_instance: {
afts {
#
# entries used for "group_A"
ipv6_unicast {
ipv6_entry {
prefix: "inner_ipv6_dst_A" # this is an IPv6 entry for the origin/inner packet.
next_hop_group: 100
}
}
ipv4_unicast {
ipv4_entry {
prefix: "ipv4_inner_dst_A" # this is an IPv4 entry for the origin/inner packet.
next_hop_group: 100
}
}
next_hop_groups {
next_hop_group {
id: 100
next_hops { # reference to a next-hop
next_hop: {
index: 100
}
}
}
}
next_hops {
next_hop {
index: 100
network_instance: "group_A"
encap-headers {
encap-header {
index: 1
pushed_mpls_label_stack: [100,]
}
}
encap-headers {
encap-header {
index: 2
src_ip: "outer_ipv6_src"
dst_ip: "outer_ipv6_dst_A"
dst_udp_port: "outer_dst_udp_port"
ip_ttl: "outer_ip-ttl"
dscp: "outer_dscp"
}
}
}
}
#
# entries used for "group_B"
ipv6_unicast {
ipv6_entry {
prefix: "inner_ipv6_dst_B"
next_hop_group: 200
}
}
ipv4_unicast {
ipv4_entry {
prefix: "ipv4_inner_dst_B"
next_hop_group: 200
}
}
next_hop_groups {
next_hop_group {
id: 200
next_hops { # reference to a next-hop
next_hop: {
index: 200
}
}
}
}
next_hops {
next_hop {
index: 200
network_instance: "group_B"
encap-headers {
encap-header {
index: 1
type : OPENCONFIG_AFT_TYPES:MPLS
mpls {
pushed_mpls_label_stack: [200,]
}
}
}
encap-headers {
encap-header {
index: 2
type: OPENCONFIG_AFT_TYPES:UDP
udp {
src_ip: "outer_ipv6_src"
dst_ip: "outer_ipv6_dst_B"
dst_udp_port: "outer_dst_udp_port"
ip_ttl: "outer_ip-ttl"
dscp: "outer_dscp"
}
}
}
}
}
}
}
}
```

* Send traffic from ATE port 1 to DUT port 1
* Validate afts next hop counters
* Using OTG, validate ATE port 2 receives MPLS-IN-UDP packets
* Validate destination IPs are outer_ipv6_dst_A and outer_ipv6_dst_B
* Validate MPLS label is set

### TE-18.1.2 Validate prefix match rule for MPLS in GRE encap using default route

Canonical OpenConfig for policy forwarding, matching IP prefix with action
encapsulate in GRE.

```yaml
openconfig-network-instance:
network-instances:
- network-instance: "group_A"
afts:
policy-forwarding:
policies:
policy: "default encap rule"
config:
policy-id: "default encap rule"
type: PBR_POLICY
rules:
rule: 1
config:
sequence-id: 1
ipv6:
config:
destination-address: "inner_ipv6_default"
action:
encapsulate-mpls-in-gre: # TODO: add to OC model/PR in progress
targets:
target: "default_dst_1"
config:
id: "default_dst_1"
network-instance: "DEFAULT"
source-ip: "outer_ipv6_src"
destination-ip: "outer_ipv6_dst_def"
ip-ttl: outer_ip-ttl
dscp: outer_dscp
inner-ttl-min: 2
```
* Generate the policy forwarding configuration
* Push the configuration to DUT using gnmi.Set with REPLACE option
* Configure ATE port 1 with traffic flow which does not match any AFT next hop route
* Generate traffic from ATE port 1 to ATE port 2
* Validate ATE port 2 receives GRE traffic with correct inner and outer IPs
### TE-18.1.3 - MPLS in GRE decapsulation set by gNMI
Canonical OpenConfig for policy forwarding, matching IP prefix with action
decapsulate in GRE. # TODO: Move to dedicated README
```yaml
openconfig-network-instance:
network-instances:
- network-instance: "DEFAULT"
afts:
policy-forwarding:
policies:
policy: "default decap rule"
config:
policy-id: "default decap rule"
type: PBR_POLICY
rules:
rule: 1
config:
sequence-id: 1
ipv6:
config:
destination-address: "decap_loopback_ipv6"
action:
decapsulate-mpls-in-gre: TRUE # TODO: add to OC model/PR in progress
```
* Push the gNMI the policy forwarding configuration
* Push the configuration to DUT using gnmi.Set with REPLACE option
* Configure ATE port 1 with traffic flow which matches the decap loopback IP address
* Generate traffic from ATE port 1
* Validate ATE port 2 receives packets with correct VLAN and the inner inner_decap_ipv6
### TE-18.1.4 - MPLS in UDP decapsulation set by gNMI
Canonical OpenConfig for policy forwarding, matching IP prefix with action
decapsulate MPLS in UDP. # TODO: Move to dedicated README
```yaml
openconfig-network-instance:
network-instances:
- network-instance: "DEFAULT"
afts:
policy-forwarding:
policies:
policy: "default decap rule"
config:
policy-id: "default decap rule"
type: PBR_POLICY
rules:
rule: 1
config:
sequence-id: 1
ipv6:
config:
destination-address: "decap_loopback_ipv6"
action:
decapsulate-mpls-in-udp: TRUE
```
* Push the gNMI the policy forwarding configuration
* Push the configuration to DUT using gnmi.Set with REPLACE option
* Configure ATE port 1 with traffic flow
* Flow should have a packet encap format : outer_decap_udp_ipv6 <- MPLS label <- inner_decap_ipv6
* Generate traffic from ATE port 1
* Validate ATE port 2 receives the innermost IPv4 traffic with correct VLAN and inner_decap_ipv6
### TE-18.1.5 - Policy forwarding to encap and forward for BGP packets
TODO: Specify a solution for ensuring BGP packets are matched, encapsulated
and forwarding to a specified destination using OC policy-forwarding terms.
## OpenConfig Path and RPC Coverage
```yaml
paths:

# afts state paths set via gRIBI
# TODO: https://github.com/openconfig/public/pull/1153

#/network-instances/network-instance/afts/next-hop-groups/next-hop-group/state/id:
#/network-instances/network-instance/afts/next-hop-groups/next-hop-group/state/next-hop-group-id:
#/network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/state/index:
#/network-instances/network-instance/afts/next-hop-groups/next-hop-group/next-hops/next-hop/state/network-instance:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/index:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/type:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/mpls/pushed-mpls-label-stack:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/udp/src-ip:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/udp/dst-ip:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/udp/dst-udp-port:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/udp/ip-ttl:
#/network-instances/network-instance/afts/next-hops/next-hop/encap-headers/encap-header/state/udp/dscp:

# afts next-hop counters
/network-instances/network-instance/afts/next-hops/next-hop/state/counters/packets-forwarded:
/network-instances/network-instance/afts/next-hops/next-hop/state/counters/octets-forwarded:


rpcs:
gnmi:
gNMI.Set:
union_replace: true
replace: true
gNMI.Subscribe:
on_change: true
gribi:
gRIBI.Modify:
network-instances:network-instance:afts:next-hops:next-hop:encapsulate_header:
network-instances:network-instance:afts:next-hops:next-hop:mpls-in-udp:
network-instances:network-instance:afts:next-hops:next-hop:decapsulate_header:
gRIBI.Flush:
```
## Required DUT platform
* FFF
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ func TestIPv6LinkLocal(t *testing.T) {
t.Run("Disable and Enable Port1", func(t *testing.T) {
p1 := dut.Port(t, "port1")
gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().Config(), false)
gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().State(), 30*time.Second, false)
// gnmi.Await(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().State(), 30*time.Second, false)
t.Logf("Sleeping for 30 seconds")
time.Sleep(30 * time.Second)
gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Enabled().Config(), true)
otgutils.WaitForARP(t, ate.OTG(), top, "IPv6")
t.Run("Interface Telemetry", func(t *testing.T) {
Expand Down Expand Up @@ -182,9 +184,12 @@ func configureDUTLinkLocalInterface(t *testing.T, dut *ondatra.DUTDevice) {
subInt4.Enabled = ygot.Bool(true)
}
subInt.GetOrCreateIpv6().Enabled = ygot.Bool(true)
if deviations.LinkLocalMaskLen(dut) {
dutSrc.IPv6Len = 128
}
subInt.GetOrCreateIpv6().GetOrCreateAddress(dutSrc.IPv6).SetType(oc.IfIp_Ipv6AddressType_LINK_LOCAL_UNICAST)
subInt.GetOrCreateIpv6().GetOrCreateAddress(dutSrc.IPv6).SetPrefixLength(dutSrc.IPv6Len)
gnmi.Replace(t, dut, gnmi.OC().Interface(p1.Name()).Config(), srcIntf)

p2 := dut.Port(t, "port2")
dstIntf := dutDst.NewOCInterface(p2.Name(), dut)
dstSubInt := dstIntf.GetOrCreateSubinterface(0)
Expand All @@ -193,8 +198,15 @@ func configureDUTLinkLocalInterface(t *testing.T, dut *ondatra.DUTDevice) {
if deviations.InterfaceEnabled(dut) && !deviations.IPv4MissingEnabled(dut) {
dstSubInt4.Enabled = ygot.Bool(true)
}
dutDst.IPv6Len = 128
if deviations.LinkLocalMaskLen(dut) {
dutDst.IPv6Len = 128
}
dstSubInt.GetOrCreateIpv6().GetOrCreateAddress(dutDst.IPv6).SetType(oc.IfIp_Ipv6AddressType_LINK_LOCAL_UNICAST)
dstSubInt.GetOrCreateIpv6().GetOrCreateAddress(dutDst.IPv6).SetPrefixLength(dutDst.IPv6Len)

gnmi.Replace(t, dut, gnmi.OC().Interface(p2.Name()).Config(), dstIntf)

if deviations.ExplicitInterfaceInDefaultVRF(dut) {
fptest.AssignToNetworkInstance(t, dut, p1.Name(), deviations.DefaultNetworkInstance(dut), 0)
fptest.AssignToNetworkInstance(t, dut, p2.Name(), deviations.DefaultNetworkInstance(dut), 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ platform_exceptions: {
default_network_instance: "default"
}
}
platform_exceptions: {
platform: {
vendor: CISCO
}
deviations: {
link_local_mask_len: true
}
} # CISCOXR
platform_exceptions: {
platform: {
vendor: NOKIA
Expand Down
5 changes: 5 additions & 0 deletions internal/deviations/deviations.go
Original file line number Diff line number Diff line change
Expand Up @@ -1135,3 +1135,8 @@ func DecapNHWithNextHopNIUnsupported(dut *ondatra.DUTDevice) bool {
func SflowSourceAddressUpdateUnsupported(dut *ondatra.DUTDevice) bool {
return lookupDUTDeviations(dut).GetSflowSourceAddressUpdateUnsupported()
}

// LinklocalMaskLen returns true if linklocal mask length is not 64

Check failure on line 1139 in internal/deviations/deviations.go

View workflow job for this annotation

GitHub Actions / Static Analysis

comment on exported function LinkLocalMaskLen should be of the form "LinkLocalMaskLen ..."
func LinkLocalMaskLen(dut *ondatra.DUTDevice) bool {
return lookupDUTDeviations(dut).GetLinkLocalMaskLen()
}
3 changes: 3 additions & 0 deletions proto/metadata.proto
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,9 @@ message Metadata {
// SFlow source address update is unsupported
// Arista: b/357914789
bool sflow_source_address_update_unsupported = 217;
// Linklocal mask length is not 64
// Cisco: b/368271859
bool link_local_mask_len = 218;
// Reserved field numbers and identifiers.
reserved 84, 9, 28, 20, 90, 97, 55, 89, 19, 36, 35, 40, 173;
}
Expand Down
Loading

0 comments on commit dfa8cb5

Please sign in to comment.