@@ -61,6 +61,10 @@ namespace {
61
61
// otherwise it can use this constant to say no VLAN ID should be set.
62
62
constexpr absl::string_view kNoVlanId = " " ;
63
63
64
+ // send_to_ingress is a special port created on the switch which allows the CPU
65
+ // to inject a packet into the ingress pipeline.
66
+ constexpr absl::string_view kSendToIngress = " send_to_ingress" ;
67
+
64
68
absl::Status AddAndSetDefaultVrf (pdpi::P4RuntimeSession& session,
65
69
const pdpi::IrP4Info& ir_p4info,
66
70
const std::string& vrf_id) {
@@ -69,14 +73,16 @@ absl::Status AddAndSetDefaultVrf(pdpi::P4RuntimeSession& session,
69
73
RETURN_IF_ERROR (gutil::ReadProtoFromString (
70
74
absl::Substitute (R"pb(
71
75
type: INSERT
72
- table_entry {
73
- table_name: "acl_pre_ingress_table"
74
- priority: 2000
75
- action {
76
- name: "set_vrf"
77
- params {
78
- name: "vrf_id"
79
- value { str: "$0" }
76
+ entity {
77
+ table_entry {
78
+ table_name: "acl_pre_ingress_table"
79
+ priority: 2000
80
+ action {
81
+ name: "set_vrf"
82
+ params {
83
+ name: "vrf_id"
84
+ value { str: "$0" }
85
+ }
80
86
}
81
87
}
82
88
}
@@ -101,21 +107,23 @@ absl::Status AllowVrfTrafficToDstMac(pdpi::P4RuntimeSession& session,
101
107
RETURN_IF_ERROR (gutil::ReadProtoFromString (
102
108
absl::Substitute (R"pb(
103
109
type: INSERT
104
- table_entry {
105
- table_name: "acl_pre_ingress_table"
106
- matches {
107
- name: "dst_mac"
108
- ternary {
109
- value { mac: "$0" }
110
- mask { mac: "ff:ff:ff:ff:ff:ff" }
110
+ entity {
111
+ table_entry {
112
+ table_name: "acl_pre_ingress_table"
113
+ matches {
114
+ name: "dst_mac"
115
+ ternary {
116
+ value { mac: "$0" }
117
+ mask { mac: "ff:ff:ff:ff:ff:ff" }
118
+ }
111
119
}
112
- }
113
- priority: 2000
114
- action {
115
- name: "set_vrf"
116
- params {
117
- name : "vrf_id"
118
- value { str: "$1" }
120
+ priority: 2000
121
+ action {
122
+ name: "set_vrf"
123
+ params {
124
+ name: "vrf_id"
125
+ value { str : "$1" }
126
+ }
119
127
}
120
128
}
121
129
}
@@ -138,14 +146,16 @@ absl::Status PuntAllPacketsToController(pdpi::P4RuntimeSession& session,
138
146
R"pb(
139
147
updates {
140
148
type: INSERT
141
- table_entry {
142
- table_name: "acl_ingress_table"
143
- priority: 2
144
- action {
145
- name: "acl_trap",
146
- params {
147
- name: "qos_queue"
148
- value { str: "0x1" }
149
+ entity {
150
+ table_entry {
151
+ table_name: "acl_ingress_table"
152
+ priority: 2
153
+ action {
154
+ name: "acl_trap",
155
+ params {
156
+ name: "qos_queue"
157
+ value { str: "0x1" }
158
+ }
149
159
}
150
160
}
151
161
}
@@ -280,7 +290,7 @@ absl::StatusOr<std::string> UdpPacket(absl::string_view dst_mac,
280
290
281
291
absl::Status SendUdpPacket (pdpi::P4RuntimeSession& session,
282
292
const pdpi::IrP4Info& ir_p4info,
283
- const std::string& port_id, int packet_count,
293
+ absl::string_view port_id, int packet_count,
284
294
absl::string_view dst_mac, absl::string_view vlan_id,
285
295
absl::string_view dst_ip,
286
296
absl::string_view payload) {
@@ -292,8 +302,15 @@ absl::Status SendUdpPacket(pdpi::P4RuntimeSession& session,
292
302
UdpPacket (dst_mac, vlan_id, dst_ip,
293
303
absl::Substitute (" [Packet:$0] $1" , i, payload)));
294
304
// Rate limit to 500pps to avoid punt packet drops on the control switch.
295
- RETURN_IF_ERROR (InjectEgressPacket (port_id, packet, ir_p4info, &session,
296
- /* packet_delay=*/ absl::Milliseconds (2 )));
305
+ if (port_id == kSendToIngress ) {
306
+ RETURN_IF_ERROR (
307
+ InjectIngressPacket (packet, ir_p4info, &session,
308
+ /* packet_delay=*/ absl::Milliseconds (2 )));
309
+ } else {
310
+ RETURN_IF_ERROR (
311
+ InjectEgressPacket (std::string{port_id}, packet, ir_p4info, &session,
312
+ /* packet_delay=*/ absl::Milliseconds (2 )));
313
+ }
297
314
}
298
315
return absl::OkStatus ();
299
316
}
@@ -805,18 +822,20 @@ TEST_P(L3AdmitTestFixture, VlanOverrideAdmitsAllPacketsToL3Routing) {
805
822
R"pb(
806
823
updates {
807
824
type: INSERT
808
- table_entry {
809
- table_name: "acl_pre_ingress_vlan_table"
810
- priority: 10
811
- matches {
812
- name: "is_ipv4"
813
- optional { value { hex_str: "0x1" } }
814
- }
815
- action {
816
- name: "set_outer_vlan_id",
817
- params {
818
- name: "vlan_id"
819
- value { hex_str: "0xfff" }
825
+ entity {
826
+ table_entry {
827
+ table_name: "acl_pre_ingress_vlan_table"
828
+ priority: 10
829
+ matches {
830
+ name: "is_ipv4"
831
+ optional { value { hex_str: "0x1" } }
832
+ }
833
+ action {
834
+ name: "set_outer_vlan_id",
835
+ params {
836
+ name: "vlan_id"
837
+ value { hex_str: "0xfff" }
838
+ }
820
839
}
821
840
}
822
841
}
@@ -877,4 +896,146 @@ TEST_P(L3AdmitTestFixture, VlanOverrideAdmitsAllPacketsToL3Routing) {
877
896
EXPECT_EQ (good_packet_count, kNumberOfTestPackets );
878
897
}
879
898
899
+ TEST_P (L3AdmitTestFixture, RoutedPacketsCanMatchOnCpuPort) {
900
+
901
+ // Only run this test if the ACL_INGRESS_QOS_TABLE exists and we can match on
902
+ // the IN_PORT.
903
+ if (!TableHasMatchField (ir_p4info_, " acl_ingress_qos_table" , " in_port" )) {
904
+ GTEST_SKIP () << " Skipping because ACL_INGRESS_QOS_TABLE does not exist." ;
905
+ }
906
+
907
+ // Get SUT and control ports to test on.
908
+ ASSERT_OK_AND_ASSIGN (
909
+ auto gnmi_stub_sut,
910
+ GetParam ().testbed_interface ->GetMirrorTestbed ().Sut ().CreateGnmiStub ());
911
+ ASSERT_OK_AND_ASSIGN (
912
+ auto gnmi_stub_control,
913
+ GetParam ().testbed_interface ->GetMirrorTestbed ().Sut ().CreateGnmiStub ());
914
+ ASSERT_OK_AND_ASSIGN (std::string sut_port,
915
+ pins_test::GetAnyUpInterfacePortId (*gnmi_stub_sut));
916
+ ASSERT_OK_AND_ASSIGN (std::string control_port,
917
+ pins_test::GetAnyUpInterfacePortId (*gnmi_stub_control));
918
+
919
+ // Punt all traffic arriving at the control switch, and collect them to verify
920
+ // forwarding.
921
+ ASSERT_OK (
922
+ PuntAllPacketsToController (*control_switch_p4rt_session_, ir_p4info_));
923
+
924
+ // Add an L3 route to enable forwarding.
925
+ L3Route l3_route{
926
+ .vrf_id = " vrf-1" ,
927
+ .switch_mac = " 00:00:00:00:00:01" ,
928
+ .switch_ip = std::make_pair (" 10.0.0.1" , 32 ),
929
+ .peer_port = sut_port,
930
+ .peer_mac = " 00:00:00:00:00:02" ,
931
+ .peer_ip = " fe80::2" ,
932
+ .router_interface_id = " rif-1" ,
933
+ .nexthop_id = " nexthop-1" ,
934
+ };
935
+ ASSERT_OK (
936
+ AddAndSetDefaultVrf (*sut_p4rt_session_, ir_p4info_, l3_route.vrf_id ));
937
+ ASSERT_OK (AddL3Route (*sut_p4rt_session_, ir_p4info_, l3_route));
938
+
939
+ // Admit only 1 MAC address to the forwarding pipeline.
940
+ ASSERT_OK (AdmitL3Route (
941
+ *sut_p4rt_session_, ir_p4info_,
942
+ L3AdmitOptions{
943
+ .priority = 2070 ,
944
+ .dst_mac = std ::make_pair (" 00:01:02:03:04:05" , " FF:FF:FF:FF:FF:FF" ),
945
+ }));
946
+
947
+ {
948
+ // Write QoS rule that will drop any packet, and a higher priority rule that
949
+ // will only allow ports matching on the CPU port.
950
+ pdpi::IrWriteRequest ir_write_request;
951
+ ASSERT_OK (gutil::ReadProtoFromString (
952
+ R"pb(
953
+ updates {
954
+ type: INSERT
955
+ entity {
956
+ table_entry {
957
+ table_name: "acl_ingress_qos_table"
958
+ priority: 10
959
+ action {
960
+ name: "acl_drop",
961
+ }
962
+ }
963
+ }
964
+ }
965
+ updates {
966
+ type: INSERT
967
+ entity {
968
+ table_entry {
969
+ table_name: "acl_ingress_qos_table"
970
+ priority: 11
971
+ matches {
972
+ name: "in_port"
973
+ optional {
974
+ value {
975
+ str: "4294967293" # OPENFLOW_PORT_CONTROLLER
976
+ }
977
+ }
978
+ }
979
+ action {
980
+ name: "acl_forward",
981
+ }
982
+ }
983
+ }
984
+ }
985
+ )pb" ,
986
+ &ir_write_request));
987
+ ASSERT_OK_AND_ASSIGN (
988
+ p4::v1::WriteRequest pi_write_request,
989
+ pdpi::IrWriteRequestToPi (ir_p4info_, ir_write_request));
990
+ ASSERT_OK (pdpi::SetMetadataAndSendPiWriteRequest (sut_p4rt_session_.get (),
991
+ pi_write_request));
992
+ }
993
+
994
+ // Send 2 sets of packets to the switch. The packets are exactly the same, but
995
+ // the first set of packets will be sent to the SUT from the control switch
996
+ // (i.e. they arrive on a physical port). The second set of packets will be
997
+ // sent through the "send_to_ingress" port.
998
+ const int kNumberOfTestPackets = 100 ;
999
+
1000
+ // Send the "bad" packets first to give them the most time.
1001
+ const std::string kBadPayload =
1002
+ " Testing L3 forwarding. This packet should be dropped." ;
1003
+ ASSERT_OK (SendUdpPacket (*control_switch_p4rt_session_, ir_p4info_,
1004
+ control_port, kNumberOfTestPackets ,
1005
+ /* dst_mac=*/ " 00:01:02:03:04:05" , kNoVlanId ,
1006
+ /* dst_ip=*/ " 10.0.0.1" , kBadPayload ));
1007
+
1008
+ // Then send the "good" packets.
1009
+ const std::string kGoodPayload =
1010
+ " Testing L3 forwarding. This packet should arrive to packet in." ;
1011
+ ASSERT_OK (SendUdpPacket (*sut_p4rt_session_, ir_p4info_, kSendToIngress ,
1012
+ kNumberOfTestPackets , /* dst_mac=*/ " 00:01:02:03:04:05" ,
1013
+ kNoVlanId , /* dst_ip=*/ " 10.0.0.1" , kGoodPayload ));
1014
+
1015
+ int good_packet_count = 0 ;
1016
+ int bad_packet_count = 0 ;
1017
+ ASSERT_OK (control_switch_p4rt_session_->HandleNextNStreamMessages (
1018
+ [&](const p4::v1::StreamMessageResponse& message) {
1019
+ // Verify this is the packet we expect.
1020
+ packetlib::Packet packet_in =
1021
+ packetlib::ParsePacket (message.packet ().payload ());
1022
+ if (absl::StrContains (packet_in.payload (), kGoodPayload )) {
1023
+ ++good_packet_count;
1024
+ return true ;
1025
+ }
1026
+ if (absl::StrContains (packet_in.payload (), kBadPayload )) {
1027
+ ++bad_packet_count;
1028
+ return false ;
1029
+ }
1030
+ LOG (WARNING) << " Unexpected P4 Stream response: "
1031
+ << message.DebugString ();
1032
+ return false ;
1033
+ },
1034
+ kNumberOfTestPackets , /* timeout=*/ absl::Minutes (1 )));
1035
+ LOG (INFO) << " Done collecting packets." ;
1036
+
1037
+ EXPECT_EQ (good_packet_count, kNumberOfTestPackets );
1038
+ EXPECT_EQ (bad_packet_count, 0 );
1039
+ }
1040
+
880
1041
} // namespace pins
0 commit comments