fidl_fuchsia_posix_socket_ext/
lib.rs
1#![deny(missing_docs)]
7
8use {fidl_fuchsia_posix_socket as fposix_socket, fidl_fuchsia_posix_socket_packet as fpacket};
9
10pub async fn datagram_socket(
12 provider: &fposix_socket::ProviderProxy,
13 domain: fposix_socket::Domain,
14 protocol: fposix_socket::DatagramSocketProtocol,
15) -> Result<Result<socket2::Socket, std::io::Error>, fidl::Error> {
16 let result = provider.datagram_socket(domain, protocol).await?;
17 Ok(async move {
18 let response =
19 result.map_err(|errno| std::io::Error::from_raw_os_error(errno.into_primitive()))?;
20 let fd = match response {
21 fposix_socket::ProviderDatagramSocketResponse::DatagramSocket(client_end) => {
22 fdio::create_fd(client_end.into()).map_err(zx::Status::into_io_error)
23 }
24 fposix_socket::ProviderDatagramSocketResponse::SynchronousDatagramSocket(
25 client_end,
26 ) => fdio::create_fd(client_end.into()).map_err(zx::Status::into_io_error),
27 }?;
28 Ok(fd.into())
29 }
30 .await)
31}
32
33pub async fn packet_socket(
35 provider: &fpacket::ProviderProxy,
36 kind: fpacket::Kind,
37) -> Result<Result<socket2::Socket, std::io::Error>, fidl::Error> {
38 let result = provider.socket(kind).await?;
39 Ok(async move {
40 let client_end =
41 result.map_err(|errno| std::io::Error::from_raw_os_error(errno.into_primitive()))?;
42 Ok(fdio::create_fd(client_end.into()).map_err(zx::Status::into_io_error)?.into())
43 }
44 .await)
45}
46
47#[cfg(test)]
48mod test {
49 use super::*;
50 use net_declare::std_socket_addr;
51 use netstack_testing_common::realms::{Netstack, TestSandboxExt as _};
52 use netstack_testing_macros::netstack_test;
53 use sockaddr::{IntoSockAddr as _, TryToSockaddrLl as _};
54 use {
55 fidl_fuchsia_net_ext as fnet_ext, fidl_fuchsia_netemul_network as fnetemul_network,
56 fidl_fuchsia_posix_socket as fposix_socket,
57 };
58
59 #[netstack_test]
60 #[variant(N, Netstack)]
61 async fn datagram_socket_send_receive<N: Netstack>(name: &str) {
62 let sandbox: netemul::TestSandbox = netemul::TestSandbox::new().unwrap();
63
64 let network =
65 sandbox.create_network(format!("{name}-test-network")).await.expect("create network");
66 let realm_a: netemul::TestRealm<'_> = sandbox
67 .create_netstack_realm::<N, _>(format!("{name}-test-realm-a"))
68 .expect("create realm");
69 let realm_b: netemul::TestRealm<'_> = sandbox
70 .create_netstack_realm::<N, _>(format!("{name}-test-realm-b"))
71 .expect("create realm");
72
73 const MAC_A: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:01");
74 const MAC_B: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:02");
75 const FIDL_SUBNET_A: fidl_fuchsia_net::Subnet = net_declare::fidl_subnet!("192.0.2.1/24");
76 const SOCKET_ADDR_A: std::net::SocketAddr = std_socket_addr!("192.0.2.1:1111");
77 const FIDL_SUBNET_B: fidl_fuchsia_net::Subnet = net_declare::fidl_subnet!("192.0.2.2/24");
78 const SOCKET_ADDR_B: std::net::SocketAddr = std_socket_addr!("192.0.2.2:2222");
79
80 let iface_a = realm_a
81 .join_network_with(
82 &network,
83 "iface_a",
84 fnetemul_network::EndpointConfig {
85 mtu: netemul::DEFAULT_MTU,
86 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_A.bytes() }.into())),
87 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
88 },
89 netemul::InterfaceConfig { name: Some("iface_a".into()), ..Default::default() },
90 )
91 .await
92 .expect("join network with realm_a");
93 let iface_b = realm_b
94 .join_network_with(
95 &network,
96 "iface_b",
97 fnetemul_network::EndpointConfig {
98 mtu: netemul::DEFAULT_MTU,
99 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_B.bytes() }.into())),
100 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
101 },
102 netemul::InterfaceConfig { name: Some("iface_b".into()), ..Default::default() },
103 )
104 .await
105 .expect("join network with realm_b");
106
107 iface_a
108 .add_address_and_subnet_route(FIDL_SUBNET_A)
109 .await
110 .expect("add address should succeed");
111 iface_b
112 .add_address_and_subnet_route(FIDL_SUBNET_B)
113 .await
114 .expect("add address should succeed");
115
116 let socket_a = datagram_socket(
117 &realm_a
118 .connect_to_protocol::<fposix_socket::ProviderMarker>()
119 .expect("connect should succeed"),
120 fposix_socket::Domain::Ipv4,
121 fposix_socket::DatagramSocketProtocol::Udp,
122 )
123 .await
124 .expect("should not have FIDL error")
125 .expect("should not have io Error");
126
127 socket_a.bind(&SOCKET_ADDR_A.into()).expect("should succeed");
128
129 let socket_b = datagram_socket(
130 &realm_b
131 .connect_to_protocol::<fposix_socket::ProviderMarker>()
132 .expect("connect should succeed"),
133 fposix_socket::Domain::Ipv4,
134 fposix_socket::DatagramSocketProtocol::Udp,
135 )
136 .await
137 .expect("should not have FIDL error")
138 .expect("should not have io Error");
139
140 socket_b.bind(&SOCKET_ADDR_B.into()).expect("should succeed");
141
142 let mut buf = [std::mem::MaybeUninit::new(0u8); netemul::DEFAULT_MTU as usize];
143
144 let payload = b"hello world!";
145
146 let n = socket_a
147 .send_to(payload.as_ref(), &SOCKET_ADDR_B.into())
148 .expect("send_to should succeed");
149 assert_eq!(n, payload.len());
150
151 let (n, address) = socket_b.recv_from(&mut buf[..]).expect("recv_from should succeed");
152 let buf = buf[..n].iter().map(|byte| unsafe { byte.assume_init() }).collect::<Vec<_>>();
153
154 assert_eq!(&buf[..], payload.as_ref());
155 assert_eq!(address.as_socket().expect("should be SocketAddr"), SOCKET_ADDR_A);
156 }
157
158 #[netstack_test]
159 #[variant(N, Netstack)]
160 async fn packet_socket_send_receive<N: Netstack>(name: &str) {
161 let sandbox: netemul::TestSandbox = netemul::TestSandbox::new().unwrap();
162
163 let network =
164 sandbox.create_network(format!("{name}-test-network")).await.expect("create network");
165 let realm_a: netemul::TestRealm<'_> = sandbox
166 .create_netstack_realm::<N, _>(format!("{name}-test-realm-a"))
167 .expect("create realm");
168 let realm_b: netemul::TestRealm<'_> = sandbox
169 .create_netstack_realm::<N, _>(format!("{name}-test-realm-b"))
170 .expect("create realm");
171
172 const MAC_A: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:01");
173 const MAC_B: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:02");
174
175 let iface_a = realm_a
176 .join_network_with(
177 &network,
178 "iface_a",
179 fnetemul_network::EndpointConfig {
180 mtu: netemul::DEFAULT_MTU,
181 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_A.bytes() }.into())),
182 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
183 },
184 netemul::InterfaceConfig { name: Some("iface_a".into()), ..Default::default() },
185 )
186 .await
187 .expect("join network with realm_a");
188 let iface_b = realm_b
189 .join_network_with(
190 &network,
191 "iface_b",
192 fnetemul_network::EndpointConfig {
193 mtu: netemul::DEFAULT_MTU,
194 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_B.bytes() }.into())),
195 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
196 },
197 netemul::InterfaceConfig { name: Some("iface_b".into()), ..Default::default() },
198 )
199 .await
200 .expect("join network with realm_b");
201
202 let socket_a = packet_socket(
203 &realm_a
204 .connect_to_protocol::<fpacket::ProviderMarker>()
205 .expect("connect should succeed"),
206 fpacket::Kind::Network,
207 )
208 .await
209 .expect("should not have FIDL error")
210 .expect("should not have io Error");
211
212 let socket_b = packet_socket(
213 &realm_b
214 .connect_to_protocol::<fpacket::ProviderMarker>()
215 .expect("connect should succeed"),
216 fpacket::Kind::Network,
217 )
218 .await
219 .expect("should not have FIDL error")
220 .expect("should not have io Error");
221
222 let sockaddr_a = libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
223 interface_id: Some(iface_a.id().try_into().expect("nonzero")),
224 addr: MAC_A,
225 protocol: packet_formats::ethernet::EtherType::Ipv4,
226 });
227
228 let sockaddr_b = libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
229 interface_id: Some(iface_b.id().try_into().expect("nonzero")),
230 addr: MAC_B,
231 protocol: packet_formats::ethernet::EtherType::Ipv4,
232 });
233
234 socket_a.bind(&sockaddr_a.into_sockaddr()).expect("should succeed");
235 socket_b.bind(&sockaddr_b.into_sockaddr()).expect("should succeed");
236
237 let mut buf = [std::mem::MaybeUninit::new(0u8); netemul::DEFAULT_MTU as usize];
238
239 let payload = b"hello world!";
240
241 let n = socket_a
242 .send_to(
243 payload.as_ref(),
244 &libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
245 interface_id: Some(iface_a.id().try_into().expect("nonzero")),
246 addr: MAC_B,
247 protocol: packet_formats::ethernet::EtherType::Ipv4,
248 })
249 .into_sockaddr(),
250 )
251 .expect("send_to should succeed");
252 assert_eq!(n, payload.len());
253
254 const NUM_ATTEMPTS: i32 = 5;
260
261 for attempt in 1..=NUM_ATTEMPTS {
262 let (n, address) = socket_b.recv_from(&mut buf[..]).expect("recv_from should succeed");
263 let buf = buf[..n].iter().map(|byte| unsafe { byte.assume_init() }).collect::<Vec<_>>();
264
265 if &buf[..] != payload.as_ref() {
266 println!("got buf={buf:?} didn't match wanted={payload:?} in attempt {attempt}");
267 continue;
268 }
269
270 let got_address = match address.try_to_sockaddr_ll() {
271 Some(addr) => addr,
272 None => {
273 println!("could not convert {address:?} to sockaddr_ll in attempt {attempt}");
274 continue;
275 }
276 };
277
278 let want_address = {
279 let mut addr = libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
280 interface_id: Some(iface_b.id().try_into().expect("nonzero")),
281 addr: MAC_A,
282 protocol: packet_formats::ethernet::EtherType::Ipv4,
283 });
284 const ARPHRD_ETHER: libc::c_ushort = 1;
285 addr.sll_hatype = ARPHRD_ETHER;
286 addr
287 };
288
289 if got_address != want_address {
290 println!(
291 "got_address {got_address:?} didn't match \
292 want_address {want_address:?} in attempt {attempt}"
293 );
294 continue;
295 }
296
297 println!("succeeded on attempt {attempt}");
298 return;
299 }
300
301 panic!("failed to receive expected frame in all {NUM_ATTEMPTS} attempts");
302 }
303}