1#![warn(missing_docs)]
9
10use core::mem;
43use std::fmt;
44use std::io::Error;
45use std::os::unix::io::RawFd;
46use std::time::Duration;
47
48use mctp::{
49 Eid, MsgIC, MsgType, Result, Tag, TagValue, MCTP_ADDR_ANY, MCTP_TAG_OWNER,
50};
51
52const AF_MCTP: libc::sa_family_t = 45;
54#[repr(C)]
55#[allow(non_camel_case_types)]
56struct sockaddr_mctp {
57 smctp_family: libc::sa_family_t,
58 __smctp_pad0: u16,
59 smctp_network: u32,
60 smctp_addr: u8,
61 smctp_type: u8,
62 smctp_tag: u8,
63 __smctp_pad1: u8,
64}
65
66pub const MCTP_NET_ANY: u32 = 0x00;
68
69pub struct MctpSockAddr(sockaddr_mctp);
71
72impl MctpSockAddr {
73 pub fn new(eid: u8, net: u32, typ: u8, tag: u8) -> Self {
76 MctpSockAddr(sockaddr_mctp {
77 smctp_family: AF_MCTP,
78 __smctp_pad0: 0,
79 smctp_network: net,
80 smctp_addr: eid,
81 smctp_type: typ,
82 smctp_tag: tag,
83 __smctp_pad1: 0,
84 })
85 }
86
87 fn zero() -> Self {
88 Self::new(0, MCTP_NET_ANY, 0, 0)
89 }
90
91 fn as_raw(&self) -> (*const libc::sockaddr, libc::socklen_t) {
92 (
93 &self.0 as *const sockaddr_mctp as *const libc::sockaddr,
94 mem::size_of::<sockaddr_mctp>() as libc::socklen_t,
95 )
96 }
97
98 fn as_raw_mut(&mut self) -> (*mut libc::sockaddr, libc::socklen_t) {
99 (
100 &mut self.0 as *mut sockaddr_mctp as *mut libc::sockaddr,
101 mem::size_of::<sockaddr_mctp>() as libc::socklen_t,
102 )
103 }
104}
105
106impl fmt::Debug for MctpSockAddr {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(
109 f,
110 "McptSockAddr(family={}, net={}, addr={}, type={}, tag={})",
111 self.0.smctp_family,
112 self.0.smctp_network,
113 self.0.smctp_addr,
114 self.0.smctp_type,
115 self.0.smctp_tag
116 )
117 }
118}
119
120fn tag_from_smctp(to: u8) -> Tag {
122 let t = TagValue(to & !MCTP_TAG_OWNER);
123 if to & MCTP_TAG_OWNER == 0 {
124 Tag::Unowned(t)
125 } else {
126 Tag::Owned(t)
127 }
128}
129
130fn tag_to_smctp(tag: &Tag) -> u8 {
132 let to_bit = if tag.is_owner() { MCTP_TAG_OWNER } else { 0 };
133 tag.tag().0 | to_bit
134}
135
136fn last_os_error() -> mctp::Error {
138 mctp::Error::Io(Error::last_os_error())
139}
140
141pub struct MctpSocket(RawFd);
143
144impl Drop for MctpSocket {
145 fn drop(&mut self) {
146 unsafe { libc::close(self.0) };
147 }
148}
149
150impl MctpSocket {
151 pub fn new() -> Result<Self> {
154 let rc = unsafe {
155 libc::socket(
156 AF_MCTP.into(),
157 libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
158 0,
159 )
160 };
161 if rc < 0 {
162 return Err(last_os_error());
163 }
164 Ok(MctpSocket(rc))
165 }
166
167 pub fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, MctpSockAddr)> {
173 let mut addr = MctpSockAddr::zero();
174 let (addr_ptr, mut addr_len) = addr.as_raw_mut();
175 let buf_ptr = buf.as_mut_ptr() as *mut libc::c_void;
176 let buf_len = buf.len() as libc::size_t;
177
178 let rc = unsafe {
179 libc::recvfrom(self.0, buf_ptr, buf_len, 0, addr_ptr, &mut addr_len)
180 };
181
182 if rc < 0 {
183 Err(last_os_error())
184 } else {
185 Ok((rc as usize, addr))
186 }
187 }
188
189 pub fn sendto(&self, buf: &[u8], addr: &MctpSockAddr) -> Result<usize> {
194 let (addr_ptr, addr_len) = addr.as_raw();
195 let buf_ptr = buf.as_ptr() as *const libc::c_void;
196 let buf_len = buf.len() as libc::size_t;
197
198 let rc = unsafe {
199 libc::sendto(self.0, buf_ptr, buf_len, 0, addr_ptr, addr_len)
200 };
201
202 if rc < 0 {
203 Err(last_os_error())
204 } else {
205 Ok(rc as usize)
206 }
207 }
208
209 pub fn bind(&self, addr: &MctpSockAddr) -> Result<()> {
211 let (addr_ptr, addr_len) = addr.as_raw();
212
213 let rc = unsafe { libc::bind(self.0, addr_ptr, addr_len) };
214
215 if rc < 0 {
216 Err(last_os_error())
217 } else {
218 Ok(())
219 }
220 }
221
222 pub fn set_read_timeout(&self, dur: Option<Duration>) -> Result<()> {
226 #![allow(deprecated)]
228
229 let dur = dur.unwrap_or(Duration::ZERO);
230 let tv = libc::timeval {
231 tv_sec: dur.as_secs() as libc::time_t,
232 tv_usec: dur.subsec_micros() as libc::suseconds_t,
233 };
234 let rc = unsafe {
235 libc::setsockopt(
236 self.0,
237 libc::SOL_SOCKET,
238 libc::SO_RCVTIMEO,
239 (&tv as *const libc::timeval) as *const libc::c_void,
240 std::mem::size_of::<libc::timeval>() as libc::socklen_t,
241 )
242 };
243
244 if rc < 0 {
245 Err(last_os_error())
246 } else {
247 Ok(())
248 }
249 }
250
251 pub fn read_timeout(&self) -> Result<Option<Duration>> {
255 #![allow(deprecated)]
259
260 let mut tv = std::mem::MaybeUninit::<libc::timeval>::uninit();
261 let mut tv_len =
262 std::mem::size_of::<libc::timeval>() as libc::socklen_t;
263 let rc = unsafe {
264 libc::getsockopt(
265 self.0,
266 libc::SOL_SOCKET,
267 libc::SO_RCVTIMEO,
268 tv.as_mut_ptr() as *mut libc::c_void,
269 &mut tv_len as *mut libc::socklen_t,
270 )
271 };
272
273 if rc < 0 {
274 Err(last_os_error())
275 } else {
276 let tv = unsafe { tv.assume_init() };
277 if tv.tv_sec < 0 || tv.tv_usec < 0 {
278 return Err(mctp::Error::Other);
280 }
281
282 if tv.tv_sec == 0 && tv.tv_usec == 0 {
283 Ok(None)
284 } else {
285 Ok(Some(
286 Duration::from_secs(tv.tv_sec as u64)
287 + Duration::from_micros(tv.tv_usec as u64),
288 ))
289 }
290 }
291 }
292}
293
294impl std::os::fd::AsRawFd for MctpSocket {
295 fn as_raw_fd(&self) -> RawFd {
296 self.0
297 }
298}
299
300pub struct MctpLinuxReq {
302 eid: Eid,
303 net: u32,
304 sock: MctpSocket,
305 sent: bool,
306}
307
308impl MctpLinuxReq {
309 pub fn new(eid: Eid, net: Option<u32>) -> Result<Self> {
311 let net = net.unwrap_or(MCTP_NET_ANY);
312 Ok(Self {
313 eid,
314 net,
315 sock: MctpSocket::new()?,
316 sent: false,
317 })
318 }
319
320 pub fn as_socket(&mut self) -> &mut MctpSocket {
322 &mut self.sock
323 }
324
325 pub fn net(&self) -> Option<u32> {
327 if self.net == MCTP_NET_ANY {
328 None
329 } else {
330 Some(self.net)
331 }
332 }
333}
334
335impl mctp::ReqChannel for MctpLinuxReq {
336 fn send_vectored(
341 &mut self,
342 typ: MsgType,
343 ic: MsgIC,
344 bufs: &[&[u8]],
345 ) -> Result<()> {
346 let typ_ic = mctp::encode_type_ic(typ, ic);
347 let addr = MctpSockAddr::new(
348 self.eid.0,
349 self.net,
350 typ_ic,
351 mctp::MCTP_TAG_OWNER,
352 );
353 let concat = bufs
355 .iter()
356 .flat_map(|b| b.iter().cloned())
357 .collect::<Vec<u8>>();
358 self.sock.sendto(&concat, &addr)?;
359 self.sent = true;
360 Ok(())
361 }
362
363 fn recv<'f>(
364 &mut self,
365 buf: &'f mut [u8],
366 ) -> Result<(MsgType, MsgIC, &'f mut [u8])> {
367 if !self.sent {
368 return Err(mctp::Error::BadArgument);
369 }
370 let (sz, addr) = self.sock.recvfrom(buf)?;
371 let src = Eid(addr.0.smctp_addr);
372 let (typ, ic) = mctp::decode_type_ic(addr.0.smctp_type);
373 if src != self.eid {
374 return Err(mctp::Error::Other);
376 }
377 Ok((typ, ic, &mut buf[..sz]))
378 }
379
380 fn remote_eid(&self) -> Eid {
381 self.eid
382 }
383}
384
385pub struct MctpLinuxListener {
387 sock: MctpSocket,
388 net: u32,
389 typ: MsgType,
390}
391
392impl MctpLinuxListener {
393 pub fn new(typ: MsgType, net: Option<u32>) -> Result<Self> {
398 let sock = MctpSocket::new()?;
399 let net = net.unwrap_or(MCTP_NET_ANY);
401 let addr = MctpSockAddr::new(
402 MCTP_ADDR_ANY.0,
403 net,
404 typ.0,
405 mctp::MCTP_TAG_OWNER,
406 );
407 sock.bind(&addr)?;
408 Ok(Self { sock, net, typ })
409 }
410
411 pub fn as_socket(&mut self) -> &mut MctpSocket {
413 &mut self.sock
414 }
415
416 pub fn net(&self) -> Option<u32> {
418 if self.net == MCTP_NET_ANY {
419 None
420 } else {
421 Some(self.net)
422 }
423 }
424}
425
426impl mctp::Listener for MctpLinuxListener {
427 type RespChannel<'a> = MctpLinuxResp<'a>;
428
429 fn recv<'f>(
430 &mut self,
431 buf: &'f mut [u8],
432 ) -> Result<(MsgType, MsgIC, &'f mut [u8], MctpLinuxResp<'_>)> {
433 let (sz, addr) = self.sock.recvfrom(buf)?;
434 let src = Eid(addr.0.smctp_addr);
435 let (typ, ic) = mctp::decode_type_ic(addr.0.smctp_type);
436 let tag = tag_from_smctp(addr.0.smctp_tag);
437 if let Tag::Unowned(_) = tag {
438 return Err(mctp::Error::InternalError);
440 }
441 if typ != self.typ {
442 return Err(mctp::Error::InternalError);
444 }
445 let ep = MctpLinuxResp {
446 eid: src,
447 tv: tag.tag(),
448 listener: self,
449 typ,
450 };
451 Ok((typ, ic, &mut buf[..sz], ep))
452 }
453}
454
455pub struct MctpLinuxResp<'a> {
457 eid: Eid,
458 tv: TagValue,
460 listener: &'a MctpLinuxListener,
461 typ: MsgType,
462}
463
464impl mctp::RespChannel for MctpLinuxResp<'_> {
465 type ReqChannel = MctpLinuxReq;
466
467 fn send_vectored(&mut self, ic: MsgIC, bufs: &[&[u8]]) -> Result<()> {
472 let typ_ic = mctp::encode_type_ic(self.typ, ic);
473 let tag = tag_to_smctp(&Tag::Unowned(self.tv));
474 let addr =
475 MctpSockAddr::new(self.eid.0, self.listener.net, typ_ic, tag);
476 let concat = bufs
478 .iter()
479 .flat_map(|b| b.iter().cloned())
480 .collect::<Vec<u8>>();
481 self.listener.sock.sendto(&concat, &addr)?;
482 Ok(())
483 }
484
485 fn remote_eid(&self) -> Eid {
486 self.eid
487 }
488
489 fn req_channel(&self) -> Result<Self::ReqChannel> {
490 MctpLinuxReq::new(self.eid, Some(self.listener.net))
491 }
492}
493
494#[derive(Debug)]
506pub struct MctpAddr {
507 eid: Eid,
508 net: Option<u32>,
509}
510
511impl std::str::FromStr for MctpAddr {
512 type Err = String;
513
514 fn from_str(s: &str) -> std::result::Result<MctpAddr, String> {
515 let mut parts = s.split(',');
516
517 let p1 = parts.next();
518 let p2 = parts.next();
519
520 let (net_str, eid_str) = match (p1, p2) {
521 (Some(n), Some(e)) => (Some(n), e),
522 (Some(e), None) => (None, e),
523 _ => return Err("invalid MCTP address format".to_string()),
524 };
525
526 const HEX_PREFIX: &str = "0x";
527 const HEX_PREFIX_LEN: usize = HEX_PREFIX.len();
528
529 let eid = if eid_str.to_ascii_lowercase().starts_with(HEX_PREFIX) {
530 u8::from_str_radix(&eid_str[HEX_PREFIX_LEN..], 16)
531 } else {
532 eid_str.parse()
533 }
534 .map_err(|e| e.to_string())?;
535 let eid = Eid(eid);
536
537 let net: Option<u32> = match net_str {
538 Some(n) => Some(
539 n.parse()
540 .map_err(|e: std::num::ParseIntError| e.to_string())?,
541 ),
542 None => None,
543 };
544
545 Ok(MctpAddr { net, eid })
546 }
547}
548
549impl MctpAddr {
550 pub fn eid(&self) -> Eid {
552 self.eid
553 }
554
555 pub fn net(&self) -> u32 {
558 self.net.unwrap_or(MCTP_NET_ANY)
559 }
560
561 pub fn create_endpoint(&self) -> Result<MctpLinuxReq> {
563 MctpLinuxReq::new(self.eid, self.net)
564 }
565
566 pub fn create_listener(&self, typ: MsgType) -> Result<MctpLinuxListener> {
571 MctpLinuxListener::new(typ, self.net)
572 }
573}