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