1use crate::ip::{self, addrobjname_to_addrobj};
4use crate::ndpd::disable_autoconf;
5use crate::sys::{
6 self, addr_family_t, dld_ioc_macaddrget_t, dld_ioc_macprop_t,
7 dld_macaddrinfo_t, mac_prop_id_t_MAC_PROP_MTU, DLDIOC_GETMACPROP,
8 DLDIOC_MACADDRGET, SIMNET_IOC_INFO, SIMNET_IOC_MODIFY,
9};
10use crate::{Error, IpInfo, IpNet, IpState, LinkFlags};
11use libc::{free, malloc};
12use libc::{
13 sockaddr_in, sockaddr_in6, sockaddr_storage, AF_INET, AF_INET6, AF_UNSPEC,
14};
15use num_enum::TryFromPrimitive;
16use rusty_doors::{door_call_slice, door_callp};
17use socket2::{Domain, Socket, Type};
18use std::collections::BTreeMap;
19use std::convert::TryFrom;
20use std::ffi::CStr;
21use std::fs::File;
22use std::mem::size_of;
23use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
24use std::os::raw::{c_char, c_int, c_void};
25use std::os::unix::io::{AsRawFd, RawFd};
26use std::ptr;
27use tracing::{debug, warn};
28
29macro_rules! ioctl {
30 ($fd:expr, $req:expr, $arg:expr) => {
31 ioctl($fd.as_raw_fd(), ($req) as i32, $arg, stringify!($req))
32 };
33}
34
35macro_rules! ioctl_ro {
36 ($fd:expr, $req:expr, $arg:expr) => {
37 ioctl_ro($fd.as_raw_fd(), ($req) as i32, $arg, stringify!($req))
38 };
39}
40
41macro_rules! ioctl_num {
42 ($fd:expr, $req:expr) => {
43 ioctl(
44 $fd.as_raw_fd(),
45 ($req) as i32,
46 ptr::null_mut::<c_void>(),
47 stringify!($req),
48 )
49 };
50 ($fd:expr, $req:expr, $arg:expr) => {
51 ioctl(
52 $fd.as_raw_fd(),
53 ($req) as i32,
54 ($arg) as usize as *mut c_void,
55 stringify!($req),
56 )
57 };
58}
59
60unsafe fn ioctl_ro<T: ?Sized>(
61 fd: RawFd,
62 cmd: i32,
63 data: *const T,
64 cmd_str: &'static str,
65) -> Result<i32, Error> {
66 ioctl(fd, cmd, data as *mut T, cmd_str)
67}
68
69#[cfg(target_os = "illumos")]
70unsafe fn ioctl<T: ?Sized>(
71 fd: RawFd,
72 cmd: i32,
73 data: *mut T,
74 cmd_str: &'static str,
75) -> Result<i32, Error> {
76 match libc::ioctl(fd, cmd, data as *mut c_void) {
77 -1 => Err(Error::Ioctl(format!(
78 "ioctl({fd}, {cmd_str}): {}",
79 std::io::Error::last_os_error(),
80 ))),
81 other => Ok(other),
82 }
83}
84
85#[cfg(not(target_os = "illumos"))]
86unsafe fn ioctl<T: ?Sized>(
87 fd: RawFd,
88 _cmd: i32,
89 _data: *mut T,
90 cmd_str: &'static str,
91) -> Result<i32, Error> {
92 Err(Error::Ioctl(format!(
97 "ioctl({fd}, {cmd_str}): illumos required"
98 )))
99}
100
101#[repr(C)]
102struct GetMacAddrIoc {
103 get: dld_ioc_macaddrget_t,
104 info: dld_macaddrinfo_t,
105}
106
107fn dld_fd() -> Result<File, Error> {
108 std::fs::OpenOptions::new()
109 .read(true)
110 .write(true)
111 .open("/dev/dld")
112 .map_err(Error::Io)
113}
114
115#[repr(C)]
116struct SimnetModifyIoc {
117 link_id: u32,
118 peer_link_id: u32,
119 flags: u32,
120}
121
122#[repr(C)]
123pub(crate) struct SimnetInfoIoc {
124 pub(crate) link_id: u32,
125 pub(crate) peer_link_id: u32,
126 pub(crate) typ: u32,
127 pub(crate) mac_len: u32,
128 pub(crate) flags: u32,
129 pub(crate) mac_addr: [u8; sys::MAXMACADDRLEN as usize],
130}
131
132pub(crate) fn get_simnet_info(link_id: u32) -> Result<SimnetInfoIoc, Error> {
133 let fd = dld_fd()?;
134
135 unsafe {
136 let mut arg = SimnetInfoIoc {
137 link_id,
138 peer_link_id: 0,
139 typ: 0,
140 mac_len: 0,
141 flags: 0,
142 mac_addr: [0; sys::MAXMACADDRLEN as usize],
143 };
144
145 ioctl!(fd, SIMNET_IOC_INFO, &mut arg)?;
146
147 Ok(arg)
148 }
149}
150
151pub(crate) fn connect_simnet_peers(
152 link_id: u32,
153 peer_link_id: u32,
154) -> Result<(), Error> {
155 let fd = dld_fd()?;
156
157 unsafe {
158 let arg = SimnetModifyIoc {
159 link_id,
160 peer_link_id,
161 flags: 0,
162 };
163
164 ioctl_ro!(fd, SIMNET_IOC_MODIFY, &arg)?;
165 Ok(())
166 }
167}
168
169#[repr(C)]
170pub(crate) struct TfportInfoIoc {
171 pub(crate) link_id: u32,
172 pub(crate) pktsrc_id: u32,
173 pub(crate) port: u16,
174 pub(crate) mac_len: u32,
175 pub(crate) mac_addr: [u8; sys::ETHERADDRL as usize],
176}
177
178pub(crate) fn get_tfport_info(link_id: u32) -> Result<TfportInfoIoc, Error> {
179 let fd = dld_fd()?;
180
181 unsafe {
182 let mut arg = TfportInfoIoc {
183 link_id,
184 pktsrc_id: 0,
185 port: 0,
186 mac_len: 0,
187 mac_addr: [0; sys::ETHERADDRL as usize],
188 };
189
190 ioctl!(fd, sys::TFPORT_IOC_INFO, &mut arg)?;
191
192 Ok(arg)
193 }
194}
195
196#[allow(dead_code)]
197#[repr(i32)]
198pub enum VnicMacAddrType {
199 Unknown = -1,
200 Fixed,
201 Random,
202 Factory,
203 Auto,
204 Primary,
205 Vrid,
206}
207
208#[allow(dead_code)]
209#[repr(i32)]
210pub enum MacPriorityLevel {
211 Low,
212 Medium,
213 High,
214 Reset,
215}
216
217#[allow(dead_code)]
218#[repr(i32)]
219pub enum MacCpuMode {
220 Invalid = 0,
221 Fanout = 1,
222 Cpus,
223}
224
225const MRP_NCPUS: usize = 256;
226
227#[repr(C)]
228pub struct MacTxIntrCPUs {
229 pub mtc_intr_cpu: [i32; MRP_NCPUS],
230 pub mtc_retargeted_cpu: [i32; MRP_NCPUS],
231}
232impl Default for MacTxIntrCPUs {
233 fn default() -> Self {
234 Self {
235 mtc_intr_cpu: [0; MRP_NCPUS],
236 mtc_retargeted_cpu: [0; MRP_NCPUS],
237 }
238 }
239}
240
241#[repr(C)]
242pub struct MacCPUsProps {
243 pub ncpus: u32,
244 pub cpus: [u32; MRP_NCPUS],
245 pub rx_fanout_cnt: u32,
246 pub rx_fanout_cpus: [u32; MRP_NCPUS],
247 pub rx_pollid: u32,
248 pub rx_workerid: u32,
249 pub rx_intr_cpu: i32,
250 pub tx_fanout_cpus: [i32; MRP_NCPUS],
251 pub tx_intr_cpus: MacTxIntrCPUs,
252 pub fanout_mode: MacCpuMode,
253}
254impl Default for MacCPUsProps {
255 fn default() -> Self {
256 Self {
257 ncpus: 0,
258 cpus: [0; MRP_NCPUS],
259 rx_fanout_cnt: 0,
260 rx_fanout_cpus: [0; MRP_NCPUS],
261 rx_pollid: 0,
262 rx_workerid: 0,
263 rx_intr_cpu: 0,
264 tx_fanout_cpus: [0; MRP_NCPUS],
265 tx_intr_cpus: MacTxIntrCPUs::default(),
266 fanout_mode: MacCpuMode::Fanout,
267 }
268 }
269}
270
271#[repr(C)]
272pub union In6Data {
273 pub parts: [u16; 8],
274 pub align: u32,
275}
276
277impl Copy for In6Data {}
278#[allow(clippy::non_canonical_clone_impl)]
279impl Clone for In6Data {
280 fn clone(&self) -> Self {
281 unsafe {
282 let mut c = In6Data { parts: [0; 8] };
283 for (i, x) in self.parts.iter().enumerate() {
284 c.parts[i] = *x;
285 }
286 c
287 }
288 }
289}
290
291#[derive(Copy, Clone)]
292#[repr(C)]
293pub struct In6Addr {
294 pub data: In6Data,
295}
296
297impl Default for In6Addr {
298 fn default() -> Self {
299 Self {
300 data: In6Data { parts: [0; 8] },
301 }
302 }
303}
304
305#[derive(Default, Copy, Clone)]
306#[repr(C)]
307pub struct MacIpaddr {
308 pub version: u32,
309 pub addr: In6Addr,
310 pub netmask: u8,
311}
312
313#[allow(dead_code)]
314#[derive(Copy, Clone)]
315#[repr(i32)]
316pub enum MacDhcpCidFrom {
317 Typed = 1,
318 Hex,
319 Str,
320}
321
322#[derive(Copy, Clone)]
323#[repr(C)]
324pub struct MacDhcpCid {
325 pub id: [u8; MPT_MAXCIDLEN],
326 pub len: u32,
327 pub form: MacDhcpCidFrom,
328}
329impl Default for MacDhcpCid {
330 fn default() -> Self {
331 Self {
332 id: [0; MPT_MAXCIDLEN],
333 len: 0,
334 form: MacDhcpCidFrom::Typed,
335 }
336 }
337}
338
339const MPT_MAXCID: usize = 32;
340const MPT_MAXIPADDR: usize = 32;
341const MPT_MAXCIDLEN: usize = 256;
342
343#[repr(C)]
344pub struct MacProtect {
345 pub types: u32,
346 pub ipaddrcnt: u32,
347 pub ipaddrs: [MacIpaddr; MPT_MAXIPADDR],
348 pub cidcnt: u32,
349 pub cids: [MacDhcpCid; MPT_MAXCID],
350}
351
352impl Default for MacProtect {
353 fn default() -> Self {
354 Self {
355 types: 0,
356 ipaddrcnt: 0,
357 ipaddrs: [MacIpaddr::default(); MPT_MAXIPADDR],
358 cidcnt: 0,
359 cids: [MacDhcpCid::default(); MPT_MAXCID],
360 }
361 }
362}
363
364#[repr(C, packed(4))]
365pub struct MacResourceProps {
366 pub mask: u32,
367 pub maxbw: u64,
368 pub priority: MacPriorityLevel,
369 pub cpus: MacCPUsProps,
370 pub protect: MacProtect,
371 pub nrxrings: u32,
372 pub ntxrings: u32,
373 pub pool: [u8; sys::MAXPATHLEN as usize],
374}
375
376impl Default for MacResourceProps {
377 fn default() -> Self {
378 Self {
379 mask: 0,
380 maxbw: 0,
381 priority: MacPriorityLevel::Low,
382 cpus: MacCPUsProps::default(),
383 protect: MacProtect::default(),
384 nrxrings: 0,
385 ntxrings: 0,
386 pool: [0; sys::MAXPATHLEN as usize],
387 }
388 }
389}
390
391#[repr(C)]
392pub struct VnicInfoIoc {
393 pub vnic_id: u32,
394 pub link_id: u32,
395 pub mac_addr_type: VnicMacAddrType,
396 pub mac_len: u32,
397 pub mac_addr: [u8; sys::MAXMACADDRLEN as usize],
398 pub mac_slot: u32,
399 pub mac_prefix_len: u32,
400 pub vid: u16,
401 pub vrid: u32,
402 pub af: u32,
403 pub force: bool,
404 pub resource_props: MacResourceProps,
405}
406
407impl Default for VnicInfoIoc {
408 fn default() -> Self {
409 Self {
410 vnic_id: 0,
411 link_id: 0,
412 mac_addr_type: VnicMacAddrType::Unknown,
413 mac_len: 0,
414 mac_addr: [0; sys::MAXMACADDRLEN as usize],
415 mac_slot: 0,
416 mac_prefix_len: 0,
417 vid: 0,
418 vrid: 0,
419 af: 0,
420 force: false,
421 resource_props: MacResourceProps::default(),
422 }
423 }
424}
425
426pub(crate) fn get_vnic_info(link_id: u32) -> Result<VnicInfoIoc, Error> {
427 let fd = dld_fd()?;
428
429 unsafe {
430 let mut arg = VnicInfoIoc {
431 vnic_id: link_id,
432 ..Default::default()
433 };
434 ioctl!(fd, sys::VNIC_IOC_INFO, &mut arg)?;
435 Ok(arg)
436 }
437}
438
439pub(crate) fn get_macaddr(linkid: u32) -> Result<[u8; 6], Error> {
440 let fd = dld_fd()?;
441
442 unsafe {
443 let mut arg = GetMacAddrIoc {
444 get: dld_ioc_macaddrget_t {
445 dig_linkid: linkid,
446 dig_count: 1,
447 dig_size: size_of::<dld_macaddrinfo_t>() as u32,
448 },
449 info: dld_macaddrinfo_t {
450 dmi_slot: 0,
451 dmi_flags: 0,
452 dmi_addrlen: 0,
453 dmi_addr: [0; 20],
454 dmi_client_name: [0; 256],
455 dma_client_linkid: 0,
456 },
457 };
458
459 ioctl!(fd, DLDIOC_MACADDRGET, &mut arg)?;
460
461 let mut res: [u8; 6] = [0; 6];
462 res[..6].clone_from_slice(&arg.info.dmi_addr[..6]);
463
464 Ok(res)
465 }
466}
467
468pub(crate) fn get_mtu(linkid: u32) -> Result<u32, Error> {
469 let fd = dld_fd()?;
470
471 let mut arg = dld_ioc_macprop_t::<u32> {
472 pr_flags: 0,
473 pr_linkid: linkid,
474 pr_num: mac_prop_id_t_MAC_PROP_MTU,
475 pr_perm_flags: 0,
476 pr_name: [0; 256],
477 pr_valsize: size_of::<u32>() as u32,
478 pr_val: 0,
479 };
480 arg.pr_name[..3].copy_from_slice(&b"mtu".map(|u| u as i8));
481
482 unsafe {
483 ioctl!(fd, DLDIOC_GETMACPROP, &mut arg)?;
484 }
485
486 Ok(arg.pr_val)
487}
488
489pub(crate) fn get_ipaddrs() -> Result<BTreeMap<String, Vec<IpInfo>>, Error> {
490 let mut result: BTreeMap<String, Vec<IpInfo>> = BTreeMap::new();
491
492 unsafe {
493 let s4 = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
496 let s6 = Socket::new(Domain::IPV6, Type::DGRAM, None)?;
497
498 let mut lifn = sys::lifnum {
501 lifn_family: AF_UNSPEC as u16,
502 lifn_flags: 0,
503 lifn_count: 0,
504 };
505
506 ioctl!(s4, sys::SIOCGLIFNUM, &mut lifn)?;
507
508 let mut ifs: Vec<sys::lifreq> = Vec::new();
511 ifs.resize(lifn.lifn_count as usize, sys::lifreq::new());
512
513 let mut lifc = sys::lifconf {
514 lifc_family: AF_UNSPEC as u16,
515 lifc_flags: (sys::LIFC_NOXMIT
516 | sys::LIFC_TEMPORARY
517 | sys::LIFC_ALLZONES
518 | sys::LIFC_UNDER_IPMP) as i32,
519 lifc_len: lifn.lifn_count * size_of::<sys::lifreq>() as i32,
520 lifc_lifcu: sys::lifconf_lifcu {
521 lifcu_buf: ifs.as_mut_ptr() as *mut c_char,
522 },
523 };
524
525 ioctl!(s4, sys::SIOCGLIFCONF, &mut lifc)?;
526
527 for x in ifs.iter_mut() {
528 let info = match ipaddr_info(x, &s4, &s6) {
529 Ok(None) => continue,
530 Ok(Some(info)) => info,
531 Err(e) => {
532 warn!("{:?}", e);
533 continue;
534 }
535 };
536
537 match result.get_mut(&info.ifname) {
538 None => {
539 result.insert(info.ifname.clone(), vec![info]);
540 }
541 Some(v) => v.push(info),
542 };
543 }
544 }
545
546 Ok(result)
547}
548
549pub(crate) fn ipaddr_exists(objname: impl AsRef<str>) -> Result<bool, Error> {
550 let f = File::open("/etc/svc/volatile/ipadm/ipmgmt_door")?;
551
552 let mut req: ip::IpmgmtAobjopArg = unsafe { std::mem::zeroed() };
554 req.cmd = ip::IpmgmtCmd::AobjName2Addrobj;
555 for (i, c) in objname.as_ref().chars().enumerate() {
556 req.objname[i] = c as c_char;
557 }
558
559 let mut response: *mut ip::IpmgmtAobjopRval = unsafe {
560 malloc(std::mem::size_of::<ip::IpmgmtAobjopRval>())
561 as *mut ip::IpmgmtAobjopRval
562 };
563
564 let respp: *mut ip::IpmgmtAobjopRval = door_callp(
565 f.as_raw_fd(),
566 req,
567 ptr::NonNull::new(&mut response).unwrap(), );
569 let resp = unsafe { *respp };
570 if resp.err != 0 {
571 if resp.err == sys::IpadmStatusT::NotFound as i32 {
572 return Ok(false);
573 }
574 return Ok(false);
577 }
578
579 if (resp.flags & LinkFlags::Active as u32) == 0 {
580 return Ok(false);
581 }
582
583 Ok(true)
584}
585
586pub(crate) fn delete_ipaddr(objname: impl AsRef<str>) -> Result<(), Error> {
587 let ifname = objname.as_ref().split('/').collect::<Vec<&str>>()[0];
588
589 let f = File::open("/etc/svc/volatile/ipadm/ipmgmt_door")?;
590
591 let mut req: ip::IpmgmtAobjopArg = unsafe { std::mem::zeroed() };
593 req.cmd = ip::IpmgmtCmd::AobjName2Addrobj;
594 for (i, c) in objname.as_ref().chars().enumerate() {
595 req.objname[i] = c as c_char;
596 }
597
598 let mut response: *mut ip::IpmgmtAobjopRval = unsafe {
599 malloc(std::mem::size_of::<ip::IpmgmtAobjopRval>())
600 as *mut ip::IpmgmtAobjopRval
601 };
602
603 let respp: *mut ip::IpmgmtAobjopRval = door_callp(
604 f.as_raw_fd(),
605 req,
606 ptr::NonNull::new(&mut response).unwrap(), );
608 let resp = unsafe { *respp };
609 if resp.err != 0 {
610 return Err(Error::Ipmgmtd(sys::err_string(resp.err)));
611 }
612
613 let af = resp.family as i32;
614
615 let mut ior: sys::lifreq = unsafe { std::mem::zeroed() };
617 let mut i = 0;
618 for c in resp.ifname {
619 if c == 0 {
620 break;
621 }
622 ior.lifr_name[i] = c;
623 i += 1;
624 }
625 if resp.lnum != 0 {
626 ior.lifr_name[i] = ':' as c_char;
627 i += 1;
628 for c in resp.lnum.to_string().chars() {
629 ior.lifr_name[i] = c as c_char;
630 i += 1;
631 }
632 }
633
634 let sock = match resp.family as i32 {
635 libc::AF_INET => Socket::new(Domain::IPV4, Type::DGRAM, None)?,
636 libc::AF_INET6 => {
637 if resp.lnum == 0 {
638 match crate::ndpd::delete_addrs(ifname) {
639 Ok(_) => {}
640 Err(e) => println!("ndp delete addrs: {}", e),
641 };
642 }
643 Socket::new(Domain::IPV6, Type::DGRAM, None)?
644 }
645 _ => {
646 return Err(Error::BadArgument(format!(
647 "unknown address family: {}",
648 resp.family
649 )));
650 }
651 };
652
653 if resp.lnum == 0 {
654 unsafe {
655 ior.lifr_lifru.lifru_flags &= !(sys::IFF_UP as u64);
656 ioctl_ro!(sock, sys::SIOCSLIFFLAGS, &ior)?;
657
658 if af == libc::AF_INET {
659 let sin = &mut ior.lifr_lifru.lifru_addr
660 as *mut sockaddr_storage
661 as *mut sockaddr_in;
662 (*sin).sin_family = AF_INET as addr_family_t;
663 (*sin).sin_addr.s_addr = 0;
664 } else if af == libc::AF_INET6 {
665 let sin6 = &mut ior.lifr_lifru.lifru_addr
666 as *mut sockaddr_storage
667 as *mut sockaddr_in6;
668 (*sin6).sin6_family = AF_INET6 as addr_family_t;
669 (*sin6).sin6_addr.s6_addr = [0u8; 16];
670 }
671
672 ioctl_ro!(sock, sys::SIOCSLIFADDR, &ior)?;
673 }
674 } else {
675 unsafe { ioctl_ro!(sock, sys::SIOCLIFREMOVEIF, &ior)? };
676 }
677
678 let mut ia: ip::IpmgmtAddrArg = unsafe { std::mem::zeroed() };
679 ia.cmd = ip::IpmgmtCmd::ResetAddr;
680 ia.flags = sys::IPMGMT_ACTIVE;
681 ia.lnum = resp.lnum as u32;
682 for (i, c) in objname.as_ref().chars().enumerate() {
683 ia.objname[i] = c as c_char;
684 }
685
686 unsafe {
688 let mut response: *mut ip::IpmgmtRval =
689 malloc(std::mem::size_of::<ip::IpmgmtRval>())
690 as *mut ip::IpmgmtRval;
691
692 let resp: *mut ip::IpmgmtRval = door_callp(
693 f.as_raw_fd(),
694 ia,
695 ptr::NonNull::new(&mut response).unwrap(), );
697
698 if (*resp).err != 0 {
699 free(response as *mut c_void);
700 return Err(Error::Ipmgmtd(format!(
701 "reset address: {}",
702 sys::err_string((*resp).err)
703 )));
704 }
705 free(response as *mut c_void);
706 }
707
708 unplumb_for_af(ifname, af)?;
709
710 Ok(())
711}
712
713pub(crate) fn create_ipaddr(
715 name: impl AsRef<str>,
716 addr: IpNet,
717) -> Result<(), Error> {
718 let parts: Vec<&str> = name.as_ref().split('/').collect();
719 if parts.len() < 2 {
720 return Err(Error::BadArgument(
721 "Expected <ifname>/<addrname>".to_string(),
722 ));
723 }
724 let ifname = parts[0];
725
726 let (iff, sock) = match addr {
727 IpNet::V4(_) => {
728 let s4 = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
729 (sys::IFF_IPV4.into(), s4)
730 }
731 IpNet::V6(_) => {
732 let s6 = Socket::new(Domain::IPV6, Type::DGRAM, None)?;
733 (sys::IFF_IPV6.into(), s6)
734 }
735 };
736
737 match is_plumbed_for_af(ifname, &sock) {
738 true => {}
739 false => {
740 plumb_for_af(ifname, iff)?;
741 }
742 };
743
744 create_ip_addr_static(ifname, name.as_ref(), addr, &sock)
745}
746
747fn is_plumbed_for_af(name: &str, sock: &Socket) -> bool {
748 let mut req = crate::sys::lifreq::new();
749 for (i, c) in name.chars().enumerate() {
750 req.lifr_name[i] = c as c_char;
751 }
752 unsafe { ioctl!(sock, sys::SIOCGLIFFLAGS, &mut req).is_ok() }
753}
754
755fn plumb_for_af(name: &str, mut ifflags: u64) -> Result<(), Error> {
756 let ip_h = match dlpi::open(name, dlpi::sys::DLPI_NOATTACH) {
760 Ok(h) => dlpi::DropHandle(h),
761 Err(e) => {
762 return Err(Error::Ioctl(format!(
763 "DLPI open: {}: {}",
764 e,
765 sys::errno_string()
766 )));
767 }
768 };
769 let ip_fd = match ip_h.fd() {
770 Ok(fd) => fd,
771 Err(e) => {
772 return Err(Error::Ioctl(format!("DLPI IP fd: {}", e)));
773 }
774 };
775
776 let ip_mod_name = CStr::from_bytes_with_nul(sys::IP_MOD_NAME).unwrap();
778 unsafe { ioctl_ro!(ip_fd, sys::I_PUSH, ip_mod_name.as_ptr())? };
779
780 let spec = parse_ifspec(name);
781
782 if (ifflags & (sys::IFF_IPV6 as u64)) != 0 {
784 ifflags |= sys::IFF_NOLINKLOCAL;
785 };
786 let mut req = sys::lifreq::new();
787 req.lifr_lifru.lifru_flags = ifflags;
788 req.lifr_lifru1.lifru_ppa = spec.ppa;
789
790 for (i, c) in name.chars().enumerate() {
791 req.lifr_name[i] = c as c_char;
792 }
793 unsafe { ioctl_ro!(ip_fd, sys::SIOCSLIFNAME, &req)? };
794
795 unsafe { ioctl!(ip_fd, sys::SIOCGLIFFLAGS, &mut req)? };
797 ifflags = unsafe { req.lifr_lifru.lifru_flags };
798
799 let mux_fd = if (ifflags & (sys::IFF_IPV6 as u64)) != 0 {
800 File::open("/dev/udp6")?
801 } else {
802 File::open("/dev/udp")?
803 };
804
805 loop {
807 if unsafe { ioctl_num!(mux_fd, sys::I_POP).is_err() } {
808 break;
809 }
810 }
811
812 let arp_mod_name = CStr::from_bytes_with_nul(sys::ARP_MOD_NAME).unwrap();
814 unsafe { ioctl_ro!(mux_fd, sys::I_PUSH, arp_mod_name)? };
815
816 if (ifflags & ((sys::IFF_NOARP | sys::IFF_IPV6) as u64)) != 0 {
818 unsafe {
819 let res = ioctl_num!(mux_fd, sys::I_PLINK, ip_fd);
820
821 return match res {
822 Ok(ip_muxid) => {
823 let mut req = sys::lifreq::new();
825 for (i, c) in name.chars().enumerate() {
826 req.lifr_name[i] = c as c_char;
827 }
828 req.lifr_lifru.lif_muxid[0] = ip_muxid;
829 ioctl!(mux_fd, sys::SIOCSLIFMUXID, &mut req)?;
830
831 Ok(disable_autoconf(name)?)
832 }
833 Err(e) => Err(e),
834 };
835 }
836 }
837
838 let arp_h = match dlpi::open(name, dlpi::sys::DLPI_NOATTACH) {
840 Ok(h) => dlpi::DropHandle(h),
841 Err(e) => return Err(Error::Ioctl(format!("DLPI ARP open: {}", e))),
842 };
843
844 let arp_fd = match arp_h.fd() {
845 Ok(fd) => fd,
846 Err(e) => {
847 return Err(Error::Ioctl(format!("DLPI ARP fd: {}", e)));
848 }
849 };
850
851 unsafe { ioctl_ro!(arp_fd, sys::I_PUSH, arp_mod_name)? };
852
853 let mut req = sys::lifreq::new();
854 req.lifr_lifru.lifru_flags = ifflags;
855 req.lifr_lifru1.lifru_ppa = spec.ppa;
856 for (i, c) in name.chars().enumerate() {
857 req.lifr_name[i] = c as c_char;
858 }
859 let arg = &mut req as *mut sys::lifreq as *mut c_char;
860 str_ioctl(
861 arp_fd,
862 sys::SIOCSLIFNAME as c_int,
863 arg,
864 size_of::<sys::lifreq>() as c_int,
865 )?;
866
867 let ip_muxid = unsafe { ioctl_num!(mux_fd, sys::I_PLINK, ip_fd)? };
869
870 unsafe {
871 let arp_muxid = match ioctl_num!(mux_fd, sys::I_PLINK, arp_fd) {
872 Ok(muxid) => muxid,
873 Err(e) => {
874 if let Err(e) = ioctl_num!(mux_fd, sys::I_PUNLINK, ip_muxid) {
876 println!("punlink failed: {}", e);
877 }
878 return Err(e);
879 }
880 };
881
882 let mut req = sys::lifreq::new();
884 for (i, c) in name.chars().enumerate() {
885 req.lifr_name[i] = c as c_char;
886 }
887 req.lifr_lifru.lif_muxid[0] = ip_muxid;
888 req.lifr_lifru.lif_muxid[1] = arp_muxid;
889 ioctl!(mux_fd, sys::SIOCSLIFMUXID, &mut req)?;
890 }
891
892 Ok(())
893}
894
895fn unplumb_for_af(name: &str, af: i32) -> Result<(), Error> {
896 let mux_fd = if af == libc::AF_INET6 {
897 File::open("/dev/udp6")?
898 } else if af == libc::AF_INET {
899 File::open("/dev/udp")?
900 } else {
901 return Err(Error::BadArgument(format!(
902 "Invalid address family: {}",
903 af
904 )));
905 };
906
907 let mut req = sys::lifreq::new();
908 for (i, c) in name.chars().enumerate() {
909 req.lifr_name[i] = c as c_char;
910 }
911
912 let (ip_muxid, arp_muxid) = unsafe {
913 ioctl!(mux_fd, sys::SIOCGLIFMUXID, &mut req)?;
914 (req.lifr_lifru.lif_muxid[0], req.lifr_lifru.lif_muxid[1])
915 };
916
917 loop {
919 if unsafe { ioctl_num!(mux_fd, sys::I_POP).is_err() } {
920 break;
921 }
922 }
923
924 let arp_mod_name = CStr::from_bytes_with_nul(sys::ARP_MOD_NAME).unwrap();
926 unsafe { ioctl_ro!(mux_fd, sys::I_PUSH, arp_mod_name)? };
927
928 unsafe {
929 if arp_muxid != 0 && arp_muxid != -1 {
930 if let Err(e) = ioctl_num!(mux_fd, sys::I_PUNLINK, arp_muxid) {
931 println!("punlink failed: {}", e);
932 }
933 }
934 if let Err(e) = ioctl_num!(mux_fd, sys::I_PUNLINK, ip_muxid) {
935 println!("punlink failed: {}", e);
936 }
937 }
938
939 Ok(())
940}
941
942fn str_ioctl(
943 s: c_int,
944 cmd: c_int,
945 buf: *mut c_char,
946 buflen: c_int,
947) -> Result<c_int, Error> {
948 let mut ioc = sys::strioctl::new();
949 ioc.ic_cmd = cmd;
950 ioc.ic_timeout = 0;
951 ioc.ic_len = buflen;
952 ioc.ic_dp = buf;
953
954 unsafe { ioctl!(s, sys::I_STR, &mut ioc) }
955}
956
957#[allow(dead_code)]
958pub struct IfSpec {
959 pub ppa: u32,
960 pub lun: u32,
961 pub lunvalid: bool,
962 pub devnm: [u8; sys::LIFNAMSIZ],
963}
964
965fn parse_ifspec(ifname: &str) -> IfSpec {
973 let parts: Vec<&str> = ifname.split(':').collect();
974 let (lun, lunvalid) = {
975 match parts.len() {
976 0 => (0, false),
977 1 => (0, false),
978 _ => match parts[1].parse::<u32>() {
979 Ok(i) => (i, true),
980 Err(_) => (0, false),
981 },
982 }
983 };
984 let name = parts[0].trim_end_matches(char::is_numeric);
985 let ppa = {
986 let s = &parts[0][..name.len()];
987 s.parse::<u32>().unwrap_or(0)
988 };
989
990 let mut devnm = [0u8; sys::LIFNAMSIZ];
991 for (i, c) in name.chars().enumerate() {
992 devnm[i] = c as u8;
993 }
994
995 IfSpec {
996 ppa,
997 lun,
998 lunvalid,
999 devnm,
1000 }
1001}
1002
1003pub fn get_ipaddr_info(name: &str) -> Result<IpInfo, Error> {
1005 unsafe {
1006 let (_, _, af, mut ifname, lifnum) = addrobjname_to_addrobj(name)
1007 .map_err(|e| Error::Ioctl(format!("get addrobj: {}", e)))?;
1008
1009 let s4 = Socket::new(Domain::IPV4, Type::DGRAM, None)?;
1010 let s6 = Socket::new(Domain::IPV6, Type::DGRAM, None)?;
1011
1012 if lifnum > 0 {
1013 ifname = format!("{}:{}", ifname, lifnum);
1014 }
1015
1016 let mut req = sys::lifreq::new();
1017 for (i, c) in ifname.chars().enumerate() {
1018 req.lifr_name[i] = c as c_char;
1019 }
1020
1021 let ss = match af as i32 {
1022 libc::AF_INET => &s4,
1023 libc::AF_INET6 => {
1024 let sin6 = &mut req.lifr_lifru.lifru_addr
1025 as *mut sockaddr_storage
1026 as *mut sockaddr_in6;
1027 (*sin6).sin6_family = AF_INET6 as addr_family_t;
1028 &s6
1029 }
1030 _ => return Err(Error::NotFound(name.into())),
1031 };
1032
1033 ioctl!(ss, sys::SIOCGLIFADDR, &mut req)?;
1035
1036 match ipaddr_info(&mut req, &s4, &s6) {
1037 Ok(Some(info)) => Ok(info),
1038 Ok(None) => Err(Error::NotFound(name.into())),
1039 Err(e) => Err(e),
1040 }
1041 }
1042}
1043
1044unsafe fn ipaddr_info(
1045 x: &mut sys::lifreq,
1046 s4: &Socket,
1047 s6: &Socket,
1048) -> Result<Option<IpInfo>, Error> {
1049 let _name = CStr::from_ptr(x.lifr_name.as_ptr());
1050 let name = match _name.to_str() {
1051 Ok(s) => s,
1052 Err(_) => {
1053 return Err(Error::Ioctl("interface name conversion".into()));
1054 }
1055 };
1056
1057 let sa = x.lifr_lifru.lifru_addr;
1058 let addr = match sockaddr2ipaddr(&sa) {
1059 Some(addr) => addr,
1060 None => {
1061 return Ok(None);
1064 }
1065 };
1066
1067 let ss = match sa.ss_family as i32 {
1068 AF_INET => &s4,
1069 AF_INET6 => &s6,
1070 _ => return Err(Error::Ioctl("unknown address family".to_string())),
1071 };
1072
1073 ioctl!(ss, sys::SIOCGLIFINDEX, x as *mut sys::lifreq)?;
1075 let idx = x.lifr_lifru.lifru_index;
1076
1077 ioctl!(ss, sys::SIOCGLIFNETMASK, x as *mut sys::lifreq)?;
1079 let _mask = x.lifr_lifru.lifru_addr;
1080 let mask = match sockaddr2ipaddr(&_mask) {
1081 Some(mask) => mask,
1082 None => {
1083 return Err(Error::Ioctl(
1084 "socaddr to ipaddr conversion".to_string(),
1085 ))
1086 }
1087 };
1088
1089 ioctl!(ss, sys::SIOCGLIFFLAGS, x as *mut sys::lifreq)?;
1092 let flags = x.lifr_lifru.lifru_flags;
1093 let state = {
1094 if flags & sys::IFF_UP as u64 != 0 {
1095 IpState::OK
1096 } else if flags & sys::IFF_RUNNING as u64 != 0 {
1097 ioctl!(ss, sys::SIOCGLIFDADSTATE, x as *mut sys::lifreq)?;
1098 if x.lifr_lifru.lifru_dadstate
1099 == sys::glif_dad_state_t_DAD_IN_PROGRESS
1100 {
1101 IpState::Tentative
1102 } else {
1103 IpState::OK
1104 }
1105 } else {
1106 IpState::Inaccessible
1107 }
1108 };
1109
1110 #[allow(clippy::unnecessary_cast)]
1111 Ok(Some(IpInfo {
1112 addr,
1113 state,
1114 ifname: name.to_string(),
1115 index: idx,
1116 mask: ip_mask(mask),
1117 family: sa.ss_family as u16,
1118 }))
1119}
1120
1121pub(crate) fn enable_v6_link_local(
1122 ifname: &str,
1123 addrname: &str,
1124) -> Result<(), Error> {
1125 let objname = format!("{}/{}", ifname, addrname);
1126
1127 let sock = Socket::new(Domain::IPV6, Type::DGRAM, None)?;
1128
1129 if !is_plumbed_for_af(ifname, &sock) {
1130 plumb_for_af(ifname, sys::IFF_IPV6.into())?;
1131 }
1132
1133 create_ip_addr_linklocal(&sock, ifname, &objname)
1134}
1135
1136pub fn create_ip_addr_linklocal(
1137 sock: &Socket,
1138 ifname: &str,
1139 objname: &str,
1140) -> Result<(), Error> {
1141 let mut req = sys::lifreq::default();
1142 for (i, c) in ifname.chars().enumerate() {
1143 req.lifr_name[i] = c as c_char;
1144 }
1145 let (lifnum, kernel_ifname) = create_logical_interface_v6(sock, &mut req)?;
1146
1147 let ll_template = [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1148
1149 unsafe {
1150 req.lifr_lifru.lifru_addr = std::mem::zeroed();
1151
1152 let sin6 = &mut req.lifr_lifru.lifru_addr as *mut sockaddr_storage
1153 as *mut sockaddr_in6;
1154
1155 (*sin6).sin6_family = AF_INET6 as addr_family_t;
1157 (*sin6).sin6_addr.s6_addr = [
1158 0b11111111, 0b11000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1159 ];
1160 ioctl_ro!(sock, sys::SIOCSLIFNETMASK, &req)?;
1161
1162 (*sin6).sin6_addr.s6_addr = ll_template;
1165 ioctl_ro!(sock, sys::SIOCSLIFPREFIX, &req)?;
1166
1167 let f = File::open("/etc/svc/volatile/ipadm/ipmgmt_door")?;
1170
1171 add_if_to_ipmgmtd(
1173 objname,
1174 ifname,
1175 AF_INET6 as u16,
1176 lifnum,
1177 &f,
1178 ip::AddrType::Ipv6Addrconf,
1179 )?;
1180
1181 let mut req: sys::lifreq = std::mem::zeroed();
1183 for (i, c) in kernel_ifname.chars().enumerate() {
1184 req.lifr_name[i] = c as c_char;
1185 }
1186 ioctl!(sock, sys::SIOCGLIFFLAGS, &mut req)?;
1187 req.lifr_lifru.lifru_flags |= sys::IFF_UP as u64;
1188 ioctl_ro!(sock, sys::SIOCSLIFFLAGS, &req)?;
1189
1190 let intfidlen = 0;
1191 let stateless = true;
1192 let stateful = false;
1193
1194 crate::ndpd::create_addrs(
1195 ifname, *sin6, intfidlen, stateless, stateful, objname,
1196 )
1197 .map_err(|e| Error::Ioctl(format!("ndp create addrs: {}", e)))?;
1198
1199 ipmgmtd_persist(objname, ifname, lifnum, None, &f)?;
1200 }
1201
1202 Ok(())
1203}
1204
1205fn create_logical_interface_v6(
1206 sock: &Socket,
1207 req: &mut sys::lifreq,
1208) -> Result<(i32, String), Error> {
1209 unsafe {
1210 ioctl!(sock, sys::SIOCGLIFADDR, req)?;
1212 let sin6 = &req.lifr_lifru.lifru_addr as *const sockaddr_storage
1213 as *const sockaddr_in6;
1214
1215 if (*sin6).sin6_addr.s6_addr != [0u8; 16] {
1218 ioctl!(sock, sys::SIOCLIFADDIF, req)?;
1219 }
1220
1221 let (kname, lifnum) = parse_ifname(req)?;
1222 Ok((lifnum, kname.into()))
1223 }
1224}
1225
1226fn create_logical_interface_v4(
1227 sock: &Socket,
1228 req: &mut sys::lifreq,
1229) -> Result<(i32, String), Error> {
1230 unsafe {
1231 ioctl!(sock, sys::SIOCGLIFADDR, req)?;
1233 let sin4 = &req.lifr_lifru.lifru_addr as *const sockaddr_storage
1234 as *const sockaddr_in;
1235
1236 if (*sin4).sin_addr.s_addr != 0 {
1239 ioctl!(sock, sys::SIOCLIFADDIF, req)?;
1240 }
1241
1242 let (kname, lifnum) = parse_ifname(req)?;
1243 Ok((lifnum, kname.into()))
1244 }
1245}
1246
1247fn parse_ifname(req: &sys::lifreq) -> Result<(&str, i32), Error> {
1248 let ifname =
1249 unsafe { std::ffi::CStr::from_ptr(&req.lifr_name[0]).to_str()? };
1250
1251 let parts: Vec<&str> = ifname.split(':').collect();
1252 let lifnum = match parts.len() {
1253 2 => parts[1].parse::<i32>().unwrap_or(0),
1254 _ => 0,
1255 };
1256
1257 Ok((ifname, lifnum))
1258}
1259
1260pub(crate) fn create_ip_addr_static(
1262 ifname: impl AsRef<str>,
1263 objname: impl AsRef<str>,
1264 addr: IpNet,
1265 sock: &Socket,
1266) -> Result<(), Error> {
1267 unsafe {
1268 let mut req: sys::lifreq = std::mem::zeroed();
1269 for (i, c) in ifname.as_ref().chars().enumerate() {
1270 req.lifr_name[i] = c as c_char;
1271 }
1272
1273 match addr {
1274 IpNet::V6(_) => {
1275 create_logical_interface_v6(sock, &mut req)?;
1276 }
1277 IpNet::V4(_) => {
1278 create_logical_interface_v4(sock, &mut req)?;
1279 }
1280 }
1281
1282 match addr {
1284 IpNet::V6(a) => {
1285 req.lifr_lifru.lifru_addr.ss_family = AF_INET6 as addr_family_t;
1286 let sas =
1287 &mut req.lifr_lifru.lifru_addr as *mut sockaddr_storage;
1288
1289 let sa6 = sas as *mut sockaddr_in6;
1290 (*sa6).sin6_addr.s6_addr = a.addr().octets();
1291 }
1292 IpNet::V4(a) => {
1293 req.lifr_lifru.lifru_addr.ss_family = AF_INET as addr_family_t;
1294 let sas =
1295 &mut req.lifr_lifru.lifru_addr as *mut sockaddr_storage;
1296 let sa4 = sas as *mut sockaddr_in;
1297 let addr: u32 = a.addr().into();
1298 (*sa4).sin_addr.s_addr = addr.to_be();
1299 }
1300 };
1301 ioctl_ro!(sock, sys::SIOCSLIFADDR, &req)?;
1302
1303 match addr {
1305 IpNet::V6(a) => {
1306 req.lifr_lifru.lifru_addr.ss_family = AF_INET6 as addr_family_t;
1307 let sas =
1308 &mut req.lifr_lifru.lifru_addr as *mut sockaddr_storage;
1309
1310 let sa6 = sas as *mut sockaddr_in6;
1311 (*sa6).sin6_addr.s6_addr = a.mask_addr().octets();
1312 }
1313 IpNet::V4(a) => {
1314 req.lifr_lifru.lifru_addr.ss_family = AF_INET as addr_family_t;
1315 let sas =
1316 &mut req.lifr_lifru.lifru_addr as *mut sockaddr_storage;
1317
1318 let sa4 = sas as *mut sockaddr_in;
1319 let addr: u32 = a.mask_addr().into();
1320 (*sa4).sin_addr.s_addr = addr.to_be();
1321 }
1322 };
1323 ioctl_ro!(sock, sys::SIOCSLIFNETMASK, &req)?;
1324
1325 let (kernel_ifname, lifnum) = parse_ifname(&req)?;
1327
1328 let af = match addr {
1329 IpNet::V6(_) => AF_INET6 as u16,
1330 IpNet::V4(_) => AF_INET as u16,
1331 };
1332
1333 let f = File::open("/etc/svc/volatile/ipadm/ipmgmt_door")?;
1334
1335 add_if_to_ipmgmtd(
1336 objname.as_ref(),
1337 ifname.as_ref(),
1338 af,
1339 lifnum,
1340 &f,
1341 ip::AddrType::Static,
1342 )?;
1343
1344 let mut req: sys::lifreq = std::mem::zeroed();
1346 for (i, c) in kernel_ifname.chars().enumerate() {
1347 req.lifr_name[i] = c as c_char;
1348 }
1349 req.lifr_lifru.lifru_flags |= sys::IFF_UP as u64;
1350 ioctl_ro!(sock, sys::SIOCSLIFFLAGS, &req)?;
1351
1352 ipmgmtd_persist(
1354 objname.as_ref(),
1355 ifname.as_ref(),
1356 lifnum,
1357 Some(addr),
1358 &f,
1359 )?;
1360
1361 };
1363
1364 Ok(())
1365}
1366
1367fn add_if_to_ipmgmtd(
1368 objname: &str,
1369 ifname: &str,
1370 af: u16,
1371 lifnum: i32,
1372 f: &File,
1373 kind: ip::AddrType,
1374) -> Result<(), Error> {
1375 let mut iaa: ip::IpmgmtAobjopArg = unsafe { std::mem::zeroed() };
1377 iaa.cmd = ip::IpmgmtCmd::AddrobjLookupAdd;
1378 for (i, c) in objname.chars().enumerate() {
1379 iaa.objname[i] = c as c_char;
1380 }
1381 for (i, c) in ifname.chars().enumerate() {
1382 iaa.ifname[i] = c as c_char;
1383 }
1384 iaa.family = af;
1385 iaa.atype = kind;
1386
1387 let mut response: *mut ip::IpmgmtRval = unsafe {
1388 malloc(std::mem::size_of::<ip::IpmgmtRval>()) as *mut ip::IpmgmtRval
1389 };
1390
1391 door_callp(
1392 f.as_raw_fd(),
1393 iaa,
1394 ptr::NonNull::new(&mut response).unwrap(), );
1396 unsafe { free(response as *mut c_void) };
1397
1398 iaa = unsafe { std::mem::zeroed() };
1401 iaa.cmd = ip::IpmgmtCmd::AddrobjSetLifnum;
1402 for (i, c) in objname.chars().enumerate() {
1403 iaa.objname[i] = c as c_char;
1404 }
1405 for (i, c) in ifname.chars().enumerate() {
1406 iaa.ifname[i] = c as c_char;
1407 }
1408 iaa.lnum = lifnum;
1409 let family = af;
1410 iaa.family = family;
1411 iaa.atype = kind;
1412
1413 let mut response: *mut ip::IpmgmtRval = unsafe {
1414 malloc(std::mem::size_of::<ip::IpmgmtRval>()) as *mut ip::IpmgmtRval
1415 };
1416
1417 door_callp(
1418 f.as_raw_fd(),
1419 iaa,
1420 ptr::NonNull::new(&mut response).unwrap(), );
1422 unsafe { free(response as *mut c_void) };
1423
1424 Ok(())
1425}
1426
1427fn ipmgmtd_persist(
1428 objname: &str,
1429 ifname: &str,
1430 lifnum: i32,
1431 addr: Option<IpNet>,
1432 f: &File,
1433) -> Result<(), Error> {
1434 let mut nvl = nvpair::NvList::new_unique_names();
1435 nvl.insert("_ifname", ifname)?;
1436 nvl.insert("_aobjname", objname)?;
1437 nvl.insert("_lifnum", &lifnum)?;
1438
1439 match addr {
1440 Some(addr) => {
1441 let ahname = addr.addr().to_string();
1442
1443 let mut addr_nvl = nvpair::NvList::new_unique_names();
1444 addr_nvl.insert("_aname", ahname.as_str())?;
1445 let addr_nvl_name = match addr {
1446 IpNet::V6(_) => "_ipv6addr",
1447 IpNet::V4(_) => "_ipv4addr",
1448 };
1449
1450 nvl.insert(addr_nvl_name, addr_nvl.as_ref())?;
1451 nvl.insert("up", "yes")?;
1452 }
1453 None => {
1455 let mut ll_nvl = nvpair::NvList::new_unique_names();
1456 let prefixlen: u32 = 0;
1460 ll_nvl.insert("prefixlen", &prefixlen)?;
1461 ll_nvl.insert("_stateless", "yes")?;
1462 ll_nvl.insert("_stateful", "no")?;
1463 nvl.insert("_intfid", ll_nvl.as_ref())?;
1464 }
1465 }
1466
1467 let nvl_c = nvl.as_mut_ptr();
1468 let mut nvl_buf: *mut c_char = std::ptr::null_mut();
1469 let mut nvl_sz: nvpair_sys::size_t = 0;
1470 let ret = unsafe {
1471 nvpair_sys::nvlist_pack(
1472 nvl_c,
1473 &mut nvl_buf,
1474 &mut nvl_sz,
1475 nvpair_sys::NV_ENCODE_NATIVE,
1476 0,
1477 )
1478 };
1479 if ret != 0 {
1480 return Err(Error::NvPair(format!("{}", ret)));
1481 }
1482
1483 let arg = ip::IpmgmtSetAddr {
1484 cmd: ip::IpmgmtCmd::SetAddr,
1485 flags: sys::IPMGMT_ACTIVE,
1486 nvlsize: nvl_sz as u32,
1487 };
1488
1489 let mut buf: Vec<c_char> = Vec::new();
1490
1491 let arg_bytes = unsafe {
1492 std::slice::from_raw_parts(
1493 (&arg as *const ip::IpmgmtSetAddr) as *const c_char,
1494 size_of::<ip::IpmgmtSetAddr>(),
1495 )
1496 };
1497 for c in arg_bytes {
1498 buf.push(*c);
1499 }
1500
1501 let nvl_bytes =
1502 unsafe { std::slice::from_raw_parts(nvl_buf, nvl_sz as usize) };
1503 for c in nvl_bytes {
1504 buf.push(*c);
1505 }
1506
1507 let resp: ip::IpmgmtRval = door_call_slice(f.as_raw_fd(), buf.as_slice());
1508 if resp.err != 0 {
1509 return Err(Error::Ipmgmtd(sys::err_string(resp.err)));
1510 }
1511
1512 Ok(())
1513}
1514
1515fn sockaddr2ipaddr(sa: &libc::sockaddr_storage) -> Option<IpAddr> {
1516 unsafe {
1517 match sa.ss_family as i32 {
1518 libc::AF_INET => {
1519 let sa4 = sa as *const sockaddr_storage as *const sockaddr_in;
1520 Some(IpAddr::V4(Ipv4Addr::from(u32::from_be(
1521 (*sa4).sin_addr.s_addr,
1522 ))))
1523 }
1524 libc::AF_INET6 => {
1525 let sa6 = sa as *const sockaddr_storage as *const sockaddr_in6;
1526 let a6 = IpAddr::V6(Ipv6Addr::from((*sa6).sin6_addr.s6_addr));
1527 if a6.is_unspecified() {
1528 return None;
1529 }
1530 Some(a6)
1531 }
1532 _ => None,
1533 }
1534 }
1535}
1536
1537fn ip_mask(addr: IpAddr) -> u32 {
1538 match addr {
1539 IpAddr::V4(a4) => {
1540 let i = u32::from_be_bytes(a4.octets());
1541 i.leading_ones()
1542 }
1543 IpAddr::V6(a6) => {
1544 let i = u128::from_be_bytes(a6.octets());
1545 i.leading_ones()
1546 }
1547 }
1548}
1549
1550#[repr(C)]
1551pub struct SimnetIocCreate {
1552 pub link_id: u32,
1553 pub typ: u32,
1554 pub mac_len: u32,
1555 pub flags: u32,
1556 pub mac_addr: [u8; sys::MAXMACADDRLEN as usize],
1557}
1558
1559#[allow(dead_code)]
1560#[repr(i32)]
1561pub enum VnicIocDiag {
1562 _None,
1563 MacaddrNIC,
1564 MacaddrInUse,
1565 MacaddrInvalid,
1566 MacaddrLenInvalid,
1567 MacFactorySlotInvalid,
1568 MacFactorySlotUsed,
1569 MacFactorySlotAllUsed,
1570 MacFactoryNotSup,
1571 MacPrefixInvalid,
1572 MacPrefixLenInvalid,
1573 MacMarginInvalid,
1574 NoHwRings,
1575 MacMtuInvalid,
1576}
1577
1578#[repr(C)]
1579pub struct VnicIocCreate {
1580 pub vnic_id: u32,
1581 pub link_id: u32,
1582 pub mac_addr_type: VnicMacAddrType,
1583 pub mac_len: u32,
1584 pub mac_addr: [u8; sys::MAXMACADDRLEN as usize],
1585 pub mac_prefix_len: u32,
1586 pub mac_slot: i32,
1587 pub vid: u16,
1588 pub vrid: u32,
1589 pub af: i32,
1590 pub status: u32,
1591 pub flags: u32,
1592 pub diag: VnicIocDiag,
1593 pub resource_props: MacResourceProps,
1594}
1595
1596impl Default for VnicIocCreate {
1597 fn default() -> Self {
1598 Self {
1599 vnic_id: 0,
1600 link_id: 0,
1601 mac_addr_type: VnicMacAddrType::Unknown,
1602 mac_len: 0,
1603 mac_addr: [0; sys::MAXMACADDRLEN as usize],
1604 mac_prefix_len: 0,
1605 mac_slot: -1,
1606 vid: 0,
1607 vrid: 0,
1608 af: 0,
1609 status: 0,
1610 flags: 0,
1611 diag: VnicIocDiag::_None,
1612 resource_props: MacResourceProps::default(),
1613 }
1614 }
1615}
1616
1617pub(crate) fn create_vnic(
1618 id: u32,
1619 link_id: u32,
1620 mac: Option<Vec<u8>>,
1621) -> Result<crate::LinkInfo, Error> {
1622 unsafe {
1623 let fd = dld_fd()?;
1624
1625 debug!("creating vnic with id {}", id);
1626
1627 let mut arg = VnicIocCreate {
1628 link_id,
1629 vnic_id: id,
1630 mac_addr_type: match mac {
1631 None => VnicMacAddrType::Auto,
1632 Some(_) => VnicMacAddrType::Fixed,
1633 },
1634 mac_len: match &mac {
1635 None => 0,
1636 Some(mac) => mac.len() as u32,
1637 },
1638 mac_prefix_len: match mac {
1639 None => 3,
1640 Some(_) => 0,
1641 },
1642 mac_slot: -1,
1643 vid: 0,
1644 vrid: 0,
1645 af: AF_UNSPEC,
1646 flags: 0,
1647 ..Default::default()
1648 };
1649 match mac {
1650 None => {
1651 arg.mac_addr[0] = 0x02;
1655 arg.mac_addr[1] = 0x08;
1656 arg.mac_addr[2] = 0x20;
1657 }
1658 Some(mac) => {
1659 if mac.len() > 20 {
1660 return Err(Error::BadArgument("mac too long".to_string()));
1661 }
1662 for (i, x) in mac.iter().enumerate() {
1663 arg.mac_addr[i] = *x;
1664 }
1665 }
1666 };
1667
1668 sys::clear_errno();
1669 ioctl_ro!(fd, sys::VNIC_IOC_CREATE, &arg)?;
1670
1671 crate::link::get_link(id)
1672 }
1673}
1674
1675pub(crate) fn create_simnet(
1676 id: u32,
1677 flags: crate::LinkFlags,
1678) -> Result<crate::LinkInfo, Error> {
1679 unsafe {
1680 let fd = dld_fd()?;
1681
1682 debug!("creating simnet with id {}", id);
1683
1684 let arg = SimnetIocCreate {
1685 link_id: id,
1686 typ: sys::DL_ETHER,
1687 mac_len: 0,
1688 flags: flags as u32,
1689 mac_addr: [0; sys::MAXMACADDRLEN as usize],
1690 };
1691
1692 ioctl_ro!(fd, sys::SIMNET_IOC_CREATE, &arg)?;
1693 }
1694
1695 crate::link::get_link(id)
1696}
1697
1698#[repr(C)]
1699pub struct SimnetIocDelete {
1700 link_id: u32,
1701 flags: u32,
1702}
1703
1704pub(crate) fn delete_simnet(id: u32) -> Result<(), Error> {
1705 unsafe {
1706 let fd = dld_fd()?;
1707 let arg = SimnetIocDelete {
1708 link_id: id,
1709 flags: 0,
1710 };
1711 ioctl_ro!(fd, sys::SIMNET_IOC_DELETE, &arg)?;
1712 }
1713 Ok(())
1714}
1715
1716#[repr(C)]
1717pub struct TfportIocCreate {
1718 pub link_id: u32,
1719 pub pktsrc_id: u32,
1720 pub port: u16,
1721 pub mac_len: u32,
1722 pub mac_addr: [u8; sys::ETHERADDRL as usize],
1723}
1724
1725pub(crate) fn create_tfport(
1726 link_id: u32,
1727 pktsrc_id: u32,
1728 port: u16,
1729 mac: Option<String>,
1730) -> Result<crate::LinkInfo, Error> {
1731 let (mac_len, mac_addr) = match mac {
1732 None => (0, [0; sys::ETHERADDRL as usize]),
1733 Some(mac) => {
1734 let mut mac_addr = [0; sys::ETHERADDRL as usize];
1735 let v: Vec<&str> = mac.split(':').collect();
1736
1737 if v.len() != 6 {
1738 return Err(Error::BadArgument(
1739 "invalid mac address".to_string(),
1740 ));
1741 }
1742
1743 for (i, octet) in v.iter().enumerate() {
1744 mac_addr[i] = u8::from_str_radix(octet, 16).map_err(|_| {
1745 Error::BadArgument("invalid mac address".to_string())
1746 })?;
1747 }
1748 (sys::ETHERADDRL, mac_addr)
1749 }
1750 };
1751
1752 unsafe {
1753 let fd = dld_fd()?;
1754 debug!("creating tfport with id {}", link_id);
1755 let arg = TfportIocCreate {
1756 link_id,
1757 pktsrc_id,
1758 port,
1759 mac_len,
1760 mac_addr,
1761 };
1762
1763 ioctl_ro!(fd, sys::TFPORT_IOC_CREATE, &arg)?;
1764 }
1765
1766 crate::link::get_link(link_id)
1767}
1768
1769#[repr(C)]
1770pub struct TfportIocDelete {
1771 link_id: u32,
1772}
1773
1774pub(crate) fn delete_tfport(link_id: u32) -> Result<(), Error> {
1775 unsafe {
1776 let fd = dld_fd()?;
1777 let arg = TfportIocDelete { link_id };
1778 ioctl_ro!(fd, sys::TFPORT_IOC_DELETE, &arg)?;
1779 }
1780 Ok(())
1781}
1782
1783#[repr(C)]
1784pub struct VnicIocDelete {
1785 link_id: u32,
1786}
1787
1788pub(crate) fn delete_vnic(id: u32) -> Result<(), Error> {
1789 unsafe {
1790 let fd = dld_fd()?;
1791 let arg = VnicIocDelete { link_id: id };
1792 ioctl_ro!(fd, sys::VNIC_IOC_DELETE, &arg)?;
1793 }
1794 Ok(())
1795}
1796
1797#[derive(Debug)]
1798pub struct Neighbor {
1799 pub state: NeighborState,
1800 pub l2_addr: Vec<i8>,
1801 pub flags: i32,
1802}
1803
1804impl Neighbor {
1805 pub fn is_router(&self) -> bool {
1806 (self.flags & sys::NDF_ISROUTER_ON) != 0
1807 }
1808 pub fn is_anycast(&self) -> bool {
1809 (self.flags & sys::NDF_ANYCAST_ON) != 0
1810 }
1811 pub fn is_proxy(&self) -> bool {
1812 (self.flags & sys::NDF_PROXY_ON) != 0
1813 }
1814 pub fn is_static(&self) -> bool {
1815 (self.flags & sys::NDF_STATIC) != 0
1816 }
1817}
1818
1819#[derive(TryFromPrimitive, Debug)]
1820#[repr(u8)]
1821pub enum NeighborState {
1822 Unchanged,
1823 Incomplete,
1824 Reachable,
1825 Stale,
1826 Delay,
1827 Probe,
1828 Unreachable,
1829}
1830
1831pub fn get_neighbor(ifname: &str, addr: Ipv6Addr) -> Result<Neighbor, Error> {
1832 let mut ior: sys::lifreq = unsafe { std::mem::zeroed() };
1833 let mut lifru_nd_req: sys::lif_nd_req = unsafe { std::mem::zeroed() };
1834 for (i, b) in ifname.bytes().enumerate() {
1835 ior.lifr_name[i] = b as i8;
1836 }
1837
1838 let sin6 = &mut lifru_nd_req.lnr_addr as *mut sockaddr_storage
1839 as *mut sockaddr_in6;
1840 unsafe {
1841 (*sin6).sin6_family = AF_INET6 as addr_family_t;
1842 (*sin6).sin6_addr.s6_addr = addr.octets();
1843 }
1844
1845 ior.lifr_lifru = sys::lifreq_ru { lifru_nd_req };
1846
1847 let sock = Socket::new(Domain::IPV6, Type::DGRAM, None)?;
1848
1849 unsafe { ioctl!(sock, sys::SIOCLIFGETND, &mut ior)? };
1850
1851 Ok(Neighbor {
1852 state: NeighborState::try_from(unsafe {
1853 ior.lifr_lifru.lifru_nd_req.lnr_state_create
1854 })?,
1855 l2_addr: unsafe { ior.lifr_lifru.lifru_nd_req.lnr_hdw_addr.to_vec() },
1856 flags: unsafe { ior.lifr_lifru.lifru_nd_req.lnr_flags },
1857 })
1858}
1859
1860pub fn get_ifnum(ifname: &str, af: u16) -> Result<i32, Error> {
1861 let s = match af as i32 {
1862 AF_INET => Socket::new(Domain::IPV4, Type::DGRAM, None)?,
1863 AF_INET6 => Socket::new(Domain::IPV6, Type::DGRAM, None)?,
1864 _ => return Err(Error::BadArgument("unknown address family".into())),
1865 };
1866 let mut ior: sys::lifreq = unsafe { std::mem::zeroed() };
1867 for (i, b) in ifname.as_bytes().iter().enumerate() {
1868 ior.lifr_name[i] = *b as i8;
1869 }
1870 unsafe {
1871 ioctl!(s, sys::SIOCGLIFINDEX, &mut ior)?;
1872 }
1873 Ok(unsafe { ior.lifr_lifru.lifru_index })
1874}