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
74#[cfg(test)]
75mod test {
76 use super::*;
77 use fidl_fuchsia_net_ext as fnet_ext;
78 use fidl_fuchsia_netemul_network as fnetemul_network;
79 use fidl_fuchsia_posix_socket as fposix_socket;
80 use net_declare::std_socket_addr;
81 use netstack_testing_common::realms::{Netstack, TestSandboxExt as _};
82 use netstack_testing_macros::netstack_test;
83 use sockaddr::{IntoSockAddr as _, TryToSockaddrLl as _};
84
85 #[netstack_test]
86 #[variant(N, Netstack)]
87 async fn datagram_socket_send_receive<N: Netstack>(name: &str) {
88 let sandbox: netemul::TestSandbox = netemul::TestSandbox::new().unwrap();
89
90 let network =
91 sandbox.create_network(format!("{name}-test-network")).await.expect("create network");
92 let realm_a: netemul::TestRealm<'_> = sandbox
93 .create_netstack_realm::<N, _>(format!("{name}-test-realm-a"))
94 .expect("create realm");
95 let realm_b: netemul::TestRealm<'_> = sandbox
96 .create_netstack_realm::<N, _>(format!("{name}-test-realm-b"))
97 .expect("create realm");
98
99 const MAC_A: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:01");
100 const MAC_B: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:02");
101 const FIDL_SUBNET_A: fidl_fuchsia_net::Subnet = net_declare::fidl_subnet!("192.0.2.1/24");
102 const SOCKET_ADDR_A: std::net::SocketAddr = std_socket_addr!("192.0.2.1:1111");
103 const FIDL_SUBNET_B: fidl_fuchsia_net::Subnet = net_declare::fidl_subnet!("192.0.2.2/24");
104 const SOCKET_ADDR_B: std::net::SocketAddr = std_socket_addr!("192.0.2.2:2222");
105
106 let iface_a = realm_a
107 .join_network_with(
108 &network,
109 "iface_a",
110 fnetemul_network::EndpointConfig {
111 mtu: netemul::DEFAULT_MTU,
112 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_A.bytes() }.into())),
113 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
114 checksum_offload: false,
115 },
116 netemul::InterfaceConfig { name: Some("iface_a".into()), ..Default::default() },
117 )
118 .await
119 .expect("join network with realm_a");
120 let iface_b = realm_b
121 .join_network_with(
122 &network,
123 "iface_b",
124 fnetemul_network::EndpointConfig {
125 mtu: netemul::DEFAULT_MTU,
126 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_B.bytes() }.into())),
127 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
128 checksum_offload: false,
129 },
130 netemul::InterfaceConfig { name: Some("iface_b".into()), ..Default::default() },
131 )
132 .await
133 .expect("join network with realm_b");
134
135 iface_a
136 .add_address_and_subnet_route(FIDL_SUBNET_A)
137 .await
138 .expect("add address should succeed");
139 iface_b
140 .add_address_and_subnet_route(FIDL_SUBNET_B)
141 .await
142 .expect("add address should succeed");
143
144 let socket_a = datagram_socket(
145 &realm_a
146 .connect_to_protocol::<fposix_socket::ProviderMarker>()
147 .expect("connect should succeed"),
148 fposix_socket::Domain::Ipv4,
149 fposix_socket::DatagramSocketProtocol::Udp,
150 )
151 .await
152 .expect("should not have FIDL error")
153 .expect("should not have io Error");
154
155 socket_a.bind(&SOCKET_ADDR_A.into()).expect("should succeed");
156
157 let socket_b = datagram_socket(
158 &realm_b
159 .connect_to_protocol::<fposix_socket::ProviderMarker>()
160 .expect("connect should succeed"),
161 fposix_socket::Domain::Ipv4,
162 fposix_socket::DatagramSocketProtocol::Udp,
163 )
164 .await
165 .expect("should not have FIDL error")
166 .expect("should not have io Error");
167
168 socket_b.bind(&SOCKET_ADDR_B.into()).expect("should succeed");
169
170 let mut buf = [std::mem::MaybeUninit::new(0u8); netemul::DEFAULT_MTU as usize];
171
172 let payload = b"hello world!";
173
174 let n = socket_a
175 .send_to(payload.as_ref(), &SOCKET_ADDR_B.into())
176 .expect("send_to should succeed");
177 assert_eq!(n, payload.len());
178
179 let (n, address) = socket_b.recv_from(&mut buf[..]).expect("recv_from should succeed");
180 let buf = buf[..n].iter().map(|byte| unsafe { byte.assume_init() }).collect::<Vec<_>>();
181
182 assert_eq!(&buf[..], payload.as_ref());
183 assert_eq!(address.as_socket().expect("should be SocketAddr"), SOCKET_ADDR_A);
184 }
185
186 #[netstack_test]
187 #[variant(N, Netstack)]
188 async fn packet_socket_send_receive<N: Netstack>(name: &str) {
189 let sandbox: netemul::TestSandbox = netemul::TestSandbox::new().unwrap();
190
191 let network =
192 sandbox.create_network(format!("{name}-test-network")).await.expect("create network");
193 let realm_a: netemul::TestRealm<'_> = sandbox
194 .create_netstack_realm::<N, _>(format!("{name}-test-realm-a"))
195 .expect("create realm");
196 let realm_b: netemul::TestRealm<'_> = sandbox
197 .create_netstack_realm::<N, _>(format!("{name}-test-realm-b"))
198 .expect("create realm");
199
200 const MAC_A: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:01");
201 const MAC_B: net_types::ethernet::Mac = net_declare::net_mac!("00:00:00:00:00:02");
202
203 let iface_a = realm_a
204 .join_network_with(
205 &network,
206 "iface_a",
207 fnetemul_network::EndpointConfig {
208 mtu: netemul::DEFAULT_MTU,
209 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_A.bytes() }.into())),
210 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
211 checksum_offload: false,
212 },
213 netemul::InterfaceConfig { name: Some("iface_a".into()), ..Default::default() },
214 )
215 .await
216 .expect("join network with realm_a");
217 let iface_b = realm_b
218 .join_network_with(
219 &network,
220 "iface_b",
221 fnetemul_network::EndpointConfig {
222 mtu: netemul::DEFAULT_MTU,
223 mac: Some(Box::new(fnet_ext::MacAddress { octets: MAC_B.bytes() }.into())),
224 port_class: fidl_fuchsia_hardware_network::PortClass::Virtual,
225 checksum_offload: false,
226 },
227 netemul::InterfaceConfig { name: Some("iface_b".into()), ..Default::default() },
228 )
229 .await
230 .expect("join network with realm_b");
231
232 let socket_a = packet_socket(
233 &realm_a
234 .connect_to_protocol::<fpacket::ProviderMarker>()
235 .expect("connect should succeed"),
236 fpacket::Kind::Network,
237 )
238 .await
239 .expect("should not have FIDL error")
240 .expect("should not have io Error");
241
242 let socket_b = packet_socket(
243 &realm_b
244 .connect_to_protocol::<fpacket::ProviderMarker>()
245 .expect("connect should succeed"),
246 fpacket::Kind::Network,
247 )
248 .await
249 .expect("should not have FIDL error")
250 .expect("should not have io Error");
251
252 let sockaddr_a = libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
253 interface_id: Some(iface_a.id().try_into().expect("nonzero")),
254 addr: MAC_A,
255 protocol: packet_formats::ethernet::EtherType::Ipv4,
256 });
257
258 let sockaddr_b = libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
259 interface_id: Some(iface_b.id().try_into().expect("nonzero")),
260 addr: MAC_B,
261 protocol: packet_formats::ethernet::EtherType::Ipv4,
262 });
263
264 socket_a.bind(&sockaddr_a.into_sockaddr()).expect("should succeed");
265 socket_b.bind(&sockaddr_b.into_sockaddr()).expect("should succeed");
266
267 let mut buf = [std::mem::MaybeUninit::new(0u8); netemul::DEFAULT_MTU as usize];
268
269 let payload = b"hello world!";
270
271 let n = socket_a
272 .send_to(
273 payload.as_ref(),
274 &libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
275 interface_id: Some(iface_a.id().try_into().expect("nonzero")),
276 addr: MAC_B,
277 protocol: packet_formats::ethernet::EtherType::Ipv4,
278 })
279 .into_sockaddr(),
280 )
281 .expect("send_to should succeed");
282 assert_eq!(n, payload.len());
283
284 const NUM_ATTEMPTS: i32 = 5;
290
291 for attempt in 1..=NUM_ATTEMPTS {
292 let (n, address) = socket_b.recv_from(&mut buf[..]).expect("recv_from should succeed");
293 let buf = buf[..n].iter().map(|byte| unsafe { byte.assume_init() }).collect::<Vec<_>>();
294
295 if &buf[..] != payload.as_ref() {
296 println!("got buf={buf:?} didn't match wanted={payload:?} in attempt {attempt}");
297 continue;
298 }
299
300 let got_address = match address.try_to_sockaddr_ll() {
301 Some(addr) => addr,
302 None => {
303 println!("could not convert {address:?} to sockaddr_ll in attempt {attempt}");
304 continue;
305 }
306 };
307
308 let want_address = {
309 let mut addr = libc::sockaddr_ll::from(sockaddr::EthernetSockaddr {
310 interface_id: Some(iface_b.id().try_into().expect("nonzero")),
311 addr: MAC_A,
312 protocol: packet_formats::ethernet::EtherType::Ipv4,
313 });
314 const ARPHRD_ETHER: libc::c_ushort = 1;
315 addr.sll_hatype = ARPHRD_ETHER;
316 addr
317 };
318
319 if got_address != want_address {
320 println!(
321 "got_address {got_address:?} didn't match \
322 want_address {want_address:?} in attempt {attempt}"
323 );
324 continue;
325 }
326
327 println!("succeeded on attempt {attempt}");
328 return;
329 }
330
331 panic!("failed to receive expected frame in all {NUM_ATTEMPTS} attempts");
332 }
333}