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