timestamped_socket/networkaddress/
linux.rs1use 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)]
18pub struct MacAddress([u8; 6]);
19
20impl From<[u8; 6]> for MacAddress {
21 fn from(value: [u8; 6]) -> Self {
22 MacAddress(value)
23 }
24}
25
26impl AsRef<[u8]> for MacAddress {
27 fn as_ref(&self) -> &[u8] {
28 &self.0
29 }
30}
31
32impl MacAddress {
33 #[must_use]
35 pub const fn new(address: [u8; 6]) -> Self {
36 MacAddress(address)
37 }
38}
39
40#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
42pub struct EthernetAddress {
43 protocol: u16,
44 mac_address: MacAddress,
45 if_index: libc::c_int,
46}
47
48impl EthernetAddress {
49 #[must_use]
51 pub const fn new(protocol: u16, mac_address: MacAddress, if_index: libc::c_int) -> Self {
52 EthernetAddress {
53 protocol,
54 mac_address,
55 if_index,
56 }
57 }
58
59 #[must_use]
61 pub const fn mac(&self) -> MacAddress {
62 self.mac_address
63 }
64
65 #[must_use]
67 pub const fn protocol(&self) -> u16 {
68 self.protocol
69 }
70
71 #[must_use]
73 pub const fn interface(&self) -> libc::c_int {
74 self.if_index
75 }
76}
77
78impl SealedNA for EthernetAddress {}
79
80impl NetworkAddress for EthernetAddress {
81 #[expect(
82 clippy::cast_possible_truncation,
83 reason = "libc constants are of inconsistent sizes."
84 )]
85 fn to_sockaddr(&self, _token: PrivateToken) -> libc::sockaddr_storage {
86 const _: () = assert!(
87 std::mem::size_of::<libc::sockaddr_storage>()
88 >= std::mem::size_of::<libc::sockaddr_ll>()
89 );
90 const _: () = assert!(
91 std::mem::align_of::<libc::sockaddr_storage>()
92 >= std::mem::align_of::<libc::sockaddr_ll>()
93 );
94
95 let mut result = zeroed_sockaddr_storage();
96 let out = unsafe { &mut (*(&raw mut result).cast::<libc::sockaddr_ll>()) };
100
101 out.sll_family = libc::AF_PACKET as _;
102 out.sll_addr[..6].copy_from_slice(&self.mac_address.0);
103 out.sll_halen = 6;
104 out.sll_protocol = u16::from_ne_bytes(self.protocol.to_be_bytes());
105 out.sll_ifindex = self.if_index;
106
107 result
108 }
109
110 #[expect(
111 clippy::cast_possible_truncation,
112 reason = "libc constants are of inconsistent sizes."
113 )]
114 fn from_sockaddr(addr: libc::sockaddr_storage, _token: PrivateToken) -> Option<Self> {
115 const _: () = assert!(
116 std::mem::size_of::<libc::sockaddr_storage>()
117 >= std::mem::size_of::<libc::sockaddr_ll>()
118 );
119 const _: () = assert!(
120 std::mem::align_of::<libc::sockaddr_storage>()
121 >= std::mem::align_of::<libc::sockaddr_ll>()
122 );
123
124 if addr.ss_family != libc::AF_PACKET as _ {
125 return None;
126 }
127
128 let input = unsafe { &(*(&raw const addr).cast::<libc::sockaddr_ll>()) };
132
133 if input.sll_halen != 6 {
134 return None;
135 }
136
137 Some(EthernetAddress::new(
138 u16::from_be_bytes(input.sll_protocol.to_ne_bytes()),
139 MacAddress::new(input.sll_addr[..6].try_into().unwrap()),
140 input.sll_ifindex,
141 ))
142 }
143
144 fn from_ip_and_port(_addr: std::net::IpAddr, _port: u16) -> Option<Self> {
145 None
146 }
147
148 fn port(&self) -> u16 {
149 0
151 }
152}
153
154impl SealedMC for EthernetAddress {}
155
156impl MulticastJoinable for EthernetAddress {
157 #[expect(
158 clippy::cast_possible_truncation,
159 reason = "a packet_mreq is small enough for its size to fit in an u32."
160 )]
161 #[expect(
162 clippy::cast_possible_wrap,
163 reason = "interface indices will always fit in i32s"
164 )]
165 fn join_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 cerr(unsafe {
191 libc::setsockopt(
192 socket,
193 libc::SOL_PACKET,
194 libc::PACKET_ADD_MEMBERSHIP,
195 (&raw const request).cast(),
196 std::mem::size_of_val(&request) as _,
197 )
198 })?;
199 Ok(())
200 }
201
202 #[expect(
203 clippy::cast_possible_truncation,
204 reason = "a packet_mreq is small enough for its size to fit in an u32."
205 )]
206 #[expect(
207 clippy::cast_possible_wrap,
208 reason = "interface indices will always fit in i32s"
209 )]
210 fn leave_multicast(
211 &self,
212 socket: RawFd,
213 interface: InterfaceName,
214 _token: PrivateToken,
215 ) -> std::io::Result<()> {
216 let request = libc::packet_mreq {
217 mr_ifindex: interface
218 .get_index()
219 .ok_or(std::io::ErrorKind::InvalidInput)? as _,
220 mr_type: libc::PACKET_MR_MULTICAST as _,
221 mr_alen: 6,
222 mr_address: [
223 self.mac_address.0[0],
224 self.mac_address.0[1],
225 self.mac_address.0[2],
226 self.mac_address.0[3],
227 self.mac_address.0[4],
228 self.mac_address.0[5],
229 0,
230 0,
231 ],
232 };
233 cerr(unsafe {
236 libc::setsockopt(
237 socket,
238 libc::SOL_PACKET,
239 libc::PACKET_DROP_MEMBERSHIP,
240 (&raw const request).cast(),
241 std::mem::size_of_val(&request) as _,
242 )
243 })?;
244 Ok(())
245 }
246}
247
248impl SealedMC for SocketAddrV4 {}
249
250impl MulticastJoinable for SocketAddrV4 {
251 #[expect(
252 clippy::cast_possible_truncation,
253 reason = "an ip_mreqn is small enough for its size to fit in an u32."
254 )]
255 #[expect(
256 clippy::cast_possible_wrap,
257 reason = "interface indices will always fit in i32s"
258 )]
259 fn join_multicast(
260 &self,
261 socket: RawFd,
262 interface: InterfaceName,
263 _token: PrivateToken,
264 ) -> std::io::Result<()> {
265 let request = libc::ip_mreqn {
266 imr_multiaddr: libc::in_addr {
267 s_addr: u32::from_ne_bytes(self.ip().octets()),
268 },
269 imr_address: libc::in_addr {
270 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
271 },
272 imr_ifindex: interface
273 .get_index()
274 .ok_or(std::io::ErrorKind::InvalidInput)? as _,
275 };
276 cerr(unsafe {
279 libc::setsockopt(
280 socket,
281 libc::IPPROTO_IP,
282 libc::IP_ADD_MEMBERSHIP,
283 (&raw const request).cast(),
284 std::mem::size_of_val(&request) as _,
285 )
286 })?;
287 Ok(())
288 }
289
290 #[expect(
291 clippy::cast_possible_truncation,
292 reason = "an ip_mreqn is small enough for its size to fit in an u32."
293 )]
294 #[expect(
295 clippy::cast_possible_wrap,
296 reason = "interface indices will always fit in i32s"
297 )]
298 fn leave_multicast(
299 &self,
300 socket: RawFd,
301 interface: InterfaceName,
302 _token: PrivateToken,
303 ) -> std::io::Result<()> {
304 let request = libc::ip_mreqn {
305 imr_multiaddr: libc::in_addr {
306 s_addr: u32::from_ne_bytes(self.ip().octets()),
307 },
308 imr_address: libc::in_addr {
309 s_addr: u32::from_ne_bytes(Ipv4Addr::UNSPECIFIED.octets()),
310 },
311 imr_ifindex: interface
312 .get_index()
313 .ok_or(std::io::ErrorKind::InvalidInput)? as _,
314 };
315 cerr(unsafe {
318 libc::setsockopt(
319 socket,
320 libc::IPPROTO_IP,
321 libc::IP_DROP_MEMBERSHIP,
322 (&raw const request).cast(),
323 std::mem::size_of_val(&request) as _,
324 )
325 })?;
326 Ok(())
327 }
328}
329
330impl SealedMC for SocketAddrV6 {}
331
332impl MulticastJoinable for SocketAddrV6 {
333 #[expect(
334 clippy::cast_possible_truncation,
335 reason = "an ipv6_mreq is small enough for its size to fit in an u32."
336 )]
337 fn join_multicast(
338 &self,
339 socket: RawFd,
340 interface: InterfaceName,
341 _token: PrivateToken,
342 ) -> std::io::Result<()> {
343 let request = libc::ipv6_mreq {
344 ipv6mr_multiaddr: libc::in6_addr {
345 s6_addr: self.ip().octets(),
346 },
347 ipv6mr_interface: interface
348 .get_index()
349 .ok_or(std::io::ErrorKind::InvalidInput)? as _,
350 };
351 cerr(unsafe {
354 libc::setsockopt(
355 socket,
356 libc::IPPROTO_IPV6,
357 libc::IPV6_ADD_MEMBERSHIP,
358 (&raw const request).cast(),
359 std::mem::size_of_val(&request) as _,
360 )
361 })?;
362 Ok(())
363 }
364
365 #[expect(
366 clippy::cast_possible_truncation,
367 reason = "an ipv6_mreq is small enough for its size to fit in an u32."
368 )]
369 fn leave_multicast(
370 &self,
371 socket: RawFd,
372 interface: InterfaceName,
373 _token: PrivateToken,
374 ) -> std::io::Result<()> {
375 let request = libc::ipv6_mreq {
376 ipv6mr_multiaddr: libc::in6_addr {
377 s6_addr: self.ip().octets(),
378 },
379 ipv6mr_interface: interface
380 .get_index()
381 .ok_or(std::io::ErrorKind::InvalidInput)? as _,
382 };
383 cerr(unsafe {
386 libc::setsockopt(
387 socket,
388 libc::IPPROTO_IPV6,
389 libc::IPV6_DROP_MEMBERSHIP,
390 (&raw const request).cast(),
391 std::mem::size_of_val(&request) as _,
392 )
393 })?;
394 Ok(())
395 }
396}