1use std::net::Ipv4Addr;
104
105use super::builder::MessageBuilder;
106use super::connection::Connection;
107use super::error::Result;
108use super::interface_ref::InterfaceRef;
109use super::message::NlMsgType;
110use super::protocol::Route;
111use super::types::link::{IfInfoMsg, IflaAttr, IflaInfo};
112
113const NLM_F_CREATE: u16 = 0x400;
115const NLM_F_EXCL: u16 = 0x200;
117
118pub trait LinkConfig: Send + Sync {
120 fn name(&self) -> &str;
122
123 fn kind(&self) -> &str;
125
126 fn peer_name(&self) -> Option<&str> {
128 None
129 }
130
131 fn parent_ref(&self) -> Option<&InterfaceRef> {
136 None
137 }
138
139 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>);
144}
145
146#[derive(Debug, Clone)]
166#[must_use = "builders do nothing unless used"]
167pub struct DummyLink {
168 name: String,
169 mtu: Option<u32>,
170 address: Option<[u8; 6]>,
171}
172
173impl DummyLink {
174 pub fn new(name: impl Into<String>) -> Self {
176 Self {
177 name: name.into(),
178 mtu: None,
179 address: None,
180 }
181 }
182
183 pub fn mtu(mut self, mtu: u32) -> Self {
185 self.mtu = Some(mtu);
186 self
187 }
188
189 pub fn address(mut self, addr: [u8; 6]) -> Self {
191 self.address = Some(addr);
192 self
193 }
194}
195
196impl LinkConfig for DummyLink {
197 fn name(&self) -> &str {
198 &self.name
199 }
200
201 fn kind(&self) -> &str {
202 "dummy"
203 }
204
205 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
206 write_simple_link(
207 builder,
208 &self.name,
209 "dummy",
210 self.mtu,
211 self.address.as_ref(),
212 );
213 }
214}
215
216#[derive(Debug, Clone)]
237#[must_use = "builders do nothing unless used"]
238pub struct VethLink {
239 name: String,
240 peer_name: String,
241 mtu: Option<u32>,
242 address: Option<[u8; 6]>,
243 peer_address: Option<[u8; 6]>,
244 peer_netns_fd: Option<i32>,
245 peer_netns_pid: Option<u32>,
246}
247
248impl VethLink {
249 pub fn new(name: impl Into<String>, peer_name: impl Into<String>) -> Self {
256 Self {
257 name: name.into(),
258 peer_name: peer_name.into(),
259 mtu: None,
260 address: None,
261 peer_address: None,
262 peer_netns_fd: None,
263 peer_netns_pid: None,
264 }
265 }
266
267 pub fn mtu(mut self, mtu: u32) -> Self {
269 self.mtu = Some(mtu);
270 self
271 }
272
273 pub fn address(mut self, addr: [u8; 6]) -> Self {
275 self.address = Some(addr);
276 self
277 }
278
279 pub fn peer_address(mut self, addr: [u8; 6]) -> Self {
281 self.peer_address = Some(addr);
282 self
283 }
284
285 pub fn peer_netns_fd(mut self, fd: i32) -> Self {
287 self.peer_netns_fd = Some(fd);
288 self.peer_netns_pid = None;
289 self
290 }
291
292 pub fn peer_netns_pid(mut self, pid: u32) -> Self {
294 self.peer_netns_pid = Some(pid);
295 self.peer_netns_fd = None;
296 self
297 }
298}
299
300mod veth {
302 pub const VETH_INFO_PEER: u16 = 1;
303}
304
305impl LinkConfig for VethLink {
306 fn name(&self) -> &str {
307 &self.name
308 }
309
310 fn kind(&self) -> &str {
311 "veth"
312 }
313
314 fn peer_name(&self) -> Option<&str> {
315 Some(&self.peer_name)
316 }
317
318 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
319 write_ifname(builder, &self.name);
321
322 if let Some(mtu) = self.mtu {
324 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
325 }
326 if let Some(ref addr) = self.address {
327 builder.append_attr(IflaAttr::Address as u16, addr);
328 }
329
330 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
332 builder.append_attr_str(IflaInfo::Kind as u16, "veth");
333
334 let data = builder.nest_start(IflaInfo::Data as u16);
336 let peer = builder.nest_start(veth::VETH_INFO_PEER);
337
338 let peer_ifinfo = IfInfoMsg::new();
340 builder.append(&peer_ifinfo);
341
342 builder.append_attr_str(IflaAttr::Ifname as u16, &self.peer_name);
344
345 if let Some(mtu) = self.mtu {
347 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
348 }
349
350 if let Some(ref addr) = self.peer_address {
352 builder.append_attr(IflaAttr::Address as u16, addr);
353 }
354
355 if let Some(fd) = self.peer_netns_fd {
357 builder.append_attr_u32(IflaAttr::NetNsFd as u16, fd as u32);
358 } else if let Some(pid) = self.peer_netns_pid {
359 builder.append_attr_u32(IflaAttr::NetNsPid as u16, pid);
360 }
361
362 builder.nest_end(peer);
363 builder.nest_end(data);
364 builder.nest_end(linkinfo);
365 }
366}
367
368#[derive(Debug, Clone)]
388#[must_use = "builders do nothing unless used"]
389pub struct BridgeLink {
390 name: String,
391 mtu: Option<u32>,
392 address: Option<[u8; 6]>,
393 forward_delay: Option<u32>,
395 hello_time: Option<u32>,
397 max_age: Option<u32>,
399 ageing_time: Option<u32>,
401 stp_state: Option<u32>,
403 priority: Option<u16>,
405 vlan_filtering: Option<bool>,
407 vlan_default_pvid: Option<u16>,
409}
410
411mod bridge {
413 pub const IFLA_BR_FORWARD_DELAY: u16 = 1;
414 pub const IFLA_BR_HELLO_TIME: u16 = 2;
415 pub const IFLA_BR_MAX_AGE: u16 = 3;
416 pub const IFLA_BR_AGEING_TIME: u16 = 4;
417 pub const IFLA_BR_STP_STATE: u16 = 5;
418 pub const IFLA_BR_PRIORITY: u16 = 6;
419 pub const IFLA_BR_VLAN_FILTERING: u16 = 7;
420 pub const IFLA_BR_VLAN_DEFAULT_PVID: u16 = 39;
421}
422
423impl BridgeLink {
424 pub fn new(name: impl Into<String>) -> Self {
426 Self {
427 name: name.into(),
428 mtu: None,
429 address: None,
430 forward_delay: None,
431 hello_time: None,
432 max_age: None,
433 ageing_time: None,
434 stp_state: None,
435 priority: None,
436 vlan_filtering: None,
437 vlan_default_pvid: None,
438 }
439 }
440
441 pub fn mtu(mut self, mtu: u32) -> Self {
443 self.mtu = Some(mtu);
444 self
445 }
446
447 pub fn address(mut self, addr: [u8; 6]) -> Self {
449 self.address = Some(addr);
450 self
451 }
452
453 pub fn stp(mut self, enabled: bool) -> Self {
455 self.stp_state = Some(if enabled { 1 } else { 0 });
456 self
457 }
458
459 pub fn forward_delay_ms(mut self, ms: u32) -> Self {
461 self.forward_delay = Some(ms / 10);
463 self
464 }
465
466 pub fn hello_time_ms(mut self, ms: u32) -> Self {
468 self.hello_time = Some(ms / 10);
469 self
470 }
471
472 pub fn max_age_ms(mut self, ms: u32) -> Self {
474 self.max_age = Some(ms / 10);
475 self
476 }
477
478 pub fn ageing_time(mut self, seconds: u32) -> Self {
480 self.ageing_time = Some(seconds * 100);
482 self
483 }
484
485 pub fn priority(mut self, priority: u16) -> Self {
487 self.priority = Some(priority);
488 self
489 }
490
491 pub fn vlan_filtering(mut self, enabled: bool) -> Self {
493 self.vlan_filtering = Some(enabled);
494 self
495 }
496
497 pub fn vlan_default_pvid(mut self, pvid: u16) -> Self {
499 self.vlan_default_pvid = Some(pvid);
500 self
501 }
502}
503
504impl LinkConfig for BridgeLink {
505 fn name(&self) -> &str {
506 &self.name
507 }
508
509 fn kind(&self) -> &str {
510 "bridge"
511 }
512
513 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
514 write_ifname(builder, &self.name);
516
517 if let Some(mtu) = self.mtu {
519 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
520 }
521 if let Some(ref addr) = self.address {
522 builder.append_attr(IflaAttr::Address as u16, addr);
523 }
524
525 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
527 builder.append_attr_str(IflaInfo::Kind as u16, "bridge");
528
529 let has_options = self.forward_delay.is_some()
531 || self.hello_time.is_some()
532 || self.max_age.is_some()
533 || self.ageing_time.is_some()
534 || self.stp_state.is_some()
535 || self.priority.is_some()
536 || self.vlan_filtering.is_some()
537 || self.vlan_default_pvid.is_some();
538
539 if has_options {
540 let data = builder.nest_start(IflaInfo::Data as u16);
541
542 if let Some(val) = self.forward_delay {
543 builder.append_attr_u32(bridge::IFLA_BR_FORWARD_DELAY, val);
544 }
545 if let Some(val) = self.hello_time {
546 builder.append_attr_u32(bridge::IFLA_BR_HELLO_TIME, val);
547 }
548 if let Some(val) = self.max_age {
549 builder.append_attr_u32(bridge::IFLA_BR_MAX_AGE, val);
550 }
551 if let Some(val) = self.ageing_time {
552 builder.append_attr_u32(bridge::IFLA_BR_AGEING_TIME, val);
553 }
554 if let Some(val) = self.stp_state {
555 builder.append_attr_u32(bridge::IFLA_BR_STP_STATE, val);
556 }
557 if let Some(val) = self.priority {
558 builder.append_attr_u16(bridge::IFLA_BR_PRIORITY, val);
559 }
560 if let Some(enabled) = self.vlan_filtering {
561 builder.append_attr_u8(bridge::IFLA_BR_VLAN_FILTERING, if enabled { 1 } else { 0 });
562 }
563 if let Some(pvid) = self.vlan_default_pvid {
564 builder.append_attr_u16(bridge::IFLA_BR_VLAN_DEFAULT_PVID, pvid);
565 }
566
567 builder.nest_end(data);
568 }
569
570 builder.nest_end(linkinfo);
571 }
572}
573
574#[derive(Debug, Clone)]
592#[must_use = "builders do nothing unless used"]
593pub struct VlanLink {
594 name: String,
595 parent: InterfaceRef,
596 vlan_id: u16,
597 mtu: Option<u32>,
598 address: Option<[u8; 6]>,
599 protocol: Option<u16>,
601 flags: VlanFlags,
602}
603
604mod vlan {
606 pub const IFLA_VLAN_ID: u16 = 1;
607 pub const IFLA_VLAN_FLAGS: u16 = 2;
608 pub const IFLA_VLAN_PROTOCOL: u16 = 5;
609
610 pub const VLAN_FLAG_REORDER_HDR: u32 = 0x1;
612 pub const VLAN_FLAG_GVRP: u32 = 0x2;
613 pub const VLAN_FLAG_LOOSE_BINDING: u32 = 0x4;
614 pub const VLAN_FLAG_MVRP: u32 = 0x8;
615}
616
617#[repr(C)]
619#[derive(Debug, Clone, Copy, Default)]
620pub struct VlanFlags {
621 pub flags: u32,
622 pub mask: u32,
623}
624
625impl VlanLink {
626 pub fn new(name: impl Into<String>, parent: impl Into<String>, vlan_id: u16) -> Self {
634 Self {
635 name: name.into(),
636 parent: InterfaceRef::Name(parent.into()),
637 vlan_id,
638 mtu: None,
639 address: None,
640 protocol: None,
641 flags: VlanFlags::default(),
642 }
643 }
644
645 pub fn with_parent_index(name: impl Into<String>, parent_index: u32, vlan_id: u16) -> Self {
655 Self {
656 name: name.into(),
657 parent: InterfaceRef::Index(parent_index),
658 vlan_id,
659 mtu: None,
660 address: None,
661 protocol: None,
662 flags: VlanFlags::default(),
663 }
664 }
665
666 pub fn mtu(mut self, mtu: u32) -> Self {
668 self.mtu = Some(mtu);
669 self
670 }
671
672 pub fn address(mut self, addr: [u8; 6]) -> Self {
674 self.address = Some(addr);
675 self
676 }
677
678 pub fn qinq(mut self) -> Self {
680 self.protocol = Some(0x88a8);
681 self
682 }
683
684 pub fn gvrp(mut self, enabled: bool) -> Self {
686 self.flags.mask |= vlan::VLAN_FLAG_GVRP;
687 if enabled {
688 self.flags.flags |= vlan::VLAN_FLAG_GVRP;
689 } else {
690 self.flags.flags &= !vlan::VLAN_FLAG_GVRP;
691 }
692 self
693 }
694
695 pub fn mvrp(mut self, enabled: bool) -> Self {
697 self.flags.mask |= vlan::VLAN_FLAG_MVRP;
698 if enabled {
699 self.flags.flags |= vlan::VLAN_FLAG_MVRP;
700 } else {
701 self.flags.flags &= !vlan::VLAN_FLAG_MVRP;
702 }
703 self
704 }
705
706 pub fn loose_binding(mut self, enabled: bool) -> Self {
708 self.flags.mask |= vlan::VLAN_FLAG_LOOSE_BINDING;
709 if enabled {
710 self.flags.flags |= vlan::VLAN_FLAG_LOOSE_BINDING;
711 } else {
712 self.flags.flags &= !vlan::VLAN_FLAG_LOOSE_BINDING;
713 }
714 self
715 }
716
717 pub fn reorder_hdr(mut self, enabled: bool) -> Self {
719 self.flags.mask |= vlan::VLAN_FLAG_REORDER_HDR;
720 if enabled {
721 self.flags.flags |= vlan::VLAN_FLAG_REORDER_HDR;
722 } else {
723 self.flags.flags &= !vlan::VLAN_FLAG_REORDER_HDR;
724 }
725 self
726 }
727}
728
729impl LinkConfig for VlanLink {
730 fn name(&self) -> &str {
731 &self.name
732 }
733
734 fn kind(&self) -> &str {
735 "vlan"
736 }
737
738 fn parent_ref(&self) -> Option<&InterfaceRef> {
739 Some(&self.parent)
740 }
741
742 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
743 write_ifname(builder, &self.name);
745
746 let idx = parent_index.expect("VlanLink requires parent_index");
748 builder.append_attr_u32(IflaAttr::Link as u16, idx);
749
750 if let Some(mtu) = self.mtu {
752 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
753 }
754 if let Some(ref addr) = self.address {
755 builder.append_attr(IflaAttr::Address as u16, addr);
756 }
757
758 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
760 builder.append_attr_str(IflaInfo::Kind as u16, "vlan");
761
762 let data = builder.nest_start(IflaInfo::Data as u16);
764
765 builder.append_attr_u16(vlan::IFLA_VLAN_ID, self.vlan_id);
767
768 if let Some(proto) = self.protocol {
770 builder.append_attr_u16_be(vlan::IFLA_VLAN_PROTOCOL, proto);
771 }
772
773 if self.flags.mask != 0 {
775 let flags_bytes = unsafe {
778 std::slice::from_raw_parts(
779 &self.flags as *const VlanFlags as *const u8,
780 std::mem::size_of::<VlanFlags>(),
781 )
782 };
783 builder.append_attr(vlan::IFLA_VLAN_FLAGS, flags_bytes);
784 }
785
786 builder.nest_end(data);
787 builder.nest_end(linkinfo);
788 }
789}
790
791#[derive(Debug, Clone)]
814#[must_use = "builders do nothing unless used"]
815pub struct VxlanLink {
816 name: String,
817 vni: u32,
818 mtu: Option<u32>,
819 address: Option<[u8; 6]>,
820 local: Option<Ipv4Addr>,
822 remote: Option<Ipv4Addr>,
824 group: Option<Ipv4Addr>,
826 dev: Option<InterfaceRef>,
828 port: Option<u16>,
830 port_range: Option<(u16, u16)>,
832 ttl: Option<u8>,
834 tos: Option<u8>,
836 learning: Option<bool>,
838 proxy: Option<bool>,
840 rsc: Option<bool>,
842 l2miss: Option<bool>,
844 l3miss: Option<bool>,
846 udp_csum: Option<bool>,
848}
849
850mod vxlan {
852 pub const IFLA_VXLAN_ID: u16 = 1;
853 pub const IFLA_VXLAN_GROUP: u16 = 2;
854 pub const IFLA_VXLAN_LINK: u16 = 3;
855 pub const IFLA_VXLAN_LOCAL: u16 = 4;
856 pub const IFLA_VXLAN_TTL: u16 = 5;
857 pub const IFLA_VXLAN_TOS: u16 = 6;
858 pub const IFLA_VXLAN_LEARNING: u16 = 7;
859 pub const IFLA_VXLAN_PORT_RANGE: u16 = 10;
860 pub const IFLA_VXLAN_PROXY: u16 = 11;
861 pub const IFLA_VXLAN_RSC: u16 = 12;
862 pub const IFLA_VXLAN_L2MISS: u16 = 13;
863 pub const IFLA_VXLAN_L3MISS: u16 = 14;
864 pub const IFLA_VXLAN_PORT: u16 = 15;
865 pub const IFLA_VXLAN_UDP_CSUM: u16 = 18;
866}
867
868impl VxlanLink {
869 pub fn new(name: impl Into<String>, vni: u32) -> Self {
876 Self {
877 name: name.into(),
878 vni,
879 mtu: None,
880 address: None,
881 local: None,
882 remote: None,
883 group: None,
884 dev: None,
885 port: None,
886 port_range: None,
887 ttl: None,
888 tos: None,
889 learning: None,
890 proxy: None,
891 rsc: None,
892 l2miss: None,
893 l3miss: None,
894 udp_csum: None,
895 }
896 }
897
898 pub fn mtu(mut self, mtu: u32) -> Self {
900 self.mtu = Some(mtu);
901 self
902 }
903
904 pub fn address(mut self, addr: [u8; 6]) -> Self {
906 self.address = Some(addr);
907 self
908 }
909
910 pub fn local(mut self, addr: Ipv4Addr) -> Self {
912 self.local = Some(addr);
913 self
914 }
915
916 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
918 self.remote = Some(addr);
919 self
920 }
921
922 pub fn group(mut self, addr: Ipv4Addr) -> Self {
924 self.group = Some(addr);
925 self
926 }
927
928 pub fn dev(mut self, dev: impl Into<String>) -> Self {
930 self.dev = Some(InterfaceRef::Name(dev.into()));
931 self
932 }
933
934 pub fn dev_index(mut self, index: u32) -> Self {
938 self.dev = Some(InterfaceRef::Index(index));
939 self
940 }
941
942 pub fn port(mut self, port: u16) -> Self {
944 self.port = Some(port);
945 self
946 }
947
948 pub fn port_range(mut self, low: u16, high: u16) -> Self {
950 self.port_range = Some((low, high));
951 self
952 }
953
954 pub fn ttl(mut self, ttl: u8) -> Self {
956 self.ttl = Some(ttl);
957 self
958 }
959
960 pub fn tos(mut self, tos: u8) -> Self {
962 self.tos = Some(tos);
963 self
964 }
965
966 pub fn learning(mut self, enabled: bool) -> Self {
968 self.learning = Some(enabled);
969 self
970 }
971
972 pub fn proxy(mut self, enabled: bool) -> Self {
974 self.proxy = Some(enabled);
975 self
976 }
977
978 pub fn rsc(mut self, enabled: bool) -> Self {
980 self.rsc = Some(enabled);
981 self
982 }
983
984 pub fn l2miss(mut self, enabled: bool) -> Self {
986 self.l2miss = Some(enabled);
987 self
988 }
989
990 pub fn l3miss(mut self, enabled: bool) -> Self {
992 self.l3miss = Some(enabled);
993 self
994 }
995
996 pub fn udp_csum(mut self, enabled: bool) -> Self {
998 self.udp_csum = Some(enabled);
999 self
1000 }
1001}
1002
1003impl LinkConfig for VxlanLink {
1004 fn name(&self) -> &str {
1005 &self.name
1006 }
1007
1008 fn kind(&self) -> &str {
1009 "vxlan"
1010 }
1011
1012 fn parent_ref(&self) -> Option<&InterfaceRef> {
1013 self.dev.as_ref()
1014 }
1015
1016 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
1017 write_ifname(builder, &self.name);
1019
1020 if let Some(mtu) = self.mtu {
1022 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
1023 }
1024 if let Some(ref addr) = self.address {
1025 builder.append_attr(IflaAttr::Address as u16, addr);
1026 }
1027
1028 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
1030 builder.append_attr_str(IflaInfo::Kind as u16, "vxlan");
1031
1032 let data = builder.nest_start(IflaInfo::Data as u16);
1034
1035 builder.append_attr_u32(vxlan::IFLA_VXLAN_ID, self.vni);
1037
1038 if let Some(addr) = self.local {
1040 builder.append_attr(vxlan::IFLA_VXLAN_LOCAL, &addr.octets());
1041 }
1042
1043 if let Some(addr) = self.remote {
1045 builder.append_attr(vxlan::IFLA_VXLAN_GROUP, &addr.octets());
1046 } else if let Some(addr) = self.group {
1047 builder.append_attr(vxlan::IFLA_VXLAN_GROUP, &addr.octets());
1048 }
1049
1050 if let Some(idx) = parent_index {
1052 builder.append_attr_u32(vxlan::IFLA_VXLAN_LINK, idx);
1053 }
1054
1055 if let Some(port) = self.port {
1057 builder.append_attr_u16_be(vxlan::IFLA_VXLAN_PORT, port);
1058 }
1059
1060 if let Some((low, high)) = self.port_range {
1062 let range = [low.to_be(), high.to_be()];
1063 let range_bytes = unsafe { std::slice::from_raw_parts(range.as_ptr() as *const u8, 4) };
1065 builder.append_attr(vxlan::IFLA_VXLAN_PORT_RANGE, range_bytes);
1066 }
1067
1068 if let Some(ttl) = self.ttl {
1070 builder.append_attr_u8(vxlan::IFLA_VXLAN_TTL, ttl);
1071 }
1072
1073 if let Some(tos) = self.tos {
1075 builder.append_attr_u8(vxlan::IFLA_VXLAN_TOS, tos);
1076 }
1077
1078 if let Some(enabled) = self.learning {
1080 builder.append_attr_u8(vxlan::IFLA_VXLAN_LEARNING, if enabled { 1 } else { 0 });
1081 }
1082 if let Some(enabled) = self.proxy {
1083 builder.append_attr_u8(vxlan::IFLA_VXLAN_PROXY, if enabled { 1 } else { 0 });
1084 }
1085 if let Some(enabled) = self.rsc {
1086 builder.append_attr_u8(vxlan::IFLA_VXLAN_RSC, if enabled { 1 } else { 0 });
1087 }
1088 if let Some(enabled) = self.l2miss {
1089 builder.append_attr_u8(vxlan::IFLA_VXLAN_L2MISS, if enabled { 1 } else { 0 });
1090 }
1091 if let Some(enabled) = self.l3miss {
1092 builder.append_attr_u8(vxlan::IFLA_VXLAN_L3MISS, if enabled { 1 } else { 0 });
1093 }
1094 if let Some(enabled) = self.udp_csum {
1095 builder.append_attr_u8(vxlan::IFLA_VXLAN_UDP_CSUM, if enabled { 1 } else { 0 });
1096 }
1097
1098 builder.nest_end(data);
1099 builder.nest_end(linkinfo);
1100 }
1101}
1102
1103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1109#[non_exhaustive]
1110pub enum MacvlanMode {
1111 Private = 1,
1113 Vepa = 2,
1115 Bridge = 4,
1117 Passthru = 8,
1119 Source = 16,
1121}
1122
1123#[derive(Debug, Clone)]
1138#[must_use = "builders do nothing unless used"]
1139pub struct MacvlanLink {
1140 name: String,
1141 parent: InterfaceRef,
1142 mode: MacvlanMode,
1143 mtu: Option<u32>,
1144 address: Option<[u8; 6]>,
1145}
1146
1147mod macvlan {
1149 pub const IFLA_MACVLAN_MODE: u16 = 1;
1150}
1151
1152impl MacvlanLink {
1153 pub fn new(name: impl Into<String>, parent: impl Into<String>) -> Self {
1155 Self {
1156 name: name.into(),
1157 parent: InterfaceRef::Name(parent.into()),
1158 mode: MacvlanMode::Bridge,
1159 mtu: None,
1160 address: None,
1161 }
1162 }
1163
1164 pub fn with_parent_index(name: impl Into<String>, parent_index: u32) -> Self {
1168 Self {
1169 name: name.into(),
1170 parent: InterfaceRef::Index(parent_index),
1171 mode: MacvlanMode::Bridge,
1172 mtu: None,
1173 address: None,
1174 }
1175 }
1176
1177 pub fn mode(mut self, mode: MacvlanMode) -> Self {
1179 self.mode = mode;
1180 self
1181 }
1182
1183 pub fn mtu(mut self, mtu: u32) -> Self {
1185 self.mtu = Some(mtu);
1186 self
1187 }
1188
1189 pub fn address(mut self, addr: [u8; 6]) -> Self {
1191 self.address = Some(addr);
1192 self
1193 }
1194}
1195
1196impl LinkConfig for MacvlanLink {
1197 fn name(&self) -> &str {
1198 &self.name
1199 }
1200
1201 fn kind(&self) -> &str {
1202 "macvlan"
1203 }
1204
1205 fn parent_ref(&self) -> Option<&InterfaceRef> {
1206 Some(&self.parent)
1207 }
1208
1209 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
1210 write_ifname(builder, &self.name);
1212
1213 let idx = parent_index.expect("MacvlanLink requires parent_index");
1215 builder.append_attr_u32(IflaAttr::Link as u16, idx);
1216
1217 if let Some(mtu) = self.mtu {
1219 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
1220 }
1221 if let Some(ref addr) = self.address {
1222 builder.append_attr(IflaAttr::Address as u16, addr);
1223 }
1224
1225 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
1227 builder.append_attr_str(IflaInfo::Kind as u16, "macvlan");
1228
1229 let data = builder.nest_start(IflaInfo::Data as u16);
1231 builder.append_attr_u32(macvlan::IFLA_MACVLAN_MODE, self.mode as u32);
1232 builder.nest_end(data);
1233
1234 builder.nest_end(linkinfo);
1235 }
1236}
1237
1238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1244#[non_exhaustive]
1245pub enum IpvlanMode {
1246 L2 = 0,
1248 L3 = 1,
1250 L3S = 2,
1252}
1253
1254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1256#[non_exhaustive]
1257pub enum IpvlanFlags {
1258 Bridge = 0,
1260 Private = 1,
1262 Vepa = 2,
1264}
1265
1266#[derive(Debug, Clone)]
1281#[must_use = "builders do nothing unless used"]
1282pub struct IpvlanLink {
1283 name: String,
1284 parent: InterfaceRef,
1285 mode: IpvlanMode,
1286 flags: IpvlanFlags,
1287 mtu: Option<u32>,
1288}
1289
1290mod ipvlan {
1292 pub const IFLA_IPVLAN_MODE: u16 = 1;
1293 pub const IFLA_IPVLAN_FLAGS: u16 = 2;
1294}
1295
1296impl IpvlanLink {
1297 pub fn new(name: impl Into<String>, parent: impl Into<String>) -> Self {
1299 Self {
1300 name: name.into(),
1301 parent: InterfaceRef::Name(parent.into()),
1302 mode: IpvlanMode::L3,
1303 flags: IpvlanFlags::Bridge,
1304 mtu: None,
1305 }
1306 }
1307
1308 pub fn with_parent_index(name: impl Into<String>, parent_index: u32) -> Self {
1312 Self {
1313 name: name.into(),
1314 parent: InterfaceRef::Index(parent_index),
1315 mode: IpvlanMode::L3,
1316 flags: IpvlanFlags::Bridge,
1317 mtu: None,
1318 }
1319 }
1320
1321 pub fn mode(mut self, mode: IpvlanMode) -> Self {
1323 self.mode = mode;
1324 self
1325 }
1326
1327 pub fn flags(mut self, flags: IpvlanFlags) -> Self {
1329 self.flags = flags;
1330 self
1331 }
1332
1333 pub fn mtu(mut self, mtu: u32) -> Self {
1335 self.mtu = Some(mtu);
1336 self
1337 }
1338}
1339
1340impl LinkConfig for IpvlanLink {
1341 fn name(&self) -> &str {
1342 &self.name
1343 }
1344
1345 fn kind(&self) -> &str {
1346 "ipvlan"
1347 }
1348
1349 fn parent_ref(&self) -> Option<&InterfaceRef> {
1350 Some(&self.parent)
1351 }
1352
1353 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
1354 write_ifname(builder, &self.name);
1356
1357 let idx = parent_index.expect("IpvlanLink requires parent_index");
1359 builder.append_attr_u32(IflaAttr::Link as u16, idx);
1360
1361 if let Some(mtu) = self.mtu {
1363 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
1364 }
1365
1366 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
1368 builder.append_attr_str(IflaInfo::Kind as u16, "ipvlan");
1369
1370 let data = builder.nest_start(IflaInfo::Data as u16);
1372 builder.append_attr_u16(ipvlan::IFLA_IPVLAN_MODE, self.mode as u16);
1373 builder.append_attr_u16(ipvlan::IFLA_IPVLAN_FLAGS, self.flags as u16);
1374 builder.nest_end(data);
1375
1376 builder.nest_end(linkinfo);
1377 }
1378}
1379
1380#[derive(Debug, Clone)]
1401#[must_use = "builders do nothing unless used"]
1402pub struct IfbLink {
1403 name: String,
1404 mtu: Option<u32>,
1405}
1406
1407impl IfbLink {
1408 pub fn new(name: impl Into<String>) -> Self {
1410 Self {
1411 name: name.into(),
1412 mtu: None,
1413 }
1414 }
1415
1416 pub fn mtu(mut self, mtu: u32) -> Self {
1418 self.mtu = Some(mtu);
1419 self
1420 }
1421}
1422
1423impl LinkConfig for IfbLink {
1424 fn name(&self) -> &str {
1425 &self.name
1426 }
1427
1428 fn kind(&self) -> &str {
1429 "ifb"
1430 }
1431
1432 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
1433 write_simple_link(builder, &self.name, "ifb", self.mtu, None);
1434 }
1435}
1436
1437#[derive(Debug, Clone)]
1457#[must_use = "builders do nothing unless used"]
1458pub struct MacvtapLink {
1459 name: String,
1460 parent: InterfaceRef,
1461 mode: MacvlanMode,
1462 mtu: Option<u32>,
1463 address: Option<[u8; 6]>,
1464}
1465
1466impl MacvtapLink {
1467 pub fn new(name: impl Into<String>, parent: impl Into<String>) -> Self {
1469 Self {
1470 name: name.into(),
1471 parent: InterfaceRef::Name(parent.into()),
1472 mode: MacvlanMode::Bridge,
1473 mtu: None,
1474 address: None,
1475 }
1476 }
1477
1478 pub fn with_parent_index(name: impl Into<String>, parent_index: u32) -> Self {
1482 Self {
1483 name: name.into(),
1484 parent: InterfaceRef::Index(parent_index),
1485 mode: MacvlanMode::Bridge,
1486 mtu: None,
1487 address: None,
1488 }
1489 }
1490
1491 pub fn mode(mut self, mode: MacvlanMode) -> Self {
1493 self.mode = mode;
1494 self
1495 }
1496
1497 pub fn mtu(mut self, mtu: u32) -> Self {
1499 self.mtu = Some(mtu);
1500 self
1501 }
1502
1503 pub fn address(mut self, addr: [u8; 6]) -> Self {
1505 self.address = Some(addr);
1506 self
1507 }
1508}
1509
1510impl LinkConfig for MacvtapLink {
1511 fn name(&self) -> &str {
1512 &self.name
1513 }
1514
1515 fn kind(&self) -> &str {
1516 "macvtap"
1517 }
1518
1519 fn parent_ref(&self) -> Option<&InterfaceRef> {
1520 Some(&self.parent)
1521 }
1522
1523 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
1524 write_ifname(builder, &self.name);
1526
1527 let idx = parent_index.expect("MacvtapLink requires parent_index");
1529 builder.append_attr_u32(IflaAttr::Link as u16, idx);
1530
1531 if let Some(mtu) = self.mtu {
1533 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
1534 }
1535 if let Some(ref addr) = self.address {
1536 builder.append_attr(IflaAttr::Address as u16, addr);
1537 }
1538
1539 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
1541 builder.append_attr_str(IflaInfo::Kind as u16, "macvtap");
1542
1543 let data = builder.nest_start(IflaInfo::Data as u16);
1545 builder.append_attr_u32(macvlan::IFLA_MACVLAN_MODE, self.mode as u32);
1546 builder.nest_end(data);
1547
1548 builder.nest_end(linkinfo);
1549 }
1550}
1551
1552#[derive(Debug, Clone)]
1574#[must_use = "builders do nothing unless used"]
1575pub struct GeneveLink {
1576 name: String,
1577 vni: u32,
1578 mtu: Option<u32>,
1579 remote: Option<Ipv4Addr>,
1581 remote6: Option<std::net::Ipv6Addr>,
1583 ttl: Option<u8>,
1585 ttl_inherit: bool,
1587 tos: Option<u8>,
1589 df: Option<GeneveDf>,
1591 label: Option<u32>,
1593 port: Option<u16>,
1595 collect_metadata: bool,
1597 udp_csum: Option<bool>,
1599 udp6_zero_csum_tx: Option<bool>,
1601 udp6_zero_csum_rx: Option<bool>,
1603 inner_proto_inherit: bool,
1605}
1606
1607#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1609#[non_exhaustive]
1610pub enum GeneveDf {
1611 Unset = 0,
1613 Set = 1,
1615 Inherit = 2,
1617}
1618
1619mod geneve {
1621 pub const IFLA_GENEVE_ID: u16 = 1;
1622 pub const IFLA_GENEVE_REMOTE: u16 = 2;
1623 pub const IFLA_GENEVE_TTL: u16 = 3;
1624 pub const IFLA_GENEVE_TOS: u16 = 4;
1625 pub const IFLA_GENEVE_PORT: u16 = 5;
1626 pub const IFLA_GENEVE_COLLECT_METADATA: u16 = 6;
1627 pub const IFLA_GENEVE_REMOTE6: u16 = 7;
1628 pub const IFLA_GENEVE_UDP_CSUM: u16 = 8;
1629 pub const IFLA_GENEVE_UDP_ZERO_CSUM6_TX: u16 = 9;
1630 pub const IFLA_GENEVE_UDP_ZERO_CSUM6_RX: u16 = 10;
1631 pub const IFLA_GENEVE_LABEL: u16 = 11;
1632 pub const IFLA_GENEVE_TTL_INHERIT: u16 = 12;
1633 pub const IFLA_GENEVE_DF: u16 = 13;
1634 pub const IFLA_GENEVE_INNER_PROTO_INHERIT: u16 = 14;
1635}
1636
1637impl GeneveLink {
1638 pub fn new(name: impl Into<String>, vni: u32) -> Self {
1645 Self {
1646 name: name.into(),
1647 vni,
1648 mtu: None,
1649 remote: None,
1650 remote6: None,
1651 ttl: None,
1652 ttl_inherit: false,
1653 tos: None,
1654 df: None,
1655 label: None,
1656 port: None,
1657 collect_metadata: false,
1658 udp_csum: None,
1659 udp6_zero_csum_tx: None,
1660 udp6_zero_csum_rx: None,
1661 inner_proto_inherit: false,
1662 }
1663 }
1664
1665 pub fn mtu(mut self, mtu: u32) -> Self {
1667 self.mtu = Some(mtu);
1668 self
1669 }
1670
1671 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
1673 self.remote = Some(addr);
1674 self.remote6 = None;
1675 self
1676 }
1677
1678 pub fn remote6(mut self, addr: std::net::Ipv6Addr) -> Self {
1680 self.remote6 = Some(addr);
1681 self.remote = None;
1682 self
1683 }
1684
1685 pub fn ttl(mut self, ttl: u8) -> Self {
1687 self.ttl = Some(ttl);
1688 self.ttl_inherit = false;
1689 self
1690 }
1691
1692 pub fn ttl_inherit(mut self) -> Self {
1694 self.ttl_inherit = true;
1695 self.ttl = None;
1696 self
1697 }
1698
1699 pub fn tos(mut self, tos: u8) -> Self {
1701 self.tos = Some(tos);
1702 self
1703 }
1704
1705 pub fn df(mut self, df: GeneveDf) -> Self {
1707 self.df = Some(df);
1708 self
1709 }
1710
1711 pub fn label(mut self, label: u32) -> Self {
1713 self.label = Some(label);
1714 self
1715 }
1716
1717 pub fn port(mut self, port: u16) -> Self {
1719 self.port = Some(port);
1720 self
1721 }
1722
1723 pub fn collect_metadata(mut self) -> Self {
1725 self.collect_metadata = true;
1726 self
1727 }
1728
1729 pub fn udp_csum(mut self, enabled: bool) -> Self {
1731 self.udp_csum = Some(enabled);
1732 self
1733 }
1734
1735 pub fn udp6_zero_csum_tx(mut self, enabled: bool) -> Self {
1737 self.udp6_zero_csum_tx = Some(enabled);
1738 self
1739 }
1740
1741 pub fn udp6_zero_csum_rx(mut self, enabled: bool) -> Self {
1743 self.udp6_zero_csum_rx = Some(enabled);
1744 self
1745 }
1746
1747 pub fn inner_proto_inherit(mut self) -> Self {
1749 self.inner_proto_inherit = true;
1750 self
1751 }
1752}
1753
1754impl LinkConfig for GeneveLink {
1755 fn name(&self) -> &str {
1756 &self.name
1757 }
1758
1759 fn kind(&self) -> &str {
1760 "geneve"
1761 }
1762
1763 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
1764 write_ifname(builder, &self.name);
1766
1767 if let Some(mtu) = self.mtu {
1768 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
1769 }
1770
1771 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
1773 builder.append_attr_str(IflaInfo::Kind as u16, "geneve");
1774
1775 let data = builder.nest_start(IflaInfo::Data as u16);
1777
1778 builder.append_attr_u32(geneve::IFLA_GENEVE_ID, self.vni);
1780
1781 if let Some(addr) = self.remote {
1783 builder.append_attr(geneve::IFLA_GENEVE_REMOTE, &addr.octets());
1784 } else if let Some(addr) = self.remote6 {
1785 builder.append_attr(geneve::IFLA_GENEVE_REMOTE6, &addr.octets());
1786 }
1787
1788 if self.ttl_inherit {
1790 builder.append_attr_u8(geneve::IFLA_GENEVE_TTL_INHERIT, 1);
1791 } else if let Some(ttl) = self.ttl {
1792 builder.append_attr_u8(geneve::IFLA_GENEVE_TTL, ttl);
1793 }
1794
1795 if let Some(tos) = self.tos {
1797 builder.append_attr_u8(geneve::IFLA_GENEVE_TOS, tos);
1798 }
1799
1800 if let Some(df) = self.df {
1802 builder.append_attr_u8(geneve::IFLA_GENEVE_DF, df as u8);
1803 }
1804
1805 if let Some(label) = self.label {
1807 builder.append_attr_u32(geneve::IFLA_GENEVE_LABEL, label);
1808 }
1809
1810 if let Some(port) = self.port {
1812 builder.append_attr_u16_be(geneve::IFLA_GENEVE_PORT, port);
1813 }
1814
1815 if self.collect_metadata {
1817 builder.append_attr_empty(geneve::IFLA_GENEVE_COLLECT_METADATA);
1818 }
1819
1820 if let Some(enabled) = self.udp_csum {
1822 builder.append_attr_u8(geneve::IFLA_GENEVE_UDP_CSUM, if enabled { 1 } else { 0 });
1823 }
1824 if let Some(enabled) = self.udp6_zero_csum_tx {
1825 builder.append_attr_u8(
1826 geneve::IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
1827 if enabled { 1 } else { 0 },
1828 );
1829 }
1830 if let Some(enabled) = self.udp6_zero_csum_rx {
1831 builder.append_attr_u8(
1832 geneve::IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
1833 if enabled { 1 } else { 0 },
1834 );
1835 }
1836
1837 if self.inner_proto_inherit {
1839 builder.append_attr_empty(geneve::IFLA_GENEVE_INNER_PROTO_INHERIT);
1840 }
1841
1842 builder.nest_end(data);
1843 builder.nest_end(linkinfo);
1844 }
1845}
1846
1847#[derive(Debug, Clone)]
1866#[must_use = "builders do nothing unless used"]
1867pub struct BareudpLink {
1868 name: String,
1869 port: u16,
1871 ethertype: u16,
1873 srcport_min: Option<u16>,
1875 multiproto_mode: bool,
1877 mtu: Option<u32>,
1878}
1879
1880mod bareudp {
1882 pub const IFLA_BAREUDP_PORT: u16 = 1;
1883 pub const IFLA_BAREUDP_ETHERTYPE: u16 = 2;
1884 pub const IFLA_BAREUDP_SRCPORT_MIN: u16 = 3;
1885 pub const IFLA_BAREUDP_MULTIPROTO_MODE: u16 = 4;
1886}
1887
1888impl BareudpLink {
1889 pub fn new(name: impl Into<String>, port: u16, ethertype: u16) -> Self {
1897 Self {
1898 name: name.into(),
1899 port,
1900 ethertype,
1901 srcport_min: None,
1902 multiproto_mode: false,
1903 mtu: None,
1904 }
1905 }
1906
1907 pub fn srcport_min(mut self, port: u16) -> Self {
1909 self.srcport_min = Some(port);
1910 self
1911 }
1912
1913 pub fn multiproto_mode(mut self) -> Self {
1915 self.multiproto_mode = true;
1916 self
1917 }
1918
1919 pub fn mtu(mut self, mtu: u32) -> Self {
1921 self.mtu = Some(mtu);
1922 self
1923 }
1924}
1925
1926impl LinkConfig for BareudpLink {
1927 fn name(&self) -> &str {
1928 &self.name
1929 }
1930
1931 fn kind(&self) -> &str {
1932 "bareudp"
1933 }
1934
1935 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
1936 write_ifname(builder, &self.name);
1938
1939 if let Some(mtu) = self.mtu {
1940 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
1941 }
1942
1943 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
1945 builder.append_attr_str(IflaInfo::Kind as u16, "bareudp");
1946
1947 let data = builder.nest_start(IflaInfo::Data as u16);
1949
1950 builder.append_attr_u16_be(bareudp::IFLA_BAREUDP_PORT, self.port);
1952
1953 builder.append_attr_u16_be(bareudp::IFLA_BAREUDP_ETHERTYPE, self.ethertype);
1955
1956 if let Some(srcport) = self.srcport_min {
1958 builder.append_attr_u16(bareudp::IFLA_BAREUDP_SRCPORT_MIN, srcport);
1959 }
1960
1961 if self.multiproto_mode {
1963 builder.append_attr_empty(bareudp::IFLA_BAREUDP_MULTIPROTO_MODE);
1964 }
1965
1966 builder.nest_end(data);
1967 builder.nest_end(linkinfo);
1968 }
1969}
1970
1971#[derive(Debug, Clone)]
1992#[must_use = "builders do nothing unless used"]
1993pub struct NetkitLink {
1994 name: String,
1995 peer_name: String,
1996 mode: Option<NetkitMode>,
1997 policy: Option<NetkitPolicy>,
1998 peer_policy: Option<NetkitPolicy>,
1999 scrub: Option<NetkitScrub>,
2000 peer_scrub: Option<NetkitScrub>,
2001 mtu: Option<u32>,
2002}
2003
2004#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2006#[non_exhaustive]
2007pub enum NetkitMode {
2008 L2 = 0,
2010 L3 = 1,
2012}
2013
2014#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2016#[non_exhaustive]
2017pub enum NetkitPolicy {
2018 Forward = 0,
2020 Blackhole = 2,
2022}
2023
2024#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2026pub enum NetkitScrub {
2027 None = 0,
2029 Default = 1,
2031}
2032
2033mod netkit {
2035 pub const IFLA_NETKIT_PEER_INFO: u16 = 1;
2036 pub const IFLA_NETKIT_MODE: u16 = 4;
2037 pub const IFLA_NETKIT_POLICY: u16 = 2;
2038 pub const IFLA_NETKIT_PEER_POLICY: u16 = 3;
2039 pub const IFLA_NETKIT_SCRUB: u16 = 5;
2040 pub const IFLA_NETKIT_PEER_SCRUB: u16 = 6;
2041}
2042
2043impl NetkitLink {
2044 pub fn new(name: impl Into<String>, peer_name: impl Into<String>) -> Self {
2051 Self {
2052 name: name.into(),
2053 peer_name: peer_name.into(),
2054 mode: None,
2055 policy: None,
2056 peer_policy: None,
2057 scrub: None,
2058 peer_scrub: None,
2059 mtu: None,
2060 }
2061 }
2062
2063 pub fn mode(mut self, mode: NetkitMode) -> Self {
2065 self.mode = Some(mode);
2066 self
2067 }
2068
2069 pub fn policy(mut self, policy: NetkitPolicy) -> Self {
2071 self.policy = Some(policy);
2072 self
2073 }
2074
2075 pub fn peer_policy(mut self, policy: NetkitPolicy) -> Self {
2077 self.peer_policy = Some(policy);
2078 self
2079 }
2080
2081 pub fn scrub(mut self, scrub: NetkitScrub) -> Self {
2083 self.scrub = Some(scrub);
2084 self
2085 }
2086
2087 pub fn peer_scrub(mut self, scrub: NetkitScrub) -> Self {
2089 self.peer_scrub = Some(scrub);
2090 self
2091 }
2092
2093 pub fn mtu(mut self, mtu: u32) -> Self {
2095 self.mtu = Some(mtu);
2096 self
2097 }
2098}
2099
2100impl LinkConfig for NetkitLink {
2101 fn name(&self) -> &str {
2102 &self.name
2103 }
2104
2105 fn kind(&self) -> &str {
2106 "netkit"
2107 }
2108
2109 fn peer_name(&self) -> Option<&str> {
2110 Some(&self.peer_name)
2111 }
2112
2113 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
2114 write_ifname(builder, &self.name);
2116
2117 if let Some(mtu) = self.mtu {
2118 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
2119 }
2120
2121 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
2123 builder.append_attr_str(IflaInfo::Kind as u16, "netkit");
2124
2125 let data = builder.nest_start(IflaInfo::Data as u16);
2127
2128 if let Some(mode) = self.mode {
2130 builder.append_attr_u32(netkit::IFLA_NETKIT_MODE, mode as u32);
2131 }
2132
2133 if let Some(policy) = self.policy {
2135 builder.append_attr_u32(netkit::IFLA_NETKIT_POLICY, policy as u32);
2136 }
2137
2138 if let Some(policy) = self.peer_policy {
2140 builder.append_attr_u32(netkit::IFLA_NETKIT_PEER_POLICY, policy as u32);
2141 }
2142
2143 if let Some(scrub) = self.scrub {
2145 builder.append_attr_u32(netkit::IFLA_NETKIT_SCRUB, scrub as u32);
2146 }
2147
2148 if let Some(scrub) = self.peer_scrub {
2150 builder.append_attr_u32(netkit::IFLA_NETKIT_PEER_SCRUB, scrub as u32);
2151 }
2152
2153 let peer_info = builder.nest_start(netkit::IFLA_NETKIT_PEER_INFO);
2155 let peer_ifinfo = IfInfoMsg::new();
2156 builder.append(&peer_ifinfo);
2157 builder.append_attr_str(IflaAttr::Ifname as u16, &self.peer_name);
2158 builder.nest_end(peer_info);
2159
2160 builder.nest_end(data);
2161 builder.nest_end(linkinfo);
2162 }
2163}
2164
2165#[derive(Debug, Clone)]
2186#[must_use = "builders do nothing unless used"]
2187pub struct NlmonLink {
2188 name: String,
2189}
2190
2191impl NlmonLink {
2192 pub fn new(name: impl Into<String>) -> Self {
2194 Self { name: name.into() }
2195 }
2196}
2197
2198impl LinkConfig for NlmonLink {
2199 fn name(&self) -> &str {
2200 &self.name
2201 }
2202
2203 fn kind(&self) -> &str {
2204 "nlmon"
2205 }
2206
2207 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
2208 write_simple_link(builder, &self.name, "nlmon", None, None);
2209 }
2210}
2211
2212#[derive(Debug, Clone)]
2231#[must_use = "builders do nothing unless used"]
2232pub struct VirtWifiLink {
2233 name: String,
2234 link: InterfaceRef,
2235}
2236
2237impl VirtWifiLink {
2238 pub fn new(name: impl Into<String>, link: impl Into<String>) -> Self {
2245 Self {
2246 name: name.into(),
2247 link: InterfaceRef::Name(link.into()),
2248 }
2249 }
2250
2251 pub fn with_link_index(name: impl Into<String>, link_index: u32) -> Self {
2255 Self {
2256 name: name.into(),
2257 link: InterfaceRef::Index(link_index),
2258 }
2259 }
2260}
2261
2262impl LinkConfig for VirtWifiLink {
2263 fn name(&self) -> &str {
2264 &self.name
2265 }
2266
2267 fn kind(&self) -> &str {
2268 "virt_wifi"
2269 }
2270
2271 fn parent_ref(&self) -> Option<&InterfaceRef> {
2272 Some(&self.link)
2273 }
2274
2275 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
2276 write_ifname(builder, &self.name);
2278
2279 let idx = parent_index.expect("VirtWifiLink requires link index");
2281 builder.append_attr_u32(IflaAttr::Link as u16, idx);
2282
2283 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
2285 builder.append_attr_str(IflaInfo::Kind as u16, "virt_wifi");
2286 builder.nest_end(linkinfo);
2287 }
2288}
2289
2290#[derive(Debug, Clone)]
2314#[must_use = "builders do nothing unless used"]
2315pub struct VtiLink {
2316 name: String,
2317 local: Option<Ipv4Addr>,
2318 remote: Option<Ipv4Addr>,
2319 ikey: Option<u32>,
2320 okey: Option<u32>,
2321 link: Option<InterfaceRef>,
2322}
2323
2324#[allow(dead_code)]
2326mod vti {
2327 pub const IFLA_VTI_LINK: u16 = 1;
2328 pub const IFLA_VTI_IKEY: u16 = 2;
2329 pub const IFLA_VTI_OKEY: u16 = 3;
2330 pub const IFLA_VTI_LOCAL: u16 = 4;
2331 pub const IFLA_VTI_REMOTE: u16 = 5;
2332 pub const IFLA_VTI_FWMARK: u16 = 6;
2333}
2334
2335impl VtiLink {
2336 pub fn new(name: impl Into<String>) -> Self {
2338 Self {
2339 name: name.into(),
2340 local: None,
2341 remote: None,
2342 ikey: None,
2343 okey: None,
2344 link: None,
2345 }
2346 }
2347
2348 pub fn local(mut self, addr: Ipv4Addr) -> Self {
2350 self.local = Some(addr);
2351 self
2352 }
2353
2354 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
2356 self.remote = Some(addr);
2357 self
2358 }
2359
2360 pub fn ikey(mut self, key: u32) -> Self {
2362 self.ikey = Some(key);
2363 self
2364 }
2365
2366 pub fn okey(mut self, key: u32) -> Self {
2368 self.okey = Some(key);
2369 self
2370 }
2371
2372 pub fn link(mut self, link: impl Into<String>) -> Self {
2374 self.link = Some(InterfaceRef::Name(link.into()));
2375 self
2376 }
2377
2378 pub fn link_index(mut self, index: u32) -> Self {
2382 self.link = Some(InterfaceRef::Index(index));
2383 self
2384 }
2385}
2386
2387impl LinkConfig for VtiLink {
2388 fn name(&self) -> &str {
2389 &self.name
2390 }
2391
2392 fn kind(&self) -> &str {
2393 "vti"
2394 }
2395
2396 fn parent_ref(&self) -> Option<&InterfaceRef> {
2397 self.link.as_ref()
2398 }
2399
2400 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
2401 write_ifname(builder, &self.name);
2403
2404 if let Some(idx) = parent_index {
2406 builder.append_attr_u32(IflaAttr::Link as u16, idx);
2407 }
2408
2409 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
2411 builder.append_attr_str(IflaInfo::Kind as u16, "vti");
2412
2413 let data = builder.nest_start(IflaInfo::Data as u16);
2415
2416 if let Some(local) = self.local {
2417 builder.append_attr(vti::IFLA_VTI_LOCAL, &local.octets());
2418 }
2419 if let Some(remote) = self.remote {
2420 builder.append_attr(vti::IFLA_VTI_REMOTE, &remote.octets());
2421 }
2422 if let Some(ikey) = self.ikey {
2423 builder.append_attr_u32_be(vti::IFLA_VTI_IKEY, ikey);
2424 }
2425 if let Some(okey) = self.okey {
2426 builder.append_attr_u32_be(vti::IFLA_VTI_OKEY, okey);
2427 }
2428
2429 builder.nest_end(data);
2430 builder.nest_end(linkinfo);
2431 }
2432}
2433
2434#[derive(Debug, Clone)]
2455#[must_use = "builders do nothing unless used"]
2456pub struct Vti6Link {
2457 name: String,
2458 local: Option<std::net::Ipv6Addr>,
2459 remote: Option<std::net::Ipv6Addr>,
2460 ikey: Option<u32>,
2461 okey: Option<u32>,
2462 link: Option<InterfaceRef>,
2463}
2464
2465impl Vti6Link {
2466 pub fn new(name: impl Into<String>) -> Self {
2468 Self {
2469 name: name.into(),
2470 local: None,
2471 remote: None,
2472 ikey: None,
2473 okey: None,
2474 link: None,
2475 }
2476 }
2477
2478 pub fn local(mut self, addr: std::net::Ipv6Addr) -> Self {
2480 self.local = Some(addr);
2481 self
2482 }
2483
2484 pub fn remote(mut self, addr: std::net::Ipv6Addr) -> Self {
2486 self.remote = Some(addr);
2487 self
2488 }
2489
2490 pub fn ikey(mut self, key: u32) -> Self {
2492 self.ikey = Some(key);
2493 self
2494 }
2495
2496 pub fn okey(mut self, key: u32) -> Self {
2498 self.okey = Some(key);
2499 self
2500 }
2501
2502 pub fn link(mut self, link: impl Into<String>) -> Self {
2504 self.link = Some(InterfaceRef::Name(link.into()));
2505 self
2506 }
2507
2508 pub fn link_index(mut self, index: u32) -> Self {
2512 self.link = Some(InterfaceRef::Index(index));
2513 self
2514 }
2515}
2516
2517impl LinkConfig for Vti6Link {
2518 fn name(&self) -> &str {
2519 &self.name
2520 }
2521
2522 fn kind(&self) -> &str {
2523 "vti6"
2524 }
2525
2526 fn parent_ref(&self) -> Option<&InterfaceRef> {
2527 self.link.as_ref()
2528 }
2529
2530 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
2531 write_ifname(builder, &self.name);
2533
2534 if let Some(idx) = parent_index {
2536 builder.append_attr_u32(IflaAttr::Link as u16, idx);
2537 }
2538
2539 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
2541 builder.append_attr_str(IflaInfo::Kind as u16, "vti6");
2542
2543 let data = builder.nest_start(IflaInfo::Data as u16);
2545
2546 if let Some(local) = self.local {
2547 builder.append_attr(vti::IFLA_VTI_LOCAL, &local.octets());
2548 }
2549 if let Some(remote) = self.remote {
2550 builder.append_attr(vti::IFLA_VTI_REMOTE, &remote.octets());
2551 }
2552 if let Some(ikey) = self.ikey {
2553 builder.append_attr_u32_be(vti::IFLA_VTI_IKEY, ikey);
2554 }
2555 if let Some(okey) = self.okey {
2556 builder.append_attr_u32_be(vti::IFLA_VTI_OKEY, okey);
2557 }
2558
2559 builder.nest_end(data);
2560 builder.nest_end(linkinfo);
2561 }
2562}
2563
2564#[derive(Debug, Clone)]
2587#[must_use = "builders do nothing unless used"]
2588pub struct Ip6GreLink {
2589 name: String,
2590 local: Option<std::net::Ipv6Addr>,
2591 remote: Option<std::net::Ipv6Addr>,
2592 ttl: Option<u8>,
2593 encap_limit: Option<u8>,
2594 flowinfo: Option<u32>,
2595 flags: Option<u32>,
2596 link: Option<InterfaceRef>,
2597}
2598
2599#[allow(dead_code)]
2602mod gre_attr {
2603 pub const IFLA_GRE_LINK: u16 = 1;
2604 pub const IFLA_GRE_IFLAGS: u16 = 2;
2605 pub const IFLA_GRE_OFLAGS: u16 = 3;
2606 pub const IFLA_GRE_IKEY: u16 = 4;
2607 pub const IFLA_GRE_OKEY: u16 = 5;
2608 pub const IFLA_GRE_LOCAL: u16 = 6;
2609 pub const IFLA_GRE_REMOTE: u16 = 7;
2610 pub const IFLA_GRE_TTL: u16 = 8;
2611 pub const IFLA_GRE_TOS: u16 = 9;
2612 pub const IFLA_GRE_PMTUDISC: u16 = 10;
2613 pub const IFLA_GRE_ENCAP_LIMIT: u16 = 11;
2614 pub const IFLA_GRE_FLOWINFO: u16 = 12;
2615 pub const IFLA_GRE_FLAGS: u16 = 13;
2616 pub const IFLA_GRE_ENCAP_TYPE: u16 = 14;
2617 pub const IFLA_GRE_ENCAP_FLAGS: u16 = 15;
2618 pub const IFLA_GRE_ENCAP_SPORT: u16 = 16;
2619 pub const IFLA_GRE_ENCAP_DPORT: u16 = 17;
2620 pub const IFLA_GRE_COLLECT_METADATA: u16 = 18;
2621 pub const IFLA_GRE_IGNORE_DF: u16 = 19;
2622 pub const IFLA_GRE_FWMARK: u16 = 20;
2623
2624 pub const GRE_KEY: u16 = 0x2000;
2626}
2627
2628#[allow(dead_code)]
2632mod iptun_attr {
2633 pub const IFLA_IPTUN_LINK: u16 = 1;
2634 pub const IFLA_IPTUN_LOCAL: u16 = 2;
2635 pub const IFLA_IPTUN_REMOTE: u16 = 3;
2636 pub const IFLA_IPTUN_TTL: u16 = 4;
2637 pub const IFLA_IPTUN_TOS: u16 = 5;
2638 pub const IFLA_IPTUN_ENCAP_LIMIT: u16 = 6;
2639 pub const IFLA_IPTUN_FLOWINFO: u16 = 7;
2640 pub const IFLA_IPTUN_FLAGS: u16 = 8;
2641 pub const IFLA_IPTUN_PROTO: u16 = 9;
2642 pub const IFLA_IPTUN_PMTUDISC: u16 = 10;
2643 pub const IFLA_IPTUN_ENCAP_TYPE: u16 = 15;
2644 pub const IFLA_IPTUN_ENCAP_FLAGS: u16 = 16;
2645 pub const IFLA_IPTUN_ENCAP_SPORT: u16 = 17;
2646 pub const IFLA_IPTUN_ENCAP_DPORT: u16 = 18;
2647 pub const IFLA_IPTUN_COLLECT_METADATA: u16 = 19;
2648 pub const IFLA_IPTUN_FWMARK: u16 = 20;
2649
2650 pub const SIT_ISATAP: u16 = 0x0001;
2652}
2653
2654impl Ip6GreLink {
2655 pub fn new(name: impl Into<String>) -> Self {
2657 Self {
2658 name: name.into(),
2659 local: None,
2660 remote: None,
2661 ttl: None,
2662 encap_limit: None,
2663 flowinfo: None,
2664 flags: None,
2665 link: None,
2666 }
2667 }
2668
2669 pub fn local(mut self, addr: std::net::Ipv6Addr) -> Self {
2671 self.local = Some(addr);
2672 self
2673 }
2674
2675 pub fn remote(mut self, addr: std::net::Ipv6Addr) -> Self {
2677 self.remote = Some(addr);
2678 self
2679 }
2680
2681 pub fn ttl(mut self, ttl: u8) -> Self {
2683 self.ttl = Some(ttl);
2684 self
2685 }
2686
2687 pub fn encap_limit(mut self, limit: u8) -> Self {
2689 self.encap_limit = Some(limit);
2690 self
2691 }
2692
2693 pub fn flowinfo(mut self, flowinfo: u32) -> Self {
2695 self.flowinfo = Some(flowinfo);
2696 self
2697 }
2698
2699 pub fn link(mut self, link: impl Into<String>) -> Self {
2701 self.link = Some(InterfaceRef::Name(link.into()));
2702 self
2703 }
2704
2705 pub fn link_index(mut self, index: u32) -> Self {
2709 self.link = Some(InterfaceRef::Index(index));
2710 self
2711 }
2712}
2713
2714impl LinkConfig for Ip6GreLink {
2715 fn name(&self) -> &str {
2716 &self.name
2717 }
2718
2719 fn kind(&self) -> &str {
2720 "ip6gre"
2721 }
2722
2723 fn parent_ref(&self) -> Option<&InterfaceRef> {
2724 self.link.as_ref()
2725 }
2726
2727 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
2728 write_ifname(builder, &self.name);
2730
2731 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
2733 builder.append_attr_str(IflaInfo::Kind as u16, "ip6gre");
2734
2735 let data = builder.nest_start(IflaInfo::Data as u16);
2737
2738 if let Some(idx) = parent_index {
2739 builder.append_attr_u32(gre_attr::IFLA_GRE_LINK, idx);
2740 }
2741 if let Some(local) = self.local {
2742 builder.append_attr(gre_attr::IFLA_GRE_LOCAL, &local.octets());
2743 }
2744 if let Some(remote) = self.remote {
2745 builder.append_attr(gre_attr::IFLA_GRE_REMOTE, &remote.octets());
2746 }
2747 if let Some(ttl) = self.ttl {
2748 builder.append_attr_u8(gre_attr::IFLA_GRE_TTL, ttl);
2749 }
2750 if let Some(limit) = self.encap_limit {
2751 builder.append_attr_u8(gre_attr::IFLA_GRE_ENCAP_LIMIT, limit);
2752 }
2753 if let Some(flowinfo) = self.flowinfo {
2754 builder.append_attr_u32_be(gre_attr::IFLA_GRE_FLOWINFO, flowinfo);
2755 }
2756 if let Some(flags) = self.flags {
2757 builder.append_attr_u32(gre_attr::IFLA_GRE_FLAGS, flags);
2758 }
2759
2760 builder.nest_end(data);
2761 builder.nest_end(linkinfo);
2762 }
2763}
2764
2765#[derive(Debug, Clone)]
2788#[must_use = "builders do nothing unless used"]
2789pub struct Ip6GretapLink {
2790 name: String,
2791 local: Option<std::net::Ipv6Addr>,
2792 remote: Option<std::net::Ipv6Addr>,
2793 ttl: Option<u8>,
2794 encap_limit: Option<u8>,
2795 flowinfo: Option<u32>,
2796 link: Option<InterfaceRef>,
2797}
2798
2799impl Ip6GretapLink {
2800 pub fn new(name: impl Into<String>) -> Self {
2802 Self {
2803 name: name.into(),
2804 local: None,
2805 remote: None,
2806 ttl: None,
2807 encap_limit: None,
2808 flowinfo: None,
2809 link: None,
2810 }
2811 }
2812
2813 pub fn local(mut self, addr: std::net::Ipv6Addr) -> Self {
2815 self.local = Some(addr);
2816 self
2817 }
2818
2819 pub fn remote(mut self, addr: std::net::Ipv6Addr) -> Self {
2821 self.remote = Some(addr);
2822 self
2823 }
2824
2825 pub fn ttl(mut self, ttl: u8) -> Self {
2827 self.ttl = Some(ttl);
2828 self
2829 }
2830
2831 pub fn encap_limit(mut self, limit: u8) -> Self {
2833 self.encap_limit = Some(limit);
2834 self
2835 }
2836
2837 pub fn flowinfo(mut self, flowinfo: u32) -> Self {
2839 self.flowinfo = Some(flowinfo);
2840 self
2841 }
2842
2843 pub fn link(mut self, link: impl Into<String>) -> Self {
2845 self.link = Some(InterfaceRef::Name(link.into()));
2846 self
2847 }
2848
2849 pub fn link_index(mut self, index: u32) -> Self {
2853 self.link = Some(InterfaceRef::Index(index));
2854 self
2855 }
2856}
2857
2858impl LinkConfig for Ip6GretapLink {
2859 fn name(&self) -> &str {
2860 &self.name
2861 }
2862
2863 fn kind(&self) -> &str {
2864 "ip6gretap"
2865 }
2866
2867 fn parent_ref(&self) -> Option<&InterfaceRef> {
2868 self.link.as_ref()
2869 }
2870
2871 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
2872 write_ifname(builder, &self.name);
2874
2875 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
2877 builder.append_attr_str(IflaInfo::Kind as u16, "ip6gretap");
2878
2879 let data = builder.nest_start(IflaInfo::Data as u16);
2881
2882 if let Some(idx) = parent_index {
2883 builder.append_attr_u32(gre_attr::IFLA_GRE_LINK, idx);
2884 }
2885 if let Some(local) = self.local {
2886 builder.append_attr(gre_attr::IFLA_GRE_LOCAL, &local.octets());
2887 }
2888 if let Some(remote) = self.remote {
2889 builder.append_attr(gre_attr::IFLA_GRE_REMOTE, &remote.octets());
2890 }
2891 if let Some(ttl) = self.ttl {
2892 builder.append_attr_u8(gre_attr::IFLA_GRE_TTL, ttl);
2893 }
2894 if let Some(limit) = self.encap_limit {
2895 builder.append_attr_u8(gre_attr::IFLA_GRE_ENCAP_LIMIT, limit);
2896 }
2897 if let Some(flowinfo) = self.flowinfo {
2898 builder.append_attr_u32_be(gre_attr::IFLA_GRE_FLOWINFO, flowinfo);
2899 }
2900
2901 builder.nest_end(data);
2902 builder.nest_end(linkinfo);
2903 }
2904}
2905
2906#[deprecated(note = "use `BondMode` enum instead")]
2912pub mod bond_mode {
2913 pub const BALANCE_RR: u8 = 0;
2914 pub const ACTIVE_BACKUP: u8 = 1;
2915 pub const BALANCE_XOR: u8 = 2;
2916 pub const BROADCAST: u8 = 3;
2917 pub const LACP: u8 = 4; pub const BALANCE_TLB: u8 = 5;
2919 pub const BALANCE_ALB: u8 = 6;
2920}
2921
2922#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2926#[repr(u8)]
2927#[non_exhaustive]
2928pub enum BondMode {
2929 BalanceRr = 0,
2931 ActiveBackup = 1,
2933 BalanceXor = 2,
2935 Broadcast = 3,
2937 Lacp = 4,
2939 BalanceTlb = 5,
2941 BalanceAlb = 6,
2943}
2944
2945impl TryFrom<u8> for BondMode {
2946 type Error = super::Error;
2947 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
2948 match value {
2949 0 => Ok(Self::BalanceRr),
2950 1 => Ok(Self::ActiveBackup),
2951 2 => Ok(Self::BalanceXor),
2952 3 => Ok(Self::Broadcast),
2953 4 => Ok(Self::Lacp),
2954 5 => Ok(Self::BalanceTlb),
2955 6 => Ok(Self::BalanceAlb),
2956 _ => Err(super::Error::InvalidAttribute(format!(
2957 "unknown bond mode: {value}"
2958 ))),
2959 }
2960 }
2961}
2962
2963#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2965#[repr(u8)]
2966#[non_exhaustive]
2967pub enum XmitHashPolicy {
2968 Layer2 = 0,
2970 Layer34 = 1,
2972 Layer23 = 2,
2974 Encap23 = 3,
2976 Encap34 = 4,
2978 VlanSrcMac = 5,
2980}
2981
2982impl TryFrom<u8> for XmitHashPolicy {
2983 type Error = super::Error;
2984 fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
2985 match value {
2986 0 => Ok(Self::Layer2),
2987 1 => Ok(Self::Layer34),
2988 2 => Ok(Self::Layer23),
2989 3 => Ok(Self::Encap23),
2990 4 => Ok(Self::Encap34),
2991 5 => Ok(Self::VlanSrcMac),
2992 _ => Err(super::Error::InvalidAttribute(format!(
2993 "unknown xmit hash policy: {value}"
2994 ))),
2995 }
2996 }
2997}
2998
2999#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3001#[repr(u8)]
3002#[non_exhaustive]
3003pub enum LacpRate {
3004 Slow = 0,
3006 Fast = 1,
3008}
3009
3010#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3012#[repr(u8)]
3013pub enum PrimaryReselect {
3014 Always = 0,
3016 Better = 1,
3018 Failure = 2,
3020}
3021
3022#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3024#[repr(u8)]
3025pub enum FailOverMac {
3026 None = 0,
3028 Active = 1,
3030 Follow = 2,
3032}
3033
3034#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3036#[repr(u8)]
3037pub enum ArpValidate {
3038 None = 0,
3040 Active = 1,
3042 Backup = 2,
3044 All = 3,
3046 FilterActive = 4,
3048 FilterBackup = 5,
3050}
3051
3052#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3054#[repr(u8)]
3055pub enum AdSelect {
3056 Stable = 0,
3058 Bandwidth = 1,
3060 Count = 2,
3062}
3063
3064#[allow(dead_code)]
3066mod bond_attr {
3067 pub const IFLA_BOND_MODE: u16 = 1;
3068 pub const IFLA_BOND_ACTIVE_SLAVE: u16 = 2;
3069 pub const IFLA_BOND_MIIMON: u16 = 3;
3070 pub const IFLA_BOND_UPDELAY: u16 = 4;
3071 pub const IFLA_BOND_DOWNDELAY: u16 = 5;
3072 pub const IFLA_BOND_USE_CARRIER: u16 = 6;
3073 pub const IFLA_BOND_ARP_INTERVAL: u16 = 7;
3074 pub const IFLA_BOND_ARP_IP_TARGET: u16 = 8;
3075 pub const IFLA_BOND_ARP_VALIDATE: u16 = 9;
3076 pub const IFLA_BOND_ARP_ALL_TARGETS: u16 = 10;
3077 pub const IFLA_BOND_PRIMARY: u16 = 11;
3078 pub const IFLA_BOND_PRIMARY_RESELECT: u16 = 12;
3079 pub const IFLA_BOND_FAIL_OVER_MAC: u16 = 13;
3080 pub const IFLA_BOND_XMIT_HASH_POLICY: u16 = 14;
3081 pub const IFLA_BOND_RESEND_IGMP: u16 = 15;
3082 pub const IFLA_BOND_NUM_PEER_NOTIF: u16 = 16;
3083 pub const IFLA_BOND_ALL_SLAVES_ACTIVE: u16 = 17;
3084 pub const IFLA_BOND_MIN_LINKS: u16 = 18;
3085 pub const IFLA_BOND_LP_INTERVAL: u16 = 19;
3086 pub const IFLA_BOND_PACKETS_PER_SLAVE: u16 = 20;
3087 pub const IFLA_BOND_AD_LACP_RATE: u16 = 21;
3088 pub const IFLA_BOND_AD_SELECT: u16 = 22;
3089 pub const IFLA_BOND_AD_INFO: u16 = 23;
3090 pub const IFLA_BOND_AD_ACTOR_SYS_PRIO: u16 = 24;
3091 pub const IFLA_BOND_AD_USER_PORT_KEY: u16 = 25;
3092 pub const IFLA_BOND_AD_ACTOR_SYSTEM: u16 = 26;
3093 pub const IFLA_BOND_TLB_DYNAMIC_LB: u16 = 27;
3094 pub const IFLA_BOND_PEER_NOTIF_DELAY: u16 = 28;
3095 pub const IFLA_BOND_AD_LACP_ACTIVE: u16 = 29;
3096 pub const IFLA_BOND_MISSED_MAX: u16 = 30;
3097 pub const IFLA_BOND_NS_IP6_TARGET: u16 = 31;
3098 pub const IFLA_BOND_COUPLED_CONTROL: u16 = 32;
3099}
3100
3101#[derive(Debug, Clone)]
3128#[must_use = "builders do nothing unless used"]
3129pub struct BondLink {
3130 name: String,
3131 mode: BondMode,
3132 mtu: Option<u32>,
3133 address: Option<[u8; 6]>,
3134
3135 miimon: Option<u32>,
3137 updelay: Option<u32>,
3138 downdelay: Option<u32>,
3139 use_carrier: Option<bool>,
3140
3141 arp_interval: Option<u32>,
3143 arp_ip_targets: Vec<Ipv4Addr>,
3144 arp_validate: Option<ArpValidate>,
3145 arp_all_targets: Option<u32>,
3146
3147 primary_reselect: Option<PrimaryReselect>,
3149 fail_over_mac: Option<FailOverMac>,
3150
3151 xmit_hash_policy: Option<XmitHashPolicy>,
3153 min_links: Option<u32>,
3154 packets_per_slave: Option<u32>,
3155
3156 lacp_rate: Option<LacpRate>,
3158 ad_select: Option<AdSelect>,
3159 ad_actor_sys_prio: Option<u16>,
3160 ad_user_port_key: Option<u16>,
3161 ad_actor_system: Option<[u8; 6]>,
3162 lacp_active: Option<bool>,
3163
3164 all_slaves_active: Option<bool>,
3166 resend_igmp: Option<u32>,
3167 num_peer_notif: Option<u8>,
3168 lp_interval: Option<u32>,
3169 tlb_dynamic_lb: Option<bool>,
3170 peer_notif_delay: Option<u32>,
3171 missed_max: Option<u8>,
3172 coupled_control: Option<bool>,
3173}
3174
3175impl BondLink {
3176 pub fn new(name: impl Into<String>) -> Self {
3178 Self {
3179 name: name.into(),
3180 mode: BondMode::BalanceRr,
3181 mtu: None,
3182 address: None,
3183 miimon: None,
3184 updelay: None,
3185 downdelay: None,
3186 use_carrier: None,
3187 arp_interval: None,
3188 arp_ip_targets: Vec::new(),
3189 arp_validate: None,
3190 arp_all_targets: None,
3191 primary_reselect: None,
3192 fail_over_mac: None,
3193 xmit_hash_policy: None,
3194 min_links: None,
3195 packets_per_slave: None,
3196 lacp_rate: None,
3197 ad_select: None,
3198 ad_actor_sys_prio: None,
3199 ad_user_port_key: None,
3200 ad_actor_system: None,
3201 lacp_active: None,
3202 all_slaves_active: None,
3203 resend_igmp: None,
3204 num_peer_notif: None,
3205 lp_interval: None,
3206 tlb_dynamic_lb: None,
3207 peer_notif_delay: None,
3208 missed_max: None,
3209 coupled_control: None,
3210 }
3211 }
3212
3213 pub fn mode(mut self, mode: BondMode) -> Self {
3215 self.mode = mode;
3216 self
3217 }
3218
3219 pub fn miimon(mut self, ms: u32) -> Self {
3221 self.miimon = Some(ms);
3222 self
3223 }
3224
3225 pub fn updelay(mut self, ms: u32) -> Self {
3227 self.updelay = Some(ms);
3228 self
3229 }
3230
3231 pub fn downdelay(mut self, ms: u32) -> Self {
3233 self.downdelay = Some(ms);
3234 self
3235 }
3236
3237 pub fn use_carrier(mut self, enabled: bool) -> Self {
3239 self.use_carrier = Some(enabled);
3240 self
3241 }
3242
3243 pub fn min_links(mut self, n: u32) -> Self {
3245 self.min_links = Some(n);
3246 self
3247 }
3248
3249 pub fn xmit_hash_policy(mut self, policy: XmitHashPolicy) -> Self {
3251 self.xmit_hash_policy = Some(policy);
3252 self
3253 }
3254
3255 pub fn lacp_rate(mut self, rate: LacpRate) -> Self {
3257 self.lacp_rate = Some(rate);
3258 self
3259 }
3260
3261 pub fn ad_select(mut self, select: AdSelect) -> Self {
3263 self.ad_select = Some(select);
3264 self
3265 }
3266
3267 pub fn arp_interval(mut self, ms: u32) -> Self {
3269 self.arp_interval = Some(ms);
3270 self
3271 }
3272
3273 pub fn arp_ip_target(mut self, addr: Ipv4Addr) -> Self {
3275 self.arp_ip_targets.push(addr);
3276 self
3277 }
3278
3279 pub fn arp_validate(mut self, validate: ArpValidate) -> Self {
3281 self.arp_validate = Some(validate);
3282 self
3283 }
3284
3285 pub fn primary_reselect(mut self, policy: PrimaryReselect) -> Self {
3287 self.primary_reselect = Some(policy);
3288 self
3289 }
3290
3291 pub fn fail_over_mac(mut self, policy: FailOverMac) -> Self {
3293 self.fail_over_mac = Some(policy);
3294 self
3295 }
3296
3297 pub fn all_slaves_active(mut self, enabled: bool) -> Self {
3299 self.all_slaves_active = Some(enabled);
3300 self
3301 }
3302
3303 pub fn tlb_dynamic_lb(mut self, enabled: bool) -> Self {
3305 self.tlb_dynamic_lb = Some(enabled);
3306 self
3307 }
3308
3309 pub fn mtu(mut self, mtu: u32) -> Self {
3311 self.mtu = Some(mtu);
3312 self
3313 }
3314
3315 pub fn address(mut self, address: [u8; 6]) -> Self {
3317 self.address = Some(address);
3318 self
3319 }
3320
3321 pub fn ad_actor_sys_prio(mut self, prio: u16) -> Self {
3323 self.ad_actor_sys_prio = Some(prio);
3324 self
3325 }
3326
3327 pub fn ad_user_port_key(mut self, key: u16) -> Self {
3329 self.ad_user_port_key = Some(key);
3330 self
3331 }
3332
3333 pub fn ad_actor_system(mut self, mac: [u8; 6]) -> Self {
3335 self.ad_actor_system = Some(mac);
3336 self
3337 }
3338
3339 pub fn lacp_active(mut self, enabled: bool) -> Self {
3341 self.lacp_active = Some(enabled);
3342 self
3343 }
3344
3345 pub fn num_peer_notif(mut self, n: u8) -> Self {
3347 self.num_peer_notif = Some(n);
3348 self
3349 }
3350
3351 pub fn resend_igmp(mut self, count: u32) -> Self {
3353 self.resend_igmp = Some(count);
3354 self
3355 }
3356
3357 pub fn lp_interval(mut self, ms: u32) -> Self {
3359 self.lp_interval = Some(ms);
3360 self
3361 }
3362
3363 pub fn packets_per_slave(mut self, n: u32) -> Self {
3365 self.packets_per_slave = Some(n);
3366 self
3367 }
3368
3369 pub fn peer_notif_delay(mut self, ms: u32) -> Self {
3371 self.peer_notif_delay = Some(ms);
3372 self
3373 }
3374
3375 pub fn missed_max(mut self, n: u8) -> Self {
3377 self.missed_max = Some(n);
3378 self
3379 }
3380
3381 pub fn coupled_control(mut self, enabled: bool) -> Self {
3383 self.coupled_control = Some(enabled);
3384 self
3385 }
3386}
3387
3388impl LinkConfig for BondLink {
3389 fn name(&self) -> &str {
3390 &self.name
3391 }
3392
3393 fn kind(&self) -> &str {
3394 "bond"
3395 }
3396
3397 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
3398 write_ifname(builder, &self.name);
3399
3400 if let Some(mtu) = self.mtu {
3401 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
3402 }
3403 if let Some(ref addr) = self.address {
3404 builder.append_attr(IflaAttr::Address as u16, addr);
3405 }
3406
3407 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
3408 builder.append_attr_str(IflaInfo::Kind as u16, "bond");
3409
3410 let data = builder.nest_start(IflaInfo::Data as u16);
3411 builder.append_attr_u8(bond_attr::IFLA_BOND_MODE, self.mode as u8);
3412
3413 if let Some(v) = self.miimon {
3414 builder.append_attr_u32(bond_attr::IFLA_BOND_MIIMON, v);
3415 }
3416 if let Some(v) = self.updelay {
3417 builder.append_attr_u32(bond_attr::IFLA_BOND_UPDELAY, v);
3418 }
3419 if let Some(v) = self.downdelay {
3420 builder.append_attr_u32(bond_attr::IFLA_BOND_DOWNDELAY, v);
3421 }
3422 if let Some(v) = self.use_carrier {
3423 builder.append_attr_u8(bond_attr::IFLA_BOND_USE_CARRIER, v as u8);
3424 }
3425 if let Some(v) = self.arp_interval {
3426 builder.append_attr_u32(bond_attr::IFLA_BOND_ARP_INTERVAL, v);
3427 }
3428 if let Some(v) = self.arp_validate {
3429 builder.append_attr_u32(bond_attr::IFLA_BOND_ARP_VALIDATE, v as u32);
3430 }
3431 if let Some(v) = self.arp_all_targets {
3432 builder.append_attr_u32(bond_attr::IFLA_BOND_ARP_ALL_TARGETS, v);
3433 }
3434 if let Some(v) = self.primary_reselect {
3435 builder.append_attr_u8(bond_attr::IFLA_BOND_PRIMARY_RESELECT, v as u8);
3436 }
3437 if let Some(v) = self.fail_over_mac {
3438 builder.append_attr_u8(bond_attr::IFLA_BOND_FAIL_OVER_MAC, v as u8);
3439 }
3440 if let Some(v) = self.xmit_hash_policy {
3441 builder.append_attr_u8(bond_attr::IFLA_BOND_XMIT_HASH_POLICY, v as u8);
3442 }
3443 if let Some(v) = self.resend_igmp {
3444 builder.append_attr_u32(bond_attr::IFLA_BOND_RESEND_IGMP, v);
3445 }
3446 if let Some(v) = self.num_peer_notif {
3447 builder.append_attr_u8(bond_attr::IFLA_BOND_NUM_PEER_NOTIF, v);
3448 }
3449 if let Some(v) = self.all_slaves_active {
3450 builder.append_attr_u8(bond_attr::IFLA_BOND_ALL_SLAVES_ACTIVE, v as u8);
3451 }
3452 if let Some(v) = self.min_links {
3453 builder.append_attr_u32(bond_attr::IFLA_BOND_MIN_LINKS, v);
3454 }
3455 if let Some(v) = self.lp_interval {
3456 builder.append_attr_u32(bond_attr::IFLA_BOND_LP_INTERVAL, v);
3457 }
3458 if let Some(v) = self.packets_per_slave {
3459 builder.append_attr_u32(bond_attr::IFLA_BOND_PACKETS_PER_SLAVE, v);
3460 }
3461 if let Some(v) = self.lacp_rate {
3462 builder.append_attr_u8(bond_attr::IFLA_BOND_AD_LACP_RATE, v as u8);
3463 }
3464 if let Some(v) = self.ad_select {
3465 builder.append_attr_u8(bond_attr::IFLA_BOND_AD_SELECT, v as u8);
3466 }
3467 if let Some(v) = self.ad_actor_sys_prio {
3468 builder.append_attr_u16(bond_attr::IFLA_BOND_AD_ACTOR_SYS_PRIO, v);
3469 }
3470 if let Some(v) = self.ad_user_port_key {
3471 builder.append_attr_u16(bond_attr::IFLA_BOND_AD_USER_PORT_KEY, v);
3472 }
3473 if let Some(ref mac) = self.ad_actor_system {
3474 builder.append_attr(bond_attr::IFLA_BOND_AD_ACTOR_SYSTEM, mac);
3475 }
3476 if let Some(v) = self.tlb_dynamic_lb {
3477 builder.append_attr_u8(bond_attr::IFLA_BOND_TLB_DYNAMIC_LB, v as u8);
3478 }
3479 if let Some(v) = self.peer_notif_delay {
3480 builder.append_attr_u32(bond_attr::IFLA_BOND_PEER_NOTIF_DELAY, v);
3481 }
3482 if let Some(v) = self.lacp_active {
3483 builder.append_attr_u8(bond_attr::IFLA_BOND_AD_LACP_ACTIVE, v as u8);
3484 }
3485 if let Some(v) = self.missed_max {
3486 builder.append_attr_u8(bond_attr::IFLA_BOND_MISSED_MAX, v);
3487 }
3488 if let Some(v) = self.coupled_control {
3489 builder.append_attr_u8(bond_attr::IFLA_BOND_COUPLED_CONTROL, v as u8);
3490 }
3491
3492 if !self.arp_ip_targets.is_empty() {
3494 let targets = builder.nest_start(bond_attr::IFLA_BOND_ARP_IP_TARGET);
3495 for (i, addr) in self.arp_ip_targets.iter().enumerate() {
3496 builder.append_attr(i as u16, &addr.octets());
3497 }
3498 builder.nest_end(targets);
3499 }
3500
3501 builder.nest_end(data);
3502 builder.nest_end(linkinfo);
3503 }
3504}
3505
3506mod vrf_attr {
3512 pub const IFLA_VRF_TABLE: u16 = 1;
3513}
3514
3515#[derive(Debug, Clone)]
3527#[must_use = "builders do nothing unless used"]
3528pub struct VrfLink {
3529 name: String,
3530 table: u32,
3531 mtu: Option<u32>,
3532}
3533
3534impl VrfLink {
3535 pub fn new(name: &str, table: u32) -> Self {
3537 Self {
3538 name: name.to_string(),
3539 table,
3540 mtu: None,
3541 }
3542 }
3543
3544 pub fn mtu(mut self, mtu: u32) -> Self {
3546 self.mtu = Some(mtu);
3547 self
3548 }
3549}
3550
3551impl LinkConfig for VrfLink {
3552 fn name(&self) -> &str {
3553 &self.name
3554 }
3555
3556 fn kind(&self) -> &str {
3557 "vrf"
3558 }
3559
3560 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
3561 write_ifname(builder, &self.name);
3562
3563 if let Some(mtu) = self.mtu {
3564 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
3565 }
3566
3567 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
3568 builder.append_attr_str(IflaInfo::Kind as u16, "vrf");
3569
3570 let data = builder.nest_start(IflaInfo::Data as u16);
3571 builder.append_attr_u32(vrf_attr::IFLA_VRF_TABLE, self.table);
3572 builder.nest_end(data);
3573
3574 builder.nest_end(linkinfo);
3575 }
3576}
3577
3578#[derive(Debug, Clone)]
3598#[must_use = "builders do nothing unless used"]
3599pub struct GreLink {
3600 name: String,
3601 local: Option<Ipv4Addr>,
3602 remote: Option<Ipv4Addr>,
3603 ttl: Option<u8>,
3604 tos: Option<u8>,
3605 ikey: Option<u32>,
3606 okey: Option<u32>,
3607 pmtudisc: Option<bool>,
3608 ignore_df: Option<bool>,
3609 fwmark: Option<u32>,
3610 mtu: Option<u32>,
3611 link: Option<InterfaceRef>,
3612}
3613
3614impl GreLink {
3615 pub fn new(name: impl Into<String>) -> Self {
3617 Self {
3618 name: name.into(),
3619 local: None,
3620 remote: None,
3621 ttl: None,
3622 tos: None,
3623 ikey: None,
3624 okey: None,
3625 pmtudisc: None,
3626 ignore_df: None,
3627 fwmark: None,
3628 mtu: None,
3629 link: None,
3630 }
3631 }
3632
3633 pub fn local(mut self, addr: Ipv4Addr) -> Self {
3635 self.local = Some(addr);
3636 self
3637 }
3638
3639 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
3641 self.remote = Some(addr);
3642 self
3643 }
3644
3645 pub fn ttl(mut self, ttl: u8) -> Self {
3647 self.ttl = Some(ttl);
3648 self
3649 }
3650
3651 pub fn tos(mut self, tos: u8) -> Self {
3653 self.tos = Some(tos);
3654 self
3655 }
3656
3657 pub fn ikey(mut self, key: u32) -> Self {
3659 self.ikey = Some(key);
3660 self
3661 }
3662
3663 pub fn okey(mut self, key: u32) -> Self {
3665 self.okey = Some(key);
3666 self
3667 }
3668
3669 pub fn key(self, key: u32) -> Self {
3671 self.ikey(key).okey(key)
3672 }
3673
3674 pub fn pmtudisc(mut self, enabled: bool) -> Self {
3676 self.pmtudisc = Some(enabled);
3677 self
3678 }
3679
3680 pub fn ignore_df(mut self, enabled: bool) -> Self {
3682 self.ignore_df = Some(enabled);
3683 self
3684 }
3685
3686 pub fn fwmark(mut self, mark: u32) -> Self {
3688 self.fwmark = Some(mark);
3689 self
3690 }
3691
3692 pub fn mtu(mut self, mtu: u32) -> Self {
3694 self.mtu = Some(mtu);
3695 self
3696 }
3697
3698 pub fn link(mut self, iface: impl Into<String>) -> Self {
3700 self.link = Some(InterfaceRef::Name(iface.into()));
3701 self
3702 }
3703
3704 pub fn link_index(mut self, index: u32) -> Self {
3706 self.link = Some(InterfaceRef::Index(index));
3707 self
3708 }
3709}
3710
3711impl LinkConfig for GreLink {
3712 fn name(&self) -> &str {
3713 &self.name
3714 }
3715
3716 fn kind(&self) -> &str {
3717 "gre"
3718 }
3719
3720 fn parent_ref(&self) -> Option<&InterfaceRef> {
3721 self.link.as_ref()
3722 }
3723
3724 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
3725 write_ifname(builder, &self.name);
3726
3727 if let Some(mtu) = self.mtu {
3728 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
3729 }
3730 if let Some(idx) = parent_index {
3731 builder.append_attr_u32(IflaAttr::Link as u16, idx);
3732 }
3733
3734 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
3735 builder.append_attr_str(IflaInfo::Kind as u16, "gre");
3736
3737 let data = builder.nest_start(IflaInfo::Data as u16);
3738 if let Some(addr) = self.local {
3739 builder.append_attr(gre_attr::IFLA_GRE_LOCAL, &addr.octets());
3740 }
3741 if let Some(addr) = self.remote {
3742 builder.append_attr(gre_attr::IFLA_GRE_REMOTE, &addr.octets());
3743 }
3744 if let Some(ttl) = self.ttl {
3745 builder.append_attr_u8(gre_attr::IFLA_GRE_TTL, ttl);
3746 }
3747 if let Some(tos) = self.tos {
3748 builder.append_attr_u8(gre_attr::IFLA_GRE_TOS, tos);
3749 }
3750 if let Some(key) = self.ikey {
3751 builder.append_attr_u16(gre_attr::IFLA_GRE_IFLAGS, gre_attr::GRE_KEY);
3752 builder.append_attr_u32(gre_attr::IFLA_GRE_IKEY, key);
3753 }
3754 if let Some(key) = self.okey {
3755 builder.append_attr_u16(gre_attr::IFLA_GRE_OFLAGS, gre_attr::GRE_KEY);
3756 builder.append_attr_u32(gre_attr::IFLA_GRE_OKEY, key);
3757 }
3758 if let Some(pmtu) = self.pmtudisc {
3759 builder.append_attr_u8(gre_attr::IFLA_GRE_PMTUDISC, pmtu as u8);
3760 }
3761 if let Some(ignore) = self.ignore_df {
3762 builder.append_attr_u8(gre_attr::IFLA_GRE_IGNORE_DF, ignore as u8);
3763 }
3764 if let Some(mark) = self.fwmark {
3765 builder.append_attr_u32(gre_attr::IFLA_GRE_FWMARK, mark);
3766 }
3767 builder.nest_end(data);
3768
3769 builder.nest_end(linkinfo);
3770 }
3771}
3772
3773#[derive(Debug, Clone)]
3792#[must_use = "builders do nothing unless used"]
3793pub struct GretapLink {
3794 name: String,
3795 local: Option<Ipv4Addr>,
3796 remote: Option<Ipv4Addr>,
3797 ttl: Option<u8>,
3798 tos: Option<u8>,
3799 ikey: Option<u32>,
3800 okey: Option<u32>,
3801 pmtudisc: Option<bool>,
3802 fwmark: Option<u32>,
3803 mtu: Option<u32>,
3804 link: Option<InterfaceRef>,
3805}
3806
3807impl GretapLink {
3808 pub fn new(name: impl Into<String>) -> Self {
3810 Self {
3811 name: name.into(),
3812 local: None,
3813 remote: None,
3814 ttl: None,
3815 tos: None,
3816 ikey: None,
3817 okey: None,
3818 pmtudisc: None,
3819 fwmark: None,
3820 mtu: None,
3821 link: None,
3822 }
3823 }
3824
3825 pub fn local(mut self, addr: Ipv4Addr) -> Self {
3827 self.local = Some(addr);
3828 self
3829 }
3830
3831 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
3833 self.remote = Some(addr);
3834 self
3835 }
3836
3837 pub fn ttl(mut self, ttl: u8) -> Self {
3839 self.ttl = Some(ttl);
3840 self
3841 }
3842
3843 pub fn tos(mut self, tos: u8) -> Self {
3845 self.tos = Some(tos);
3846 self
3847 }
3848
3849 pub fn ikey(mut self, key: u32) -> Self {
3851 self.ikey = Some(key);
3852 self
3853 }
3854
3855 pub fn okey(mut self, key: u32) -> Self {
3857 self.okey = Some(key);
3858 self
3859 }
3860
3861 pub fn key(self, key: u32) -> Self {
3863 self.ikey(key).okey(key)
3864 }
3865
3866 pub fn pmtudisc(mut self, enabled: bool) -> Self {
3868 self.pmtudisc = Some(enabled);
3869 self
3870 }
3871
3872 pub fn fwmark(mut self, mark: u32) -> Self {
3874 self.fwmark = Some(mark);
3875 self
3876 }
3877
3878 pub fn mtu(mut self, mtu: u32) -> Self {
3880 self.mtu = Some(mtu);
3881 self
3882 }
3883
3884 pub fn link(mut self, iface: impl Into<String>) -> Self {
3886 self.link = Some(InterfaceRef::Name(iface.into()));
3887 self
3888 }
3889
3890 pub fn link_index(mut self, index: u32) -> Self {
3892 self.link = Some(InterfaceRef::Index(index));
3893 self
3894 }
3895}
3896
3897impl LinkConfig for GretapLink {
3898 fn name(&self) -> &str {
3899 &self.name
3900 }
3901
3902 fn kind(&self) -> &str {
3903 "gretap"
3904 }
3905
3906 fn parent_ref(&self) -> Option<&InterfaceRef> {
3907 self.link.as_ref()
3908 }
3909
3910 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
3911 write_ifname(builder, &self.name);
3912
3913 if let Some(mtu) = self.mtu {
3914 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
3915 }
3916 if let Some(idx) = parent_index {
3917 builder.append_attr_u32(IflaAttr::Link as u16, idx);
3918 }
3919
3920 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
3921 builder.append_attr_str(IflaInfo::Kind as u16, "gretap");
3922
3923 let data = builder.nest_start(IflaInfo::Data as u16);
3924 if let Some(addr) = self.local {
3925 builder.append_attr(gre_attr::IFLA_GRE_LOCAL, &addr.octets());
3926 }
3927 if let Some(addr) = self.remote {
3928 builder.append_attr(gre_attr::IFLA_GRE_REMOTE, &addr.octets());
3929 }
3930 if let Some(ttl) = self.ttl {
3931 builder.append_attr_u8(gre_attr::IFLA_GRE_TTL, ttl);
3932 }
3933 if let Some(tos) = self.tos {
3934 builder.append_attr_u8(gre_attr::IFLA_GRE_TOS, tos);
3935 }
3936 if let Some(key) = self.ikey {
3937 builder.append_attr_u16(gre_attr::IFLA_GRE_IFLAGS, gre_attr::GRE_KEY);
3938 builder.append_attr_u32(gre_attr::IFLA_GRE_IKEY, key);
3939 }
3940 if let Some(key) = self.okey {
3941 builder.append_attr_u16(gre_attr::IFLA_GRE_OFLAGS, gre_attr::GRE_KEY);
3942 builder.append_attr_u32(gre_attr::IFLA_GRE_OKEY, key);
3943 }
3944 if let Some(pmtu) = self.pmtudisc {
3945 builder.append_attr_u8(gre_attr::IFLA_GRE_PMTUDISC, pmtu as u8);
3946 }
3947 if let Some(mark) = self.fwmark {
3948 builder.append_attr_u32(gre_attr::IFLA_GRE_FWMARK, mark);
3949 }
3950 builder.nest_end(data);
3951
3952 builder.nest_end(linkinfo);
3953 }
3954}
3955
3956#[derive(Debug, Clone)]
3975#[must_use = "builders do nothing unless used"]
3976pub struct IpipLink {
3977 name: String,
3978 local: Option<Ipv4Addr>,
3979 remote: Option<Ipv4Addr>,
3980 ttl: Option<u8>,
3981 tos: Option<u8>,
3982 pmtudisc: Option<bool>,
3983 fwmark: Option<u32>,
3984 mtu: Option<u32>,
3985 link: Option<InterfaceRef>,
3986}
3987
3988impl IpipLink {
3989 pub fn new(name: impl Into<String>) -> Self {
3991 Self {
3992 name: name.into(),
3993 local: None,
3994 remote: None,
3995 ttl: None,
3996 tos: None,
3997 pmtudisc: None,
3998 fwmark: None,
3999 mtu: None,
4000 link: None,
4001 }
4002 }
4003
4004 pub fn local(mut self, addr: Ipv4Addr) -> Self {
4006 self.local = Some(addr);
4007 self
4008 }
4009
4010 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
4012 self.remote = Some(addr);
4013 self
4014 }
4015
4016 pub fn ttl(mut self, ttl: u8) -> Self {
4018 self.ttl = Some(ttl);
4019 self
4020 }
4021
4022 pub fn tos(mut self, tos: u8) -> Self {
4024 self.tos = Some(tos);
4025 self
4026 }
4027
4028 pub fn pmtudisc(mut self, enabled: bool) -> Self {
4030 self.pmtudisc = Some(enabled);
4031 self
4032 }
4033
4034 pub fn fwmark(mut self, mark: u32) -> Self {
4036 self.fwmark = Some(mark);
4037 self
4038 }
4039
4040 pub fn mtu(mut self, mtu: u32) -> Self {
4042 self.mtu = Some(mtu);
4043 self
4044 }
4045
4046 pub fn link(mut self, iface: impl Into<String>) -> Self {
4048 self.link = Some(InterfaceRef::Name(iface.into()));
4049 self
4050 }
4051
4052 pub fn link_index(mut self, index: u32) -> Self {
4054 self.link = Some(InterfaceRef::Index(index));
4055 self
4056 }
4057}
4058
4059impl LinkConfig for IpipLink {
4060 fn name(&self) -> &str {
4061 &self.name
4062 }
4063
4064 fn kind(&self) -> &str {
4065 "ipip"
4066 }
4067
4068 fn parent_ref(&self) -> Option<&InterfaceRef> {
4069 self.link.as_ref()
4070 }
4071
4072 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
4073 write_ifname(builder, &self.name);
4074
4075 if let Some(mtu) = self.mtu {
4076 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
4077 }
4078 if let Some(idx) = parent_index {
4079 builder.append_attr_u32(IflaAttr::Link as u16, idx);
4080 }
4081
4082 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
4083 builder.append_attr_str(IflaInfo::Kind as u16, "ipip");
4084
4085 let data = builder.nest_start(IflaInfo::Data as u16);
4086 if let Some(addr) = self.local {
4087 builder.append_attr(iptun_attr::IFLA_IPTUN_LOCAL, &addr.octets());
4088 }
4089 if let Some(addr) = self.remote {
4090 builder.append_attr(iptun_attr::IFLA_IPTUN_REMOTE, &addr.octets());
4091 }
4092 if let Some(ttl) = self.ttl {
4093 builder.append_attr_u8(iptun_attr::IFLA_IPTUN_TTL, ttl);
4094 }
4095 if let Some(tos) = self.tos {
4096 builder.append_attr_u8(iptun_attr::IFLA_IPTUN_TOS, tos);
4097 }
4098 if let Some(pmtu) = self.pmtudisc {
4099 builder.append_attr_u8(iptun_attr::IFLA_IPTUN_PMTUDISC, pmtu as u8);
4100 }
4101 if let Some(mark) = self.fwmark {
4102 builder.append_attr_u32(iptun_attr::IFLA_IPTUN_FWMARK, mark);
4103 }
4104 builder.nest_end(data);
4105
4106 builder.nest_end(linkinfo);
4107 }
4108}
4109
4110#[derive(Debug, Clone)]
4131#[must_use = "builders do nothing unless used"]
4132pub struct SitLink {
4133 name: String,
4134 local: Option<Ipv4Addr>,
4135 remote: Option<Ipv4Addr>,
4136 ttl: Option<u8>,
4137 tos: Option<u8>,
4138 pmtudisc: Option<bool>,
4139 fwmark: Option<u32>,
4140 isatap: bool,
4141 mtu: Option<u32>,
4142 link: Option<InterfaceRef>,
4143}
4144
4145impl SitLink {
4146 pub fn new(name: impl Into<String>) -> Self {
4148 Self {
4149 name: name.into(),
4150 local: None,
4151 remote: None,
4152 ttl: None,
4153 tos: None,
4154 pmtudisc: None,
4155 fwmark: None,
4156 isatap: false,
4157 mtu: None,
4158 link: None,
4159 }
4160 }
4161
4162 pub fn local(mut self, addr: Ipv4Addr) -> Self {
4164 self.local = Some(addr);
4165 self
4166 }
4167
4168 pub fn remote(mut self, addr: Ipv4Addr) -> Self {
4170 self.remote = Some(addr);
4171 self
4172 }
4173
4174 pub fn ttl(mut self, ttl: u8) -> Self {
4176 self.ttl = Some(ttl);
4177 self
4178 }
4179
4180 pub fn tos(mut self, tos: u8) -> Self {
4182 self.tos = Some(tos);
4183 self
4184 }
4185
4186 pub fn pmtudisc(mut self, enabled: bool) -> Self {
4188 self.pmtudisc = Some(enabled);
4189 self
4190 }
4191
4192 pub fn fwmark(mut self, mark: u32) -> Self {
4194 self.fwmark = Some(mark);
4195 self
4196 }
4197
4198 pub fn isatap(mut self) -> Self {
4200 self.isatap = true;
4201 self
4202 }
4203
4204 pub fn mtu(mut self, mtu: u32) -> Self {
4206 self.mtu = Some(mtu);
4207 self
4208 }
4209
4210 pub fn link(mut self, iface: impl Into<String>) -> Self {
4212 self.link = Some(InterfaceRef::Name(iface.into()));
4213 self
4214 }
4215
4216 pub fn link_index(mut self, index: u32) -> Self {
4218 self.link = Some(InterfaceRef::Index(index));
4219 self
4220 }
4221}
4222
4223impl LinkConfig for SitLink {
4224 fn name(&self) -> &str {
4225 &self.name
4226 }
4227
4228 fn kind(&self) -> &str {
4229 "sit"
4230 }
4231
4232 fn parent_ref(&self) -> Option<&InterfaceRef> {
4233 self.link.as_ref()
4234 }
4235
4236 fn write_to(&self, builder: &mut MessageBuilder, parent_index: Option<u32>) {
4237 write_ifname(builder, &self.name);
4238
4239 if let Some(mtu) = self.mtu {
4240 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
4241 }
4242 if let Some(idx) = parent_index {
4243 builder.append_attr_u32(IflaAttr::Link as u16, idx);
4244 }
4245
4246 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
4247 builder.append_attr_str(IflaInfo::Kind as u16, "sit");
4248
4249 let data = builder.nest_start(IflaInfo::Data as u16);
4250 if let Some(addr) = self.local {
4251 builder.append_attr(iptun_attr::IFLA_IPTUN_LOCAL, &addr.octets());
4252 }
4253 if let Some(addr) = self.remote {
4254 builder.append_attr(iptun_attr::IFLA_IPTUN_REMOTE, &addr.octets());
4255 }
4256 if let Some(ttl) = self.ttl {
4257 builder.append_attr_u8(iptun_attr::IFLA_IPTUN_TTL, ttl);
4258 }
4259 if let Some(tos) = self.tos {
4260 builder.append_attr_u8(iptun_attr::IFLA_IPTUN_TOS, tos);
4261 }
4262 if let Some(pmtu) = self.pmtudisc {
4263 builder.append_attr_u8(iptun_attr::IFLA_IPTUN_PMTUDISC, pmtu as u8);
4264 }
4265 if let Some(mark) = self.fwmark {
4266 builder.append_attr_u32(iptun_attr::IFLA_IPTUN_FWMARK, mark);
4267 }
4268 if self.isatap {
4269 builder.append_attr_u16(iptun_attr::IFLA_IPTUN_FLAGS, iptun_attr::SIT_ISATAP);
4270 }
4271 builder.nest_end(data);
4272
4273 builder.nest_end(linkinfo);
4274 }
4275}
4276
4277#[derive(Debug, Clone)]
4300#[must_use = "builders do nothing unless used"]
4301pub struct WireguardLink {
4302 name: String,
4303 mtu: Option<u32>,
4304}
4305
4306impl WireguardLink {
4307 pub fn new(name: &str) -> Self {
4309 Self {
4310 name: name.to_string(),
4311 mtu: None,
4312 }
4313 }
4314
4315 pub fn mtu(mut self, mtu: u32) -> Self {
4317 self.mtu = Some(mtu);
4318 self
4319 }
4320}
4321
4322impl LinkConfig for WireguardLink {
4323 fn name(&self) -> &str {
4324 &self.name
4325 }
4326
4327 fn kind(&self) -> &str {
4328 "wireguard"
4329 }
4330
4331 fn write_to(&self, builder: &mut MessageBuilder, _parent_index: Option<u32>) {
4332 write_simple_link(builder, &self.name, "wireguard", self.mtu, None);
4333 }
4334}
4335
4336fn write_ifname(builder: &mut MessageBuilder, name: &str) {
4342 builder.append_attr_str(IflaAttr::Ifname as u16, name);
4343}
4344
4345fn write_simple_link(
4347 builder: &mut MessageBuilder,
4348 name: &str,
4349 kind: &str,
4350 mtu: Option<u32>,
4351 address: Option<&[u8; 6]>,
4352) {
4353 write_ifname(builder, name);
4355
4356 if let Some(mtu) = mtu {
4358 builder.append_attr_u32(IflaAttr::Mtu as u16, mtu);
4359 }
4360 if let Some(addr) = address {
4361 builder.append_attr(IflaAttr::Address as u16, addr);
4362 }
4363
4364 let linkinfo = builder.nest_start(IflaAttr::Linkinfo as u16);
4366 builder.append_attr_str(IflaInfo::Kind as u16, kind);
4367 builder.nest_end(linkinfo);
4368}
4369
4370impl Connection<Route> {
4375 pub async fn add_link<L: LinkConfig>(&self, config: L) -> Result<()> {
4395 use super::message::{NLM_F_ACK, NLM_F_REQUEST};
4396
4397 crate::util::ifname::validate(config.name()).map_err(super::error::Error::Interface)?;
4399 if let Some(peer) = config.peer_name() {
4400 crate::util::ifname::validate(peer).map_err(super::error::Error::Interface)?;
4401 }
4402
4403 let parent_index = match config.parent_ref() {
4405 Some(iface) => Some(self.resolve_interface(iface).await?),
4406 None => None,
4407 };
4408
4409 let mut builder = MessageBuilder::new(
4411 NlMsgType::RTM_NEWLINK,
4412 NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL,
4413 );
4414
4415 let ifinfo = IfInfoMsg::new();
4417 builder.append(&ifinfo);
4418
4419 let link_name = config.name().to_string();
4421 let link_kind = config.kind().to_string();
4422 config.write_to(&mut builder, parent_index);
4423
4424 self.send_ack(builder)
4425 .await
4426 .map_err(|e| e.with_context(format!("add_link({link_name}, kind={link_kind})")))
4427 }
4428
4429 pub async fn set_link_master(
4443 &self,
4444 iface: impl Into<InterfaceRef>,
4445 master: impl Into<InterfaceRef>,
4446 ) -> Result<()> {
4447 let ifindex = self.resolve_interface(&iface.into()).await?;
4448 let master_index = self.resolve_interface(&master.into()).await?;
4449 self.set_link_master_by_index(ifindex, master_index).await
4450 }
4451
4452 pub async fn set_link_master_by_index(&self, ifindex: u32, master_index: u32) -> Result<()> {
4454 use super::connection::ack_request;
4455
4456 let ifinfo = IfInfoMsg::new().with_index(ifindex as i32);
4457
4458 let mut builder = ack_request(NlMsgType::RTM_SETLINK);
4459 builder.append(&ifinfo);
4460 builder.append_attr_u32(IflaAttr::Master as u16, master_index);
4461
4462 self.send_ack(builder)
4463 .await
4464 .map_err(|e| e.with_context("set_link_master"))
4465 }
4466
4467 pub async fn enslave(
4480 &self,
4481 member: impl Into<InterfaceRef>,
4482 master: impl Into<InterfaceRef>,
4483 ) -> Result<()> {
4484 let member = member.into();
4485 let master = master.into();
4486 let member_idx = self.resolve_interface(&member).await?;
4487 let master_idx = self.resolve_interface(&master).await?;
4488 self.set_link_down_by_index(member_idx).await?;
4489 self.set_link_master_by_index(member_idx, master_idx)
4490 .await?;
4491 self.set_link_up_by_index(member_idx).await
4492 }
4493
4494 pub async fn enslave_by_index(&self, member_index: u32, master_index: u32) -> Result<()> {
4496 self.set_link_down_by_index(member_index).await?;
4497 self.set_link_master_by_index(member_index, master_index)
4498 .await?;
4499 self.set_link_up_by_index(member_index).await
4500 }
4501
4502 pub async fn set_link_nomaster(&self, iface: impl Into<InterfaceRef>) -> Result<()> {
4513 let ifindex = self.resolve_interface(&iface.into()).await?;
4514 self.set_link_nomaster_by_index(ifindex).await
4515 }
4516
4517 pub async fn set_link_nomaster_by_index(&self, ifindex: u32) -> Result<()> {
4519 use super::connection::ack_request;
4520
4521 let ifinfo = IfInfoMsg::new().with_index(ifindex as i32);
4522
4523 let mut builder = ack_request(NlMsgType::RTM_SETLINK);
4524 builder.append(&ifinfo);
4525 builder.append_attr_u32(IflaAttr::Master as u16, 0);
4526
4527 self.send_ack(builder)
4528 .await
4529 .map_err(|e| e.with_context("set_link_nomaster"))
4530 }
4531
4532 pub async fn set_link_name(
4543 &self,
4544 iface: impl Into<InterfaceRef>,
4545 new_name: &str,
4546 ) -> Result<()> {
4547 let ifindex = self.resolve_interface(&iface.into()).await?;
4548 self.set_link_name_by_index(ifindex, new_name).await
4549 }
4550
4551 pub async fn set_link_name_by_index(&self, ifindex: u32, new_name: &str) -> Result<()> {
4553 use super::connection::ack_request;
4554
4555 crate::util::ifname::validate(new_name).map_err(super::error::Error::Interface)?;
4557
4558 let ifinfo = IfInfoMsg::new().with_index(ifindex as i32);
4559
4560 let mut builder = ack_request(NlMsgType::RTM_SETLINK);
4561 builder.append(&ifinfo);
4562 builder.append_attr_str(IflaAttr::Ifname as u16, new_name);
4563
4564 self.send_ack(builder)
4565 .await
4566 .map_err(|e| e.with_context("set_link_name"))
4567 }
4568
4569 pub async fn set_link_address(
4579 &self,
4580 iface: impl Into<InterfaceRef>,
4581 address: [u8; 6],
4582 ) -> Result<()> {
4583 let ifindex = self.resolve_interface(&iface.into()).await?;
4584 self.set_link_address_by_index(ifindex, address).await
4585 }
4586
4587 pub async fn set_link_address_by_index(&self, ifindex: u32, address: [u8; 6]) -> Result<()> {
4589 use super::connection::ack_request;
4590
4591 let ifinfo = IfInfoMsg::new().with_index(ifindex as i32);
4592
4593 let mut builder = ack_request(NlMsgType::RTM_SETLINK);
4594 builder.append(&ifinfo);
4595 builder.append_attr(IflaAttr::Address as u16, &address);
4596
4597 self.send_ack(builder)
4598 .await
4599 .map_err(|e| e.with_context("set_link_address"))
4600 }
4601
4602 pub async fn set_link_netns_pid(&self, iface: impl Into<InterfaceRef>, pid: u32) -> Result<()> {
4613 let ifindex = self.resolve_interface(&iface.into()).await?;
4614 self.set_link_netns_pid_by_index(ifindex, pid).await
4615 }
4616
4617 pub async fn set_link_netns_pid_by_index(&self, ifindex: u32, pid: u32) -> Result<()> {
4619 use super::connection::ack_request;
4620
4621 let ifinfo = IfInfoMsg::new().with_index(ifindex as i32);
4622
4623 let mut builder = ack_request(NlMsgType::RTM_SETLINK);
4624 builder.append(&ifinfo);
4625 builder.append_attr_u32(IflaAttr::NetNsPid as u16, pid);
4626
4627 self.send_ack(builder)
4628 .await
4629 .map_err(|e| e.with_context("set_link_netns"))
4630 }
4631
4632 pub async fn set_link_netns_fd(&self, iface: impl Into<InterfaceRef>, fd: i32) -> Result<()> {
4636 let ifindex = self.resolve_interface(&iface.into()).await?;
4637 self.set_link_netns_fd_by_index(ifindex, fd).await
4638 }
4639
4640 pub async fn set_link_netns_fd_by_index(&self, ifindex: u32, fd: i32) -> Result<()> {
4642 use super::connection::ack_request;
4643
4644 let ifinfo = IfInfoMsg::new().with_index(ifindex as i32);
4645
4646 let mut builder = ack_request(NlMsgType::RTM_SETLINK);
4647 builder.append(&ifinfo);
4648 builder.append_attr_u32(IflaAttr::NetNsFd as u16, fd as u32);
4649
4650 self.send_ack(builder)
4651 .await
4652 .map_err(|e| e.with_context("set_link_netns"))
4653 }
4654}
4655
4656#[cfg(test)]
4657mod tests {
4658 use super::*;
4659
4660 #[test]
4661 fn test_bond_mode_try_from() {
4662 assert!(matches!(BondMode::try_from(0u8), Ok(BondMode::BalanceRr)));
4663 assert!(matches!(
4664 BondMode::try_from(1u8),
4665 Ok(BondMode::ActiveBackup)
4666 ));
4667 assert!(matches!(BondMode::try_from(2u8), Ok(BondMode::BalanceXor)));
4668 assert!(matches!(BondMode::try_from(3u8), Ok(BondMode::Broadcast)));
4669 assert!(matches!(BondMode::try_from(4u8), Ok(BondMode::Lacp)));
4670 assert!(matches!(BondMode::try_from(5u8), Ok(BondMode::BalanceTlb)));
4671 assert!(matches!(BondMode::try_from(6u8), Ok(BondMode::BalanceAlb)));
4672 assert!(BondMode::try_from(7u8).is_err());
4673 assert!(BondMode::try_from(255u8).is_err());
4674 }
4675
4676 #[test]
4677 fn test_xmit_hash_policy_try_from() {
4678 assert!(matches!(
4679 XmitHashPolicy::try_from(0u8),
4680 Ok(XmitHashPolicy::Layer2)
4681 ));
4682 assert!(matches!(
4683 XmitHashPolicy::try_from(1u8),
4684 Ok(XmitHashPolicy::Layer34)
4685 ));
4686 assert!(matches!(
4687 XmitHashPolicy::try_from(2u8),
4688 Ok(XmitHashPolicy::Layer23)
4689 ));
4690 assert!(matches!(
4691 XmitHashPolicy::try_from(3u8),
4692 Ok(XmitHashPolicy::Encap23)
4693 ));
4694 assert!(matches!(
4695 XmitHashPolicy::try_from(4u8),
4696 Ok(XmitHashPolicy::Encap34)
4697 ));
4698 assert!(matches!(
4699 XmitHashPolicy::try_from(5u8),
4700 Ok(XmitHashPolicy::VlanSrcMac)
4701 ));
4702 assert!(XmitHashPolicy::try_from(6u8).is_err());
4703 assert!(XmitHashPolicy::try_from(255u8).is_err());
4704 }
4705
4706 #[test]
4707 fn test_bond_mode_debug_format() {
4708 assert_eq!(format!("{:?}", BondMode::BalanceRr), "BalanceRr");
4709 assert_eq!(format!("{:?}", BondMode::Lacp), "Lacp");
4710 assert_eq!(format!("{:?}", BondMode::BalanceAlb), "BalanceAlb");
4711 }
4712
4713 #[test]
4714 fn test_xmit_hash_policy_debug_format() {
4715 assert_eq!(format!("{:?}", XmitHashPolicy::Layer2), "Layer2");
4716 assert_eq!(format!("{:?}", XmitHashPolicy::Layer34), "Layer34");
4717 assert_eq!(format!("{:?}", XmitHashPolicy::VlanSrcMac), "VlanSrcMac");
4718 }
4719}