Skip to content

Commit 005b9ad

Browse files
committed
Prevent guest traffic from reaching the backplane
1 parent 9f6b8f7 commit 005b9ad

22 files changed

Lines changed: 10430 additions & 287 deletions

File tree

dpd-api/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ api_versions!([
5959
// | example for the next person.
6060
// v
6161
// (next_int, IDENT),
62+
(5, UPLINK_PORTS),
6263
(4, V4_OVER_V6_ROUTES),
6364
(3, ATTACHED_SUBNETS),
6465
(2, DUAL_STACK_NAT_WORKFLOW),
@@ -988,21 +989,50 @@ pub trait DpdApi {
988989
#[endpoint {
989990
method = GET,
990991
path = "/ports/{port_id}/links/{link_id}/nat_only",
992+
versions = ..VERSION_UPLINK_PORTS
991993
}]
992994
async fn link_nat_only_get(
993995
rqctx: RequestContext<Self::Context>,
994996
path: Path<LinkPath>,
997+
) -> Result<HttpResponseOk<bool>, HttpError> {
998+
Self::link_uplink_get(rqctx, path).await
999+
}
1000+
1001+
/// Return whether a port is intended to carry uplink traffic
1002+
#[endpoint {
1003+
method = GET,
1004+
path = "/ports/{port_id}/links/{link_id}/uplink",
1005+
versions = VERSION_UPLINK_PORTS..
1006+
}]
1007+
async fn link_uplink_get(
1008+
rqctx: RequestContext<Self::Context>,
1009+
path: Path<LinkPath>,
9951010
) -> Result<HttpResponseOk<bool>, HttpError>;
9961011

9971012
/// Set whether a port is configured to use drop non-nat traffic
9981013
#[endpoint {
9991014
method = PUT,
10001015
path = "/ports/{port_id}/links/{link_id}/nat_only",
1016+
versions = ..VERSION_UPLINK_PORTS
10011017
}]
10021018
async fn link_nat_only_set(
10031019
rqctx: RequestContext<Self::Context>,
10041020
path: Path<LinkPath>,
10051021
body: TypedBody<bool>,
1022+
) -> Result<HttpResponseUpdatedNoContent, HttpError> {
1023+
Self::link_uplink_set(rqctx, path, body).await
1024+
}
1025+
1026+
/// Set whether a port is intended to carry uplink traffic
1027+
#[endpoint {
1028+
method = PUT,
1029+
path = "/ports/{port_id}/links/{link_id}/uplink",
1030+
versions = VERSION_UPLINK_PORTS..
1031+
}]
1032+
async fn link_uplink_set(
1033+
rqctx: RequestContext<Self::Context>,
1034+
path: Path<LinkPath>,
1035+
body: TypedBody<bool>,
10061036
) -> Result<HttpResponseUpdatedNoContent, HttpError>;
10071037

10081038
/// Get the event history for the given link.

dpd-client/tests/integration_tests/attached_subnet.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ async fn test_egress(switch: &Switch, test: &ExternalTest) -> TestResult {
116116
tag: switch.client.inner().tag.clone(),
117117
};
118118
switch.client.link_ipv6_create(&port_id, &link_id, &entry).await.unwrap();
119+
switch.set_uplink(test.uplink_port, true).await;
119120

