1use crate::sys;
4use crate::{Error, LinkClass, LinkFlags, LinkInfo};
5use libc::ENOENT;
6use rusty_doors::door_call;
7use std::fs::File;
8use std::os::unix::io::AsRawFd;
9use std::str;
10use tracing::{debug, warn};
11
12const DATALINK_ANY_MEDIATYPE: u64 = 0x01 << 32;
13
14#[derive(Debug)]
15#[repr(u32)]
16pub enum DlmgmtCmd {
17 DLScreate = 1,
18 DLSgetattr = 2,
19 DLSdestroy = 3,
20 GetName = 4,
21 GetLinkId = 5,
22 GetNext = 6,
23 DLSupdate = 7,
24 LinkPropInit = 8,
25 SetZoneId = 9,
26 CreateLinkId = 128,
27 DestroyLinkId = 129,
28 RemapLinkId = 130,
29 CreateConf = 131,
30 OpenConf = 132,
31 WriteConf = 133,
32 UpLinkId = 134,
33 SetAttr = 135,
34 UnsetAttr = 136,
35 RemoveConf = 137,
36 DestroyConf = 138,
37 GetAttr = 139,
38 GetConfSnapshot = 140,
39 ZoneBoot = 141,
40 ZoneHalt = 142,
41}
42
43#[derive(Debug)]
44#[repr(C)]
45pub struct DlmgmtGetNext {
46 pub cmd: u32,
47 pub linkid: u32,
48 pub class: LinkClass,
49 pub flags: LinkFlags,
50 pub media: u64,
51}
52
53impl Default for DlmgmtGetNext {
54 fn default() -> Self {
55 DlmgmtGetNext {
56 cmd: DlmgmtCmd::GetNext as u32,
57 linkid: 0,
58 class: LinkClass::All,
59 flags: LinkFlags::ActivePersistent,
60 media: DATALINK_ANY_MEDIATYPE,
61 }
62 }
63}
64
65#[derive(Debug)]
66#[repr(C)]
67pub struct DlmgmtGetName {
68 pub cmd: u32,
69 pub linkid: u32,
70}
71
72impl Default for DlmgmtGetName {
73 fn default() -> Self {
74 DlmgmtGetName {
75 cmd: DlmgmtCmd::GetName as u32,
76 linkid: 0,
77 }
78 }
79}
80
81#[derive(Debug)]
82#[repr(C)]
83pub struct DlmgmtNameRetval {
84 pub err: u32,
85 pub link: [u8; 32],
86 pub class: LinkClass,
87 pub media: u32,
88 pub flags: LinkFlags,
89}
90
91impl Default for DlmgmtNameRetval {
92 fn default() -> Self {
93 DlmgmtNameRetval {
94 err: 0,
95 link: [0; 32],
96 class: LinkClass::Phys,
97 media: 0,
98 flags: LinkFlags::ActivePersistent,
99 }
100 }
101}
102
103#[derive(Debug)]
104#[repr(C)]
105pub struct DlmgmtLinkRetval {
106 pub err: u32,
107 pub linkid: u32,
108 pub flags: LinkFlags,
109 pub class: LinkClass,
110 pub media: u32,
111 pub padding: u32,
112}
113
114impl Default for DlmgmtLinkRetval {
115 fn default() -> Self {
116 DlmgmtLinkRetval {
117 err: 0,
118 linkid: 0,
119 flags: LinkFlags::ActivePersistent,
120 class: LinkClass::All,
121 media: 0,
122 padding: 0,
123 }
124 }
125}
126
127pub(crate) fn get_links() -> Result<Vec<LinkInfo>, Error> {
128 let mut result = Vec::new();
129 let mut linkid = 0;
130 let f = dlmgmt_door_fd()?;
131
132 loop {
133 let request = DlmgmtGetNext {
134 linkid,
135 ..Default::default()
136 };
137 let response: DlmgmtLinkRetval = door_call(f.as_raw_fd(), request);
138
139 if response.linkid == 0 || response.err == (ENOENT as u32) {
140 break;
141 }
142 if response.err != 0 {
143 warn!("Door error: {:#?}", response);
144 break;
145 }
146
147 linkid = response.linkid;
148
149 match get_link(response.linkid) {
150 Ok(lnk) => result.push(lnk),
151 Err(e) => warn!("{}", e),
152 };
153 }
154
155 Ok(result)
156}
157
158pub(crate) fn get_link(id: u32) -> Result<LinkInfo, Error> {
159 let f = dlmgmt_door_fd()?;
160
161 let name_request = DlmgmtGetName {
162 cmd: DlmgmtCmd::GetName as u32,
163 linkid: id,
164 };
165
166 let response: DlmgmtNameRetval = door_call(f.as_raw_fd(), name_request);
167 let name: &str = match response.err {
168 0 => {
169 let end = response
170 .link
171 .iter()
172 .position(|&c| c == b'\0')
173 .expect("bad link name");
174 str::from_utf8(&response.link[0..end])?
175 }
176 _ => return Err(Error::NotFound(format!("link-id {} not found", id))),
177 };
178
179 let link_state = match crate::kstat::get_linkstate(name) {
180 Ok(state) => match state {
181 sys::link_state_t_LINK_STATE_UP => crate::LinkState::Up,
182 sys::link_state_t_LINK_STATE_DOWN => crate::LinkState::Down,
183 _ => crate::LinkState::Unknown,
184 },
185 Err(e) => {
186 warn!("error fetching link state on linkid {}: {}", id, e);
187 crate::LinkState::Unknown
188 }
189 };
190
191 let mac = match crate::ioctl::get_macaddr(id) {
192 Ok(mac) => mac,
193 Err(e) => {
194 warn!("error fetching mac address on linkid {}: {}", id, e);
195 [0u8; 6]
196 }
197 };
198
199 let mtu = match crate::ioctl::get_mtu(id) {
200 Ok(mtu) => Some(mtu),
201 Err(e) => {
202 warn!("error fetching mtu on linkid {}: {}", id, e);
203 None
204 }
205 };
206
207 let over = match response.class {
208 LinkClass::Simnet => match crate::ioctl::get_simnet_info(id) {
209 Ok(info) => info.peer_link_id,
210 Err(_) => {
211 warn!("could not get vnic info for {} ({})", name, id);
212 0
213 }
214 },
215 LinkClass::Tfport => match crate::ioctl::get_tfport_info(id) {
216 Ok(info) => info.pktsrc_id,
217 Err(_) => {
218 warn!("could not get tfport info for {} ({})", name, id);
219 0
220 }
221 },
222 LinkClass::Vnic => match crate::ioctl::get_vnic_info(id) {
223 Ok(info) => info.link_id,
224 Err(_) => {
225 warn!("could not get vnic info for {} ({})", name, id);
226 0
227 }
228 },
229 _ => 0,
230 };
231
232 Ok(LinkInfo {
233 id,
234 mac,
235 mtu,
236 over,
237 name: name.to_string(),
238 flags: response.flags,
239 class: response.class,
240 state: link_state,
241 })
242}
243
244#[repr(C)]
245pub enum DlmgmtDoorAttrType {
246 Str,
247 Boolean,
248 Uint64,
249}
250
251#[repr(C)]
252struct DlmgmtDoorWriteConf {
253 cmd: DlmgmtCmd,
254 conf_id: u32,
255}
256
257#[repr(C)]
258#[derive(Default)]
259struct DlmgmtRetval {
260 err: u32,
261}
262
263#[repr(C)]
264struct DlmgmtDoorOpenConf {
265 cmd: DlmgmtCmd,
266 linkid: u32,
267}
268
269#[repr(C)]
270#[derive(Default)]
271struct DlmgmtOpenConfRetval {
272 err: u32,
273 conf_id: u32,
274}
275
276const MAXLINKATTRLEN: usize = 32;
277const MAXLINKATTRVALLEN: usize = 1024;
278
279#[repr(C)]
280struct DlmgmtDoorSetAttr {
281 cmd: DlmgmtCmd,
282 conf_id: u32,
283 attr: [u8; MAXLINKATTRLEN],
284 attr_sz: u32,
285 typ: DlmgmtDoorAttrType,
286 val: [u8; MAXLINKATTRVALLEN],
287}
288
289#[repr(C)]
290struct DlmgmtDoorUnsetAttr {
291 cmd: DlmgmtCmd,
292 conf_id: u32,
293 attr: [u8; MAXLINKATTRLEN],
294}
295
296#[repr(C)]
297struct DlmgmtDoorDestroyConf {
298 cmd: DlmgmtCmd,
299 conf_id: u32,
300}
301
302#[allow(dead_code)]
304pub(crate) fn connect_simnet_peers(
305 link_id_a: u32,
306 link_id_b: u32,
307) -> Result<(), Error> {
308 let peer_info = get_link(link_id_b)?;
309
310 let key = "simnetpeer";
311
312 let f = dlmgmt_door_fd()?;
313
314 let open_request = DlmgmtDoorOpenConf {
316 cmd: DlmgmtCmd::OpenConf,
317 linkid: link_id_a,
318 };
319
320 let open_response: DlmgmtOpenConfRetval =
321 door_call(f.as_raw_fd(), open_request);
322 if open_response.err != 0 {
323 return Err(Error::Dlmgmtd(format!(
324 "openconf failed: {}",
325 open_response.err
326 )));
327 }
328 if open_response.conf_id == 0 {
329 return Err(Error::Dlmgmtd("open conf returned confid 0".into()));
330 }
331 let conf_id = open_response.conf_id;
332 debug!("got confid {}", conf_id);
333
334 let mut clear_request = DlmgmtDoorUnsetAttr {
336 cmd: DlmgmtCmd::UnsetAttr,
337 conf_id,
338 attr: [0; MAXLINKATTRLEN],
339 };
340 for (i, c) in key.chars().enumerate() {
341 clear_request.attr[i] = c as u8;
342 }
343 let clear_response: DlmgmtRetval = door_call(f.as_raw_fd(), clear_request);
344 if clear_response.err != 0 {
345 close_conf(f.as_raw_fd(), conf_id)?;
346 return Err(Error::Dlmgmtd(format!(
347 "clear conf failed: {}",
348 open_response.err
349 )));
350 }
351
352 debug!("setting peer={} for link id {}", peer_info.name, link_id_a);
353
354 let mut set_request = DlmgmtDoorSetAttr {
356 cmd: DlmgmtCmd::SetAttr,
357 conf_id,
358 attr: [0; MAXLINKATTRLEN],
359 attr_sz: (peer_info.name.len() + 1) as u32,
360 typ: DlmgmtDoorAttrType::Str,
361 val: [0; MAXLINKATTRVALLEN],
362 };
363 for (i, c) in key.chars().enumerate() {
364 set_request.attr[i] = c as u8;
365 }
366 for (i, b) in peer_info.name.chars().enumerate() {
367 set_request.val[i] = b as u8;
368 }
369 let set_response: DlmgmtRetval = door_call(f.as_raw_fd(), set_request);
370 if set_response.err != 0 {
371 close_conf(f.as_raw_fd(), conf_id)?;
372 return Err(Error::Dlmgmtd(format!(
373 "set conf attr failed: {}",
374 set_response.err
375 )));
376 }
377
378 let write_request = DlmgmtDoorWriteConf {
380 cmd: DlmgmtCmd::WriteConf,
381 conf_id,
382 };
383 let write_response: DlmgmtRetval = door_call(f.as_raw_fd(), write_request);
384 if write_response.err != 0 {
385 close_conf(f.as_raw_fd(), conf_id)?;
386 return Err(Error::Dlmgmtd(format!(
387 "write conf failed: {}",
388 write_response.err
389 )));
390 }
391
392 close_conf(f.as_raw_fd(), conf_id)?;
394
395 Ok(())
396}
397
398fn close_conf(fd: i32, conf_id: u32) -> Result<(), Error> {
399 let close_request = DlmgmtDoorDestroyConf {
400 cmd: DlmgmtCmd::DestroyConf,
401 conf_id,
402 };
403 let close_response: DlmgmtRetval = door_call(fd, close_request);
404 if close_response.err != 0 {
405 return Err(Error::Dlmgmtd(format!(
406 "close conf failed: {}",
407 close_response.err
408 )));
409 }
410 Ok(())
411}
412
413pub(crate) fn create_simnet_link(
414 name: &str,
415 flags: LinkFlags,
416) -> Result<LinkInfo, Error> {
417 let id = crate::link::create_link_id(name, LinkClass::Simnet, flags)?;
418 let link_info = match crate::ioctl::create_simnet(id, flags) {
419 Ok(l) => Ok(l),
420 Err(e) => {
421 let _ = delete_link_id(id, flags);
422 Err(e)
423 }
424 }?;
425 if (flags as u32 & LinkFlags::Persistent as u32) != 0 {
426 }
429
430 Ok(link_info)
431}
432
433pub(crate) fn create_tfport_link(
434 name: &str,
435 over: &str,
436 port: u16,
437 mac: Option<String>,
438 flags: LinkFlags,
439) -> Result<LinkInfo, Error> {
440 let over_id = linkname_to_id(over)?;
441 let link_id = crate::link::create_link_id(name, LinkClass::Tfport, flags)?;
442 match crate::ioctl::create_tfport(link_id, over_id, port, mac) {
443 Ok(l) => Ok(l),
444 Err(e) => {
445 let _ = delete_link_id(link_id, flags);
446 Err(e)
447 }
448 }
449}
450
451pub fn create_vnic_link(
452 name: &str,
453 link: u32,
454 mac: Option<Vec<u8>>,
455 flags: LinkFlags,
456) -> Result<LinkInfo, Error> {
457 let id = crate::link::create_link_id(name, LinkClass::Vnic, flags)?;
458 let link_info = match crate::ioctl::create_vnic(id, link, mac) {
459 Ok(l) => Ok(l),
460 Err(e) => {
461 let _ = delete_link_id(id, flags);
462 Err(e)
463 }
464 }?;
465 if (flags as u32 & LinkFlags::Persistent as u32) != 0 {
466 }
469
470 Ok(link_info)
471}
472
473#[repr(C)]
474pub struct DlmgmtGetLinkId {
475 pub cmd: DlmgmtCmd,
476 pub name: [u8; crate::sys::MAXLINKNAMELEN as usize],
477}
478
479pub fn linkname_to_id(name: &str) -> Result<u32, Error> {
480 let mut request = DlmgmtGetLinkId {
481 cmd: DlmgmtCmd::GetLinkId,
482 name: [0; crate::sys::MAXLINKNAMELEN as usize],
483 };
484 for (i, c) in name.chars().enumerate() {
485 request.name[i] = c as u8;
486 }
487
488 let f = dlmgmt_door_fd()?;
489
490 let response: DlmgmtLinkRetval = door_call(f.as_raw_fd(), request);
491 if response.err == (ENOENT as u32) || response.linkid == 0 {
492 return Err(Error::NotFound(format!("link {} not found", name)));
493 }
494 if response.err != 0 {
495 return Err(Error::Dlmgmtd(format!("get linkid: {}", response.err)));
496 }
497
498 Ok(response.linkid)
499}
500
501pub(crate) fn delete_link(id: u32, flags: LinkFlags) -> Result<(), Error> {
508 let link = match get_link(id) {
510 Err(Error::NotFound(_)) => return Ok(()),
511 Err(e) => return Err(e),
512 Ok(link) => link,
513 };
514
515 if let Err(err) = match link.class {
516 LinkClass::Simnet => crate::ioctl::delete_simnet(id),
517 LinkClass::Vnic => crate::ioctl::delete_vnic(id),
518 LinkClass::Tfport => crate::ioctl::delete_tfport(id),
519 _ => Err(Error::NotImplemented),
520 } {
521 warn!("class-specific delete error: {}", err);
522 return Err(err);
523 }
524
525 if let Err(e) = delete_link_id(id, flags) {
526 warn!("failed to delete link: {}", e);
527 return Err(e);
528 }
529
530 Ok(())
532}
533
534#[derive(Debug)]
535#[repr(C)]
536pub enum Bool {
537 False,
538 True,
539}
540
541#[derive(Debug)]
542#[repr(C)]
543pub struct DlmgmtDoorCreateId {
544 pub cmd: u32,
545 pub link: [u8; crate::sys::MAXLINKNAMELEN as usize],
546 pub class: u32,
547 pub media: u32,
548 pub prefix: Bool,
549 pub flags: u32,
550}
551
552fn dlmgmt_door_fd() -> Result<File, Error> {
553 File::open("/etc/svc/volatile/dladm/dlmgmt_door").map_err(Error::Io)
554}
555
556pub fn create_link_id(
557 name: &str,
558 class: LinkClass,
559 flags: LinkFlags,
560) -> Result<u32, Error> {
561 let f = dlmgmt_door_fd()?;
562
563 let mut link = [0u8; crate::sys::MAXLINKNAMELEN as usize];
564 if name.len() >= crate::sys::MAXLINKNAMELEN as usize {
565 return Err(Error::BadArgument(format!(
566 "link name must be less than {} characters",
567 crate::sys::MAXLINKNAMELEN,
568 )));
569 }
570 for (i, c) in name.chars().enumerate() {
571 link[i] = c as u8;
572 }
573
574 let request = DlmgmtDoorCreateId {
575 cmd: crate::link::DlmgmtCmd::CreateLinkId as u32,
576 link,
577 class: class as u32,
578 media: crate::sys::DL_ETHER,
579 prefix: Bool::False,
580 flags: flags as u32,
581 };
582
583 let response: DlmgmtLinkRetval = door_call(f.as_raw_fd(), request);
584 if response.linkid == 0 || response.err != 0 {
585 return Err(Error::Dlmgmtd(format!(
586 "link id creation failed: {}",
587 response.err
588 )));
589 }
590
591 Ok(response.linkid)
592}
593
594#[repr(C)]
595struct DlmgmtDoorDestroyId {
596 cmd: u32,
597 id: u32,
598 flags: u32,
599}
600
601pub fn delete_link_id(id: u32, flags: LinkFlags) -> Result<(), Error> {
602 let f = dlmgmt_door_fd()?;
603
604 let request = DlmgmtDoorDestroyId {
605 cmd: crate::link::DlmgmtCmd::DestroyLinkId as u32,
606 id,
607 flags: flags as u32,
608 };
609
610 let response: DlmgmtLinkRetval = door_call(f.as_raw_fd(), request);
611 if response.err != 0 {
612 return Err(Error::Dlmgmtd(format!(
613 "link id delete failed: {}",
614 response.err
615 )));
616 }
617
618 Ok(())
619}