netstack_testing_common/
nud.rs1use anyhow::Context;
8use fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin;
9use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
10use futures::StreamExt as _;
11use net_types::SpecifiedAddress as _;
12
13#[derive(Debug, Eq, PartialEq)]
15pub enum FrameMetadata {
16 NeighborSolicitation(fidl_fuchsia_net::IpAddress),
18 Udp(fidl_fuchsia_net::IpAddress),
20 Other,
22}
23
24fn extract_frame_metadata(data: Vec<u8>) -> crate::Result<FrameMetadata> {
30 use packet::ParsablePacket;
31 use packet_formats::arp::{ArpOp, ArpPacket};
32 use packet_formats::ethernet::{EtherType, EthernetFrame, EthernetFrameLengthCheck};
33 use packet_formats::icmp::ndp::NdpPacket;
34 use packet_formats::icmp::{IcmpParseArgs, Icmpv6Packet};
35 use packet_formats::ip::{IpPacket, IpProto, Ipv6Proto};
36 use packet_formats::ipv4::Ipv4Packet;
37 use packet_formats::ipv6::Ipv6Packet;
38
39 let mut bv = &data[..];
40 let ethernet = EthernetFrame::parse(&mut bv, EthernetFrameLengthCheck::NoCheck)
41 .context("failed to parse Ethernet frame")?;
42 match ethernet
43 .ethertype()
44 .ok_or_else(|| anyhow::anyhow!("missing ethertype in Ethernet frame"))?
45 {
46 EtherType::Ipv4 => {
47 let ipv4 = Ipv4Packet::parse(&mut bv, ()).context("failed to parse IPv4 packet")?;
48 if ipv4.proto() != IpProto::Udp.into() {
49 return Ok(FrameMetadata::Other);
50 }
51 Ok(FrameMetadata::Udp(fidl_fuchsia_net::IpAddress::Ipv4(
52 fidl_fuchsia_net::Ipv4Address { addr: ipv4.dst_ip().ipv4_bytes() },
53 )))
54 }
55 EtherType::Arp => {
56 let arp = ArpPacket::<_, net_types::ethernet::Mac, net_types::ip::Ipv4Addr>::parse(
57 &mut bv,
58 (),
59 )
60 .context("failed to parse ARP packet")?;
61 match arp.operation() {
62 ArpOp::Request => Ok(FrameMetadata::NeighborSolicitation(
63 fidl_fuchsia_net::IpAddress::Ipv4(fidl_fuchsia_net::Ipv4Address {
64 addr: arp.target_protocol_address().ipv4_bytes(),
65 }),
66 )),
67 ArpOp::Response => Ok(FrameMetadata::Other),
68 ArpOp::Other(other) => Err(anyhow::anyhow!("unrecognized ARP operation {}", other)),
69 }
70 }
71 EtherType::Ipv6 => {
72 let ipv6 = Ipv6Packet::parse(&mut bv, ()).context("failed to parse IPv6 packet")?;
73 match ipv6.proto() {
74 Ipv6Proto::Icmpv6 => {
75 if !ipv6.src_ip().is_specified() {
78 return Ok(FrameMetadata::Other);
79 }
80 let parse_args = IcmpParseArgs::new(ipv6.src_ip(), ipv6.dst_ip());
81 match Icmpv6Packet::parse(&mut bv, parse_args)
82 .context("failed to parse ICMP packet")?
83 {
84 Icmpv6Packet::Ndp(NdpPacket::NeighborSolicitation(solicit)) => {
85 Ok(FrameMetadata::NeighborSolicitation(
86 fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
87 addr: solicit.message().target_address().ipv6_bytes(),
88 }),
89 ))
90 }
91 _ => Ok(FrameMetadata::Other),
92 }
93 }
94 Ipv6Proto::Proto(IpProto::Udp) => {
95 Ok(FrameMetadata::Udp(fidl_fuchsia_net::IpAddress::Ipv6(
96 fidl_fuchsia_net::Ipv6Address { addr: ipv6.dst_ip().ipv6_bytes() },
97 )))
98 }
99 _ => Ok(FrameMetadata::Other),
100 }
101 }
102 EtherType::Other(other) => {
103 Err(anyhow::anyhow!("unrecognized ethertype in Ethernet frame {}", other))
104 }
105 }
106}
107
108pub fn create_metadata_stream<'a>(
111 ep: &'a netemul::TestFakeEndpoint<'a>,
112) -> impl futures::Stream<Item = crate::Result<FrameMetadata>> + 'a {
113 ep.frame_stream().map(|r| {
114 let (data, dropped) = r.context("fake_ep FIDL error")?;
115 if dropped != 0 {
116 Err(anyhow::anyhow!("dropped {} frames on fake endpoint", dropped))
117 } else {
118 extract_frame_metadata(data)
119 }
120 })
121}
122
123pub async fn apply_nud_flake_workaround(
132 control: &fnet_interfaces_ext::admin::Control,
133) -> crate::Result {
134 control
135 .set_configuration(&fnet_interfaces_admin::Configuration {
136 ipv4: Some(fnet_interfaces_admin::Ipv4Configuration {
137 arp: Some(fnet_interfaces_admin::ArpConfiguration {
138 nud: Some(fnet_interfaces_admin::NudConfiguration {
139 max_multicast_solicitations: Some(u16::MAX),
140 ..Default::default()
141 }),
142 ..Default::default()
143 }),
144 ..Default::default()
145 }),
146 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
147 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
148 nud: Some(fnet_interfaces_admin::NudConfiguration {
149 max_multicast_solicitations: Some(u16::MAX),
150 ..Default::default()
151 }),
152 ..Default::default()
153 }),
154 ..Default::default()
155 }),
156 ..Default::default()
157 })
158 .await
159 .map_err(|e| e.into())
160 .and_then(|r| {
161 r.map(|fnet_interfaces_admin::Configuration { .. }| ())
162 .map_err(|e| anyhow::anyhow!("can't set device configuration: {e:?}"))
163 })
164 .context("apply nud flake workaround")
165}