1use anyhow::Context as _;
6use fuchsia_async::{DurationExt as _, TimeoutExt as _};
7use futures::io::{AsyncReadExt as _, AsyncWriteExt as _};
8use futures::{FutureExt as _, StreamExt, TryFutureExt as _};
9use net_declare::{fidl_mac, fidl_subnet};
10use netemul::{RealmTcpListener as _, RealmTcpStream as _, RealmUdpSocket as _};
11use netfilter::FidlReturn as _;
12use netstack_testing_common::realms::{Netstack2, TestSandboxExt as _};
13use netstack_testing_common::{
14 ASYNC_EVENT_NEGATIVE_CHECK_TIMEOUT, ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT,
15};
16use std::ops::RangeInclusive;
17use {fidl_fuchsia_net as fnet, fidl_fuchsia_net_filter_deprecated as fnetfilter};
18
19pub const CLIENT_IPV4_SUBNET: fnet::Subnet = fidl_subnet!("192.168.0.2/24");
20pub const SERVER_IPV4_SUBNET: fnet::Subnet = fidl_subnet!("192.168.0.1/24");
21pub const CLIENT_MAC_ADDRESS: fnet::MacAddress = fidl_mac!("02:00:00:00:00:01");
22pub const SERVER_MAC_ADDRESS: fnet::MacAddress = fidl_mac!("02:00:00:00:00:02");
23
24pub const CLIENT_PORT: u16 = 1234;
25pub const SERVER_PORT: u16 = 8080;
26
27pub const CLIENT_PAYLOAD: &'static str = "Enjoy your food!";
28pub const SERVER_PAYLOAD: &'static str = "Thanks, you too...";
29
30#[derive(Copy, Clone)]
31pub enum ExpectedTraffic {
32 ClientToServerOnly,
33 TwoWay,
34}
35
36pub struct Test {
37 pub proto: fnetfilter::SocketProtocol,
38 pub client_updates: Option<Vec<fnetfilter::Rule>>,
39 pub server_updates: Option<Vec<fnetfilter::Rule>>,
40 pub expected_traffic: ExpectedTraffic,
41}
42
43pub async fn run_udp_socket_test(
44 server: &netemul::TestRealm<'_>,
45 server_addr: fidl_fuchsia_net::IpAddress,
46 server_port: u16,
47 client: &netemul::TestRealm<'_>,
48 client_addr: fidl_fuchsia_net::IpAddress,
49 client_port: u16,
50 expected_traffic: ExpectedTraffic,
51) {
52 let fidl_fuchsia_net_ext::IpAddress(client_addr) =
53 fidl_fuchsia_net_ext::IpAddress::from(client_addr);
54 let client_addr = std::net::SocketAddr::new(client_addr, client_port);
55
56 let fidl_fuchsia_net_ext::IpAddress(server_addr) =
57 fidl_fuchsia_net_ext::IpAddress::from(server_addr);
58 let server_addr = std::net::SocketAddr::new(server_addr, server_port);
59
60 let client_sock = fuchsia_async::net::UdpSocket::bind_in_realm(client, client_addr)
61 .await
62 .expect("failed to create client socket");
63 let client_addr =
64 client_sock.local_addr().expect("failed to get local address from client socket");
65
66 let server_sock = fuchsia_async::net::UdpSocket::bind_in_realm(server, server_addr)
67 .await
68 .expect("failed to create server socket");
69
70 let server_fut = async move {
71 let mut buf = [0u8; 1024];
72 let (r, from) = server_sock.recv_from(&mut buf[..]).await.expect("recvfrom failed");
73 assert_eq!(r, CLIENT_PAYLOAD.as_bytes().len());
74 assert_eq!(&buf[..r], CLIENT_PAYLOAD.as_bytes());
75 assert_eq!(from, client_addr);
76 let r = server_sock
77 .send_to(SERVER_PAYLOAD.as_bytes(), client_addr)
78 .await
79 .expect("send to failed");
80 assert_eq!(r, SERVER_PAYLOAD.as_bytes().len());
81 };
82
83 let client_fut = async move {
84 let r = client_sock
85 .send_to(CLIENT_PAYLOAD.as_bytes(), server_addr)
86 .await
87 .expect("sendto failed");
88 assert_eq!(r, CLIENT_PAYLOAD.as_bytes().len());
89
90 let mut buf = [0u8; 1024];
91 match expected_traffic {
92 ExpectedTraffic::ClientToServerOnly => {
93 match client_sock
94 .recv_from(&mut buf[..])
95 .map_ok(Some)
96 .on_timeout(ASYNC_EVENT_NEGATIVE_CHECK_TIMEOUT.after_now(), || Ok(None))
97 .await
98 .expect("recvfrom failed")
99 {
100 Some((r, from)) => {
101 panic!("unexpectedly received packet {:?} from {:?}", &buf[..r], from)
102 }
103 None => (),
104 }
105 }
106 ExpectedTraffic::TwoWay => {
107 let (r, from) = client_sock
108 .recv_from(&mut buf[..])
109 .map(|r| r.expect("recvfrom failed"))
110 .on_timeout(ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT.after_now(), || {
111 panic!(
112 "timed out waiting for packet from server after {:?}",
113 ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT
114 );
115 })
116 .await;
117 assert_eq!(r, SERVER_PAYLOAD.as_bytes().len());
118 assert_eq!(&buf[..r], SERVER_PAYLOAD.as_bytes());
119 assert_eq!(from, server_addr);
120 }
121 }
122 };
123 let ((), ()) = futures::future::join(server_fut, client_fut).await;
124}
125
126pub async fn run_tcp_socket_test(
127 server: &netemul::TestRealm<'_>,
128 server_addr: fidl_fuchsia_net::IpAddress,
129 server_port: u16,
130 client: &netemul::TestRealm<'_>,
131 client_addr: fidl_fuchsia_net::IpAddress,
132 client_port: u16,
133 expected_traffic: ExpectedTraffic,
134) {
135 let fidl_fuchsia_net_ext::IpAddress(client_addr) =
136 fidl_fuchsia_net_ext::IpAddress::from(client_addr);
137 let client_addr = std::net::SocketAddr::new(client_addr, client_port);
138
139 let fidl_fuchsia_net_ext::IpAddress(server_addr) =
140 fidl_fuchsia_net_ext::IpAddress::from(server_addr);
141 let server_addr = std::net::SocketAddr::new(server_addr, server_port);
142
143 let listener =
144 fuchsia_async::net::TcpListener::listen_in_realm_with(server, server_addr, |sock| {
145 sock.set_reuse_port(true).context("failed to set reuse port")
146 })
147 .await
148 .expect("failed to create server socket");
149
150 let server_fut = async move {
151 match expected_traffic {
152 ExpectedTraffic::ClientToServerOnly => {
153 match listener
154 .accept()
155 .map_ok(Some)
156 .on_timeout(ASYNC_EVENT_NEGATIVE_CHECK_TIMEOUT.after_now(), || Ok(None))
157 .await
158 .expect("failed to accept a connection")
159 {
160 Some(_stream) => panic!("unexpectedly connected successfully"),
161 None => (),
162 }
163 }
164 ExpectedTraffic::TwoWay => {
165 let (_listener, mut stream, from) = listener
166 .accept()
167 .map(|r| r.expect("accept failed"))
168 .on_timeout(ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT.after_now(), || {
169 panic!(
170 "timed out waiting for a connection after {:?}",
171 ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT
172 );
173 })
174 .await;
175
176 let mut buf = [0u8; 1024];
177 let read_count =
178 stream.read(&mut buf).await.expect("read from tcp server stream failed");
179
180 assert_eq!(from.ip(), client_addr.ip());
181 assert_eq!(read_count, CLIENT_PAYLOAD.as_bytes().len());
182 assert_eq!(&buf[..read_count], CLIENT_PAYLOAD.as_bytes());
183
184 let write_count = stream
185 .write(SERVER_PAYLOAD.as_bytes())
186 .await
187 .expect("write to tcp server stream failed");
188 assert_eq!(write_count, SERVER_PAYLOAD.as_bytes().len());
189 }
190 }
191 };
192
193 let client_fut = async move {
194 match expected_traffic {
195 ExpectedTraffic::ClientToServerOnly => {
196 match fuchsia_async::net::TcpStream::connect_in_realm(client, server_addr)
197 .map_ok(Some)
198 .on_timeout(ASYNC_EVENT_NEGATIVE_CHECK_TIMEOUT.after_now(), || Ok(None))
199 .await
200 .expect("failed to create client socket")
201 {
202 Some(_stream) => panic!("unexpectedly connected successfully"),
203 None => (),
204 }
205 }
206 ExpectedTraffic::TwoWay => {
207 let mut stream =
208 fuchsia_async::net::TcpStream::connect_in_realm(client, server_addr)
209 .map(|r| r.expect("connect_in_realm failed"))
210 .on_timeout(ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT.after_now(), || {
211 panic!(
212 "timed out waiting for a connection after {:?}",
213 ASYNC_EVENT_POSITIVE_CHECK_TIMEOUT
214 );
215 })
216 .await;
217
218 let write_count = stream
219 .write(CLIENT_PAYLOAD.as_bytes())
220 .await
221 .expect("write to tcp client stream failed");
222
223 assert_eq!(write_count, CLIENT_PAYLOAD.as_bytes().len());
224
225 let mut buf = [0u8; 1024];
226 let read_count =
227 stream.read(&mut buf).await.expect("read from tcp client stream failed");
228
229 assert_eq!(read_count, SERVER_PAYLOAD.as_bytes().len());
230 assert_eq!(&buf[..read_count], SERVER_PAYLOAD.as_bytes());
231 }
232 }
233 };
234
235 let ((), ()) = futures::future::join(client_fut, server_fut).await;
236}
237
238pub async fn run_socket_test(
239 proto: fnetfilter::SocketProtocol,
240 server: &netemul::TestRealm<'_>,
241 server_addr: fidl_fuchsia_net::IpAddress,
242 server_port: u16,
243 client: &netemul::TestRealm<'_>,
244 client_addr: fidl_fuchsia_net::IpAddress,
245 client_port: u16,
246 expected_traffic: ExpectedTraffic,
247) {
248 match proto {
249 fnetfilter::SocketProtocol::Udp => {
250 run_udp_socket_test(
251 server,
252 server_addr,
253 server_port,
254 client,
255 client_addr,
256 client_port,
257 expected_traffic,
258 )
259 .await
260 }
261 fnetfilter::SocketProtocol::Tcp => {
262 run_tcp_socket_test(
263 server,
264 server_addr,
265 server_port,
266 client,
267 client_addr,
268 client_port,
269 expected_traffic,
270 )
271 .await
272 }
273 proto => panic!("unexpected protocol {:?}", proto),
274 }
275}
276
277pub async fn test_filter(name: &str, test: Test) {
278 let sandbox = netemul::TestSandbox::new().expect("failed to create sandbox");
279 let net = sandbox.create_network("net").await.expect("failed to create network");
280
281 let client = sandbox
282 .create_netstack_realm::<Netstack2, _>(format!("{}_client", name))
283 .expect("failed to create client realm");
284 let client_ep = client
285 .join_network_with(
286 &net,
287 "client",
288 netemul::new_endpoint_config(netemul::DEFAULT_MTU, Some(CLIENT_MAC_ADDRESS)),
289 Default::default(),
290 )
291 .await
292 .expect("client failed to join network");
293 client_ep.add_address_and_subnet_route(CLIENT_IPV4_SUBNET).await.expect("configure address");
294 let client_filter = client
295 .connect_to_protocol::<fnetfilter::FilterMarker>()
296 .expect("client failed to connect to filter service");
297
298 let server = sandbox
299 .create_netstack_realm::<Netstack2, _>(format!("{}_server", name))
300 .expect("failed to create server realm");
301 let server_ep = server
302 .join_network_with(
303 &net,
304 "server",
305 netemul::new_endpoint_config(netemul::DEFAULT_MTU, Some(SERVER_MAC_ADDRESS)),
306 Default::default(),
307 )
308 .await
309 .expect("server failed to join network");
310 server_ep.add_address_and_subnet_route(SERVER_IPV4_SUBNET).await.expect("configure address");
311
312 let () = futures::stream::iter([
316 (&server, &server_ep, CLIENT_IPV4_SUBNET.addr, CLIENT_MAC_ADDRESS),
317 (&client, &client_ep, SERVER_IPV4_SUBNET.addr, SERVER_MAC_ADDRESS),
318 ])
319 .for_each_concurrent(None, |(realm, ep, addr, mac)| {
320 realm.add_neighbor_entry(ep.id(), addr, mac).map(|r| r.expect("add_neighbor_entry"))
321 })
322 .await;
323
324 let server_filter = server
325 .connect_to_protocol::<fnetfilter::FilterMarker>()
326 .expect("server failed to connect to filter service");
327
328 let Test { proto, client_updates, server_updates, expected_traffic } = test;
329
330 let () = client_filter
332 .enable_interface(client_ep.id())
333 .await
334 .transform_result()
335 .expect("error enabling filter on client");
336 let () = server_filter
337 .enable_interface(server_ep.id())
338 .await
339 .transform_result()
340 .expect("error enabling filter on server");
341 let () = run_socket_test(
342 proto,
343 &server,
344 SERVER_IPV4_SUBNET.addr,
345 SERVER_PORT,
346 &client,
347 CLIENT_IPV4_SUBNET.addr,
348 CLIENT_PORT,
349 ExpectedTraffic::TwoWay,
350 )
351 .await;
352
353 let (_rules, mut server_generation) =
355 server_filter.get_rules().await.expect("failed to get server's filter rules");
356 let (_rules, mut client_generation) =
357 client_filter.get_rules().await.expect("failed to get client's filter rules");
358 if let Some(updates) = client_updates {
359 let () = client_filter
360 .update_rules(&updates, client_generation)
361 .await
362 .transform_result()
363 .expect("failed to update client's filter rules");
364 client_generation += 1;
365 }
366 if let Some(updates) = server_updates {
367 let () = server_filter
368 .update_rules(&updates, server_generation)
369 .await
370 .transform_result()
371 .expect("failed to update server's filter rules");
372 server_generation += 1;
373 }
374 let () = run_socket_test(
375 proto,
376 &server,
377 SERVER_IPV4_SUBNET.addr,
378 SERVER_PORT,
379 &client,
380 CLIENT_IPV4_SUBNET.addr,
381 CLIENT_PORT,
382 expected_traffic,
383 )
384 .await;
385
386 let () = client_filter
388 .disable_interface(client_ep.id())
389 .await
390 .transform_result()
391 .expect("error disabling filter on client");
392 let () = server_filter
393 .disable_interface(server_ep.id())
394 .await
395 .transform_result()
396 .expect("error disabling filter on server");
397 let () = run_socket_test(
398 proto,
399 &server,
400 SERVER_IPV4_SUBNET.addr,
401 SERVER_PORT,
402 &client,
403 CLIENT_IPV4_SUBNET.addr,
404 CLIENT_PORT,
405 ExpectedTraffic::TwoWay,
406 )
407 .await;
408
409 let () = client_filter
411 .enable_interface(client_ep.id())
412 .await
413 .transform_result()
414 .expect("error re-enabling filter on client");
415 let () = server_filter
416 .enable_interface(server_ep.id())
417 .await
418 .transform_result()
419 .expect("error re-enabling filter on server");
420 let () = server_filter
421 .update_rules(&[], server_generation)
422 .await
423 .transform_result()
424 .expect("failed to reset client's filter rules");
425 let () = client_filter
426 .update_rules(&[], client_generation)
427 .await
428 .transform_result()
429 .expect("failed to reset client's filter rules");
430 run_socket_test(
431 proto,
432 &server,
433 SERVER_IPV4_SUBNET.addr,
434 SERVER_PORT,
435 &client,
436 CLIENT_IPV4_SUBNET.addr,
437 CLIENT_PORT,
438 ExpectedTraffic::TwoWay,
439 )
440 .await
441}
442
443pub fn server_outgoing_drop_test(
445 proto: fnetfilter::SocketProtocol,
446 src_subnet: Option<fnet::Subnet>,
447 src_subnet_invert_match: bool,
448 dst_subnet: Option<fnet::Subnet>,
449 dst_subnet_invert_match: bool,
450 src_port_range: RangeInclusive<u16>,
451 dst_port_range: RangeInclusive<u16>,
452 expected_traffic: ExpectedTraffic,
453) -> Test {
454 Test {
455 proto,
456 client_updates: None,
457 server_updates: Some(vec![fnetfilter::Rule {
458 action: fnetfilter::Action::Drop,
459 direction: fnetfilter::Direction::Outgoing,
460 proto,
461 src_subnet: src_subnet.map(Box::new),
462 src_subnet_invert_match,
463 dst_subnet: dst_subnet.map(Box::new),
464 dst_subnet_invert_match,
465 src_port_range: fnetfilter::PortRange {
466 start: *src_port_range.start(),
467 end: *src_port_range.end(),
468 },
469 dst_port_range: fnetfilter::PortRange {
470 start: *dst_port_range.start(),
471 end: *dst_port_range.end(),
472 },
473 nic: 0,
474 log: false,
475 keep_state: false,
476 device_class: fnetfilter::DeviceClass::Any(fnetfilter::Empty {}),
477 }]),
478 expected_traffic,
479 }
480}
481
482pub fn client_incoming_drop_test(
484 proto: fnetfilter::SocketProtocol,
485 src_subnet: Option<fnet::Subnet>,
486 src_subnet_invert_match: bool,
487 dst_subnet: Option<fnet::Subnet>,
488 dst_subnet_invert_match: bool,
489 src_port_range: RangeInclusive<u16>,
490 dst_port_range: RangeInclusive<u16>,
491 expected_traffic: ExpectedTraffic,
492) -> Test {
493 Test {
494 proto,
495 client_updates: Some(vec![fnetfilter::Rule {
496 action: fnetfilter::Action::Drop,
497 direction: fnetfilter::Direction::Incoming,
498 proto,
499 src_subnet: src_subnet.map(Box::new),
500 src_subnet_invert_match,
501 dst_subnet: dst_subnet.map(Box::new),
502 dst_subnet_invert_match,
503 src_port_range: fnetfilter::PortRange {
504 start: *src_port_range.start(),
505 end: *src_port_range.end(),
506 },
507 dst_port_range: fnetfilter::PortRange {
508 start: *dst_port_range.start(),
509 end: *dst_port_range.end(),
510 },
511 nic: 0,
512 log: false,
513 keep_state: false,
514 device_class: fnetfilter::DeviceClass::Any(fnetfilter::Empty {}),
515 }]),
516 server_updates: None,
517 expected_traffic,
518 }
519}
520
521pub fn subnet_with_offset(
522 fnet::Subnet { mut addr, prefix_len }: fnet::Subnet,
523 offset: u8,
524) -> fnet::Subnet {
525 let last_mut = match addr {
526 fnet::IpAddress::Ipv4(fnet::Ipv4Address { ref mut addr }) => addr.last_mut(),
527 fnet::IpAddress::Ipv6(fnet::Ipv6Address { ref mut addr }) => addr.last_mut(),
528 };
529 *last_mut.expect("should have at least 1 byte in addresses") += offset;
530
531 fnet::Subnet { addr, prefix_len }
532}