120121
// populate the ndp/arp table with the upstream router's mac and IP.
121122
if test.upstream_router_ip.parse::<Ipv4Addr>().is_ok() {

dpd-client/tests/integration_tests/common.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,15 @@ impl Switch {
537537
Ok(())
538538
}
539539

540+
pub async fn set_uplink(&self, phys_port: PhysPort, uplink: bool) {
541+
let (port_id, link_id) = self.link_id(phys_port).unwrap();
542+
self.client
543+
.link_uplink_set(&port_id, &link_id, uplink)
544+
.await
545+
.unwrap()
546+
.into_inner()
547+
}
548+
540549
pub fn tofino_port(&self, phys_port: PhysPort) -> u16 {
541550
let idx: usize = phys_port.into();
542551
if phys_port == NO_PORT {

dpd-client/tests/integration_tests/counters.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,16 +202,16 @@ async fn test_nat_filtering() -> TestResult {
202202
);
203203

204204
let (port_id, link_id) = switch.link_id(ingress).unwrap();
205-
// Mark the port as NAT-only
206-
switch.client.link_nat_only_set(&port_id, &link_id, true).await.unwrap();
205+
// Mark the port as uplink
206+
switch.client.link_uplink_set(&port_id, &link_id, true).await.unwrap();
207207

208208
// We run the test now, but defer the evaluation of the result. This lets
209-
// us clean up the NAT-only change even on a test failure.
209+
// us clean up the uplink change even on a test failure.
210210
let result =
211211
one_drop_test(switch, ingress, packet, "nat_ingress_miss").await;
212212

213-
// Remove the NAT-only property
214-
switch.client.link_nat_only_set(&port_id, &link_id, false).await.unwrap();
213+
// Remove the uplink property
214+
switch.client.link_uplink_set(&port_id, &link_id, false).await.unwrap();
215215

216216
assert!(result?);
217217
Ok(())

dpd-client/tests/integration_tests/icmp_ipv4.rs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,22 +88,14 @@ async fn execute_ping_reply_test(
8888

8989
if enable_nat_filtering {
9090
// Mark the port as NAT-only
91-
switch
92-
.client
93-
.link_nat_only_set(&port_id, &link_id, true)
94-
.await
95-
.unwrap();
91+
switch.client.link_uplink_set(&port_id, &link_id, true).await.unwrap();
9692
}
9793

9894
let result = switch.packet_test(vec![send], vec![recv]);
9995

10096
if enable_nat_filtering {
10197
// Mark the port as NAT-only
102-
switch
103-
.client
104-
.link_nat_only_set(&port_id, &link_id, false)
105-
.await
106-
.unwrap();
98+
switch.client.link_uplink_set(&port_id, &link_id, false).await.unwrap();
10799
}
108100
result
109101
}
@@ -116,7 +108,7 @@ async fn test_ping_ipv4_reply() -> TestResult {
116108

117109
#[tokio::test]
118110
#[ignore]
119-
async fn test_ping_ipv4_reply_nat_only() -> TestResult {
111+
async fn test_ping_ipv4_reply_uplink() -> TestResult {
120112
execute_ping_reply_test(true, false).await
121113
}
122114

@@ -128,7 +120,7 @@ async fn test_ping_ipv4_vlan_reply() -> TestResult {
128120

129121
#[tokio::test]
130122
#[ignore]
131-
async fn test_ping_ipv4_vlan_reply_nat_only() -> TestResult {
123+
async fn test_ping_ipv4_vlan_reply_uplink() -> TestResult {
132124
execute_ping_reply_test(true, true).await
133125
}
134126

@@ -177,22 +169,14 @@ async fn execute_ping_ipv4_forward_test(
177169

178170
if enable_nat_filtering {
179171
// Mark the port as NAT-only
180-
switch
181-
.client
182-
.link_nat_only_set(&port_id, &link_id, true)
183-
.await
184-
.unwrap();
172+
switch.client.link_uplink_set(&port_id, &link_id, true).await.unwrap();
185173
}
186174

187175
let result = switch.packet_test(vec![send], vec![recv]);
188176

189177
if enable_nat_filtering {
190178
// Mark the port as NAT-only
191-
switch
192-
.client
193-
.link_nat_only_set(&port_id, &link_id, false)
194-
.await
195-
.unwrap();
179+
switch.client.link_uplink_set(&port_id, &link_id, false).await.unwrap();
196180
}
197181
result
198182
}
@@ -205,6 +189,6 @@ async fn test_ping_ipv4_forward() -> TestResult {
205189

206190
#[tokio::test]
207191
#[ignore]
208-
async fn test_ping_ipv4_forward_nat_only() -> TestResult {
192+
async fn test_ping_ipv4_forward_uplink() -> TestResult {
209193
execute_ping_ipv4_forward_test(true).await
210194
}

dpd-client/tests/integration_tests/nat.rs

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ struct NatTest {
171171
// uplink network info
172172
uplink_port: PhysPort,
173173
uplink_port_external: String, // external addr assigned to our upstream port
174+
uplink_port_registered: bool, // register this port as an uplink
174175
uplink_route: String, // subnet to which the switch is connected
175176
router_ip: String, // ip address of the upstream router
176177
router_mac: String, // mac address of the upstream router
@@ -207,6 +208,8 @@ async fn test_nat_egress(switch: &Switch, test: &NatTest) -> TestResult {
207208
};
208209
switch.client.link_ipv6_create(&port_id, &link_id, &entry).await.unwrap();
209210

211+
switch.set_uplink(test.uplink_port, test.uplink_port_registered).await;
212+
210213
if test.router_ip.parse::<Ipv4Addr>().is_ok() {
211214
common::set_route_ipv4(
212215
switch,
@@ -293,15 +296,25 @@ async fn test_nat_egress(switch: &Switch, test: &NatTest) -> TestResult {
293296
&payload[14..],
294297
);
295298

296-
let mut to_recv =
297-
common::gen_packet_routed(switch, test.uplink_port, &payload_pkt);
298-
eth::EthHdr::rewrite_dmac(&mut to_recv, router_mac);
299+
let expected = match test.uplink_port_registered {
300+
true => {
301+
let mut to_recv = common::gen_packet_routed(
302+
switch,
303+
test.uplink_port,
304+
&payload_pkt,
305+
);
306+
eth::EthHdr::rewrite_dmac(&mut to_recv, router_mac);
307+
vec![TestPacket {
308+
packet: Arc::new(to_recv),
309+
port: test.uplink_port,
310+
}]
311+
}
312+
false => Vec::new(),
313+
};
299314

300315
let send = TestPacket { packet: Arc::new(to_send), port: test.gimlet_port };
301-
let expected =
302-
TestPacket { packet: Arc::new(to_recv), port: test.uplink_port };
303316

304-
switch.packet_test(vec![send], vec![expected])
317+
switch.packet_test(vec![send], expected)
305318
}
306319

307320
async fn test_nat_ingress(switch: &Switch, test: &NatTest) -> TestResult {
@@ -448,15 +461,16 @@ async fn test_nat_ingress(switch: &Switch, test: &NatTest) -> TestResult {
448461
switch.packet_test(send, expected)
449462
}
450463

451-
// UDP packet to/from IPv4 addresses, with an IPv6 address for the OPTE host
452-
#[tokio::test]
453-
#[ignore]
454-
async fn test_egress_ipv4() -> TestResult {
455-
let switch = &*get_switch().await;
456-
464+
// packet to/from IPv4 addresses, with an IPv6 address for the OPTE host
465+
async fn test_egress_ipv4(
466+
switch: &Switch,
467+
l4_protocol: L4Protocol,
468+
uplink_port_registered: bool,
469+
) -> TestResult {
457470
let test = NatTest {
458471
uplink_port: PhysPort(14),
459472
uplink_port_external: "192.168.1.2".to_string(),
473+
uplink_port_registered,
460474
uplink_route: "0.0.0.0/0".to_string(),
461475
router_ip: "192.168.1.1".to_string(),
462476
router_mac: "02:aa:bb:cc:dd:ee".to_string(),
@@ -474,44 +488,43 @@ async fn test_egress_ipv4() -> TestResult {
474488
gimlet_port_ip: "fd00:1122:3344:0101::5".to_string(),
475489

476490
nat_l4_port: 10,
477-
l4_protocol: L4Protocol::Udp,
491+
l4_protocol,
478492
geneve_vni: 1, // not used on egress tests
479493
};
480494

481495
test_nat_egress(switch, &test).await
482496
}
483497

484-
// ICMP packet to/from IPv4 addresses, with an IPv6 address for the OPTE host
498+
// UDP packet to/from IPv4 addresses
485499
#[tokio::test]
486500
#[ignore]
487-
async fn test_egress_ipv4_icmp() -> TestResult {
501+
async fn test_egress_ipv4_udp() -> TestResult {
488502
let switch = &*get_switch().await;
503+
test_egress_ipv4(switch, L4Protocol::Udp, true).await
504+
}
489505

490-
let test = NatTest {
491-
uplink_port: PhysPort(14),
492-
uplink_port_external: "192.168.1.2".to_string(),
493-
uplink_route: "0.0.0.0/0".to_string(),
494-
router_ip: "192.168.1.1".to_string(),
495-
router_mac: "02:aa:bb:cc:dd:ee".to_string(),
496-
497-
vpc_src_ip: "172.16.10.33".to_string(),
498-
vpc_src_mac: "04:01:01:01:01:01".to_string(),
499-
vpc_src_port: 3333,
500-
vpc_dst_ip: "10.10.10.32".to_string(),
501-
vpc_dst_mac: "04:01:01:01:01:02".to_string(),
502-
vpc_dst_port: 4444,
503-
504-
gimlet_port: PhysPort(10),
505-
gimlet_ip: "fd00:1122:7788:0101::4".to_string(),
506-
gimlet_mac: "11:22:33:44:55:66".to_string(),
507-
gimlet_port_ip: "fd00:1122:3344:0101::5".to_string(),
506+
// TCP packet to/from IPv4 addresses
507+
#[tokio::test]
508+
#[ignore]
509+
async fn test_egress_ipv4_tcp() -> TestResult {
510+
let switch = &*get_switch().await;
511+
test_egress_ipv4(switch, L4Protocol::Tcp, true).await
512+
}
508513

509-
nat_l4_port: 10,
510-
l4_protocol: L4Protocol::Icmp,
511-
geneve_vni: 1, // not used on egress tests
512-
};
514+
// ICMP packet to/from IPv4 addresses
515+
#[tokio::test]
516+
#[ignore]
517+
async fn test_egress_ipv4_icmp() -> TestResult {
518+
let switch = &*get_switch().await;
519+
test_egress_ipv4(switch, L4Protocol::Icmp, true).await
520+
}
513521

514-
test_nat_egress(switch, &test).await
522+
// UDP packet to an IPv4 address, egressing a backplane port.
523+
#[tokio::test]
524+
#[ignore]
525+
async fn test_backplane_egress_ipv4_udp() -> TestResult {
526+
let switch = &*get_switch().await;
527+
test_egress_ipv4(switch, L4Protocol::Udp, false).await
515528
}
516529

517530
async fn test_ingress_ipv4(
@@ -521,6 +534,7 @@ async fn test_ingress_ipv4(
521534
let test = NatTest {
522535
uplink_port: PhysPort(14),
523536
uplink_port_external: "192.168.1.2".to_string(),
537+
uplink_port_registered: true,
524538
uplink_route: "unused".to_string(),
525539
router_ip: "192.168.1.1".to_string(),
526540
router_mac: "02:aa:bb:cc:dd:ee".to_string(),
@@ -574,14 +588,16 @@ async fn test_ingress_ipv4_tcp() -> TestResult {
574588
test_ingress_ipv4(switch, L4Protocol::Tcp).await
575589
}
576590

577-
// UDP packet to/from IPv6 addresses,
591+
// packet to/from IPv6 addresses,
578592
async fn test_egress_ipv6(
579593
switch: &Switch,
580594
l4_protocol: L4Protocol,
595+
uplink_port_registered: bool,
581596
) -> TestResult {
582597
let test = NatTest {
583598
uplink_port: PhysPort(14),
584599
uplink_port_external: "fd00:3344:5566::4".to_string(),
600+
uplink_port_registered,
585601
uplink_route: "0::0/0".to_string(),
586602
router_ip: "fd00:3344:5566::1".to_string(),
587603
router_mac: "02:aa:bb:cc:dd:ee".to_string(),
@@ -610,14 +626,21 @@ async fn test_egress_ipv6(
610626
#[ignore]
611627
async fn test_egress_ipv6_udp() -> TestResult {
612628
let switch = &*get_switch().await;
613-
test_egress_ipv6(switch, L4Protocol::Udp).await
629+
test_egress_ipv6(switch, L4Protocol::Udp, true).await
614630
}
615631

616632
#[tokio::test]
617633
#[ignore]
618634
async fn test_egress_ipv6_tcp() -> TestResult {
619635
let switch = &*get_switch().await;
620-
test_egress_ipv6(switch, L4Protocol::Tcp).await
636+
test_egress_ipv6(switch, L4Protocol::Tcp, true).await
637+
}
638+
639+
#[tokio::test]
640+
#[ignore]
641+
async fn test_backplane_egress_ipv6_tcp() -> TestResult {
642+
let switch = &*get_switch().await;
643+
test_egress_ipv6(switch, L4Protocol::Tcp, false).await
621644
}
622645

623646
async fn test_ingress_ipv6(
@@ -627,6 +650,7 @@ async fn test_ingress_ipv6(
627650
let test = NatTest {
628651
uplink_port: PhysPort(14),
629652
uplink_port_external: "fd00:3344:5566::4".to_string(),
653+
uplink_port_registered: true,
630654
uplink_route: "0:0::0/0".to_string(),
631655
router_ip: "fd00:3344:5566::1".to_string(),
632656
router_mac: "02:aa:bb:cc:dd:ee".to_string(),

0 commit comments

Comments
 (0)