common/
lib.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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    // Put client and server in each other's neighbor table. We've observed
313    // flakes in CQ due to ARP timeouts and ARP resolution is immaterial to the
314    // tests we run here.
315    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    // Initial check (no filters set).
331    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    // Set the filters and do the test.
354    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    // Disable the filters on the interface and expect full connectivity.
387    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    // Reset and enable filters and expect full connectivity.
410    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
443/// Tests how the filter on server drops the outgoing traffic from server.
444pub 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
482/// Tests if the filter on client drops the incoming traffic to client.
483pub 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}