1use std::os::unix::io::AsRawFd;
2
3use bytes::Bytes;
4use ndn_packet::Name;
5use ndn_transport::{Face, FaceError, FaceId, FaceKind};
6use tokio::io::unix::AsyncFd;
7
8use super::af_packet::{
9 MacAddr, PacketRing, get_ifindex, make_sockaddr_ll, open_packet_socket, setup_packet_ring,
10};
11use super::radio::RadioFaceMetadata;
12use crate::NDN_ETHERTYPE;
13
14pub struct NamedEtherFace {
29 id: FaceId,
30 pub node_name: Name,
32 peer_mac: MacAddr,
34 iface: String,
36 ifindex: i32,
38 pub radio: RadioFaceMetadata,
40 socket: AsyncFd<std::os::unix::io::OwnedFd>,
42 ring: PacketRing,
44}
45
46impl NamedEtherFace {
47 pub fn new(
53 id: FaceId,
54 node_name: Name,
55 peer_mac: MacAddr,
56 iface: impl Into<String>,
57 radio: RadioFaceMetadata,
58 ) -> std::io::Result<Self> {
59 let iface = iface.into();
60
61 let probe_fd = unsafe {
63 libc::socket(
64 libc::AF_PACKET,
65 libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
66 NDN_ETHERTYPE.to_be() as i32,
67 )
68 };
69 if probe_fd == -1 {
70 return Err(std::io::Error::last_os_error());
71 }
72 let ifindex = {
73 let idx = get_ifindex(probe_fd, &iface);
74 unsafe {
75 libc::close(probe_fd);
76 }
77 idx?
78 };
79
80 let fd = open_packet_socket(ifindex, NDN_ETHERTYPE)?;
81
82 let ring = setup_packet_ring(fd.as_raw_fd())?;
84 let socket = AsyncFd::new(fd)?;
85
86 Ok(Self {
87 id,
88 node_name,
89 peer_mac,
90 iface,
91 ifindex,
92 radio,
93 socket,
94 ring,
95 })
96 }
97
98 pub fn set_peer_mac(&mut self, mac: MacAddr) {
100 self.peer_mac = mac;
101 }
102
103 pub fn peer_mac(&self) -> MacAddr {
105 self.peer_mac
106 }
107
108 pub fn iface(&self) -> &str {
110 &self.iface
111 }
112}
113
114impl Face for NamedEtherFace {
115 fn id(&self) -> FaceId {
116 self.id
117 }
118 fn kind(&self) -> FaceKind {
119 FaceKind::Ethernet
120 }
121
122 async fn recv(&self) -> Result<Bytes, FaceError> {
123 loop {
124 if let Some(pkt) = self.ring.try_pop_rx() {
125 return Ok(pkt);
126 }
127 let mut guard = self.socket.readable().await?;
128 guard.clear_ready();
129 }
130 }
131
132 async fn send(&self, pkt: Bytes) -> Result<(), FaceError> {
133 loop {
135 if self.ring.try_push_tx(&pkt) {
136 break;
137 }
138 let mut guard = self.socket.writable().await?;
139 guard.clear_ready();
140 }
141
142 let dst = make_sockaddr_ll(self.ifindex, &self.peer_mac, NDN_ETHERTYPE);
144 let fd = self.socket.get_ref().as_raw_fd();
145 let ret = unsafe {
146 libc::sendto(
147 fd,
148 std::ptr::null(),
149 0,
150 0,
151 &dst as *const libc::sockaddr_ll as *const libc::sockaddr,
152 std::mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t,
153 )
154 };
155 if ret == -1 {
156 let err = std::io::Error::last_os_error();
157 if err.kind() != std::io::ErrorKind::WouldBlock {
158 return Err(FaceError::Io(err));
159 }
160 }
161 Ok(())
162 }
163}
164
165#[cfg(test)]
168mod tests {
169 use super::*;
170 use std::str::FromStr;
171
172 #[tokio::test]
174 async fn new_fails_without_cap_net_raw() {
175 let name = Name::from_str("/test/node").unwrap();
176 let result = NamedEtherFace::new(
177 FaceId(1),
178 name,
179 MacAddr::BROADCAST,
180 "lo",
181 RadioFaceMetadata::default(),
182 );
183 if let Err(e) = result {
184 let raw = e.raw_os_error().unwrap_or(0);
185 assert!(
186 raw == libc::EPERM || raw == libc::EACCES,
187 "expected EPERM or EACCES, got: {e}"
188 );
189 }
190 }
191
192 #[tokio::test]
194 #[ignore = "requires CAP_NET_RAW"]
195 async fn loopback_roundtrip() {
196 let name = Name::from_str("/test/node").unwrap();
197 let lo_mac = MacAddr::new([0; 6]);
198 let face_a = NamedEtherFace::new(
199 FaceId(1),
200 name.clone(),
201 lo_mac,
202 "lo",
203 RadioFaceMetadata::default(),
204 )
205 .expect("need CAP_NET_RAW");
206 let face_b =
207 NamedEtherFace::new(FaceId(2), name, lo_mac, "lo", RadioFaceMetadata::default())
208 .expect("need CAP_NET_RAW");
209
210 let pkt = Bytes::from_static(b"\x05\x03\x01\x02\x03");
211 face_a.send(pkt.clone()).await.unwrap();
212
213 let received = tokio::time::timeout(std::time::Duration::from_secs(2), face_b.recv())
214 .await
215 .expect("timed out")
216 .unwrap();
217
218 assert_eq!(received, pkt);
219 }
220}