diff --git a/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/metadata.textproto b/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/metadata.textproto index f5fa5b798c8..423aa3029f4 100644 --- a/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/metadata.textproto +++ b/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/metadata.textproto @@ -4,4 +4,23 @@ uuid: "16e91827-1d4f-4836-8438-d17b387a5ea8" plan_id: "RT-3.52" description: "Multidimensional test for Static GUE Encap/Decap based on BGP path selection and selective DSCP marking" -testbed: TESTBED_DUT_ATE_2LINKS +testbed: TESTBED_DUT_ATE_4LINKS +platform_exceptions: { + platform: { + vendor: ARISTA + } + deviations: { + static_protocol_name: "STATIC" + interface_enabled: true + default_network_instance: "default" + isis_instance_enabled_required: true + missing_value_for_defaults: true + isis_interface_afi_unsupported: true + isis_lsp_metadata_leafs_unsupported: true + next_hop_group_config_unsupported: true + interface_policy_forwarding_unsupported: true + qos_classification_unsupported: true + static_route_to_nhg_oc_unsupported: true + gue_gre_decap_unsupported: true + } +} diff --git a/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/staticgueencap_and_bgp_path_selection_test.go b/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/staticgueencap_and_bgp_path_selection_test.go new file mode 100644 index 00000000000..266febbffb0 --- /dev/null +++ b/feature/policy_forwarding/encapsulation/otg_tests/staticgueencap_and_bgp_path_selection/staticgueencap_and_bgp_path_selection_test.go @@ -0,0 +1,2013 @@ +package staticgueencapbgppathselection_test + +import ( + "fmt" + "regexp" + "testing" + "time" + + "github.com/open-traffic-generator/snappi/gosnappi" + "github.com/openconfig/featureprofiles/internal/attrs" + "github.com/openconfig/featureprofiles/internal/cfgplugins" + "github.com/openconfig/featureprofiles/internal/deviations" + "github.com/openconfig/featureprofiles/internal/fptest" + "github.com/openconfig/featureprofiles/internal/iputil" + otgconfighelpers "github.com/openconfig/featureprofiles/internal/otg_helpers/otg_config_helpers" + otgvalidationhelpers "github.com/openconfig/featureprofiles/internal/otg_helpers/otg_validation_helpers" + "github.com/openconfig/featureprofiles/internal/otg_helpers/packetvalidationhelpers" + "github.com/openconfig/featureprofiles/internal/otgutils" + "github.com/openconfig/ondatra" + "github.com/openconfig/ondatra/gnmi" + "github.com/openconfig/ondatra/gnmi/oc" + "github.com/openconfig/ondatra/netutil" + "github.com/openconfig/ondatra/otg" + "github.com/openconfig/ygnmi/ygnmi" + "github.com/openconfig/ygot/ygot" +) + +func TestMain(m *testing.M) { + fptest.RunTests(m) +} + +// Constants for address families, AS numbers, and protocol settings. +const ( + plenIPv4p2p = 31 + plenIPv6p2p = 127 + plenIPv4lo = 32 + plenIPv6lo = 128 + dutAS = 65501 + ateIBGPAS = 65501 // For iBGP with DUT + ateEBGPAS = 65502 // For eBGP with DUT + isisInstance = "DEFAULT" + isisSysID1 = "640000000001" + isisSysID2 = "640000000002" + isisAreaAddr = "49.0001" + dutSysID = "1920.0000.2001" + ibgpPeerGroup = "IBGP-PEERS" + ebgpPeerGroup = "EBGP-PEERS" + udpEncapPort = 6080 + ttl = 64 + + // Static and GUE address + nexthopGroupName1 = "GUE_TE10" + nexthopGroupName2 = "GUE_TE11" + guePolicyName = "GUE-Policy" + decapPolicy1 = "DECAP_TE10" + decapPolicy2 = "DECAP_TE11" + + totalPackets = 50000 + trafficPps = 1000 +) + +type otgBGPConfigData struct { + port string + otgPortData []*attrs.Attributes + dutPortData []*attrs.Attributes + otgDevice []gosnappi.Device + bgpCfg []*bgpNbr +} + +type trafficFlow struct { + flows otgconfighelpers.Flow + innerParams otgconfighelpers.Flow +} + +var ( + otgBGPConfig = []*otgBGPConfigData{ + { + port: "port1", + otgPortData: []*attrs.Attributes{ + { + Name: "port1", + IPv4: "192.0.2.2", + IPv6: "2001:db8:1::2", + MAC: "02:01:01:01:01:01", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + }, + }, + dutPortData: []*attrs.Attributes{ + { + Desc: "DUT to ATE Port 1", + IPv4: "192.0.2.3", + IPv6: "2001:db8:1::3", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + }, + }, + bgpCfg: []*bgpNbr{ + {peerAs: ateIBGPAS, nbrIp: "192.0.2.2", isV4: true, peerGrpName: ibgpPeerGroup + "-v4ate1", srcIp: dutloopback0.IPv4, routeReflector: true}, + {peerAs: ateIBGPAS, nbrIp: "2001:db8:1::2", isV4: false, peerGrpName: ibgpPeerGroup + "-v6ate1", srcIp: dutloopback0.IPv6, routeReflector: true}, + }, + }, + { + port: "port2", + otgPortData: []*attrs.Attributes{ + { + Name: "R100", + IPv4: "192.0.2.4", + IPv6: "2001:db8:1::4", + MAC: "02:02:02:02:02:01", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + Subinterface: 100, + }, + { + Name: "R200", + IPv4: "192.0.2.6", + IPv6: "2001:db8:1::6", + MAC: "02:02:02:02:02:02", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + Subinterface: 200, + }, + { + Name: "R300", + IPv4: "192.0.2.8", + IPv6: "2001:db8:1::8", + MAC: "02:02:02:02:02:03", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + Subinterface: 300, + }, + }, + dutPortData: []*attrs.Attributes{ + { + Desc: "DUT Port 2 Vlan 100", + IPv4: "192.0.2.5", + IPv6: "2001:db8:1::5", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + Subinterface: 100, + }, + { + Desc: "DUT Port 2 Vlan 200", + IPv4: "192.0.2.7", + IPv6: "2001:db8:1::7", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + Subinterface: 200, + }, + { + Desc: "DUT Port 2 Vlan 300", + IPv4: "192.0.2.9", + IPv6: "2001:db8:1::9", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + Subinterface: 300, + }, + }, + bgpCfg: []*bgpNbr{ + {peerAs: ateIBGPAS, nbrIp: "192.0.2.4", isV4: true, peerGrpName: ibgpPeerGroup + "-v4ate_IBGP", srcIp: dutloopback0.IPv4}, + {peerAs: ateIBGPAS, nbrIp: "2001:db8:1::4", isV4: false, peerGrpName: ibgpPeerGroup + "-v6ate_IBGP", srcIp: dutloopback0.IPv6}, + {peerAs: ateIBGPAS, nbrIp: "192.0.2.6", isV4: true, peerGrpName: ibgpPeerGroup + "-v4ate2-200", srcIp: dutloopback0.IPv4}, + {peerAs: ateIBGPAS, nbrIp: "2001:db8:1::6", isV4: false, peerGrpName: ibgpPeerGroup + "-v6ate_C_IBGP", srcIp: dutloopback0.IPv6}, + {peerAs: ateIBGPAS, nbrIp: "192.0.2.8", isV4: true, peerGrpName: ibgpPeerGroup + "-v4ate_M_IBGP", srcIp: dutloopback0.IPv4}, + {peerAs: ateIBGPAS, nbrIp: "2001:db8:1::8", isV4: false, peerGrpName: ibgpPeerGroup + "-v6ate_M_IBGP", srcIp: dutloopback0.IPv6}, + }, + }, + { + port: "port4", + otgPortData: []*attrs.Attributes{ + { + Name: "port4", + IPv4: "192.0.2.10", + IPv6: "2001:db8:1::10", + MAC: "02:03:03:03:03:03", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + }, + }, + dutPortData: []*attrs.Attributes{ + { + Desc: "DUT to ATE2 Port 3", + IPv4: "192.0.2.11", + IPv6: "2001:db8:1::11", + IPv4Len: plenIPv4p2p, + IPv6Len: plenIPv6p2p, + }, + }, + bgpCfg: []*bgpNbr{ + {peerAs: ateEBGPAS, nbrIp: "192.0.2.10", isV4: true, peerGrpName: ebgpPeerGroup, srcIp: dutloopback0.IPv4}, + {peerAs: ateEBGPAS, nbrIp: "2001:db8:1::10", isV4: false, peerGrpName: ebgpPeerGroup, srcIp: dutloopback0.IPv6}, + }, + }, + } + + // DUT loopback 0 ($DUT_lo0) + dutloopback0 = &attrs.Attributes{ + Desc: "DUT Loopback 0", + IPv4: "203.0.113.10", + IPv6: "2001:db8:100::10", + IPv4Len: plenIPv4lo, + IPv6Len: plenIPv6lo, + } + + // ATE Port1 user prefixes + ate1UserPrefixesV4 = "198.61.100.1" + ate1UserPrefixesV6 = "2001:db8:100:1::" + ate1UserPrefixesCount = uint32(5) + ate1UserPrefixesV4List = iputil.GenerateIPs(ate1UserPrefixesV4+"/24", int(ate1UserPrefixesCount)) + ate1UserPrefixesV6List, _ = iputil.GenerateIPv6(ate1UserPrefixesV6+"/64", uint64(ate1UserPrefixesCount)) + + // $ATE2_INTERNAL - Prefixes to be advertised by ATE Port2 IBGP/ ATE2_C + ate2InternalPrefixesV4 = "198.71.100.1" + ate2InternalPrefixesV6 = "2001:db8:200:1::" + ate2InternalPrefixCount = uint32(5) + ate2InternalPrefixesV4List = iputil.GenerateIPs(ate2InternalPrefixesV4+"/24", int(ate2InternalPrefixCount)) + ate2InternalPrefixesV6List, _ = iputil.GenerateIPv6(ate2InternalPrefixesV6+"/64", uint64(ate2InternalPrefixCount)) + + // ATE2 Port3 bgp prefixes + bgpInternalTE11 = &attrs.Attributes{ + Name: "ate2InternalTE11", + IPv6: "2001:db8:c0fe:affe::1", + IPv6Len: 128, + } + bgpInternalTE10 = &attrs.Attributes{ + Name: "ate2InternalTE10", + IPv6: "2001:db8:c0fe:affe::2", + IPv6Len: 128, + } + + // ATE Port2 C.IBGP ---> DUT connected via Pseudo Protocol Next-Hops + ate2ppnh1 = &attrs.Attributes{Name: "ate2ppnh1", IPv6: "2001:db8:2::0", IPv6Len: plenIPv6lo} + ate2ppnh2 = &attrs.Attributes{Name: "ate2ppnh2", IPv6: "2001:db8:3::0", IPv6Len: plenIPv6lo} + + ate2ppnh1Prefix = "2001:db8:2::0/128" + ate2ppnh2Prefix = "2001:db8:3::0/128" + + loopbackIntfName string + + dscpValue = map[string]uint32{ + "BE1": 0, + "AF1": 10, + "AF2": 18, + "AF3": 26, + "AF4": 34, + } + + expectedDscpValue = map[string]uint32{ + "BE1": 0, + "AF1": 40, + "AF2": 72, + "AF3": 104, + "AF4": 136, + } + + atePort1RouteV4 = "v4-user-routes" + atePort1RouteV6 = "v6-user-routes" + + atePort2RoutesV4 = "v4-internal-routes" + atePort2RoutesV6 = "v6-internal-routes" + atePort2RoutesTE10 = "v4-TE10-routes" + atePort2RoutesTE11 = "v4-TE11-routes" + + trafficFlowData []trafficFlow + + port1DstMac string + port2DstMac string + + FlowIPv4Validation = &otgvalidationhelpers.OTGValidation{ + Flow: &otgvalidationhelpers.FlowParams{TolerancePct: 0.5}, + } + + outerGUEIPLayerIPv6 = &packetvalidationhelpers.IPv6Layer{ + NextHeader: 17, + HopLimit: ttl, + DstIP: bgpInternalTE11.IPv6, + } + + outerv6GUEIPLayerIPv6 = &packetvalidationhelpers.IPv6Layer{ + NextHeader: 17, + HopLimit: ttl, + DstIP: bgpInternalTE10.IPv6, + } + + innerGUEIPLayerIPv6 = &packetvalidationhelpers.IPv6Layer{ + NextHeader: udpEncapPort, + HopLimit: ttl - 1, + } + + innerGUEIPLayerIPv4 = &packetvalidationhelpers.IPv4Layer{ + Protocol: udpEncapPort, + SkipProtocolCheck: true, + TTL: ttl - 1, + } + + udpLayer = &packetvalidationhelpers.UDPLayer{ + DstPort: udpEncapPort, + } + + validations = []packetvalidationhelpers.ValidationType{ + packetvalidationhelpers.ValidateIPv6Header, + packetvalidationhelpers.ValidateUDPHeader, + packetvalidationhelpers.ValidateInnerIPv4Header, + } + + encapValidation = &packetvalidationhelpers.PacketValidation{ + PortName: "port4", + Validations: validations, + IPv6Layer: outerGUEIPLayerIPv6, + UDPLayer: udpLayer, + InnerIPLayerIPv4: innerGUEIPLayerIPv4, + } + + validationsV6 = []packetvalidationhelpers.ValidationType{ + packetvalidationhelpers.ValidateIPv6Header, + packetvalidationhelpers.ValidateUDPHeader, + // packetvalidationhelpers.ValidateInnerIPv6Header, + } + + encapValidationv6 = &packetvalidationhelpers.PacketValidation{ + PortName: "port4", + Validations: validationsV6, + IPv6Layer: outerv6GUEIPLayerIPv6, + UDPLayer: udpLayer, + InnerIPLayerIPv6: innerGUEIPLayerIPv6, + } + + decapValidation = &packetvalidationhelpers.PacketValidation{ + PortName: "port1", + CaptureName: "decapCapture", + Validations: []packetvalidationhelpers.ValidationType{packetvalidationhelpers.ValidateIPv4Header}, + IPv6Layer: outerGUEIPLayerIPv6, + } +) + +type isisConfig struct { + port string + level oc.E_Isis_LevelType +} + +type bgpNbr struct { + peerGrpName string + nbrIp string + srcIp string + peerAs uint32 + isV4 bool + routeReflector bool +} + +type flowGroupData struct { + Flows []gosnappi.Flow +} + +var flowGroups = make(map[string]flowGroupData) + +// configureDUT configures interfaces, BGP, IS-IS, and static tunnel routes on the DUT. +func configureDUT(t *testing.T, dut *ondatra.DUTDevice, port *ondatra.Port, portAttr *attrs.Attributes) { + d := gnmi.OC() + gnmi.Update(t, dut, d.Interface(port.Name()).Config(), configInterfaceDUT(t, port, new(oc.Root), portAttr, dut)) + + // Configure Network instance type on DUT + t.Log("Configure/update Network Instance") + fptest.ConfigureDefaultNetworkInstance(t, dut) + + if deviations.ExplicitInterfaceInDefaultVRF(dut) { + fptest.AssignToNetworkInstance(t, dut, port.Name(), deviations.DefaultNetworkInstance(dut), 0) + } + + if deviations.ExplicitPortSpeed(dut) { + fptest.SetPortSpeed(t, port) + } +} + +func configureStaticRoute(t *testing.T, dut *ondatra.DUTDevice) { + b := &gnmi.SetBatch{} + + // Configuring Static Route: PNH-IPv6 --> IPv4 GUE tunnel. + sV4 := &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: ate2ppnh1Prefix, + NexthopGroup: true, + NexthopGroupName: nexthopGroupName2, + T: t, + TrafficType: oc.Aft_EncapsulationHeaderType_UDPV4, + PolicyName: guePolicyName, + Rule: "rule1", + } + + if _, err := cfgplugins.NewStaticRouteCfg(b, sV4, dut); err != nil { + t.Fatalf("Failed to configure IPv6 static route: %v", err) + } + b.Set(t, dut) + + sV4 = &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: ate2ppnh2Prefix, + NexthopGroup: true, + NexthopGroupName: nexthopGroupName1, + T: t, + TrafficType: oc.Aft_EncapsulationHeaderType_UDPV6, + PolicyName: guePolicyName, + Rule: "rule2", + } + + if _, err := cfgplugins.NewStaticRouteCfg(b, sV4, dut); err != nil { + t.Fatalf("Failed to configure IPv6 static route: %v", err) + } + b.Set(t, dut) +} + +// Configures the given DUT interface. +func configInterfaceDUT(t *testing.T, p *ondatra.Port, d *oc.Root, a *attrs.Attributes, dut *ondatra.DUTDevice) *oc.Interface { + t.Helper() + + i := d.GetOrCreateInterface(p.Name()) + if deviations.InterfaceEnabled(dut) { + i.Enabled = ygot.Bool(true) + } + + // Always create subif 0 + subif := i.GetOrCreateSubinterface(0) + subif.Index = ygot.Uint32(0) + iv4 := subif.GetOrCreateIpv4() + iv6 := subif.GetOrCreateIpv6() + if deviations.InterfaceEnabled(dut) { + iv4.Enabled = ygot.Bool(true) + iv6.Enabled = ygot.Bool(true) + } + + s := i.GetOrCreateSubinterface(a.Subinterface) + + if a.Subinterface != 0 { + if deviations.DeprecatedVlanID(dut) { + s.GetOrCreateVlan().VlanId = oc.UnionUint16(a.Subinterface) + } else { + s.GetOrCreateVlan().GetOrCreateMatch().GetOrCreateSingleTagged().VlanId = ygot.Uint16(uint16(a.Subinterface)) + } + } + s4 := s.GetOrCreateIpv4() + a4 := s4.GetOrCreateAddress(a.IPv4) + a4.PrefixLength = ygot.Uint8(uint8(a.IPv4Len)) + if deviations.InterfaceEnabled(dut) { + s4.Enabled = ygot.Bool(true) + } + s6 := s.GetOrCreateIpv6() + a6 := s6.GetOrCreateAddress(a.IPv6) + a6.PrefixLength = ygot.Uint8(uint8(a.IPv6Len)) + if deviations.InterfaceEnabled(dut) { + s6.Enabled = ygot.Bool(true) + } + + return i +} + +func configureLoopback(t *testing.T, dut *ondatra.DUTDevice) { + // Configure interface loopback + loopbackIntfName = netutil.LoopbackInterface(t, dut, 0) + lo0 := gnmi.OC().Interface(loopbackIntfName).Subinterface(0) + ipv4Addrs := gnmi.LookupAll(t, dut, lo0.Ipv4().AddressAny().State()) + ipv6Addrs := gnmi.LookupAll(t, dut, lo0.Ipv6().AddressAny().State()) + if len(ipv4Addrs) == 0 && len(ipv6Addrs) == 0 { + loop1 := dutloopback0.NewOCInterface(loopbackIntfName, dut) + loop1.Type = oc.IETFInterfaces_InterfaceType_softwareLoopback + gnmi.Update(t, dut, gnmi.OC().Interface(loopbackIntfName).Config(), loop1) + } else { + v4, ok := ipv4Addrs[0].Val() + if ok { + dutloopback0.IPv4 = v4.GetIp() + } + v6, ok := ipv6Addrs[0].Val() + if ok { + dutloopback0.IPv6 = v6.GetIp() + } + t.Logf("Got DUT IPv4 loopback address: %v", dutloopback0.IPv4) + t.Logf("Got DUT IPv6 loopback address: %v", dutloopback0.IPv6) + } +} + +func configureISIS(t *testing.T, dut *ondatra.DUTDevice) { + isisConf := []*isisConfig{ + {port: dut.Port(t, otgBGPConfig[0].port).Name(), level: oc.Isis_LevelType_LEVEL_2}, + {port: dut.Port(t, otgBGPConfig[1].port).Name(), level: oc.Isis_LevelType_LEVEL_2}, + } + + // Configure IS-IS protocol on port1 and port2 + root := &oc.Root{} + dutConfIsisPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + ni := root.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + isisProtocol := ni.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_ISIS, isisInstance) + + isisProtocol.SetEnabled(true) + isis := isisProtocol.GetOrCreateIsis() + + globalISIS := isis.GetOrCreateGlobal() + if deviations.ISISInstanceEnabledRequired(dut) { + globalISIS.SetInstance(isisInstance) + } + + // Configure Global ISIS settings + globalISIS.SetNet([]string{fmt.Sprintf("%s.%s.00", isisAreaAddr, dutSysID)}) + globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).SetEnabled(true) + globalISIS.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).SetEnabled(true) + level := isis.GetOrCreateLevel(2) + level.MetricStyle = oc.Isis_MetricStyle_WIDE_METRIC + // Configure ISIS enabled flag at level + if deviations.ISISLevelEnabled(dut) { + level.SetEnabled(true) + } + + for _, isisPort := range isisConf { + intf := isis.GetOrCreateInterface(isisPort.port) + intf.CircuitType = oc.Isis_CircuitType_POINT_TO_POINT + intf.SetEnabled(true) + if deviations.ISISInterfaceLevel1DisableRequired(dut) { + intf.GetOrCreateLevel(1).SetEnabled(false) + } else { + intf.GetOrCreateLevel(2).SetEnabled(true) + } + globalISIS.LevelCapability = isisPort.level + intf.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV4, oc.IsisTypes_SAFI_TYPE_UNICAST).SetEnabled(true) + intf.GetOrCreateAf(oc.IsisTypes_AFI_TYPE_IPV6, oc.IsisTypes_SAFI_TYPE_UNICAST).SetEnabled(true) + if deviations.ISISInterfaceAfiUnsupported(dut) { + intf.Af = nil + } + } + + // Push ISIS configuration to DUT + gnmi.Replace(t, dut, dutConfIsisPath.Config(), isisProtocol) + +} + +func bgpCreateNbr(t *testing.T, localAs uint32, dut *ondatra.DUTDevice, nbrs []*bgpNbr) { + localAddressLeaf := "" + dutConfPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + dutOcRoot := &oc.Root{} + ni1 := dutOcRoot.GetOrCreateNetworkInstance(deviations.DefaultNetworkInstance(dut)) + niProto := ni1.GetOrCreateProtocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP") + bgp := niProto.GetOrCreateBgp() + + global := bgp.GetOrCreateGlobal() + global.SetRouterId(dutloopback0.IPv4) + global.SetAs(localAs) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST).SetEnabled(true) + global.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST).SetEnabled(true) + + for _, nbr := range nbrs { + pg1 := bgp.GetOrCreatePeerGroup(nbr.peerGrpName) + pg1.SetPeerAs(nbr.peerAs) + + bgpNbr := bgp.GetOrCreateNeighbor(nbr.nbrIp) + bgpNbr.SetPeerGroup(nbr.peerGrpName) + bgpNbr.SetPeerAs(nbr.peerAs) + bgpNbr.SetEnabled(true) + bgpNbrT := bgpNbr.GetOrCreateTransport() + + localAddressLeaf = nbr.srcIp + + if dut.Vendor() == ondatra.CISCO { + localAddressLeaf = dutloopback0.Name + } + bgpNbrT.SetLocalAddress(localAddressLeaf) + if nbr.routeReflector { + routeReflector := bgpNbr.GetOrCreateRouteReflector() + routeReflector.SetRouteReflectorClient(true) + } + af4 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST) + af4.SetEnabled(true) + af6 := bgpNbr.GetOrCreateAfiSafi(oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST) + af6.SetEnabled(true) + + } + + gnmi.Update(t, dut, dutConfPath.Config(), niProto) +} + +func configureOTG() { + // Enable ISIS and BGP Protocols on port 1. + port1Data := otgBGPConfig[0] + iDut1Dev := port1Data.otgDevice[0] + + isisDut := iDut1Dev.Isis().SetName("ISIS1").SetSystemId(isisSysID1) + isisDut.Basic().SetIpv4TeRouterId(port1Data.otgPortData[0].IPv4).SetHostname(isisDut.Name()).SetLearnedLspFilter(true) + isisDut.Interfaces().Add().SetEthName(iDut1Dev.Ethernets().Items()[0].Name()). + SetName("devIsisInt1"). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT) + + iDutBgp := iDut1Dev.Bgp().SetRouterId(port1Data.otgPortData[0].IPv4) + iDutBgp4Peer := iDutBgp.Ipv4Interfaces().Add().SetIpv4Name(iDut1Dev.Ethernets().Items()[0].Ipv4Addresses().Items()[0].Name()). + Peers().Add().SetName(port1Data.otgPortData[0].Name + ".BGP4.peer") + iDutBgp4Peer.SetPeerAddress(dutloopback0.IPv4).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDutBgp4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDutBgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + // Advertise user prefixes on port 1 + v4routes := iDutBgp4Peer.V4Routes().Add().SetName(atePort1RouteV4) + v4routes.Addresses().Add().SetAddress(ate1UserPrefixesV4).SetStep(1).SetPrefix(24).SetCount(ate1UserPrefixesCount) + + iDutBgp6Peer := iDutBgp.Ipv6Interfaces().Add().SetIpv6Name(iDut1Dev.Ethernets().Items()[0].Ipv6Addresses().Items()[0].Name()). + Peers().Add().SetName(port1Data.otgPortData[0].Name + ".BGP6.peer") + iDutBgp6Peer.SetPeerAddress(dutloopback0.IPv6).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + iDutBgp6Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDutBgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + // Advertise user prefixes v6 on port 1 + v6routes := iDutBgp6Peer.V6Routes().Add().SetName(atePort1RouteV6) + v6routes.Addresses().Add().SetAddress(ate1UserPrefixesV6).SetStep(1).SetPrefix(64).SetCount(ate1UserPrefixesCount) + + // Configure OTG Port2 + port2Data := otgBGPConfig[1] + + // Enable ISIS and BGP Protocols on port 2 VLAN 100 + iDut2Dev := port2Data.otgDevice[0] + + isis2Dut := iDut2Dev.Isis().SetName("ISIS2").SetSystemId(isisSysID2) + isis2Dut.Basic().SetIpv4TeRouterId(port2Data.otgPortData[0].IPv4).SetHostname(isis2Dut.Name()).SetLearnedLspFilter(true) + isis2Dut.Interfaces().Add().SetEthName(iDut2Dev.Ethernets().Items()[0].Name()). + SetName("devIsisInt2"). + SetLevelType(gosnappi.IsisInterfaceLevelType.LEVEL_2). + SetNetworkType(gosnappi.IsisInterfaceNetworkType.POINT_TO_POINT) + + // Configure IBGP Peer on port2 VLAN100 + iDut2Bgp := iDut2Dev.Bgp().SetRouterId(port2Data.otgPortData[0].IPv4) + iDut2Bgp4Peer := iDut2Bgp.Ipv4Interfaces().Add().SetIpv4Name(iDut2Dev.Ethernets().Items()[0].Ipv4Addresses().Items()[0].Name()).Peers().Add().SetName(port2Data.otgPortData[0].Name + ".BGP4.peer") + iDut2Bgp4Peer.SetPeerAddress(dutloopback0.IPv4).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + iDut2Bgp4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDut2Bgp4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + // Advertise prefixes from IBGP Peer + iDut2Bgpv4routes := iDut2Bgp4Peer.V4Routes().Add().SetName(atePort2RoutesV4) + iDut2Bgpv4routes.Addresses().Add().SetAddress(ate2InternalPrefixesV4).SetStep(1).SetPrefix(24).SetCount(ate2InternalPrefixCount) + + // iDut2Bgpv6 := iDut2Dev.Bgp().SetRouterId(port2Data.otgPortData[0].IPv6) + iDut2Bgp6Peer := iDut2Bgp.Ipv6Interfaces().Add().SetIpv6Name(iDut2Dev.Ethernets().Items()[0].Ipv6Addresses().Items()[0].Name()).Peers().Add().SetName(port2Data.otgPortData[0].Name + ".BGP6.peer") + iDut2Bgp6Peer.SetPeerAddress(dutloopback0.IPv6).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + iDut2Bgp6Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + iDut2Bgp6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + iDut2BgpTe10Routes := iDut2Bgp6Peer.V6Routes().Add().SetName(atePort2RoutesTE10) + iDut2BgpTe10Routes.Addresses().Add().SetAddress(bgpInternalTE10.IPv6).SetPrefix(uint32(64)).SetCount(1) + + iDut2BgpTe11Routes := iDut2Bgp6Peer.V6Routes().Add().SetName(atePort2RoutesTE11) + iDut2BgpTe11Routes.Addresses().Add().SetAddress(bgpInternalTE11.IPv6).SetPrefix(uint32(64)).SetCount(1) + + iDut2Bgpv6routes := iDut2Bgp6Peer.V6Routes().Add().SetName(atePort2RoutesV6) + iDut2Bgpv6routes.Addresses().Add().SetAddress(ate2InternalPrefixesV6).SetStep(1).SetPrefix(64).SetCount(ate1UserPrefixesCount) + + // Configure IBGP_C on port 2 VLAN 200 + iDut2Dev200 := port2Data.otgDevice[1] + iDut2Bgp200 := iDut2Dev200.Bgp().SetRouterId(port2Data.otgPortData[1].IPv4) + + ate2CBgpv4Peer := iDut2Bgp200.Ipv4Interfaces().Add().SetIpv4Name(iDut2Dev200.Ethernets().Items()[0].Ipv4Addresses().Items()[0].Name()).Peers().Add().SetName(port2Data.otgPortData[1].Name + ".CBGP4.peer") + ate2CBgpv4Peer.SetPeerAddress(dutloopback0.IPv4).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + ate2CBgpv4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + ate2CBgpv4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + ate2CBgpv6Peer := iDut2Bgp200.Ipv6Interfaces().Add().SetIpv6Name(iDut2Dev200.Ethernets().Items()[0].Ipv6Addresses().Items()[0].Name()).Peers().Add().SetName(port2Data.otgPortData[1].Name + ".CBGP6.peer") + ate2CBgpv6Peer.SetPeerAddress(dutloopback0.IPv6).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + ate2CBgpv6Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + ate2CBgpv6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + // Configure IBGP_M on port 2 VLAN 300 + iDut2Dev300 := port2Data.otgDevice[2] + iDut2Bgp300 := iDut2Dev300.Bgp().SetRouterId(port2Data.otgPortData[2].IPv4) + + ate2MBgpv4Peer := iDut2Bgp300.Ipv4Interfaces().Add().SetIpv4Name(iDut2Dev300.Ethernets().Items()[0].Ipv4Addresses().Items()[0].Name()).Peers().Add().SetName(port2Data.otgPortData[2].Name + ".MBGP4.peer") + ate2MBgpv4Peer.SetPeerAddress(dutloopback0.IPv4).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV4PeerAsType.IBGP) + ate2MBgpv4Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + ate2MBgpv4Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + ate2MBgpv6Peer := iDut2Bgp300.Ipv6Interfaces().Add().SetIpv6Name(iDut2Dev300.Ethernets().Items()[0].Ipv6Addresses().Items()[0].Name()).Peers().Add().SetName(port2Data.otgPortData[2].Name + ".MBGP6.peer") + ate2MBgpv6Peer.SetPeerAddress(dutloopback0.IPv6).SetAsNumber(ateIBGPAS).SetAsType(gosnappi.BgpV6PeerAsType.IBGP) + ate2MBgpv6Peer.Capability().SetIpv4Unicast(true).SetIpv6Unicast(true) + ate2MBgpv6Peer.LearnedInformationFilter().SetUnicastIpv4Prefix(true).SetUnicastIpv6Prefix(true) + + // Configure ATE2 Port3 + port4Data := otgBGPConfig[2] + iDut4Dev := port4Data.otgDevice[0] + + ate3Bgp := iDut4Dev.Bgp().SetRouterId(port4Data.otgPortData[0].IPv4) + + ate3Bgpv4Peer := ate3Bgp.Ipv4Interfaces().Add().SetIpv4Name(port4Data.otgPortData[0].Name + ".IPv4").Peers().Add().SetName("ate3.bgp4.peer") + ate3Bgpv4Peer.SetPeerAddress(dutloopback0.IPv4).SetAsNumber(ateEBGPAS).SetAsType(gosnappi.BgpV4PeerAsType.EBGP).LearnedInformationFilter().SetUnicastIpv4Prefix(true) + + ate3Bgpv6Peer := ate3Bgp.Ipv6Interfaces().Add().SetIpv6Name(port4Data.otgPortData[0].Name + ".IPv6").Peers().Add().SetName("ate3.bgp6.peer") + ate3Bgpv6Peer.SetPeerAddress(dutloopback0.IPv6).SetAsNumber(ateEBGPAS).SetAsType(gosnappi.BgpV6PeerAsType.EBGP).LearnedInformationFilter().SetUnicastIpv4Prefix(true) + + ebgpRoutes := ate3Bgpv6Peer.V6Routes().Add().SetName("ebgp4-te10-routes") + ebgpRoutes.Addresses().Add().SetAddress(bgpInternalTE10.IPv6).SetPrefix(uint32(128)) + + ebgpRoutes11 := ate3Bgpv6Peer.V6Routes().Add().SetName("ebgp4-te11-routes") + ebgpRoutes11.Addresses().Add().SetAddress(bgpInternalTE11.IPv6).SetPrefix(uint32(128)) +} + +func advertiseRoutesWithiBGP(prefixes []string, nexthopIp string, ipv4 bool, peerName string) { + port2Data := otgBGPConfig[1] + iDut2Dev := port2Data.otgDevice[1] + + if ipv4 { + bgpPeer := iDut2Dev.Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] + for _, addr := range prefixes { + v4routes2a := bgpPeer.V4Routes().Add().SetName(peerName) + v4routes2a.Addresses().Add().SetAddress(addr).SetPrefix(24).SetCount(1) + v4routes2a.SetNextHopIpv6Address(nexthopIp).SetNextHopAddressType(gosnappi.BgpV4RouteRangeNextHopAddressType.IPV6).SetNextHopMode(gosnappi.BgpV4RouteRangeNextHopMode.MANUAL).AddPath().SetPathId(1) + v4routes2a.Advanced().SetIncludeLocalPreference(true).SetLocalPreference(200) + } + } else { + bgpPeer := iDut2Dev.Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] + for _, addr := range prefixes { + v6routes2a := bgpPeer.V6Routes().Add().SetName(peerName) + v6routes2a.SetNextHopIpv6Address(nexthopIp).SetNextHopAddressType(gosnappi.BgpV6RouteRangeNextHopAddressType.IPV6).SetNextHopMode(gosnappi.BgpV6RouteRangeNextHopMode.MANUAL).AddPath().SetPathId(1) + v6routes2a.Advanced().SetIncludeLocalPreference(true).SetLocalPreference(200) + v6routes2a.Addresses().Add().SetAddress(addr).SetPrefix(64).SetCount(1) + } + } +} + +func configureInterfaces(otgConfig gosnappi.Config, portObj gosnappi.Port, portAttr *attrs.Attributes, dutAttr *attrs.Attributes) gosnappi.Device { + iDutDev := otgConfig.Devices().Add().SetName(portAttr.Name) + iDutEth := iDutDev.Ethernets().Add().SetName(portAttr.Name + ".Eth").SetMac(portAttr.MAC) + iDutEth.Connection().SetPortName(portObj.Name()) + + if portAttr.Subinterface != 0 { + iDutEth.Vlans().Add().SetName(portAttr.Name + ".Eth" + ".VLAN").SetId(portAttr.Subinterface) + } + + iDutIpv4 := iDutEth.Ipv4Addresses().Add().SetName(portAttr.Name + ".IPv4") + iDutIpv4.SetAddress(portAttr.IPv4).SetGateway(dutAttr.IPv4).SetPrefix(uint32(portAttr.IPv4Len)) + iDutIpv6 := iDutEth.Ipv6Addresses().Add().SetName(portAttr.Name + ".IPv6") + iDutIpv6.SetAddress(portAttr.IPv6).SetGateway(dutAttr.IPv6).SetPrefix(uint32(portAttr.IPv6Len)) + + return iDutDev +} + +func configureTrafficFlows(t *testing.T, otgConfig gosnappi.Config, trafficFlowData []trafficFlow) { + flowSetNum := regexp.MustCompile(`^flowSet(\d+)`) + + for _, trafficFlow := range trafficFlowData { + flow := createflow(otgConfig, &trafficFlow.flows, false, &trafficFlow.innerParams) + matches := flowSetNum.FindStringSubmatch(trafficFlow.flows.FlowName) + if len(matches) == 0 { + t.Fatalf("flow name %s does not match expected pattern", trafficFlow.flows.FlowName) + } + flowSet := matches[0] + fg := flowGroups[flowSet] + + fg.Flows = append(fg.Flows, flow) + flowGroups[flowSet] = fg + } +} + +func createflow(top gosnappi.Config, params *otgconfighelpers.Flow, clearFlows bool, paramsInner *otgconfighelpers.Flow) gosnappi.Flow { + if clearFlows { + top.Flows().Clear() + } + + params.CreateFlow(top) + + params.AddEthHeader() + + if params.VLANFlow != nil { + params.AddVLANHeader() + } + + if params.IPv4Flow != nil { + params.AddIPv4Header() + } + + if params.IPv6Flow != nil { + params.AddIPv6Header() + } + + if params.UDPFlow != nil { + params.AddUDPHeader() + } + + if paramsInner != nil { + if paramsInner.IPv4Flow != nil { + params.IPv4Flow = paramsInner.IPv4Flow + params.AddIPv4Header() + } + + if paramsInner.IPv6Flow != nil { + params.IPv6Flow = paramsInner.IPv6Flow + params.AddIPv6Header() + } + } + + return params.GetFlow() +} + +func withdrawBGPRoutes(t *testing.T, routeNames []string) { + ate := ondatra.ATE(t, "ate") + otg := ate.OTG() + cs := gosnappi.NewControlState() + cs.Protocol().Route().SetNames(routeNames).SetState(gosnappi.StateProtocolRouteState.WITHDRAW) + otg.SetControlState(t, cs) + +} + +func verifyTrafficFlow(t *testing.T, ate *ondatra.ATEDevice, flowName string) { + FlowIPv4Validation.Flow.Name = flowName + if err := FlowIPv4Validation.ValidateLossOnFlows(t, ate); err != nil { + t.Errorf("validation on flows failed (): %q", err) + } +} + +func validatePrefixes(t *testing.T, dut *ondatra.DUTDevice, neighborIP string, isV4 bool, PfxRcd, PfxSent uint32) { + t.Helper() + var afiSafi oc.E_BgpTypes_AFI_SAFI_TYPE + + t.Logf("Validate prefixes for %s. Expecting prefix received %v", neighborIP, PfxRcd) + switch isV4 { + case true: + afiSafi = oc.BgpTypes_AFI_SAFI_TYPE_IPV4_UNICAST + case false: + afiSafi = oc.BgpTypes_AFI_SAFI_TYPE_IPV6_UNICAST + } + + bgpPath := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_BGP, "BGP").Bgp() + query := bgpPath.Neighbor(neighborIP).AfiSafi(afiSafi).Prefixes().Received().State() + _, ok := gnmi.Watch(t, dut, query, 30*time.Second, func(val *ygnmi.Value[uint32]) bool { + if v, ok := val.Val(); ok { + if v != uint32(PfxRcd) { + t.Errorf("received prefixes - got: %v, want: %v", v, PfxRcd) + } + } + return true + }).Await(t) + if !ok { + t.Errorf("no received prefixes found") + } + + sentQuery := bgpPath.Neighbor(neighborIP).AfiSafi(afiSafi).Prefixes().Sent().State() + _, ok = gnmi.Watch(t, dut, sentQuery, 30*time.Second, func(val *ygnmi.Value[uint32]) bool { + if v, ok := val.Val(); ok { + if v != uint32(PfxSent) { + t.Errorf("sent prefixes - got: %v, want: %v", v, PfxSent) + } + } + return true + }).Await(t) + if !ok { + t.Errorf("no sent prefixes found") + } + +} + +func validateOutCounters(t *testing.T, dut *ondatra.DUTDevice, otg *otg.OTG) { + var totalTxFromATE uint64 + + flows := otg.FetchConfig(t).Flows().Items() + for _, flow := range flows { + txPkts := gnmi.Get(t, otg, gnmi.OTG().Flow(flow.Name()).Counters().OutPkts().State()) + + totalTxFromATE += txPkts + } + + dutOutCounters := gnmi.Get(t, dut, gnmi.OC().Interface(dut.Port(t, otgBGPConfig[2].port).Name()).Counters().State()).GetOutUnicastPkts() + + expectedTotalTraffic := uint64(totalPackets * len(flows)) + if totalTxFromATE > 0 { + if float64(dutOutCounters) < float64(totalTxFromATE)*0.98 { + t.Errorf("dut counters is significantly less than ATE Tx (%d). Recieved: %d, Expected approx %d", totalTxFromATE, dutOutCounters, expectedTotalTraffic) + } + } else if expectedTotalTraffic > 0 { + t.Errorf("no traffic was reported as transmitted by ATE flows, but %d total packets were expected", expectedTotalTraffic) + } +} + +// configureGueTunnel configures a GUE tunnel with optional ToS and TTL. +func configureGueEncap(t *testing.T, dut *ondatra.DUTDevice) { + + _, ni, _ := cfgplugins.SetupPolicyForwardingInfraOC(deviations.DefaultNetworkInstance(dut)) + + v4NexthopUDPParams := cfgplugins.NexthopGroupUDPParams{ + IPFamily: "V6Udp", + NexthopGrpName: nexthopGroupName1, + Index: "0", + SrcInterface: loopbackIntfName, + DstIp: []string{bgpInternalTE10.IPv6}, + TTL: 64, + DstUdpPort: udpEncapPort, + NetworkInstanceObj: ni, + DeleteTtl: false, + } + // Create nexthop group for v4 + cfgplugins.NextHopGroupConfigForIpOverUdp(t, dut, v4NexthopUDPParams) + + v4NexthopUDPParams2 := cfgplugins.NexthopGroupUDPParams{ + IPFamily: "V6Udp", + NexthopGrpName: nexthopGroupName2, + Index: "1", + SrcInterface: loopbackIntfName, + DstIp: []string{bgpInternalTE11.IPv6}, + TTL: 64, + DstUdpPort: udpEncapPort, + NetworkInstanceObj: ni, + DeleteTtl: false, + } + // Create nexthop group for v4 + cfgplugins.NextHopGroupConfigForIpOverUdp(t, dut, v4NexthopUDPParams2) + + // Apply traffic policy on interface + if deviations.NextHopGroupOCUnsupported(dut) { + interfacePolicyParams := cfgplugins.OcPolicyForwardingParams{ + InterfaceName: dut.Port(t, "port1").Name(), + AppliedPolicyName: guePolicyName, + NetworkInstanceObj: ni, + PolicyName: guePolicyName, + } + cfgplugins.InterfacePolicyForwardingApply(t, dut, interfacePolicyParams) + } +} + +func sendTrafficCapture(t *testing.T, ate *ondatra.ATEDevice, flowNames []string) { + cs := gosnappi.NewControlState() + if flowNames[0] == "all" { + ate.OTG().StartTraffic(t) + } else { + cs.Traffic().FlowTransmit(). + SetState(gosnappi.StateTrafficFlowTransmitState.START). + SetFlowNames(flowNames) + ate.OTG().SetControlState(t, cs) + } + cs = packetvalidationhelpers.StartCapture(t, ate) + time.Sleep(60 * time.Second) + ate.OTG().StopTraffic(t) + time.Sleep(60 * time.Second) + packetvalidationhelpers.StopCapture(t, ate, cs) +} + +func validatePacket(t *testing.T, ate *ondatra.ATEDevice, validationPacket *packetvalidationhelpers.PacketValidation) error { + err := packetvalidationhelpers.CaptureAndValidatePackets(t, ate, validationPacket) + return err +} + +func validateAFTCounters(t *testing.T, dut *ondatra.DUTDevice, isV4 bool, routeIp string) { + t.Logf("Validate AFT parameters for %s", routeIp) + if isV4 { + ipv4Path := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Afts().Ipv4Entry(routeIp) + if _, ok := gnmi.Watch(t, dut, ipv4Path.State(), time.Minute, func(val *ygnmi.Value[*oc.NetworkInstance_Afts_Ipv4Entry]) bool { + ipv4Entry, present := val.Val() + return present && ipv4Entry.GetPrefix() == routeIp + }).Await(t); ok { + t.Error("ipv4-entry/state/prefix got but should not be present") + } + } else { + ipv6Path := gnmi.OC().NetworkInstance(deviations.DefaultNetworkInstance(dut)).Afts().Ipv6Entry(routeIp) + if _, ok := gnmi.Watch(t, dut, ipv6Path.State(), time.Minute, func(val *ygnmi.Value[*oc.NetworkInstance_Afts_Ipv6Entry]) bool { + ipv6Entry, present := val.Val() + return present && ipv6Entry.GetPrefix() == routeIp + }).Await(t); ok { + t.Error("ipv6-entry/state/prefix got but should not be present") + } + } +} + +func testTrafficMigration(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config, flowNames map[string][]string, dscpVal string) { + t.Log("Validate IPv6 GUE encapsulation and decapsulation") + sendTrafficCapture(t, ate, flowNames["v4"]) + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + otgutils.LogPortMetrics(t, ate.OTG(), otgConfig) + + for _, flow := range flowNames["v4"] { + verifyTrafficFlow(t, ate, flow) + } + + // Validating GUEv6 encapsulation and decapsulation for v4 traffic + t.Log("Validate GUE encapsulation with ipv4 traffic") + gueLayer := *outerGUEIPLayerIPv6 + gueLayer.TrafficClass = uint8(expectedDscpValue[dscpVal]) + + gueInnerLayer := *innerGUEIPLayerIPv4 + gueInnerLayer.Tos = uint8(expectedDscpValue[dscpVal]) + + encapValidation.IPv6Layer = &gueLayer + encapValidation.InnerIPLayerIPv4 = &gueInnerLayer + + if err := validatePacket(t, ate, encapValidation); err != nil { + t.Errorf("capture and validatePackets failed (): %q", err) + } else { + t.Log("GUE encapsulated packets are received") + } + + t.Log("Validate GUE Decapsulation with v4 traffic") + decapInner := *innerGUEIPLayerIPv4 + decapInner.SkipProtocolCheck = true + decapInner.Tos = uint8(expectedDscpValue[dscpVal]) + decapValidation.IPv4Layer = &decapInner + + if err := validatePacket(t, ate, decapValidation); err != nil { + t.Errorf("capture and validatePackets failed (): %q", err) + } else { + t.Log("GUE decapsulated packets are received") + } + + t.Log("Validate GUE encapsulation with ipv6 traffic") + sendTrafficCapture(t, ate, flowNames["v6"]) + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + + for _, flow := range flowNames["v6"] { + verifyTrafficFlow(t, ate, flow) + } + + encapValidationv6.IPv6Layer.TrafficClass = uint8(dscpValue[dscpVal]) + if err := validatePacket(t, ate, encapValidationv6); err != nil { + t.Errorf("capture and validatePackets failed (): %q", err) + } + + // Validate the counters received on ATE and DUT are same + validateOutCounters(t, dut, ate.OTG()) +} + +func configureHardwareInit(t *testing.T, dut *ondatra.DUTDevice) { + hardwareInitCfg := cfgplugins.NewDUTHardwareInit(t, dut, cfgplugins.FeaturePolicyForwarding) + if hardwareInitCfg == "" { + return + } + cfgplugins.PushDUTHardwareInitConfig(t, dut, hardwareInitCfg) +} + +func TestStaticGue(t *testing.T) { + var deviceObj gosnappi.Device + + dut := ondatra.DUT(t, "dut") + ate := ondatra.ATE(t, "ate") + otgConfig := gosnappi.NewConfig() + + t.Logf("Configuring Hardware Init") + configureHardwareInit(t, dut) + + for _, cfg := range otgBGPConfig { + dutPort := dut.Port(t, cfg.port) + portObj := otgConfig.Ports().Add().SetName(cfg.port) + + // Configure ATE & DUT interfaces + for index, ap := range cfg.otgPortData { + configureDUT(t, dut, dutPort, cfg.dutPortData[index]) + deviceObj = configureInterfaces(otgConfig, portObj, ap, cfg.dutPortData[index]) + cfg.otgDevice = append(cfg.otgDevice, deviceObj) + } + + // Configure BGP Peers on DUT + bgpCreateNbr(t, dutAS, dut, cfg.bgpCfg) + } + + configureLoopback(t, dut) + configureStaticRoute(t, dut) + configureISIS(t, dut) + configureGueEncap(t, dut) + + // Configure gue decap config + ocPFParams := cfgplugins.OcPolicyForwardingParams{ + NetworkInstanceName: "DEFAULT", + AppliedPolicyName: decapPolicy1, + TunnelIP: fmt.Sprintf("%s/64", bgpInternalTE10.IPv6), + GUEPort: udpEncapPort, + IPType: "ip", + Dynamic: true, + } + _, _, pf := cfgplugins.SetupPolicyForwardingInfraOC(ocPFParams.NetworkInstanceName) + cfgplugins.DecapGroupConfigGue(t, dut, pf, ocPFParams) + + ocPFParams = cfgplugins.OcPolicyForwardingParams{ + NetworkInstanceName: "DEFAULT", + AppliedPolicyName: decapPolicy2, + TunnelIP: fmt.Sprintf("%s/64", bgpInternalTE11.IPv6), + GUEPort: udpEncapPort, + IPType: "ip", + Dynamic: true, + } + cfgplugins.DecapGroupConfigGue(t, dut, pf, ocPFParams) + + port1DstMac = gnmi.Get(t, dut, gnmi.OC().Interface(dut.Port(t, "port1").Name()).Ethernet().MacAddress().State()) + port2DstMac = gnmi.Get(t, dut, gnmi.OC().Interface(dut.Port(t, "port2").Name()).Ethernet().MacAddress().State()) + + // configure interfaces on OTG + configureOTG() + + packetvalidationhelpers.ConfigurePacketCapture(t, otgConfig, encapValidation) + packetvalidationhelpers.ConfigurePacketCapture(t, otgConfig, decapValidation) + + trafficFlowData = []trafficFlow{ + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet1-v4-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate1UserPrefixesV4List[0], IPv4Dst: ate2InternalPrefixesV4List[0], DSCP: dscpValue["BE1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet1-v6-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate1UserPrefixesV6List[0], IPv6Dst: ate2InternalPrefixesV6List[0], TrafficClass: dscpValue["BE1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet1-v4-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate1UserPrefixesV4List[1], IPv4Dst: ate2InternalPrefixesV4List[1], DSCP: dscpValue["AF1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet1-v6-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate1UserPrefixesV6List[1], IPv6Dst: ate2InternalPrefixesV6List[1], TrafficClass: dscpValue["AF1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet1-v4-3", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate1UserPrefixesV4List[2], IPv4Dst: ate2InternalPrefixesV4List[2], DSCP: dscpValue["AF2"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet1-v6-3", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate1UserPrefixesV6List[2], IPv6Dst: ate2InternalPrefixesV6List[2], TrafficClass: dscpValue["AF2"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet2-v4-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate1UserPrefixesV4List[3], IPv4Dst: ate2InternalPrefixesV4List[3], DSCP: dscpValue["AF3"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet2-v6-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate1UserPrefixesV6List[3], IPv6Dst: ate2InternalPrefixesV6List[3], TrafficClass: dscpValue["AF3"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet2-v4-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate1UserPrefixesV4List[4], IPv4Dst: ate2InternalPrefixesV4List[4], DSCP: dscpValue["AF4"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[0].port, + RxPorts: []string{otgBGPConfig[2].port, otgBGPConfig[1].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet2-v6-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[0].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate1UserPrefixesV6List[4], IPv6Dst: ate2InternalPrefixesV6List[4], TrafficClass: dscpValue["AF4"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet3-v4-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[0], IPv4Dst: ate1UserPrefixesV4List[0], DSCP: dscpValue["BE1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet3-v6-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[0], IPv6Dst: ate1UserPrefixesV6List[0], TrafficClass: dscpValue["BE1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet3-v4-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[1], IPv4Dst: ate1UserPrefixesV4List[1], DSCP: dscpValue["AF1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet3-v6-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[1], IPv6Dst: ate1UserPrefixesV6List[1], TrafficClass: dscpValue["AF1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet3-v4-3", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[2], IPv4Dst: ate1UserPrefixesV4List[2], DSCP: dscpValue["AF2"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet3-v6-3", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[2], IPv6Dst: ate1UserPrefixesV6List[2], TrafficClass: dscpValue["AF2"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet4-v4-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE10.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[3], IPv4Dst: ate1UserPrefixesV4List[3], DSCP: dscpValue["AF3"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet4-v6-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE10.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[3], IPv6Dst: ate1UserPrefixesV6List[3], TrafficClass: dscpValue["AF3"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet4-v4-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE10.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[4], IPv4Dst: ate1UserPrefixesV4List[4], DSCP: dscpValue["AF4"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[2].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet4-v6-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[2].otgPortData[0].MAC, DstMAC: port1DstMac}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE10.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[4], IPv6Dst: ate1UserPrefixesV6List[4], TrafficClass: dscpValue["AF4"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v4-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[0], IPv4Dst: ate1UserPrefixesV4List[0], DSCP: dscpValue["BE1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v6-1", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[0], IPv6Dst: ate1UserPrefixesV6List[0], TrafficClass: dscpValue["BE1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v4-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[1], IPv4Dst: ate1UserPrefixesV4List[1], DSCP: dscpValue["AF1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v6-2", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[1], IPv6Dst: ate1UserPrefixesV6List[1], TrafficClass: dscpValue["AF1"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v4-3", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[2], IPv4Dst: ate1UserPrefixesV4List[2], DSCP: dscpValue["AF2"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v6-3", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[2], IPv6Dst: ate1UserPrefixesV6List[2], TrafficClass: dscpValue["AF2"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v4-4", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[3], IPv4Dst: ate1UserPrefixesV4List[3], DSCP: dscpValue["AF3"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v6-4", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[3], IPv6Dst: ate1UserPrefixesV6List[3], TrafficClass: dscpValue["AF3"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v4-5", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv4Flow: &otgconfighelpers.IPv4FlowParams{IPv4Src: ate2InternalPrefixesV4List[4], IPv4Dst: ate1UserPrefixesV4List[4], DSCP: dscpValue["AF4"]}, + }, + }, + { + flows: otgconfighelpers.Flow{ + TxPort: otgBGPConfig[1].port, + RxPorts: []string{otgBGPConfig[0].port}, + IsTxRxPort: true, + PacketsToSend: totalPackets, + PpsRate: trafficPps, + FlowName: "flowSet5-v6-5", + EthFlow: &otgconfighelpers.EthFlowParams{SrcMAC: otgBGPConfig[1].otgPortData[1].MAC, DstMAC: port2DstMac}, + VLANFlow: &otgconfighelpers.VLANFlowParams{VLANId: otgBGPConfig[1].otgPortData[1].Subinterface}, + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: dutloopback0.IPv6, IPv6Dst: bgpInternalTE11.IPv6, HopLimit: ttl}, + UDPFlow: &otgconfighelpers.UDPFlowParams{UDPDstPort: udpEncapPort}, + }, + innerParams: otgconfighelpers.Flow{ + IPv6Flow: &otgconfighelpers.IPv6FlowParams{IPv6Src: ate2InternalPrefixesV6List[4], IPv6Dst: ate1UserPrefixesV6List[4], TrafficClass: dscpValue["AF4"]}, + }, + }, + } + + configureTrafficFlows(t, otgConfig, trafficFlowData) + + type testCase struct { + Name string + Description string + testFunc func(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) + } + + testCases := []testCase{ + { + Name: "Testcase: Validate the basic config", + Description: "Validate traffic with basic config", + testFunc: testBaselineTraffic, + }, + { + Name: "Testcase: Verify BE1 Traffic Migrated from being routed over the DUT_Port2", + Description: "Verify BE1 Traffic Migrated from being routed over the DUT_Port2", + testFunc: testBE1TrafficMigration, + }, + { + Name: "Testcase: Verify AF1 Traffic Migrated from being routed over the DUT_Port2", + Description: "Verify AF1 Traffic Migrated from being routed over the DUT_Port2", + testFunc: testAF1TrafficMigration, + }, + { + Name: "Testcase: Verify AF2 Traffic Migrated from being routed over the DUT_Port2", + Description: "Verify AF2 Traffic Migrated from being routed over the DUT_Port2", + testFunc: testAF2TrafficMigration, + }, + { + Name: "Testcase: Verify AF3 Traffic Migrated from being routed over the DUT_Port2", + Description: "Verify AF3 Traffic Migrated from being routed over the DUT_Port2", + testFunc: testAF3TrafficMigration, + }, + { + Name: "Testcase: Verify AF4 Traffic Migrated from being routed over the DUT_Port2", + Description: "Verify AF4 Traffic Migrated from being routed over the DUT_Port2", + testFunc: testAF4TrafficMigration, + }, + { + Name: "Testcase: DUT as a GUE Decap Node", + Description: "Verify DUT as a GUE Decap Node", + testFunc: testDUTDecapNode, + }, + { + Name: "Testcase: Negative Scenario - EBGP Route for remote tunnel endpoints Removed", + Description: "Verify EBGP Route for remote tunnel endpoints Removed", + testFunc: testTunnelEndpointRemoved, + }, + { + Name: "Testcase: Negative Scenario - IBGP Route for Remote Tunnel Endpoints Removed", + Description: "Verify IBGP Route for Remote Tunnel Endpoints Removed", + testFunc: testIbgpTunnelEndpointRemoved, + }, + { + Name: "Testcase: Establish IBGP Peering over EBGP", + Description: "Verify Establish IBGP Peering over EBGP", + testFunc: testEstablishIBGPoverEBGP, + }, + } + + // Run the test cases. + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + t.Logf("Description: %s", tc.Description) + tc.testFunc(t, dut, ate, otgConfig) + }) + } + +} + +func testBaselineTraffic(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + flowsets := []string{"flowSet1", "flowSet2", "flowSet5"} + + otgConfig.Flows().Clear() + + for _, flowset := range flowsets { + otgConfig.Flows().Append(flowGroups[flowset].Flows...) + } + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + // Validating no prefixes are exchanged over the IBGP peering between $ATE2_C.IBGP.v6 and $DUT_lo0.v6 + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[2].IPv6, false, 0, 0) + + sendTrafficCapture(t, ate, []string{"all"}) + + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + otgutils.LogPortMetrics(t, ate.OTG(), otgConfig) + + for _, flows := range flowsets { + for _, flow := range flowGroups[flows].Flows { + verifyTrafficFlow(t, ate, flow.Name()) + } + } +} + +func testBE1TrafficMigration(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV4List[0]}, ate2ppnh1.IPv6, true, fmt.Sprintf("%s-CBGP-1", atePort2RoutesV4)) + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV6List[0]}, ate2ppnh1.IPv6, false, fmt.Sprintf("%s-CBGP-1", atePort2RoutesV6)) + + flowsets := []string{"flowSet1", "flowSet5"} + + flowNames := map[string][]string{ + "v4": {}, + "v6": {}, + } + + otgConfig.Flows().Clear() + + for _, flow := range flowsets { + otgConfig.Flows().Append(flowGroups[flow].Flows[0:2]...) + + flowNames["v4"] = append(flowNames["v4"], flowGroups[flow].Flows[0].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups[flow].Flows[1].Name()) + } + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + time.Sleep(10 * time.Second) + // Validating routes to prefixes learnt from $ATE2_C.IBGP.v6/128 + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[1].IPv6, false, 1, 7) + + testTrafficMigration(t, dut, ate, otgConfig, flowNames, "BE1") + +} + +func testAF1TrafficMigration(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV4List[1]}, ate2ppnh1.IPv6, true, fmt.Sprintf("%s-CBGP-2", atePort2RoutesV4)) + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV6List[1]}, ate2ppnh1.IPv6, false, fmt.Sprintf("%s-CBGP-2", atePort2RoutesV6)) + flowsets := []string{"flowSet1", "flowSet5"} + flowNames := map[string][]string{ + "v4": {}, + "v6": {}, + } + + otgConfig.Flows().Clear() + + for _, flow := range flowsets { + otgConfig.Flows().Append(flowGroups[flow].Flows[2:4]...) + + flowNames["v4"] = append(flowNames["v4"], flowGroups[flow].Flows[2].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups[flow].Flows[3].Name()) + } + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + time.Sleep(10 * time.Second) + // Validating routes to prefixes learnt from $ATE2_C.IBGP.v6/128 + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[1].IPv6, false, 2, 7) + + testTrafficMigration(t, dut, ate, otgConfig, flowNames, "AF1") +} + +func testAF2TrafficMigration(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV4List[2]}, ate2ppnh1.IPv6, true, fmt.Sprintf("%s-CBGP-3", atePort2RoutesV4)) + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV6List[2]}, ate2ppnh1.IPv6, false, fmt.Sprintf("%s-CBGP-3", atePort2RoutesV6)) + + flowNames := map[string][]string{ + "v4": {}, + "v6": {}, + } + + otgConfig.Flows().Clear() + + otgConfig.Flows().Append(flowGroups["flowSet1"].Flows[4:]...) + flowNames["v4"] = append(flowNames["v4"], flowGroups["flowSet1"].Flows[4].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups["flowSet1"].Flows[5].Name()) + + otgConfig.Flows().Append(flowGroups["flowSet5"].Flows[4:6]...) + flowNames["v4"] = append(flowNames["v4"], flowGroups["flowSet5"].Flows[4].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups["flowSet5"].Flows[5].Name()) + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + time.Sleep(10 * time.Second) + // Validating routes to prefixes learnt from $ATE2_C.IBGP.v6/128 + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[1].IPv6, false, 3, 7) + + testTrafficMigration(t, dut, ate, otgConfig, flowNames, "AF2") +} + +func testAF3TrafficMigration(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV4List[3]}, ate2ppnh2.IPv6, true, fmt.Sprintf("%s-CBGP-4", atePort2RoutesV4)) + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV6List[3]}, ate2ppnh2.IPv6, false, fmt.Sprintf("%s-CBGP-4", atePort2RoutesV6)) + + flowNames := map[string][]string{ + "v4": {}, + "v6": {}, + } + + otgConfig.Flows().Clear() + + otgConfig.Flows().Append(flowGroups["flowSet2"].Flows[0:2]...) + flowNames["v4"] = append(flowNames["v4"], flowGroups["flowSet2"].Flows[0].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups["flowSet2"].Flows[1].Name()) + + otgConfig.Flows().Append(flowGroups["flowSet5"].Flows[6:8]...) + flowNames["v4"] = append(flowNames["v4"], flowGroups["flowSet5"].Flows[6].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups["flowSet5"].Flows[7].Name()) + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + time.Sleep(10 * time.Second) + // Validating routes to prefixes learnt from $ATE2_C.IBGP.v6/128 + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[1].IPv6, false, 4, 7) + + testTrafficMigration(t, dut, ate, otgConfig, flowNames, "AF3") +} + +func testAF4TrafficMigration(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV4List[4]}, ate2ppnh2.IPv6, true, fmt.Sprintf("%s-CBGP-5", atePort2RoutesV4)) + advertiseRoutesWithiBGP([]string{ate2InternalPrefixesV6List[4]}, ate2ppnh2.IPv6, false, fmt.Sprintf("%s-CBGP-5", atePort2RoutesV6)) + + flowNames := map[string][]string{ + "v4": {}, + "v6": {}, + } + + otgConfig.Flows().Clear() + + otgConfig.Flows().Append(flowGroups["flowSet2"].Flows[2:]...) + flowNames["v4"] = append(flowNames["v4"], flowGroups["flowSet2"].Flows[2].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups["flowSet2"].Flows[3].Name()) + + otgConfig.Flows().Append(flowGroups["flowSet5"].Flows[8:10]...) + flowNames["v4"] = append(flowNames["v4"], flowGroups["flowSet5"].Flows[8].Name()) + flowNames["v6"] = append(flowNames["v6"], flowGroups["flowSet5"].Flows[9].Name()) + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + time.Sleep(10 * time.Second) + // Validating routes to prefixes learnt from $ATE2_C.IBGP.v6/128 + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[1].IPv6, false, 5, 7) + + testTrafficMigration(t, dut, ate, otgConfig, flowNames, "AF4") +} + +func testDUTDecapNode(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + // Active flows for Flow-Set #1 through Flow-Set #4. + flowsets := []string{"flowSet1", "flowSet2", "flowSet3", "flowSet4"} + + otgConfig.Flows().Clear() + + for _, flowset := range flowsets { + otgConfig.Flows().Append(flowGroups[flowset].Flows...) + } + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + sendTrafficCapture(t, ate, []string{"all"}) + + // Verify Traffic + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + otgutils.LogPortMetrics(t, ate.OTG(), otgConfig) + + for _, flows := range flowsets { + for _, flow := range flowGroups[flows].Flows { + verifyTrafficFlow(t, ate, flow.Name()) + } + } +} + +func testTunnelEndpointRemoved(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + flowsets := []string{"flowSet1", "flowSet2", "flowSet3", "flowSet4"} + + otgConfig.Flows().Clear() + + for _, flowset := range flowsets { + otgConfig.Flows().Append(flowGroups[flowset].Flows...) + } + + ate.OTG().PushConfig(t, otgConfig) + ate.OTG().StartProtocols(t) + + withdrawBGPRoutes(t, []string{"ebgp4-te10-routes", "ebgp4-te11-routes"}) + time.Sleep(20 * time.Second) + + t.Logf("Verify OTG BGP sessions up") + cfgplugins.VerifyOTGBGPEstablished(t, ate) + + t.Logf("Verify DUT BGP sessions up") + cfgplugins.VerifyDUTBGPEstablished(t, dut) + + time.Sleep(10 * time.Second) + // validatePrefixes(t, dut, otgBGPConfig[2].otgPortData[0].IPv4, true, 0, 12) + validatePrefixes(t, dut, otgBGPConfig[2].otgPortData[0].IPv6, true, 0, 10) + + sendTrafficCapture(t, ate, []string{"all"}) + + // Verify Traffic + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + otgutils.LogPortMetrics(t, ate.OTG(), otgConfig) + + for _, flows := range flowsets { + for _, flow := range flowGroups[flows].Flows { + verifyTrafficFlow(t, ate, flow.Name()) + } + } +} + +func testIbgpTunnelEndpointRemoved(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + _, ni, _ := cfgplugins.SetupPolicyForwardingInfraOC(deviations.DefaultNetworkInstance(dut)) + + if deviations.NextHopGroupOCUnsupported(dut) { + interfacePolicyParams := cfgplugins.OcPolicyForwardingParams{ + InterfaceName: dut.Port(t, "port1").Name(), + PolicyName: guePolicyName, + RemovePolicyName: true, + NetworkInstanceObj: ni, + } + cfgplugins.InterfacePolicyForwardingApply(t, dut, interfacePolicyParams) + } + + t.Log("Stop advertising tunnel endpoints on ATE Port2") + withdrawBGPRoutes(t, []string{atePort2RoutesTE10, atePort2RoutesTE11}) + time.Sleep(10 * time.Second) + + b := &gnmi.SetBatch{} + + // Configuring Static Route: PNH-IPv6 --> IPv4 GUE tunnel. + sV4 := &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: ate2ppnh1Prefix, + NexthopGroup: true, + NexthopGroupName: nexthopGroupName2, + T: t, + RemoveStaticRoute: true, + } + + if _, err := cfgplugins.NewStaticRouteCfg(b, sV4, dut); err != nil { + t.Fatalf("Failed to configure IPv6 static route: %v", err) + } + b.Set(t, dut) + + sV4 = &cfgplugins.StaticRouteCfg{ + NetworkInstance: deviations.DefaultNetworkInstance(dut), + Prefix: ate2ppnh2Prefix, + NexthopGroup: true, + NexthopGroupName: nexthopGroupName1, + T: t, + RemoveStaticRoute: true, + } + + if _, err := cfgplugins.NewStaticRouteCfg(b, sV4, dut); err != nil { + t.Fatalf("Failed to configure IPv6 static route: %v", err) + } + b.Set(t, dut) + + validateAFTCounters(t, dut, false, ate2ppnh1Prefix) + validateAFTCounters(t, dut, false, ate2ppnh2Prefix) + + validatePrefixes(t, dut, otgBGPConfig[1].otgPortData[0].IPv6, false, 5, 5) + + sendTrafficCapture(t, ate, []string{"all"}) + + // Verify Traffic + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + otgutils.LogPortMetrics(t, ate.OTG(), otgConfig) + + for _, flows := range []string{"flowSet1", "flowSet2", "flowSet3", "flowSet4"} { + for _, flow := range flowGroups[flows].Flows { + verifyTrafficFlow(t, ate, flow.Name()) + } + } + +} + +func testEstablishIBGPoverEBGP(t *testing.T, dut *ondatra.DUTDevice, ate *ondatra.ATEDevice, otgConfig gosnappi.Config) { + _, ni, _ := cfgplugins.SetupPolicyForwardingInfraOC(deviations.DefaultNetworkInstance(dut)) + + if deviations.NextHopGroupOCUnsupported(dut) { + interfacePolicyParams := cfgplugins.OcPolicyForwardingParams{ + InterfaceName: dut.Port(t, "port1").Name(), + PolicyName: guePolicyName, + NetworkInstanceObj: ni, + } + cfgplugins.InterfacePolicyForwardingApply(t, dut, interfacePolicyParams) + } + + configureStaticRoute(t, dut) + + // Active flows for Flow-Set #1 through Flow-Set #4. + port4Data := otgBGPConfig[2] + iDut4Dev := port4Data.otgDevice[0] + + bgpPeer := iDut4Dev.Bgp().Ipv4Interfaces().Items()[0].Peers().Items()[0] + v4routes := bgpPeer.V4Routes().Add().SetName("ATE2_C_IBGP_via_EBGP") + v4routes.Addresses().Add().SetAddress(ate2InternalPrefixesV4).SetPrefix(24).SetCount(5) + + bgpPeerv6 := iDut4Dev.Bgp().Ipv6Interfaces().Items()[0].Peers().Items()[0] + v6routes := bgpPeerv6.V6Routes().Add().SetName("ATE2_C_IBGP_via_EBGPv6") + v6routes.Addresses().Add().SetAddress(ate2InternalPrefixesV6).SetPrefix(64).SetCount(5) + ate.OTG().PushConfig(t, otgConfig) + + time.Sleep(20 * time.Second) + d := &oc.Root{} + i := d.GetOrCreateInterface(dut.Port(t, "port2").Name()) + i.SetEnabled(false) + gnmi.Replace(t, dut, gnmi.OC().Interface(dut.Port(t, "port2").Name()).Config(), i) + + ate.OTG().StartProtocols(t) + + // Validating one flow to be encapsulated when sent from Port1 -> ATE2 Port3 + sendTrafficCapture(t, ate, []string{flowGroups["flowSet1"].Flows[0].Name()}) + + gueLayer := *outerGUEIPLayerIPv6 + gueLayer.TrafficClass = uint8(expectedDscpValue["BE1"]) + encapValidation.IPv6Layer = &gueLayer + + innerGueLayer := *innerGUEIPLayerIPv4 + innerGueLayer.Tos = uint8(expectedDscpValue["BE1"]) + encapValidation.InnerIPLayerIPv4 = &innerGueLayer + if err := validatePacket(t, ate, encapValidation); err != nil { + t.Errorf("capture and validatePackets failed (): %q", err) + } + + // Validting no traffic loss for other flows + sendTrafficCapture(t, ate, []string{"all"}) + + otgutils.LogFlowMetrics(t, ate.OTG(), otgConfig) + otgutils.LogPortMetrics(t, ate.OTG(), otgConfig) + + for _, flows := range []string{"flowSet1", "flowSet2", "flowSet3", "flowSet4"} { + for _, flow := range flowGroups[flows].Flows { + verifyTrafficFlow(t, ate, flow.Name()) + } + } +} diff --git a/internal/cfgplugins/policyforwarding.go b/internal/cfgplugins/policyforwarding.go index 88de7a7d034..42f3cbe364b 100644 --- a/internal/cfgplugins/policyforwarding.go +++ b/internal/cfgplugins/policyforwarding.go @@ -49,6 +49,7 @@ type OcPolicyForwardingParams struct { NetworkInstanceName string InterfaceID string AppliedPolicyName string + RemovePolicyName bool // Policy Rule specific params InnerDstIPv6 string @@ -71,6 +72,7 @@ type OcPolicyForwardingParams struct { RemovePolicy bool } +// PolicyForwardingRule holds parameters for generating Policy Forwarding config for GRE. type PolicyForwardingRule struct { Id uint32 Name string @@ -661,7 +663,6 @@ func DecapGroupConfigGue(t *testing.T, dut *ondatra.DUTDevice, pf *oc.NetworkIns switch dut.Vendor() { case ondatra.ARISTA: if ocPFParams.Dynamic { - t.Logf("Going into decap") aristaGueDecapCLIConfig(t, dut, ocPFParams) } else { helpers.GnmiCLIConfig(t, dut, decapGroupGUEArista) @@ -684,12 +685,13 @@ func aristaGueDecapCLIConfig(t *testing.T, dut *ondatra.DUTDevice, params OcPoli cliConfig := fmt.Sprintf(` ip decap-group type udp destination port %v payload %s - tunnel type %s-over-udp udp destination port %v ip decap-group %s tunnel type UDP tunnel decap-ip %s - tunnel decap-interface %s - `, params.GUEPort, decapProto, params.IPType, params.GUEPort, params.AppliedPolicyName, params.TunnelIP, params.InterfaceID) + `, params.GUEPort, decapProto, params.IPType, params.TunnelIP) + if params.InterfaceID != "" { + cliConfig += fmt.Sprintf("tunnel decap-interface %s", params.InterfaceID) + } helpers.GnmiCLIConfig(t, dut, cliConfig) } @@ -709,6 +711,38 @@ func aristaGreDecapCLIConfig(t *testing.T, dut *ondatra.DUTDevice, params OcPoli } +// QosClassificationConfig configures the interface qos classification. +func ConfigureTOSGUE(t *testing.T, dut *ondatra.DUTDevice, policyName string, dscpValue uint32, port string, deleteTOS bool) { + if deviations.QosClassificationOCUnsupported(dut) { + switch dut.Vendor() { + case ondatra.ARISTA: + if deleteTOS { + cli := fmt.Sprintf(` + policy-map type quality-of-service %s + class class-default + no set dscp %d`, policyName, dscpValue) + helpers.GnmiCLIConfig(t, dut, cli) + } else { + cli := fmt.Sprintf(` + policy-map type quality-of-service %s + class class-default + set dscp cs%d + + qos rewrite dscp + + interface %s + service-policy type qos input %s + `, policyName, dscpValue, port, policyName) + helpers.GnmiCLIConfig(t, dut, cli) + } + + default: + t.Logf("Unsupported vendor %s for native command support for deviation 'qos classification'", dut.Vendor()) + } + } + +} + // Configure GRE decapsulated. Adding deviation when device doesn't support OC func PolicyForwardingGreDecapsulation(t *testing.T, batch *gnmi.SetBatch, dut *ondatra.DUTDevice, decapIP string, policyName string, portName string, decapGrpName string) { if deviations.GreDecapsulationOCUnsupported(dut) { @@ -1190,9 +1224,13 @@ func InterfacePolicyForwardingApply(t *testing.T, dut *ondatra.DUTDevice, params // If deviations exist, apply configuration using vendor-specific CLI commands. switch dut.Vendor() { case ondatra.ARISTA: - pfa := fmt.Sprintf(`interface %s + if params.RemovePolicyName { + helpers.GnmiCLIConfig(t, dut, fmt.Sprintf("interface %s \n no traffic-policy input %s \n", params.InterfaceName, params.PolicyName)) + } else { + pfa := fmt.Sprintf(`interface %s traffic-policy input %s`, params.InterfaceName, params.PolicyName) - helpers.GnmiCLIConfig(t, dut, pfa) + helpers.GnmiCLIConfig(t, dut, pfa) + } default: t.Logf("Unsupported vendor %s for native command support for deviation 'policy-forwarding config'", dut.Vendor()) } diff --git a/internal/cfgplugins/staticnexthop.go b/internal/cfgplugins/staticnexthop.go index e38fec5f3ee..2fcff5fbdfc 100644 --- a/internal/cfgplugins/staticnexthop.go +++ b/internal/cfgplugins/staticnexthop.go @@ -217,11 +217,14 @@ type NexthopGroupUDPParams struct { NexthopGrpName string DstIp []string SrcIp string + SrcInterface string DstUdpPort uint16 SrcUdpPort uint16 TTL uint8 DSCP uint8 NetworkInstanceObj *oc.NetworkInstance + Index string + DeleteTtl bool } // configureNextHopGroups configures the next-hop groups and their encapsulation headers. @@ -293,11 +296,17 @@ func NextHopGroupConfigForIpOverUdp(t *testing.T, dut *ondatra.DUTDevice, params tunnelDst += fmt.Sprintf("entry %d tunnel-destination %s \n", i, addr) } cli = fmt.Sprintf(` + qos rewrite ipv4-over-udp inner dscp disabled + qos rewrite ipv6-over-udp inner dscp disabled nexthop-group %s type %s - tunnel-source %s fec hierarchical %s - `, params.NexthopGrpName, groupType, params.SrcIp, tunnelDst) + `, params.NexthopGrpName, groupType, tunnelDst) + if params.SrcInterface != "" { + cli += fmt.Sprintf("tunnel-source intf %s", params.SrcInterface) + } else { + cli += fmt.Sprintf("tunnel-source %s", params.SrcIp) + } helpers.GnmiCLIConfig(t, dut, cli) } if params.TTL != 0 { @@ -316,6 +325,14 @@ func NextHopGroupConfigForIpOverUdp(t *testing.T, dut *ondatra.DUTDevice, params helpers.GnmiCLIConfig(t, dut, cli) } + if params.DeleteTtl { + cli = fmt.Sprintf( + `nexthop-group %s type %s + no ttl %v + `, params.NexthopGrpName, groupType, params.TTL) + helpers.GnmiCLIConfig(t, dut, cli) + } + if params.DstUdpPort != 0 { // Select and apply the appropriate CLI snippet based on 'traffictype'. cli = fmt.Sprintf(`tunnel type %s udp destination port %v`, groupType, params.DstUdpPort) @@ -325,17 +342,15 @@ func NextHopGroupConfigForIpOverUdp(t *testing.T, dut *ondatra.DUTDevice, params t.Logf("Unsupported vendor %s for native command support for deviation 'next-hop-group config'", dut.Vendor()) } } else { - t.Helper() nhg := params.NetworkInstanceObj.GetOrCreateStatic().GetOrCreateNextHopGroup(params.NexthopGrpName) - nhg.GetOrCreateNextHop("Dest A-NH1").Index = ygot.String("Dest A-NH1") + nhg.GetOrCreateNextHop(params.Index).SetIndex(params.Index) - // Set the encap header for each next-hop - ueh1 := params.NetworkInstanceObj.GetOrCreateStatic().GetOrCreateNextHop("Dest A-NH1").GetOrCreateEncapHeader(1) + ueh1 := params.NetworkInstanceObj.GetOrCreateStatic().GetOrCreateNextHop(params.Index).GetOrCreateEncapHeader(1) for _, addr := range params.DstIp { - ueh1.GetOrCreateUdpV4().DstIp = ygot.String(addr) + ueh1.GetOrCreateUdpV4().SetDstIp(addr) } if params.TTL != 0 { - ueh1.GetOrCreateUdpV4().IpTtl = ygot.Uint8(params.TTL) + ueh1.GetOrCreateUdpV4().SetIpTtl(params.TTL) } ueh1.GetOrCreateUdpV4().SetSrcIp(params.SrcIp) ueh1.GetOrCreateUdpV4().SetDscp(params.DSCP) diff --git a/internal/cfgplugins/staticroute.go b/internal/cfgplugins/staticroute.go index 05204fd75f4..97a8a4da3a0 100644 --- a/internal/cfgplugins/staticroute.go +++ b/internal/cfgplugins/staticroute.go @@ -29,11 +29,20 @@ import ( // StaticRouteCfg defines commonly used attributes for setting a static route type StaticRouteCfg struct { - NetworkInstance string - Prefix string - NextHops map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union - IPType string - NextHopAddr string + NetworkInstance string + Prefix string + NextHops map[string]oc.NetworkInstance_Protocol_Static_NextHop_NextHop_Union + IPType string + NextHopAddr string + NexthopGroup bool + NexthopGroupName string + Metric uint32 + Recurse bool + T *testing.T + TrafficType oc.E_Aft_EncapsulationHeaderType + PolicyName string + Rule string + RemoveStaticRoute bool } // StaticVRFRouteCfg represents a static route configuration within a specific network instance (VRF). It defines the destination prefix, associated next-hop group, and the protocol string used for identification. @@ -60,9 +69,36 @@ func NewStaticRouteCfg(batch *gnmi.SetBatch, cfg *StaticRouteCfg, d *ondatra.DUT Name: ygot.String(deviations.StaticProtocolName(d)), } s := c.GetOrCreateStatic(cfg.Prefix) + if cfg.NexthopGroup { + if deviations.StaticRouteToNHGOCUnsupported(d) { + switch d.Vendor() { + case ondatra.ARISTA: + if cfg.RemoveStaticRoute { + helpers.GnmiCLIConfig(cfg.T, d, fmt.Sprintf(`no ipv6 route %s nexthop-group %s`, cfg.Prefix, cfg.NexthopGroupName)) + } else { + cli := fmt.Sprintf(`ipv6 route %s nexthop-group %s`, cfg.Prefix, cfg.NexthopGroupName) + helpers.GnmiCLIConfig(cfg.T, d, cli) + staticRouteToNextHopGroupCLI(cfg.T, d, *cfg) + } + default: + return s, fmt.Errorf("deviation IPv4StaticRouteWithIPv6NextHopUnsupported is not handled for the dut: %s", d.Vendor()) + } + return s, nil + } else { + nhg := s.GetOrCreateNextHopGroup() + nhg.SetName(cfg.NexthopGroupName) + } + } for k, v := range cfg.NextHops { nh := s.GetOrCreateNextHop(k) + nh.SetIndex(k) nh.NextHop = v + if cfg.Metric != 0 { + nh.SetMetric(cfg.Metric) + } + if cfg.Recurse { + nh.SetRecurse(cfg.Recurse) + } } sp := gnmi.OC().NetworkInstance(ni).Protocol(oc.PolicyTypes_INSTALL_PROTOCOL_TYPE_STATIC, deviations.StaticProtocolName(d)) gnmi.BatchUpdate(batch, sp.Config(), c) @@ -133,3 +169,31 @@ func NewStaticVRFRoute(t *testing.T, batch *gnmi.SetBatch, cfg *StaticVRFRouteCf return s, nil } + +// staticRouteToNextHopGroupCLI configures routes to a next-hop-group for gue encapsulation +func staticRouteToNextHopGroupCLI(t *testing.T, dut *ondatra.DUTDevice, params StaticRouteCfg) { + t.Helper() + groupType := "" + + switch params.TrafficType { + case oc.Aft_EncapsulationHeaderType_UDPV4: + groupType = "ipv4" + case oc.Aft_EncapsulationHeaderType_UDPV6: + groupType = "ipv6" + } + + // Configure traffic policy + cli := "" + switch dut.Vendor() { + case ondatra.ARISTA: + cli = fmt.Sprintf(` + traffic-policies + traffic-policy %s + match %s %s + actions + redirect next-hop group %s`, params.PolicyName, params.Rule, groupType, params.NexthopGroupName) + helpers.GnmiCLIConfig(t, dut, cli) + default: + t.Logf("Unsupported vendor %s for native command support for deviation 'policy-forwarding config'", dut.Vendor()) + } +} diff --git a/internal/deviations/deviations.go b/internal/deviations/deviations.go index e1a5c31f2b9..d787a031ee8 100644 --- a/internal/deviations/deviations.go +++ b/internal/deviations/deviations.go @@ -2011,3 +2011,8 @@ func ArpFT(dut *ondatra.DUTDevice) string { func PrefixLimitConfigUnsupported(dut *ondatra.DUTDevice) bool { return lookupDUTDeviations(dut).GetPrefixLimitConfigUnsupported() } + +// StaticRouteToNextHopGroupOCNotSupported returns true if device does not support oc state path static route to nexthop group +func StaticRouteToNHGOCUnsupported(dut *ondatra.DUTDevice) bool { + return lookupDUTDeviations(dut).GetStaticRouteToNhgOcUnsupported() +} diff --git a/internal/iputil/iputil.go b/internal/iputil/iputil.go index 94127336996..0d2bc05a512 100644 --- a/internal/iputil/iputil.go +++ b/internal/iputil/iputil.go @@ -21,6 +21,7 @@ import ( "math" "math/big" "net" + "net/netip" ) // GenerateIPs creates list of n IPs using ipBlock @@ -248,3 +249,45 @@ func IncrementMAC(startMAC string, i int) (string, error) { } return macs[0], nil } + +// generateIPv6Entries creates IPv6 Entries given the totalCount and starting prefix +func GenerateIPv6(startIP string, count uint64) ([]string, error) { + if startIP == "" { + return nil, fmt.Errorf("invalid IPv6 address") + } + + _, netCIDR, _ := net.ParseCIDR(startIP) + fmt.Println(netCIDR) + + if netCIDR == nil { + return nil, fmt.Errorf("parsed CIDR is nil for input: %s", startIP) + } + + // Ensure it's IPv6 + ipBytes := netCIDR.IP.To16() + if ipBytes == nil || netCIDR.IP.To4() != nil { + return nil, fmt.Errorf("IPv4 address given, expected IPv6: %s", startIP) + } + + maskSize, bits := netCIDR.Mask.Size() + if bits != 128 { + return nil, fmt.Errorf("expected IPv6 mask, got %d bits", bits) + } + + firstIP := binary.BigEndian.Uint64(netCIDR.IP) + netMask := binary.BigEndian.Uint64(netCIDR.Mask) + lastIP := (firstIP & netMask) | (netMask ^ 0xffffffff) + entries := []string{} + + for i := firstIP; i <= lastIP; i++ { + ipv6 := make(net.IP, 16) + binary.BigEndian.PutUint64(ipv6, i) + // make last byte non-zero + p, _ := netip.ParsePrefix(fmt.Sprintf("%v/%d", ipv6, maskSize)) + entries = append(entries, p.Addr().Next().String()) + if uint64(len(entries)) >= count { + break + } + } + return entries, nil +} diff --git a/internal/iputil/iputil_test.go b/internal/iputil/iputil_test.go index c807eeb0720..db4cd573854 100644 --- a/internal/iputil/iputil_test.go +++ b/internal/iputil/iputil_test.go @@ -332,3 +332,59 @@ func TestGenerateIPv6s(t *testing.T) { }) } } + +func TestGenerateIPv6(t *testing.T) { + tests := []struct { + name string + prefix string + count uint64 + want []string + wantErr bool + }{{ + name: "Generate single IPv6", + prefix: "2001:db8::1/64", + count: 1, + want: []string{"2001:db8::1"}, + }, { + name: "Increment across boundary", + prefix: "2001:db8::ff/64", + count: 2, + want: []string{ + "2001:db8::1", + "2001:db8:0:1::1", + }, + }, { + name: "Zero count", + prefix: "2001:db8::1/64", + count: 0, + want: []string{"2001:db8::1"}, + }, { + name: "Invalid IPv6 address", + prefix: "invalid", + count: 5, + want: []string{}, + wantErr: true, + }, { + name: "IPv4 address given", + prefix: "192.168.1.1/24", + count: 1, + want: []string{}, + wantErr: true, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GenerateIPv6(tt.prefix, tt.count) + + if (err != nil) != tt.wantErr { + t.Fatalf("GenerateIPv6() error = %v, wantErr %v", err, tt.wantErr) + } + + if !tt.wantErr { + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("GenerateIPv6() mismatch (-want +got):\n%s", diff) + } + } + }) + } +} diff --git a/internal/otg_helpers/otg_config_helpers/otgflowhelpers.go b/internal/otg_helpers/otg_config_helpers/otgflowhelpers.go index 9e404846e7f..a7575920a0f 100644 --- a/internal/otg_helpers/otg_config_helpers/otgflowhelpers.go +++ b/internal/otg_helpers/otg_config_helpers/otgflowhelpers.go @@ -323,3 +323,8 @@ func (f *Flow) AddUDPHeader() { udpHdr.DstPort().SetValue(f.UDPFlow.UDPDstPort) } } + +// GetFlow will return the flow object +func (f *Flow) GetFlow() gosnappi.Flow { + return f.flow +} diff --git a/internal/otg_helpers/packetvalidationhelpers/packetvalidationhelpers.go b/internal/otg_helpers/packetvalidationhelpers/packetvalidationhelpers.go index f9d7a7f5951..5d9c77c0c10 100644 --- a/internal/otg_helpers/packetvalidationhelpers/packetvalidationhelpers.go +++ b/internal/otg_helpers/packetvalidationhelpers/packetvalidationhelpers.go @@ -53,6 +53,8 @@ Validations = []packetvalidationhelpers.ValidationType{ 2. Validate the IPV6 header (ValidateIPv6Header). */ +var packetSourceObj *gopacket.PacketSource + // IPv4 and IPv6 are the IP protocol types. const ( IPv4 = "IPv4" @@ -177,37 +179,37 @@ func CaptureAndValidatePackets(t *testing.T, ate *ondatra.ATEDevice, packetVal * } defer handle.Close() - packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) + packetSourceObj := gopacket.NewPacketSource(handle, handle.LinkType()) // Iterate over the validations specified in packetVal.Validations. for _, validation := range packetVal.Validations { switch validation { case ValidateIPv4Header: - if err := validateIPv4Header(t, packetSource, packetVal); err != nil { + if err := validateIPv4Header(t, packetSourceObj, packetVal); err != nil { return err } case ValidateInnerIPv4Header: - if err := validateInnerIPv4Header(t, packetSource, packetVal); err != nil { + if err := validateInnerIPv4Header(t, packetSourceObj, packetVal); err != nil { return err } case ValidateIPv6Header: - if err := validateIPv6Header(t, packetSource, packetVal); err != nil { + if err := validateIPv6Header(t, packetSourceObj, packetVal); err != nil { return err } case ValidateInnerIPv6Header: - if err := validateInnerIPv6Header(t, packetSource, packetVal); err != nil { + if err := validateInnerIPv6Header(t, packetSourceObj, packetVal); err != nil { return err } case ValidateMPLSLayer: - if err := validateMPLSLayer(t, packetSource, packetVal); err != nil { + if err := validateMPLSLayer(t, packetSourceObj, packetVal); err != nil { return err } case ValidateTCPHeader: - if err := validateTCPHeader(t, packetSource, packetVal); err != nil { + if err := validateTCPHeader(t, packetSourceObj, packetVal); err != nil { return err } case ValidateUDPHeader: - if err := validateUDPHeader(t, packetSource, packetVal); err != nil { + if err := validateUDPHeader(t, packetSourceObj, packetVal); err != nil { return err } default: @@ -230,7 +232,6 @@ func validateIPv4Header(t *testing.T, packetSource *gopacket.PacketSource, packe t.Log("Validating IPv4 header") for packet := range packetSource.Packets() { - t.Logf("packet: %v", packet) if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil { ip, _ := ipLayer.(*layers.IPv4) if !packetVal.IPv4Layer.SkipProtocolCheck { @@ -238,8 +239,10 @@ func validateIPv4Header(t *testing.T, packetSource *gopacket.PacketSource, packe return fmt.Errorf("packet is not encapsulated properly. Encapsulated protocol is: %d, expected: %d", ip.Protocol, packetVal.IPv4Layer.Protocol) } } - if ip.DstIP.String() != packetVal.IPv4Layer.DstIP { - return fmt.Errorf("IP Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.IPv4Layer.DstIP, ip.DstIP) + if packetVal.IPv4Layer.DstIP != "" { + if ip.DstIP.String() != packetVal.IPv4Layer.DstIP { + return fmt.Errorf("IP Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.IPv4Layer.DstIP, ip.DstIP) + } } if ip.TTL != packetVal.IPv4Layer.TTL { return fmt.Errorf("IP TTL value is altered to: %d, expected: %d", ip.TTL, packetVal.IPv4Layer.TTL) @@ -260,13 +263,15 @@ func validateIPv6Header(t *testing.T, packetSource *gopacket.PacketSource, packe t.Log("Validating IPv6 header") for packet := range packetSource.Packets() { - t.Logf("packet: %v", packet) if ipLayer := packet.Layer(layers.LayerTypeIPv6); ipLayer != nil { ipv6, _ := ipLayer.(*layers.IPv6) - if ipv6.DstIP.String() != packetVal.IPv6Layer.DstIP { - return fmt.Errorf("IPv6 Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.IPv6Layer.DstIP, ipv6.DstIP) + if packetVal.IPv6Layer.DstIP != "" { + if ipv6.DstIP.String() != packetVal.IPv6Layer.DstIP { + return fmt.Errorf("IPv6 Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.IPv6Layer.DstIP, ipv6.DstIP) + } } + if ipv6.HopLimit != packetVal.IPv6Layer.HopLimit { return fmt.Errorf("IPv6 HopLimit value is altered to: %d. Expected: %d", ipv6.HopLimit, packetVal.IPv6Layer.HopLimit) } @@ -290,31 +295,43 @@ func validateInnerIPv4Header(t *testing.T, packetSource *gopacket.PacketSource, t.Helper() t.Log("Validating inner IPv4 header") - for packet := range packetSource.Packets() { - if greLayer := packet.Layer(layers.LayerTypeGRE); greLayer != nil { - gre := greLayer.(*layers.GRE) - encapPacket := gopacket.NewPacket(gre.Payload, gre.NextLayerType(), gopacket.Default) + var encapPacket gopacket.Packet - if ipLayer := encapPacket.Layer(layers.LayerTypeIPv4); ipLayer != nil { - ip, _ := ipLayer.(*layers.IPv4) + innerLayer := *packetVal.InnerIPLayerIPv4 + protocol := innerLayer.Protocol + for packet := range packetSource.Packets() { + if protocol == packetVal.UDPLayer.DstPort { + udpLayer := packet.Layer(layers.LayerTypeUDP) + udp, _ := udpLayer.(*layers.UDP) + encapPacket = gopacket.NewPacket(udp.Payload, layers.LayerTypeIPv4, gopacket.Default) + innerLayer.Protocol = 0 + } else { + if greLayer := packet.Layer(layers.LayerTypeGRE); greLayer != nil { + gre := greLayer.(*layers.GRE) + encapPacket = gopacket.NewPacket(gre.Payload, gre.NextLayerType(), gopacket.Default) + } + } + if ipLayer := encapPacket.Layer(layers.LayerTypeIPv4); ipLayer != nil { + ip, _ := ipLayer.(*layers.IPv4) + if packetVal.InnerIPLayerIPv4.DstIP != "" { if ip.DstIP.String() != packetVal.InnerIPLayerIPv4.DstIP { return fmt.Errorf("IP Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.InnerIPLayerIPv4.DstIP, ip.DstIP) } - if ip.TTL != packetVal.InnerIPLayerIPv4.TTL { - return fmt.Errorf("IP TTL value is altered to: %d. Expected: %d", ip.TTL, packetVal.InnerIPLayerIPv4.TTL) - } - if ip.TOS != packetVal.InnerIPLayerIPv4.Tos { - return fmt.Errorf("DSCP(TOS) value is altered to: %d .Expected: %d", ip.TOS, packetVal.InnerIPLayerIPv4.Tos) - } - if packetVal.InnerIPLayerIPv4.Protocol != 0 { - if uint32(ip.Protocol) != packetVal.InnerIPLayerIPv4.Protocol { - return fmt.Errorf("protocol value is altered to: %d. expected: %d", ip.Protocol, packetVal.InnerIPLayerIPv4.Protocol) - } + } + if ip.TTL != packetVal.InnerIPLayerIPv4.TTL { + return fmt.Errorf("IP TTL value is altered to: %d. Expected: %d", ip.TTL, packetVal.InnerIPLayerIPv4.TTL) + } + if ip.TOS != packetVal.InnerIPLayerIPv4.Tos { + return fmt.Errorf("DSCP(TOS) value is altered to: %d .Expected: %d", ip.TOS, packetVal.InnerIPLayerIPv4.Tos) + } + if innerLayer.Protocol != 0 { + if uint32(ip.Protocol) != packetVal.InnerIPLayerIPv4.Protocol { + return fmt.Errorf("protocol value is altered to: %d. expected: %d", ip.Protocol, packetVal.InnerIPLayerIPv4.Protocol) } - // If validation is successful for one packet, we can return. - return nil } + // If validation is successful for one packet, we can return. + return nil } } return fmt.Errorf("no inner IPv4 packets found") @@ -325,30 +342,40 @@ func validateInnerIPv6Header(t *testing.T, packetSource *gopacket.PacketSource, t.Helper() t.Log("Validating inner IPv6 header") + var encapPacket gopacket.Packet + innerLayer := *packetVal.InnerIPLayerIPv6 + protocol := innerLayer.NextHeader + for packet := range packetSource.Packets() { - if greLayer := packet.Layer(layers.LayerTypeGRE); greLayer != nil { - gre := greLayer.(*layers.GRE) - encapPacket := gopacket.NewPacket(gre.Payload, gre.NextLayerType(), gopacket.Default) - - if ipv6Layer := encapPacket.Layer(layers.LayerTypeIPv6); ipv6Layer != nil { - ipv6, _ := ipv6Layer.(*layers.IPv6) - if ipv6.DstIP.String() != packetVal.InnerIPLayerIPv6.DstIP { - return fmt.Errorf("IPv6 Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.InnerIPLayerIPv6.DstIP, ipv6.DstIP) - } - if ipv6.HopLimit != packetVal.InnerIPLayerIPv6.HopLimit { - return fmt.Errorf("IPv6 HopLimit value is altered to: %d. Expected: %d", ipv6.HopLimit, packetVal.InnerIPLayerIPv6.HopLimit) - } - if ipv6.TrafficClass != packetVal.InnerIPLayerIPv6.TrafficClass { - return fmt.Errorf("traffic class value is altered to: %d. expected: %d", ipv6.TrafficClass, packetVal.InnerIPLayerIPv6.TrafficClass) - } - if packetVal.InnerIPLayerIPv6.NextHeader != 0 { - if uint32(ipv6.NextHeader) != packetVal.InnerIPLayerIPv6.NextHeader { - return fmt.Errorf("next header value is altered to: %d. expected: %d", ipv6.NextHeader, packetVal.InnerIPLayerIPv6.NextHeader) - } + if protocol == packetVal.UDPLayer.DstPort { + udpLayer := packet.Layer(layers.LayerTypeUDP) + udp, _ := udpLayer.(*layers.UDP) + encapPacket = gopacket.NewPacket(udp.Payload, layers.LayerTypeIPv6, gopacket.Default) + innerLayer.NextHeader = 0 + } else { + if greLayer := packet.Layer(layers.LayerTypeGRE); greLayer != nil { + gre := greLayer.(*layers.GRE) + encapPacket = gopacket.NewPacket(gre.Payload, gre.NextLayerType(), gopacket.Default) + } + } + if ipv6Layer := encapPacket.Layer(layers.LayerTypeIPv6); ipv6Layer != nil { + ipv6, _ := ipv6Layer.(*layers.IPv6) + if ipv6.DstIP.String() != packetVal.InnerIPLayerIPv6.DstIP { + return fmt.Errorf("IPv6 Dst IP is not set properly. Expected: %s, Actual: %s", packetVal.InnerIPLayerIPv6.DstIP, ipv6.DstIP) + } + if ipv6.HopLimit != packetVal.InnerIPLayerIPv6.HopLimit { + return fmt.Errorf("IPv6 HopLimit value is altered to: %d. Expected: %d", ipv6.HopLimit, packetVal.InnerIPLayerIPv6.HopLimit) + } + if ipv6.TrafficClass != packetVal.InnerIPLayerIPv6.TrafficClass { + return fmt.Errorf("traffic class value is altered to: %d. expected: %d", ipv6.TrafficClass, packetVal.InnerIPLayerIPv6.TrafficClass) + } + if packetVal.InnerIPLayerIPv6.NextHeader != 0 { + if uint32(ipv6.NextHeader) != packetVal.InnerIPLayerIPv6.NextHeader { + return fmt.Errorf("next header value is altered to: %d. expected: %d", ipv6.NextHeader, packetVal.InnerIPLayerIPv6.NextHeader) } - // If validation is successful for one packet, we can return. - return nil } + // If validation is successful for one packet, we can return. + return nil } } return fmt.Errorf("no inner IPv6 packets found") @@ -410,8 +437,10 @@ func validateUDPHeader(t *testing.T, packetSource *gopacket.PacketSource, packet if uint32(udp.DstPort) != packetVal.UDPLayer.DstPort { return fmt.Errorf("UDP Dst Port is not set properly. Expected: %d, Actual: %d", packetVal.UDPLayer.DstPort, udp.DstPort) } - if uint32(udp.SrcPort) != packetVal.UDPLayer.SrcPort { - return fmt.Errorf("UDP Src Port is not set properly. Expected: %d, Actual: %d", packetVal.UDPLayer.SrcPort, udp.SrcPort) + if packetVal.UDPLayer.SrcPort != 0 { + if uint32(udp.SrcPort) != packetVal.UDPLayer.SrcPort { + return fmt.Errorf("UDP Src Port is not set properly. Expected: %d, Actual: %d", packetVal.UDPLayer.SrcPort, udp.SrcPort) + } } // If validation is successful for one packet, we can return. return nil @@ -429,3 +458,8 @@ func ConfigurePacketCapture(t *testing.T, top gosnappi.Config, packetVal *Packet SetPortNames(ports). SetFormat(gosnappi.CaptureFormat.PCAP) } + +// SourceObj to get the packet object captured on the port +func SourceObj() *gopacket.PacketSource { + return packetSourceObj +} diff --git a/proto/metadata.proto b/proto/metadata.proto index e33db62a74d..0e46212d8d6 100644 --- a/proto/metadata.proto +++ b/proto/metadata.proto @@ -1208,6 +1208,10 @@ message Metadata { // Cisco: https://partnerissuetracker.corp.google.com/issues/447509237 bool prefix_limit_config_unsupported = 393; + // Partner issue: https://partnerissuetracker.corp.google.com/issues/456362593 + // Devices that do not support oc path for static route to nexthop + bool static_route_to_nhg_oc_unsupported = 394; + // Reserved field numbers and identifiers. reserved 84, 9, 28, 20, 38, 43, 90, 97, 55, 89, 19, 36, 35, 40, 113, 131, 141, 173, 234, 254, 231, 300, 241; } diff --git a/proto/metadata_go_proto/metadata.pb.go b/proto/metadata_go_proto/metadata.pb.go index df951dc811c..34171599d81 100644 --- a/proto/metadata_go_proto/metadata.pb.go +++ b/proto/metadata_go_proto/metadata.pb.go @@ -14,20 +14,19 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.7 +// protoc-gen-go v1.36.6 // protoc v3.21.12 // source: metadata.proto package metadata_go_proto import ( - reflect "reflect" - sync "sync" - unsafe "unsafe" - proto "github.com/openconfig/ondatra/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" ) const ( @@ -1356,8 +1355,11 @@ type Metadata_Deviations struct { // Device does not support configuring prefix limit received paths through OC // Cisco: https://partnerissuetracker.corp.google.com/issues/447509237 PrefixLimitConfigUnsupported bool `protobuf:"varint,393,opt,name=prefix_limit_config_unsupported,json=prefixLimitConfigUnsupported,proto3" json:"prefix_limit_config_unsupported,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Partner issue: https://partnerissuetracker.corp.google.com/issues/456362593 + // Devices that do not support oc path for static route to nexthop + StaticRouteToNhgOcUnsupported bool `protobuf:"varint,394,opt,name=static_route_to_nhg_oc_unsupported,json=staticRouteToNhgOcUnsupported,proto3" json:"static_route_to_nhg_oc_unsupported,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Metadata_Deviations) Reset() { @@ -3903,6 +3905,13 @@ func (x *Metadata_Deviations) GetPrefixLimitConfigUnsupported() bool { return false } +func (x *Metadata_Deviations) GetStaticRouteToNhgOcUnsupported() bool { + if x != nil { + return x.StaticRouteToNhgOcUnsupported + } + return false +} + type Metadata_PlatformExceptions struct { state protoimpl.MessageState `protogen:"open.v1"` Platform *Metadata_Platform `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` @@ -3959,7 +3968,7 @@ var File_metadata_proto protoreflect.FileDescriptor const file_metadata_proto_rawDesc = "" + "\n" + - "\x0emetadata.proto\x12\x12openconfig.testing\x1a1github.com/openconfig/ondatra/proto/testbed.proto\"\x96\xdb\x01\n" + + "\x0emetadata.proto\x12\x12openconfig.testing\x1a1github.com/openconfig/ondatra/proto/testbed.proto\"\xe2\xdb\x01\n" + "\bMetadata\x12\x12\n" + "\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x17\n" + "\aplan_id\x18\x02 \x01(\tR\x06planId\x12 \n" + @@ -3971,7 +3980,7 @@ const file_metadata_proto_rawDesc = "" + "\bPlatform\x12.\n" + "\x06vendor\x18\x01 \x01(\x0e2\x16.ondatra.Device.VendorR\x06vendor\x120\n" + "\x14hardware_model_regex\x18\x03 \x01(\tR\x12hardwareModelRegex\x124\n" + - "\x16software_version_regex\x18\x04 \x01(\tR\x14softwareVersionRegexJ\x04\b\x02\x10\x03R\x0ehardware_model\x1a\xe2\xd0\x01\n" + + "\x16software_version_regex\x18\x04 \x01(\tR\x14softwareVersionRegexJ\x04\b\x02\x10\x03R\x0ehardware_model\x1a\xae\xd1\x01\n" + "\n" + "Deviations\x120\n" + "\x14ipv4_missing_enabled\x18\x01 \x01(\bR\x12ipv4MissingEnabled\x129\n" + @@ -4335,7 +4344,8 @@ const file_metadata_proto_rawDesc = "" + "\x06qos_ft\x18\x86\x03 \x01(\tR\x05qosFt\x12=\n" + "\x1bsystem_mount_point_state_ft\x18\x87\x03 \x01(\tR\x17systemMountPointStateFt\x12\x16\n" + "\x06arp_ft\x18\x88\x03 \x01(\tR\x05arpFt\x12F\n" + - "\x1fprefix_limit_config_unsupported\x18\x89\x03 \x01(\bR\x1cprefixLimitConfigUnsupportedJ\x04\bT\x10UJ\x04\b\t\x10\n" + + "\x1fprefix_limit_config_unsupported\x18\x89\x03 \x01(\bR\x1cprefixLimitConfigUnsupported\x12J\n" + + "\"static_route_to_nhg_oc_unsupported\x18\x8a\x03 \x01(\bR\x1dstaticRouteToNhgOcUnsupportedJ\x04\bT\x10UJ\x04\b\t\x10\n" + "J\x04\b\x1c\x10\x1dJ\x04\b\x14\x10\x15J\x04\b&\x10'J\x04\b+\x10,J\x04\bZ\x10[J\x04\ba\x10bJ\x04\b7\x108J\x04\bY\x10ZJ\x04\b\x13\x10\x14J\x04\b$\x10%J\x04\b#\x10$J\x04\b(\x10)J\x04\bq\x10rJ\x06\b\x83\x01\x10\x84\x01J\x06\b\x8d\x01\x10\x8e\x01J\x06\b\xad\x01\x10\xae\x01J\x06\b\xea\x01\x10\xeb\x01J\x06\b\xfe\x01\x10\xff\x01J\x06\b\xe7\x01\x10\xe8\x01J\x06\b\xac\x02\x10\xad\x02J\x06\b\xf1\x01\x10\xf2\x01\x1a\xa0\x01\n" + "\x12PlatformExceptions\x12A\n" + "\bplatform\x18\x01 \x01(\v2%.openconfig.testing.Metadata.PlatformR\bplatform\x12G\n" +