1use std::io;
2use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3use std::str::FromStr;
4
5use crate::platform::{DeviceImpl, SyncDevice};
6
7#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
12#[non_exhaustive]
13pub enum Layer {
14 #[cfg(any(
16 target_os = "windows",
17 target_os = "linux",
18 target_os = "freebsd",
19 target_os = "macos",
20 target_os = "openbsd",
21 target_os = "netbsd",
22 ))]
23 L2,
24 #[default]
26 L3,
27}
28
29#[derive(Clone, Default, Debug)]
34pub(crate) struct DeviceConfig {
35 pub(crate) dev_name: Option<String>,
37 #[cfg(windows)]
39 pub(crate) description: Option<String>,
40 #[cfg(target_os = "macos")]
42 pub(crate) peer_feth: Option<String>,
43 #[cfg(any(
47 target_os = "macos",
48 target_os = "freebsd",
49 target_os = "openbsd",
50 target_os = "netbsd"
51 ))]
52 pub(crate) associate_route: Option<bool>,
53 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
56 pub(crate) reuse_dev: Option<bool>,
57 #[cfg(any(target_os = "macos", target_os = "windows"))]
60 pub(crate) persist: Option<bool>,
61 #[allow(dead_code)]
63 pub(crate) layer: Option<Layer>,
64 #[cfg(windows)]
66 pub(crate) device_guid: Option<u128>,
67 #[cfg(windows)]
68 pub(crate) wintun_log: Option<bool>,
69 #[cfg(windows)]
71 pub(crate) wintun_file: Option<String>,
72 #[cfg(windows)]
74 pub(crate) ring_capacity: Option<u32>,
75 #[cfg(windows)]
78 pub(crate) delete_driver: Option<bool>,
79 #[cfg(windows)]
80 pub(crate) mac_address: Option<String>,
81 #[cfg(any(
83 target_os = "macos",
84 target_os = "linux",
85 target_os = "freebsd",
86 target_os = "openbsd",
87 target_os = "netbsd"
88 ))]
89 pub(crate) packet_information: Option<bool>,
90 #[cfg(target_os = "linux")]
93 pub(crate) offload: Option<bool>,
94 #[cfg(target_os = "linux")]
96 pub(crate) multi_queue: Option<bool>,
97}
98type IPV4 = (
99 io::Result<Ipv4Addr>,
100 io::Result<u8>,
101 Option<io::Result<Ipv4Addr>>,
102);
103#[doc(hidden)]
174pub struct DeviceBuilderGuard<'a>(&'a mut DeviceBuilder);
175
176#[doc(hidden)]
177impl DeviceBuilderGuard<'_> {
178 #[cfg(windows)]
180 pub fn description<S: Into<String>>(&mut self, description: S) -> &mut Self {
181 self.0.description = Some(description.into());
182 self
183 }
184
185 #[cfg(windows)]
187 pub fn mtu_v4(&mut self, mtu: u16) -> &mut Self {
188 self.0.mtu = Some(mtu);
189 self
190 }
191 #[cfg(windows)]
193 pub fn mtu_v6(&mut self, mtu: u16) -> &mut Self {
194 self.0.mtu_v6 = Some(mtu);
195 self
196 }
197 #[cfg(any(
199 target_os = "windows",
200 target_os = "linux",
201 target_os = "freebsd",
202 target_os = "openbsd",
203 target_os = "macos",
204 target_os = "netbsd"
205 ))]
206 pub fn mac_addr(&mut self, mac_addr: [u8; 6]) -> &mut Self {
207 self.0.mac_addr = Some(mac_addr);
208 self
209 }
210
211 #[cfg(windows)]
214 pub fn device_guid(&mut self, device_guid: u128) -> &mut Self {
215 self.0.device_guid = Some(device_guid);
216 self
217 }
218 #[cfg(windows)]
222 pub fn wintun_log(&mut self, wintun_log: bool) -> &mut Self {
223 self.0.wintun_log = Some(wintun_log);
224 self
225 }
226 #[cfg(windows)]
228 pub fn wintun_file(&mut self, wintun_file: String) -> &mut Self {
229 self.0.wintun_file = Some(wintun_file);
230 self
231 }
232 #[cfg(windows)]
236 pub fn ring_capacity(&mut self, ring_capacity: u32) -> &mut Self {
237 self.0.ring_capacity = Some(ring_capacity);
238 self
239 }
240 #[cfg(windows)]
242 pub fn metric(&mut self, metric: u16) -> &mut Self {
243 self.0.metric = Some(metric);
244 self
245 }
246 #[cfg(windows)]
251 pub fn delete_driver(&mut self, delete_driver: bool) -> &mut Self {
252 self.0.delete_driver = Some(delete_driver);
253 self
254 }
255 #[cfg(target_os = "linux")]
257 pub fn tx_queue_len(&mut self, tx_queue_len: u32) -> &mut Self {
258 self.0.tx_queue_len = Some(tx_queue_len);
259 self
260 }
261 #[cfg(target_os = "linux")]
264 pub fn offload(&mut self, offload: bool) -> &mut Self {
265 self.0.offload = Some(offload);
266 self
267 }
268 #[cfg(target_os = "linux")]
270 pub fn multi_queue(&mut self, multi_queue: bool) -> &mut Self {
271 self.0.multi_queue = Some(multi_queue);
272 self
273 }
274 #[cfg(any(
284 target_os = "macos",
285 target_os = "linux",
286 target_os = "freebsd",
287 target_os = "openbsd",
288 target_os = "netbsd"
289 ))]
290 pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
291 self.0.packet_information = Some(packet_information);
292 self
293 }
294 #[cfg(target_os = "macos")]
297 pub fn peer_feth<S: Into<String>>(&mut self, peer_feth: S) -> &mut Self {
298 self.0.peer_feth = Some(peer_feth.into());
299 self
300 }
301 #[cfg(any(
305 target_os = "macos",
306 target_os = "freebsd",
307 target_os = "openbsd",
308 target_os = "netbsd"
309 ))]
310 pub fn associate_route(&mut self, associate_route: bool) -> &mut Self {
311 self.0.associate_route = Some(associate_route);
312 self
313 }
314 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
318 pub fn reuse_dev(&mut self, reuse: bool) -> &mut Self {
319 self.0.reuse_dev = Some(reuse);
320 self
321 }
322 #[cfg(any(target_os = "macos", target_os = "windows"))]
326 pub fn persist(&mut self, persist: bool) -> &mut Self {
327 self.0.persist = Some(persist);
328 self
329 }
330}
331#[derive(Default)]
334pub struct DeviceBuilder {
335 dev_name: Option<String>,
336 #[cfg(windows)]
337 description: Option<String>,
338 #[cfg(target_os = "macos")]
339 peer_feth: Option<String>,
340 #[cfg(any(
341 target_os = "macos",
342 target_os = "freebsd",
343 target_os = "openbsd",
344 target_os = "netbsd"
345 ))]
346 associate_route: Option<bool>,
347 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
348 reuse_dev: Option<bool>,
349 #[cfg(any(target_os = "macos", target_os = "windows"))]
350 persist: Option<bool>,
351 enabled: Option<bool>,
352 mtu: Option<u16>,
353 #[cfg(windows)]
354 mtu_v6: Option<u16>,
355 ipv4: Option<IPV4>,
356 ipv6: Option<Vec<(io::Result<Ipv6Addr>, io::Result<u8>)>>,
357 layer: Option<Layer>,
358 #[cfg(any(
359 target_os = "windows",
360 target_os = "linux",
361 target_os = "freebsd",
362 target_os = "openbsd",
363 target_os = "macos",
364 target_os = "netbsd"
365 ))]
366 mac_addr: Option<[u8; 6]>,
367 #[cfg(windows)]
368 device_guid: Option<u128>,
369 #[cfg(windows)]
370 wintun_log: Option<bool>,
371 #[cfg(windows)]
372 wintun_file: Option<String>,
373 #[cfg(windows)]
374 ring_capacity: Option<u32>,
375 #[cfg(windows)]
376 metric: Option<u16>,
377 #[cfg(windows)]
378 delete_driver: Option<bool>,
379 #[cfg(any(
381 target_os = "macos",
382 target_os = "linux",
383 target_os = "freebsd",
384 target_os = "openbsd",
385 target_os = "netbsd"
386 ))]
387 packet_information: Option<bool>,
388 #[cfg(target_os = "linux")]
389 tx_queue_len: Option<u32>,
390 #[cfg(target_os = "linux")]
393 offload: Option<bool>,
394 #[cfg(target_os = "linux")]
396 multi_queue: Option<bool>,
397}
398
399impl DeviceBuilder {
400 pub fn new() -> Self {
402 Self::default()
403 }
404 pub fn name<S: Into<String>>(mut self, dev_name: S) -> Self {
406 self.dev_name = Some(dev_name.into());
407 self
408 }
409 #[cfg(windows)]
411 pub fn description<S: Into<String>>(mut self, description: S) -> Self {
412 self.description = Some(description.into());
413 self
414 }
415 pub fn mtu(mut self, mtu: u16) -> Self {
417 self.mtu = Some(mtu);
418 #[cfg(windows)]
419 {
420 self.mtu_v6 = Some(mtu);
422 }
423 self
424 }
425 #[cfg(windows)]
427 pub fn mtu_v4(mut self, mtu: u16) -> Self {
428 self.mtu = Some(mtu);
429 self
430 }
431 #[cfg(windows)]
433 pub fn mtu_v6(mut self, mtu: u16) -> Self {
434 self.mtu_v6 = Some(mtu);
435 self
436 }
437 #[cfg(any(
439 target_os = "windows",
440 target_os = "linux",
441 target_os = "freebsd",
442 target_os = "openbsd",
443 target_os = "macos",
444 target_os = "netbsd"
445 ))]
446 pub fn mac_addr(mut self, mac_addr: [u8; 6]) -> Self {
447 self.mac_addr = Some(mac_addr);
448 self
449 }
450 pub fn ipv4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
462 mut self,
463 address: IPv4,
464 mask: Netmask,
465 destination: Option<IPv4>,
466 ) -> Self {
467 self.ipv4 = Some((address.ipv4(), mask.prefix(), destination.map(|v| v.ipv4())));
468 self
469 }
470 pub fn ipv6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
480 mut self,
481 address: IPv6,
482 mask: Netmask,
483 ) -> Self {
484 if let Some(v) = &mut self.ipv6 {
485 v.push((address.ipv6(), mask.prefix()));
486 } else {
487 self.ipv6 = Some(vec![(address.ipv6(), mask.prefix())]);
488 }
489
490 self
491 }
492 pub fn ipv6_tuple<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
504 mut self,
505 addrs: &[(IPv6, Netmask)],
506 ) -> Self {
507 if let Some(v) = &mut self.ipv6 {
508 for (address, mask) in addrs {
509 v.push((address.ipv6(), mask.prefix()));
510 }
511 } else {
512 self.ipv6 = Some(
513 addrs
514 .iter()
515 .map(|(ip, mask)| (ip.ipv6(), mask.prefix()))
516 .collect(),
517 );
518 }
519 self
520 }
521 pub fn layer(mut self, layer: Layer) -> Self {
526 self.layer = Some(layer);
527 self
528 }
529 #[cfg(windows)]
532 pub fn device_guid(mut self, device_guid: u128) -> Self {
533 self.device_guid = Some(device_guid);
534 self
535 }
536 #[cfg(windows)]
540 pub fn wintun_log(mut self, wintun_log: bool) -> Self {
541 self.wintun_log = Some(wintun_log);
542 self
543 }
544 #[cfg(windows)]
546 pub fn wintun_file(mut self, wintun_file: String) -> Self {
547 self.wintun_file = Some(wintun_file);
548 self
549 }
550 #[cfg(windows)]
554 pub fn ring_capacity(mut self, ring_capacity: u32) -> Self {
555 self.ring_capacity = Some(ring_capacity);
556 self
557 }
558 #[cfg(windows)]
560 pub fn metric(mut self, metric: u16) -> Self {
561 self.metric = Some(metric);
562 self
563 }
564 #[cfg(windows)]
569 pub fn delete_driver(mut self, delete_driver: bool) -> Self {
570 self.delete_driver = Some(delete_driver);
571 self
572 }
573 #[cfg(target_os = "linux")]
575 pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
576 self.tx_queue_len = Some(tx_queue_len);
577 self
578 }
579 #[cfg(target_os = "linux")]
582 pub fn offload(mut self, offload: bool) -> Self {
583 self.offload = Some(offload);
584 self
585 }
586 #[cfg(target_os = "linux")]
588 pub fn multi_queue(mut self, multi_queue: bool) -> Self {
589 self.multi_queue = Some(multi_queue);
590 self
591 }
592 #[cfg(any(
602 target_os = "macos",
603 target_os = "linux",
604 target_os = "freebsd",
605 target_os = "openbsd",
606 target_os = "netbsd"
607 ))]
608 pub fn packet_information(mut self, packet_information: bool) -> Self {
609 self.packet_information = Some(packet_information);
610 self
611 }
612 #[cfg(target_os = "macos")]
615 pub fn peer_feth<S: Into<String>>(mut self, peer_feth: S) -> Self {
616 self.peer_feth = Some(peer_feth.into());
617 self
618 }
619 #[cfg(any(
623 target_os = "macos",
624 target_os = "freebsd",
625 target_os = "openbsd",
626 target_os = "netbsd"
627 ))]
628 pub fn associate_route(mut self, associate_route: bool) -> Self {
629 self.associate_route = Some(associate_route);
630 self
631 }
632 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
636 pub fn reuse_dev(mut self, reuse: bool) -> Self {
637 self.reuse_dev = Some(reuse);
638 self
639 }
640 #[cfg(any(target_os = "macos", target_os = "windows"))]
644 pub fn persist(mut self, persist: bool) -> Self {
645 self.persist = Some(persist);
646 self
647 }
648 pub fn enable(mut self, enable: bool) -> Self {
651 self.enabled = Some(enable);
652 self
653 }
654 pub(crate) fn build_config(&mut self) -> DeviceConfig {
655 DeviceConfig {
656 dev_name: self.dev_name.take(),
657 #[cfg(windows)]
658 description: self.description.take(),
659 #[cfg(target_os = "macos")]
660 peer_feth: self.peer_feth.take(),
661 #[cfg(any(
662 target_os = "macos",
663 target_os = "freebsd",
664 target_os = "openbsd",
665 target_os = "netbsd"
666 ))]
667 associate_route: self.associate_route,
668 #[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
669 reuse_dev: self.reuse_dev,
670 #[cfg(any(target_os = "macos", target_os = "windows"))]
671 persist: self.persist,
672 layer: self.layer.take(),
673 #[cfg(windows)]
674 device_guid: self.device_guid.take(),
675 #[cfg(windows)]
676 wintun_log: self.wintun_log.take(),
677 #[cfg(windows)]
678 wintun_file: self.wintun_file.take(),
679 #[cfg(windows)]
680 ring_capacity: self.ring_capacity.take(),
681 #[cfg(windows)]
682 delete_driver: self.delete_driver.take(),
683 #[cfg(windows)]
684 mac_address: self.mac_addr.map(|v| {
685 use std::fmt::Write;
686 v.iter()
687 .fold(String::with_capacity(v.len() * 2), |mut s, b| {
688 write!(&mut s, "{b:02X}").unwrap();
689 s
690 })
691 }),
692 #[cfg(any(
693 target_os = "macos",
694 target_os = "linux",
695 target_os = "freebsd",
696 target_os = "openbsd",
697 target_os = "netbsd"
698 ))]
699 packet_information: self.packet_information.take(),
700 #[cfg(target_os = "linux")]
701 offload: self.offload.take(),
702 #[cfg(target_os = "linux")]
703 multi_queue: self.multi_queue.take(),
704 }
705 }
706 pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
707 if let Some(mtu) = self.mtu {
708 device.set_mtu(mtu)?;
709 }
710 #[cfg(windows)]
711 if let Some(mtu) = self.mtu_v6 {
712 device.set_mtu_v6(mtu)?;
713 }
714 #[cfg(windows)]
715 if let Some(metric) = self.metric {
716 device.set_metric(metric)?;
717 }
718 #[cfg(target_os = "linux")]
719 if let Some(tx_queue_len) = self.tx_queue_len {
720 device.set_tx_queue_len(tx_queue_len)?;
721 }
722 #[cfg(any(
723 target_os = "linux",
724 target_os = "freebsd",
725 target_os = "macos",
726 target_os = "openbsd",
727 target_os = "netbsd"
728 ))]
729 if let Some(mac_addr) = self.mac_addr {
730 device.set_mac_address(mac_addr)?;
731 }
732
733 if let Some((address, prefix, destination)) = self.ipv4 {
734 let prefix = prefix?;
735 let address = address?;
736 let destination = destination.transpose()?;
737 device.set_network_address(address, prefix, destination)?;
738 }
739 if let Some(ipv6) = self.ipv6 {
740 for (address, prefix) in ipv6 {
741 let prefix = prefix?;
742 let address = address?;
743 device.add_address_v6(address, prefix)?;
744 }
745 }
746 device.enabled(self.enabled.unwrap_or(true))?;
747 Ok(())
748 }
749 pub fn build_sync(mut self) -> io::Result<SyncDevice> {
751 let device = DeviceImpl::new(self.build_config())?;
752 self.config(&device)?;
753 Ok(SyncDevice(device))
754 }
755 #[cfg(any(feature = "async_io", feature = "async_tokio"))]
762 pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
763 let sync_device = self.build_sync()?;
764 let device = crate::AsyncDevice::new_dev(sync_device.0)?;
765 Ok(device)
766 }
767 pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
793 let mut borrow = DeviceBuilderGuard(&mut self);
794 f(&mut borrow);
795 self
796 }
797}
798
799pub trait ToIpv4Address {
801 fn ipv4(&self) -> io::Result<Ipv4Addr>;
804}
805impl ToIpv4Address for Ipv4Addr {
806 fn ipv4(&self) -> io::Result<Ipv4Addr> {
807 Ok(*self)
808 }
809}
810impl ToIpv4Address for IpAddr {
811 fn ipv4(&self) -> io::Result<Ipv4Addr> {
812 match self {
813 IpAddr::V4(ip) => Ok(*ip),
814 IpAddr::V6(_) => Err(io::Error::new(
815 io::ErrorKind::InvalidData,
816 "invalid address",
817 )),
818 }
819 }
820}
821impl ToIpv4Address for String {
822 fn ipv4(&self) -> io::Result<Ipv4Addr> {
823 self.as_str().ipv4()
824 }
825}
826impl ToIpv4Address for &str {
827 fn ipv4(&self) -> io::Result<Ipv4Addr> {
828 match Ipv4Addr::from_str(self) {
829 Ok(ip) => Ok(ip),
830 Err(_e) => Err(io::Error::new(
831 io::ErrorKind::InvalidData,
832 "invalid IPv4 str",
833 )),
834 }
835 }
836}
837
838pub trait ToIpv6Address {
840 fn ipv6(&self) -> io::Result<Ipv6Addr>;
843}
844
845impl ToIpv6Address for Ipv6Addr {
846 fn ipv6(&self) -> io::Result<Ipv6Addr> {
847 Ok(*self)
848 }
849}
850impl ToIpv6Address for IpAddr {
851 fn ipv6(&self) -> io::Result<Ipv6Addr> {
852 match self {
853 IpAddr::V4(_) => Err(io::Error::new(
854 io::ErrorKind::InvalidData,
855 "invalid address",
856 )),
857 IpAddr::V6(ip) => Ok(*ip),
858 }
859 }
860}
861impl ToIpv6Address for String {
862 fn ipv6(&self) -> io::Result<Ipv6Addr> {
863 self.as_str().ipv6()
864 }
865}
866impl ToIpv6Address for &str {
867 fn ipv6(&self) -> io::Result<Ipv6Addr> {
868 match Ipv6Addr::from_str(self) {
869 Ok(ip) => Ok(ip),
870 Err(_e) => Err(io::Error::new(
871 io::ErrorKind::InvalidData,
872 "invalid IPv6 str",
873 )),
874 }
875 }
876}
877pub trait ToIpv4Netmask {
879 fn prefix(&self) -> io::Result<u8>;
881 fn netmask(&self) -> io::Result<Ipv4Addr> {
883 let ip = u32::MAX
884 .checked_shl(32 - self.prefix()? as u32)
885 .unwrap_or(0);
886 Ok(Ipv4Addr::from(ip))
887 }
888}
889
890impl ToIpv4Netmask for u8 {
891 fn prefix(&self) -> io::Result<u8> {
892 if *self > 32 {
893 return Err(io::Error::new(
894 io::ErrorKind::InvalidData,
895 "invalid IP prefix length",
896 ));
897 }
898 Ok(*self)
899 }
900}
901
902impl ToIpv4Netmask for Ipv4Addr {
903 fn prefix(&self) -> io::Result<u8> {
904 let ip = u32::from_be_bytes(self.octets());
905 if ip.leading_ones() != ip.count_ones() {
907 return Err(io::Error::new(
908 io::ErrorKind::InvalidData,
909 "invalid netmask",
910 ));
911 }
912 Ok(ip.leading_ones() as u8)
913 }
914}
915impl ToIpv4Netmask for String {
916 fn prefix(&self) -> io::Result<u8> {
917 ToIpv4Netmask::prefix(&self.as_str())
918 }
919}
920impl ToIpv4Netmask for &str {
921 fn prefix(&self) -> io::Result<u8> {
922 match Ipv4Addr::from_str(self) {
923 Ok(ip) => ip.prefix(),
924 Err(_e) => Err(io::Error::new(
925 io::ErrorKind::InvalidData,
926 "invalid netmask str",
927 )),
928 }
929 }
930}
931pub trait ToIpv6Netmask {
933 fn prefix(&self) -> io::Result<u8>;
935 fn netmask(&self) -> io::Result<Ipv6Addr> {
937 let ip = u128::MAX
938 .checked_shl(128 - self.prefix()? as u32)
939 .unwrap_or(0);
940 Ok(Ipv6Addr::from(ip))
941 }
942}
943
944impl ToIpv6Netmask for u8 {
945 fn prefix(&self) -> io::Result<u8> {
946 if *self > 128 {
947 return Err(io::Error::new(
948 io::ErrorKind::InvalidData,
949 "invalid IP prefix length",
950 ));
951 }
952 Ok(*self)
953 }
954}
955
956impl ToIpv6Netmask for Ipv6Addr {
957 fn prefix(&self) -> io::Result<u8> {
958 let ip = u128::from_be_bytes(self.octets());
959 if ip.leading_ones() != ip.count_ones() {
960 return Err(io::Error::new(
961 io::ErrorKind::InvalidData,
962 "invalid netmask",
963 ));
964 }
965 Ok(ip.leading_ones() as u8)
966 }
967}
968impl ToIpv6Netmask for String {
969 fn prefix(&self) -> io::Result<u8> {
970 ToIpv6Netmask::prefix(&self.as_str())
971 }
972}
973impl ToIpv6Netmask for &str {
974 fn prefix(&self) -> io::Result<u8> {
975 match Ipv6Addr::from_str(self) {
976 Ok(ip) => ip.prefix(),
977 Err(_e) => Err(io::Error::new(
978 io::ErrorKind::InvalidData,
979 "invalid netmask str",
980 )),
981 }
982 }
983}