timestamped_socket/networkaddress/
linux.rs

1use std::{
2    net::{Ipv4Addr, SocketAddrV4, SocketAddrV6},
3    os::fd::RawFd,
4};
5
6use crate::{
7    cerr, control_message::zeroed_sockaddr_storage, interface::InterfaceName,
8    networkaddress::NetworkAddress,
9};
10
11use super::{
12    sealed::{PrivateToken, SealedMC, SealedNA},
13    MulticastJoinable,
14};
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct MacAddress([u8; 6]);
18
19impl From<[u8; 6]> for MacAddress {
20    fn from(value: [u8; 6]) -> Self {
21        MacAddress(value)
22    }
23}
24
25impl AsRef<[u8]> for MacAddress {
26    fn as_ref(&self) -> &[u8] {
27        &self.0
28    }
29}
30
31impl MacAddress {
32    pub const fn new(address: [u8; 6]) -> Self {
33        MacAddress(address)
34    }
35}
36
37#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
38pub struct EthernetAddress {
39    protocol: u16,
40    mac_address: MacAddress,
41    if_index: libc::c_int,
42}
43
44impl EthernetAddress {
45    pub const fn new(protocol: u16, mac_address: MacAddress, if_index: libc::c_int) -> Self {
46        EthernetAddress {
47            protocol,
48            mac_address,
49            if_index,
50        }
51    }
52
53    pub const fn mac(&self) -> MacAddress {
54        self.mac_address
55    }
56
57    pub const fn protocol(&self) -> u16 {
58        self.protocol
59    }
60
61    pub const fn interface(&self) -> libc::c_int {
62        self.if_index
63    }
64}
65
66impl SealedNA for EthernetAddress {}
67
68impl NetworkAddress for EthernetAddress {
69    fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
70        const _: () = assert!(
71            std::mem::size_of::<libc::sockaddr_storage>()
72                >= std::mem::size_of::<libc::sockaddr_ll>()
73        );
74        const _: () = assert!(
75            std::mem::align_of::<libc::sockaddr_storage>()
76                >= std::mem::align_of::<libc::sockaddr_ll>()
77        );
78
79        let mut result = zeroed_sockaddr_storage();
80        // Safety: the above assertions guarantee that alignment and size are correct.
81        // the resulting reference won't outlast the function, and result lives the entire
82        // duration of the function
83        let out = unsafe { &mut (*(&mut result as *mut _ as *mut libc::sockaddr_ll)) };
84
85        out.sll_family = libc::AF_PACKET as _;
86        out.sll_addr[..6].copy_from_slice(&self.mac_address.0);
87        out.sll_halen = 6;
88        out.sll_protocol = u16::from_ne_bytes(self.protocol.to_be_bytes());
89        out.sll_ifindex = self.if_index;
90
91        result
92    }
93
94    fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
95        const _: () = assert!(
96            std::mem::size_of::<libc::sockaddr_storage>()
97                >= std::mem::size_of::<libc::sockaddr_ll>()
98        );
99        const _: () = assert!(
100            std::mem::align_of::<libc::sockaddr_storage>()
101                >= std::mem::align_of::<libc::sockaddr_ll>()
102        );
103
104        if addr.ss_family != libc::AF_PACKET as _ {
105            return None;
106        }
107
108        // Safety: the above assertions guarantee that alignment and size are correct
109        // the resulting reference won't outlast the function, and addr lives the entire
110        // duration of the function
111        let input = unsafe { &(*(&addr as *const _ as *const libc::sockaddr_ll)) };
112
113        if input.sll_halen != 6 {
114            return None;
115        }
116
117        Some(EthernetAddress::new(
118            u16::from_be_bytes(input.sll_protocol.to_ne_bytes()),
119            MacAddress::new(input.sll_addr[..6].try_into().unwrap()),
120            input.sll_ifindex,
121        ))
122    }
123}
124
125impl SealedMC for EthernetAddress {}
126
127impl MulticastJoinable for EthernetAddress {
128    fn join_multicast(
129        &self,
130        socket: RawFd,
131        interface: InterfaceName,
132        _token: PrivateToken,
133    ) -> std::io::Result<()> {
134        let request = libc::packet_mreq {
135            mr_ifindex: interface
136                .get_index()
137                .ok_or(std::io::ErrorKind::InvalidInput)? as _,
138            mr_type: libc::PACKET_MR_MULTICAST as _,
139            mr_alen: 6,
140            mr_address: [
141                self.mac_address.0[0],
142                self.mac_address.0[1],
143                self.mac_address.0[2],
144                self.mac_address.0[3],
145                self.mac_address.0[4],
146                self.mac_address.0[5],
147                0,
148                0,
149            ],
150        };
151        // Safety:
152        // value points to a struct of length option_len, of type ip_mreq as expected for IPPROTO_IPV6/IPV6_ADD_MEMBERSHIP
153        cerr(unsafe {
154            libc::setsockopt(
155                socket,
156                libc::SOL_PACKET,
157                libc::PACKET_ADD_MEMBERSHIP,
158                &request as *const _ as *const _,
159                std::mem::size_of_val(&request) as _,
160            )
161        })?;
162        Ok(())
163    }
164
165    fn leave_multicast(
166        &self,
167        socket: RawFd,
168        interface: InterfaceName,
169        _token: PrivateToken,
170    ) -> std::io::Result<()> {
171        let request = libc::packet_mreq {
172            mr_ifindex: interface
173                .get_index()
174                .ok_or(std::io::ErrorKind::InvalidInput)? as _,
175            mr_type: libc::PACKET_MR_MULTICAST as _,
176            mr_alen: 6,
177            mr_address: [
178                self.mac_address.0[0],
179                self.mac_address.0[1],
180                self.mac_address.0[2],
181                self.mac_address.0[3],
182                self.mac_address.0[4],
183                self.mac_address.0[5],
184                0,
185                0,
186            ],
187        };
188        // Safety:
189        // value points to a struct of length option_len, of type ip_mreq as expected for IPPROTO_IPV6/IPV6_ADD_MEMBERSHIP
190        cerr(unsafe {
191            libc::setsockopt(
192                socket,
193                libc::SOL_PACKET,
194                libc::PACKET_DROP_MEMBERSHIP,
195                &request as *const _ as *const _,
196                std::mem::size_of_val(&request) as _,
197            )
198        })?;
199        Ok(())
200    }
201}
202
203impl SealedMC for SocketAddrV4 {}
204
205impl MulticastJoinable for SocketAddrV4 {
206    fn join_multicast(
207        &self,
208        socket: RawFd,
209        interface: InterfaceName,
210        _token: PrivateToken,
211    ) -> std::io::Result<()> {
212        let request = libc::ip_mreqn {
213            imr_multiaddr: libc::in_addr {
214                s_addr: u32::from_ne_bytes(self.ip().octets()),
215            },
216            imr_address: libc::in_addr {
217                s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
218            },
219            imr_ifindex: interface
220                .get_index()
221                .ok_or(std::io::ErrorKind::InvalidInput)? as _,
222        };
223        // Safety:
224        // value points to a struct of length option_len, of type ip_mreq as expected for IPPROTO_IP/IP_ADD_MEMBERSHIP
225        cerr(unsafe {
226            libc::setsockopt(
227                socket,
228                libc::IPPROTO_IP,
229                libc::IP_ADD_MEMBERSHIP,
230                &request as *const _ as *const _,
231                std::mem::size_of_val(&request) as _,
232            )
233        })?;
234        Ok(())
235    }
236
237    fn leave_multicast(
238        &self,
239        socket: RawFd,
240        interface: InterfaceName,
241        _token: PrivateToken,
242    ) -> std::io::Result<()> {
243        let request = libc::ip_mreqn {
244            imr_multiaddr: libc::in_addr {
245                s_addr: u32::from_ne_bytes(self.ip().octets()),
246            },
247            imr_address: libc::in_addr {
248                s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
249            },
250            imr_ifindex: interface
251                .get_index()
252                .ok_or(std::io::ErrorKind::InvalidInput)? as _,
253        };
254        // Safety:
255        // value points to a struct of length option_len, of type ip_mreq as expected for IPPROTO_IP/IP_DROP_MEMBERSHIP
256        cerr(unsafe {
257            libc::setsockopt(
258                socket,
259                libc::IPPROTO_IP,
260                libc::IP_DROP_MEMBERSHIP,
261                &request as *const _ as *const _,
262                std::mem::size_of_val(&request) as _,
263            )
264        })?;
265        Ok(())
266    }
267}
268
269impl SealedMC for SocketAddrV6 {}
270
271impl MulticastJoinable for SocketAddrV6 {
272    fn join_multicast(
273        &self,
274        socket: RawFd,
275        interface: InterfaceName,
276        _token: PrivateToken,
277    ) -> std::io::Result<()> {
278        let request = libc::ipv6_mreq {
279            ipv6mr_multiaddr: libc::in6_addr {
280                s6_addr: self.ip().octets(),
281            },
282            ipv6mr_interface: interface
283                .get_index()
284                .ok_or(std::io::ErrorKind::InvalidInput)? as _,
285        };
286        // Safety:
287        // value points to a struct of length option_len, of type ip_mreq as expected for IPPROTO_IPV6/IPV6_ADD_MEMBERSHIP
288        cerr(unsafe {
289            libc::setsockopt(
290                socket,
291                libc::IPPROTO_IPV6,
292                libc::IPV6_ADD_MEMBERSHIP,
293                &request as *const _ as *const _,
294                std::mem::size_of_val(&request) as _,
295            )
296        })?;
297        Ok(())
298    }
299
300    fn leave_multicast(
301        &self,
302        socket: RawFd,
303        interface: InterfaceName,
304        _token: PrivateToken,
305    ) -> std::io::Result<()> {
306        let request = libc::ipv6_mreq {
307            ipv6mr_multiaddr: libc::in6_addr {
308                s6_addr: self.ip().octets(),
309            },
310            ipv6mr_interface: interface
311                .get_index()
312                .ok_or(std::io::ErrorKind::InvalidInput)? as _,
313        };
314        // Safety:
315        // value points to a struct of length option_len, of type ip_mreq as expected for IPPROTO_IPV6/IPV6_DROP_MEMBERSHIP
316        cerr(unsafe {
317            libc::setsockopt(
318                socket,
319                libc::IPPROTO_IPV6,
320                libc::IPV6_DROP_MEMBERSHIP,
321                &request as *const _ as *const _,
322                std::mem::size_of_val(&request) as _,
323            )
324        })?;
325        Ok(())
326    }
327}