1#![warn(missing_docs)]
8
9use std::num::NonZeroU64;
10
11pub trait TryToSockaddrLl {
13 fn try_to_sockaddr_ll(&self) -> Option<libc::sockaddr_ll>;
16}
17
18impl TryToSockaddrLl for socket2::SockAddr {
19 fn try_to_sockaddr_ll(&self) -> Option<libc::sockaddr_ll> {
20 if self.family()
21 != libc::sa_family_t::try_from(libc::AF_PACKET)
22 .expect("libc::AF_PACKET should fit in libc::sa_family_t")
23 {
24 return None;
25 }
26
27 let mut sockaddr_ll = libc::sockaddr_ll {
28 sll_family: 0,
29 sll_protocol: 0,
30 sll_ifindex: 0,
31 sll_hatype: 0,
32 sll_pkttype: 0,
33 sll_halen: 0,
34 sll_addr: [0; 8],
35 };
36
37 let len = usize::try_from(self.len()).expect("socklen_t should fit in usize");
38 assert!(std::mem::size_of::<libc::sockaddr_ll>() >= len);
42
43 unsafe {
45 let sockaddr: *const libc::sockaddr = self.as_ptr();
46 let sockaddr_ll: *mut libc::sockaddr_ll = &mut sockaddr_ll;
47 std::ptr::copy_nonoverlapping(sockaddr.cast::<u8>(), sockaddr_ll.cast::<u8>(), len);
48 }
49
50 Some(sockaddr_ll)
51 }
52}
53
54pub trait IntoSockAddr {
56 fn into_sockaddr(self) -> socket2::SockAddr;
58}
59
60impl IntoSockAddr for libc::sockaddr_ll {
61 fn into_sockaddr(self) -> socket2::SockAddr {
62 if libc::c_int::from(self.sll_family) != libc::AF_PACKET {
63 panic!("sll_family != AF_PACKET, got {:?}", self.sll_family);
64 }
65 let sockaddr_ll_len = libc::socklen_t::try_from(std::mem::size_of::<libc::sockaddr_ll>())
66 .expect("size of sockaddr_ll should always fit in socklen_t");
67
68 let ((), sock_addr) = unsafe {
72 socket2::SockAddr::try_init(|sockaddr_storage, len_ptr| {
73 (sockaddr_storage as *mut libc::sockaddr_ll).write(self);
74 len_ptr.write(sockaddr_ll_len);
75 Ok(())
76 })
77 }
78 .expect(
79 "initialize socket address should always succeed because \
80 our init fn always returns Ok(())",
81 );
82 sock_addr
83 }
84}
85
86pub struct EthernetSockaddr {
88 pub interface_id: Option<NonZeroU64>,
90 pub addr: net_types::ethernet::Mac,
92 pub protocol: packet_formats::ethernet::EtherType,
94}
95
96impl From<EthernetSockaddr> for libc::sockaddr_ll {
97 fn from(value: EthernetSockaddr) -> Self {
98 let EthernetSockaddr { interface_id, addr, protocol } = value;
99
100 let mut sll_addr = [0; 8];
101 let addr_bytes = addr.bytes();
102 sll_addr[..addr_bytes.len()].copy_from_slice(&addr_bytes);
103
104 let ethertype = u16::from(protocol);
105 libc::sockaddr_ll {
106 sll_family: libc::AF_PACKET
107 .try_into()
108 .expect("libc::AF_PACKET should fit in sll_family field"),
109 sll_ifindex: interface_id
110 .map_or(0, NonZeroU64::get)
111 .try_into()
112 .expect("interface_id should fit in sll_ifindex field"),
113 sll_protocol: ethertype.to_be(),
115 sll_halen: addr_bytes
116 .len()
117 .try_into()
118 .expect("hardware address length should fit in sll_halen field"),
119 sll_addr,
120 sll_hatype: 0, sll_pkttype: 0, }
123 }
124}
125
126pub struct PureIpSockaddr {
128 pub interface_id: Option<NonZeroU64>,
130 pub protocol: net_types::ip::IpVersion,
132}
133
134impl From<PureIpSockaddr> for libc::sockaddr_ll {
135 fn from(value: PureIpSockaddr) -> Self {
136 let PureIpSockaddr { interface_id, protocol } = value;
137 libc::sockaddr_ll {
138 sll_family: libc::AF_PACKET
139 .try_into()
140 .expect("libc::AF_PACKET should fit in sll_family field"),
141 sll_ifindex: interface_id
142 .map_or(0, NonZeroU64::get)
143 .try_into()
144 .expect("interface_id should fit in sll_ifindex field"),
145 sll_protocol: sockaddr_ll_ip_protocol(protocol),
146 sll_halen: 0,
148 sll_addr: [0, 0, 0, 0, 0, 0, 0, 0],
149 sll_hatype: 0, sll_pkttype: 0, }
152 }
153}
154
155pub fn sockaddr_ll_ip_protocol(ip_version: net_types::ip::IpVersion) -> u16 {
157 let protocol = match ip_version {
158 net_types::ip::IpVersion::V4 => libc::ETH_P_IP,
159 net_types::ip::IpVersion::V6 => libc::ETH_P_IPV6,
160 };
161 u16::try_from(protocol).expect("protocol should fit in a u16 field").to_be()
163}
164
165#[cfg(test)]
166mod test {
167 use super::*;
168 use proptest::prelude::*;
169
170 #[derive(proptest_derive::Arbitrary, Debug)]
171 struct SockaddrLl {
172 sll_family: u16,
173 sll_protocol: u16,
174 sll_ifindex: i32,
175 sll_hatype: u16,
176 sll_pkttype: u8,
177 sll_halen: u8,
178 sll_addr: [u8; 8],
179 }
180
181 impl From<SockaddrLl> for libc::sockaddr_ll {
182 fn from(
183 SockaddrLl {
184 sll_family,
185 sll_protocol,
186 sll_ifindex,
187 sll_hatype,
188 sll_pkttype,
189 sll_halen,
190 sll_addr,
191 }: SockaddrLl,
192 ) -> Self {
193 libc::sockaddr_ll {
194 sll_family,
195 sll_protocol,
196 sll_ifindex,
197 sll_hatype,
198 sll_pkttype,
199 sll_halen,
200 sll_addr,
201 }
202 }
203 }
204
205 proptest! {
206 #![proptest_config(ProptestConfig {
207 failure_persistence: Some(
208 Box::<proptest::test_runner::MapFailurePersistence>::default()
209 ),
210 ..ProptestConfig::default()
211 })]
212
213 #[test]
214 fn roundtrip(addr in any::<SockaddrLl>().prop_map(|addr| SockaddrLl {
215 sll_family: u16::try_from(libc::AF_PACKET).unwrap(),
216 ..addr
217 })) {
218 let sockaddr_ll_start = libc::sockaddr_ll::from(addr);
219 let sockaddr = sockaddr_ll_start.into_sockaddr();
220 let sockaddr_ll_end = sockaddr.try_to_sockaddr_ll().unwrap();
221 prop_assert_eq!(sockaddr_ll_start, sockaddr_ll_end);
222 }
223 }
224
225 #[test]
226 fn not_sockaddr_ll_returns_none() {
227 let sockaddr = socket2::SockAddr::from(net_declare::std_socket_addr!("192.168.0.1:8080"));
228 assert_eq!(sockaddr.try_to_sockaddr_ll(), None);
229 }
230}