std_embedded_nal_async/
udp.rs

1//! UDP implementation on the standard stack for embedded-nal-async
2
3use crate::conversion;
4use core::net::SocketAddr;
5use std::io::Error;
6
7use std::os::fd::AsRawFd;
8
9pub struct ConnectedSocket(async_io::Async<std::net::UdpSocket>);
10pub struct UniquelyBoundSocket {
11    socket: async_io::Async<std::net::UdpSocket>,
12    // By storing this, we avoid the whole recvmsg hell, which we can because there's really only
13    // one relevant address. (Alternatively, we could call `.local_addr()` over and over).
14    bound_address: SocketAddr,
15}
16pub struct MultiplyBoundSocket {
17    socket: async_io::Async<std::net::UdpSocket>,
18    // Storing this so we can return a full SocketAddr, even though pktinfo doesn't provide that
19    // information
20    port: u16,
21}
22
23impl embedded_nal_async::UdpStack for crate::Stack {
24    type Error = Error;
25    type Connected = ConnectedSocket;
26    type UniquelyBound = UniquelyBoundSocket;
27    type MultiplyBound = MultiplyBoundSocket;
28
29    async fn connect_from(
30        &self,
31        local: SocketAddr,
32        remote: SocketAddr,
33    ) -> Result<(SocketAddr, Self::Connected), Self::Error> {
34        let sock = async_io::Async::<std::net::UdpSocket>::bind(local)?;
35
36        sock.get_ref().connect(remote)?;
37
38        Ok((sock.get_ref().local_addr()?, ConnectedSocket(sock)))
39    }
40
41    async fn bind_single(
42        &self,
43        local: SocketAddr,
44    ) -> Result<(SocketAddr, Self::UniquelyBound), Self::Error> {
45        let sock = async_io::Async::<std::net::UdpSocket>::bind(local)?;
46
47        let final_local = sock.get_ref().local_addr()?;
48
49        Ok((
50            final_local,
51            UniquelyBoundSocket {
52                socket: sock,
53                bound_address: final_local,
54            },
55        ))
56    }
57
58    async fn bind_multiple(
59        &self,
60        local: core::net::SocketAddr,
61    ) -> Result<Self::MultiplyBound, Self::Error> {
62        let is_v4 = matches!(&local, core::net::SocketAddr::V4(_));
63
64        let sock = async_io::Async::<std::net::UdpSocket>::bind(local)?;
65
66        let plain_sock = sock.get_ref();
67
68        if is_v4 {
69            nix::sys::socket::setsockopt(
70                &plain_sock,
71                nix::sys::socket::sockopt::Ipv4PacketInfo,
72                &true,
73            )?;
74        } else {
75            nix::sys::socket::setsockopt(
76                &plain_sock,
77                nix::sys::socket::sockopt::Ipv6RecvPacketInfo,
78                &true,
79            )?;
80        }
81
82        let mut local_port = local.port();
83        if local_port == 0 {
84            local_port = plain_sock.local_addr()?.port();
85        }
86
87        Ok(MultiplyBoundSocket {
88            socket: sock,
89            port: local_port,
90        })
91    }
92}
93
94impl embedded_nal_async::ConnectedUdp for ConnectedSocket {
95    type Error = Error;
96
97    async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error> {
98        let sent_len = self.0.send(data).await?;
99        assert!(
100            sent_len == data.len(),
101            "Datagram was not sent in a single operation"
102        );
103        Ok(())
104    }
105
106    async fn receive_into(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error> {
107        self.0.recv(buffer).await
108    }
109}
110
111impl embedded_nal_async::UnconnectedUdp for UniquelyBoundSocket {
112    type Error = Error;
113
114    async fn send(
115        &mut self,
116        local: core::net::SocketAddr,
117        remote: core::net::SocketAddr,
118        data: &[u8],
119    ) -> Result<(), Self::Error> {
120        debug_assert!(
121            local == self.bound_address,
122            "A socket created from bind_single must always provide its original local address (or the one returned from a receive) in send"
123        );
124        let sent_len = self.socket.send_to(data, remote).await?;
125        assert!(
126            sent_len == data.len(),
127            "Datagram was not sent in a single operation"
128        );
129        Ok(())
130    }
131
132    async fn receive_into(
133        &mut self,
134        buffer: &mut [u8],
135    ) -> Result<(usize, core::net::SocketAddr, core::net::SocketAddr), Self::Error> {
136        let (length, remote) = self.socket.recv_from(buffer).await?;
137        Ok((length, self.bound_address, remote))
138    }
139}
140
141impl embedded_nal_async::UnconnectedUdp for MultiplyBoundSocket {
142    type Error = Error;
143
144    async fn send(
145        &mut self,
146        local: core::net::SocketAddr,
147        remote: core::net::SocketAddr,
148        data: &[u8],
149    ) -> Result<(), Self::Error> {
150        if local.port() != 0 {
151            debug_assert_eq!(
152                local.port(),
153                self.port,
154                "Packets can only be sent from the locally bound to port"
155            );
156        }
157        match remote {
158            // The whole cases are distinct as send_msg is polymorphic
159            core::net::SocketAddr::V6(remote) => {
160                // Taking this step on foot due to https://github.com/nix-rust/nix/issues/1754
161                let remote = nix::sys::socket::SockaddrIn6::from(remote);
162                let local_pktinfo = conversion::IpAddr(local.ip()).into();
163                let control = [nix::sys::socket::ControlMessage::Ipv6PacketInfo(
164                    &local_pktinfo,
165                )];
166                self.socket
167                    .write_with(|s| {
168                        let sent_len = nix::sys::socket::sendmsg(
169                            s.as_raw_fd(),
170                            &[std::io::IoSlice::new(data)],
171                            // FIXME this ignores the IP part of the local address
172                            &control,
173                            nix::sys::socket::MsgFlags::empty(),
174                            Some(&remote),
175                        )?;
176                        assert!(
177                            sent_len == data.len(),
178                            "Datagram was not sent in a single operation"
179                        );
180                        Ok(())
181                    })
182                    .await
183            }
184            core::net::SocketAddr::V4(remote) => {
185                // Taking this step on foot due to https://github.com/nix-rust/nix/issues/1754
186                let remote = nix::sys::socket::SockaddrIn::from(remote);
187                let local_pktinfo = conversion::IpAddr(local.ip()).into();
188                let control = [nix::sys::socket::ControlMessage::Ipv4PacketInfo(
189                    &local_pktinfo,
190                )];
191                self.socket
192                    .write_with(|s| {
193                        let sent_len = nix::sys::socket::sendmsg(
194                            s.as_raw_fd(),
195                            &[std::io::IoSlice::new(data)],
196                            // FIXME this ignores the IP part of the local address
197                            &control,
198                            nix::sys::socket::MsgFlags::empty(),
199                            Some(&remote),
200                        )?;
201                        assert!(
202                            sent_len == data.len(),
203                            "Datagram was not sent in a single operation"
204                        );
205                        Ok(())
206                    })
207                    .await
208            }
209        }
210    }
211
212    async fn receive_into(
213        &mut self,
214        buffer: &mut [u8],
215    ) -> Result<(usize, core::net::SocketAddr, core::net::SocketAddr), Self::Error> {
216        let (length, remote, local) = self.socket.read_with(|s| {
217            let mut iov = [std::io::IoSliceMut::new(buffer)];
218            let mut cmsg = nix::cmsg_space!(nix::libc::in6_pktinfo);
219            let received = nix::sys::socket::recvmsg(
220                s.as_raw_fd(),
221                &mut iov,
222                Some(&mut cmsg),
223                nix::sys::socket::MsgFlags::MSG_TRUNC,
224                )
225                .map_err(Error::from)?;
226            let local = match received.cmsgs()?.next() {
227                Some(nix::sys::socket::ControlMessageOwned::Ipv6PacketInfo(pi)) => {
228                    SocketAddr::new(
229                        conversion::IpAddr::from(pi).0,
230                        self.port,
231                        )
232                },
233                Some(nix::sys::socket::ControlMessageOwned::Ipv4PacketInfo(pi)) => {
234                    SocketAddr::new(
235                        conversion::IpAddr::from(pi).0,
236                        self.port,
237                        )
238                },
239                _ => panic!("Operating system failed to send IPv4/IPv6 packet info after acknowledging the socket option")
240            };
241            Ok((received.bytes, received.address, local))
242        }).await?;
243
244        let remote: nix::sys::socket::SockaddrStorage =
245            remote.expect("recvmsg on UDP always returns a remote address");
246        // Taking this step on foot due to https://github.com/nix-rust/nix/issues/1754
247        let remote = match (remote.as_sockaddr_in6(), remote.as_sockaddr_in()) {
248            (Some(remote), None) => std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
249                remote.ip(),
250                remote.port(),
251                remote.flowinfo(),
252                remote.scope_id(),
253            )),
254            (None, Some(remote)) => {
255                std::net::SocketAddr::V4(std::net::SocketAddrV4::new(remote.ip(), remote.port()))
256            }
257            _ => panic!("Unexpected address type"),
258        };
259
260        // We could probably shorten things by going more directly from SockaddrLike
261        Ok((length, local, remote))
262    }
263}