@@ -14,6 +14,7 @@ use pnet::{
14
14
ethernet:: { EtherTypes , MutableEthernetPacket } ,
15
15
ip:: IpNextHeaderProtocols ,
16
16
ipv4:: MutableIpv4Packet ,
17
+ ipv6:: MutableIpv6Packet ,
17
18
tcp:: MutableTcpPacket ,
18
19
udp:: MutableUdpPacket ,
19
20
MutablePacket , Packet ,
@@ -436,7 +437,7 @@ async fn redirect_packets_for_pktap_stream(
436
437
mut pktap_stream : PktapStream ,
437
438
default_interface : DefaultInterface ,
438
439
vpn_interface : Option < VpnInterface > ,
439
- classify : Box < dyn Fn ( & PktapPacket ) -> RoutingDecision + Send + ' static > ,
440
+ mut classify : Box < dyn Fn ( & PktapPacket ) -> RoutingDecision + Send + ' static > ,
440
441
) -> Result < RedirectHandle , Error > {
441
442
let default_dev = bpf:: Bpf :: open ( ) . map_err ( Error :: CreateDefaultBpf ) ?;
442
443
let buffer_size = default_dev
@@ -478,6 +479,7 @@ async fn redirect_packets_for_pktap_stream(
478
479
} ;
479
480
480
481
let vpn_v4 = vpn_interface. as_ref ( ) . and_then ( |iface| iface. v4_address ) ;
482
+ let vpn_v6 = vpn_interface. as_ref ( ) . and_then ( |iface| iface. v6_address ) ;
481
483
482
484
let ingress_task: tokio:: task:: JoinHandle < (
483
485
tokio:: io:: ReadHalf < tun:: AsyncDevice > ,
@@ -518,7 +520,7 @@ async fn redirect_packets_for_pktap_stream(
518
520
let bpf_payload = & mut read_data[ header. bh_hdrlen as usize
519
521
..( header. bh_hdrlen as usize + header. bh_caplen as usize ) ] ;
520
522
521
- handle_incoming_data( & mut tun_writer, bpf_payload, vpn_v4) . await ;
523
+ handle_incoming_data( & mut tun_writer, bpf_payload, vpn_v4, vpn_v6 ) . await ;
522
524
523
525
if new_offset < read_data. len( ) {
524
526
let read_len = read_data. len( ) ;
@@ -542,17 +544,9 @@ async fn redirect_packets_for_pktap_stream(
542
544
} ) ;
543
545
544
546
let default_interface_clone = default_interface. clone ( ) ;
547
+ let vpn_interface_clone = vpn_interface. clone ( ) ;
545
548
546
549
let classify_task = tokio:: spawn ( async move {
547
- let dest_mac = MacAddr :: from (
548
- default_interface
549
- . v4_addrs
550
- . as_ref ( )
551
- . unwrap ( )
552
- . gateway_address
553
- . into_bytes ( ) ,
554
- ) ;
555
-
556
550
loop {
557
551
tokio:: select! {
558
552
packet = pktap_stream. next( ) => {
@@ -561,35 +555,13 @@ async fn redirect_packets_for_pktap_stream(
561
555
Error :: PktapStreamStopped
562
556
} ) ??;
563
557
564
- match classify( & packet) {
565
- RoutingDecision :: DefaultInterface => {
566
- packet. frame. set_destination( dest_mac) ;
567
- let mut ip = MutableIpv4Packet :: new( packet. frame. payload_mut( ) ) . unwrap( ) ;
568
-
569
- fix_ip_checksums(
570
- & mut ip,
571
- Some ( default_interface. v4_addrs. as_ref( ) . unwrap( ) . source_ip) ,
572
- None ,
573
- ) ;
574
- if let Err ( error) = default_write. write( packet. frame. packet( ) ) {
575
- log:: error!( "Failed to forward to non-tun device: {error}" ) ;
576
- }
577
- }
578
- RoutingDecision :: VpnTunnel => {
579
- let Some ( mut ip) = MutableIpv4Packet :: new( packet. frame. payload_mut( ) ) else {
580
- continue ;
581
- } ;
582
- if let Some ( ref mut vpn_dev) = vpn_dev {
583
- fix_ip_checksums( & mut ip, vpn_v4, None ) ;
584
- if let Err ( error) = vpn_dev. write( packet. frame. payload( ) ) {
585
- log:: error!( "Failed to forward to tun device: {error}" ) ;
586
- }
587
- }
588
- }
589
- RoutingDecision :: Drop => {
590
- log:: trace!( "Dropped packet from pid {}" , packet. header. pth_pid) ;
591
- }
592
- }
558
+ let vpn_device = match ( vpn_interface. as_ref( ) , vpn_dev. as_mut( ) ) {
559
+ ( Some ( interface) , Some ( device) ) => Some ( ( interface, device) ) ,
560
+ ( None , None ) => None ,
561
+ _ => unreachable!( "missing tun interface or addresses" ) ,
562
+ } ;
563
+
564
+ classify = classify_and_send( classify, & mut packet, & default_interface, & mut default_write, vpn_device) . await ;
593
565
}
594
566
Ok ( ( ) ) | Err ( _) = egress_abort_rx. recv( ) => {
595
567
log:: debug!( "stopping packet processing" ) ;
@@ -604,55 +576,187 @@ async fn redirect_packets_for_pktap_stream(
604
576
ingress_task,
605
577
classify_task,
606
578
default_interface : default_interface_clone,
607
- vpn_interface,
579
+ vpn_interface : vpn_interface_clone ,
608
580
} )
609
581
}
610
582
583
+ async fn classify_and_send (
584
+ classify : Box < dyn Fn ( & PktapPacket ) -> RoutingDecision + Send + ' static > ,
585
+ packet : & mut PktapPacket ,
586
+ default_interface : & DefaultInterface ,
587
+ default_write : & mut bpf:: WriteHalf ,
588
+ vpn_interface : Option < ( & VpnInterface , & mut bpf:: Bpf ) > ,
589
+ ) -> Box < dyn Fn ( & PktapPacket ) -> RoutingDecision + Send + ' static > {
590
+ match classify ( & packet) {
591
+ RoutingDecision :: DefaultInterface => match packet. frame . get_ethertype ( ) {
592
+ EtherTypes :: Ipv4 => {
593
+ let Some ( ref addrs) = default_interface. v4_addrs else {
594
+ log:: trace!( "dropping IPv4 packet since there's no default route" ) ;
595
+ return classify;
596
+ } ;
597
+ let gateway_address = MacAddr :: from ( addrs. gateway_address . into_bytes ( ) ) ;
598
+ packet. frame . set_destination ( gateway_address) ;
599
+ let Some ( mut ip) = MutableIpv4Packet :: new ( packet. frame . payload_mut ( ) ) else {
600
+ log:: error!( "dropping invalid IPv4 packet" ) ;
601
+ return classify;
602
+ } ;
603
+ fix_ipv4_checksums ( & mut ip, Some ( addrs. source_ip ) , None ) ;
604
+ if let Err ( error) = default_write. write ( packet. frame . packet ( ) ) {
605
+ log:: error!( "Failed to forward to default device: {error}" ) ;
606
+ }
607
+ }
608
+ EtherTypes :: Ipv6 => {
609
+ let Some ( ref addrs) = default_interface. v6_addrs else {
610
+ log:: trace!( "dropping IPv6 packet since there's no default route" ) ;
611
+ return classify;
612
+ } ;
613
+ let gateway_address = MacAddr :: from ( addrs. gateway_address . into_bytes ( ) ) ;
614
+ packet. frame . set_destination ( gateway_address) ;
615
+ let Some ( mut ip) = MutableIpv6Packet :: new ( packet. frame . payload_mut ( ) ) else {
616
+ log:: error!( "dropping invalid IPv6 packet" ) ;
617
+ return classify;
618
+ } ;
619
+ fix_ipv6_checksums ( & mut ip, Some ( addrs. source_ip ) , None ) ;
620
+ if let Err ( error) = default_write. write ( packet. frame . packet ( ) ) {
621
+ log:: error!( "Failed to forward to default device: {error}" ) ;
622
+ }
623
+ }
624
+ other => log:: error!( "unknown ethertype: {other}" ) ,
625
+ } ,
626
+ RoutingDecision :: VpnTunnel => {
627
+ let Some ( ( vpn_interface, vpn_write) ) = vpn_interface else {
628
+ log:: trace!( "dropping IP packet since there's no tun route" ) ;
629
+ return classify;
630
+ } ;
631
+
632
+ match packet. frame . get_ethertype ( ) {
633
+ EtherTypes :: Ipv4 => {
634
+ let Some ( addr) = vpn_interface. v4_address else {
635
+ log:: trace!( "dropping IPv4 packet since there's no tun route" ) ;
636
+ return classify;
637
+ } ;
638
+ let Some ( mut ip) = MutableIpv4Packet :: new ( packet. frame . payload_mut ( ) ) else {
639
+ log:: error!( "dropping invalid IPv4 packet" ) ;
640
+ return classify;
641
+ } ;
642
+ fix_ipv4_checksums ( & mut ip, Some ( addr) , None ) ;
643
+ if let Err ( error) = vpn_write. write ( packet. frame . payload ( ) ) {
644
+ log:: error!( "Failed to forward to tun device: {error}" ) ;
645
+ }
646
+ }
647
+ EtherTypes :: Ipv6 => {
648
+ let Some ( addr) = vpn_interface. v6_address else {
649
+ log:: trace!( "dropping IPv6 packet since there's no tun route" ) ;
650
+ return classify;
651
+ } ;
652
+ let Some ( mut ip) = MutableIpv6Packet :: new ( packet. frame . payload_mut ( ) ) else {
653
+ log:: error!( "dropping invalid IPv6 packet" ) ;
654
+ return classify;
655
+ } ;
656
+ fix_ipv6_checksums ( & mut ip, Some ( addr) , None ) ;
657
+ if let Err ( error) = vpn_write. write ( packet. frame . payload ( ) ) {
658
+ log:: error!( "Failed to forward to tun device: {error}" ) ;
659
+ }
660
+ }
661
+ other => log:: error!( "unknown ethertype: {other}" ) ,
662
+ }
663
+ }
664
+ RoutingDecision :: Drop => {
665
+ log:: trace!( "Dropped packet from pid {}" , packet. header. pth_pid) ;
666
+ }
667
+ }
668
+ classify
669
+ }
670
+
611
671
async fn handle_incoming_data (
612
672
tun_writer : & mut tokio:: io:: WriteHalf < tun:: AsyncDevice > ,
613
673
payload : & mut [ u8 ] ,
614
674
vpn_v4 : Option < Ipv4Addr > ,
675
+ vpn_v6 : Option < Ipv6Addr > ,
615
676
) {
616
677
let Some ( mut frame) = MutableEthernetPacket :: new ( payload) else {
617
678
log:: trace!( "discarding non-Ethernet frame" ) ;
618
679
return ;
619
680
} ;
620
681
621
- if frame. get_ethertype ( ) != EtherTypes :: Ipv4 {
622
- log:: trace!( "discarding non-IPv4 frame" ) ;
623
- return ;
682
+ match frame. get_ethertype ( ) {
683
+ EtherTypes :: Ipv4 => {
684
+ let Some ( vpn_addr) = vpn_v4 else {
685
+ log:: trace!( "discarding incoming IPv4 packet: no tun V4 addr" ) ;
686
+ return ;
687
+ } ;
688
+ let Some ( ip) = MutableIpv4Packet :: new ( frame. payload_mut ( ) ) else {
689
+ log:: trace!( "discarding non-IPv4 packet" ) ;
690
+ return ;
691
+ } ;
692
+ handle_incoming_data_v4 ( tun_writer, ip, vpn_addr) . await ;
693
+ }
694
+ EtherTypes :: Ipv6 => {
695
+ let Some ( vpn_addr) = vpn_v6 else {
696
+ log:: trace!( "discarding incoming IPv6 packet: no tun V6 addr" ) ;
697
+ return ;
698
+ } ;
699
+ let Some ( ip) = MutableIpv6Packet :: new ( frame. payload_mut ( ) ) else {
700
+ log:: trace!( "discarding non-IPv6 packet" ) ;
701
+ return ;
702
+ } ;
703
+ handle_incoming_data_v6 ( tun_writer, ip, vpn_addr) . await ;
704
+ }
705
+ ethertype => {
706
+ log:: trace!( "discarding non-IP frame: {ethertype}" ) ;
707
+ }
624
708
}
709
+ }
625
710
626
- let Some ( mut ip) = MutableIpv4Packet :: new ( frame. payload_mut ( ) ) else {
627
- log:: trace!( "discarding non-IPv4 packet" ) ;
711
+ async fn handle_incoming_data_v4 (
712
+ tun_writer : & mut tokio:: io:: WriteHalf < tun:: AsyncDevice > ,
713
+ mut ip : MutableIpv4Packet < ' _ > ,
714
+ vpn_addr : Ipv4Addr ,
715
+ ) {
716
+ if ip. get_destination ( ) == vpn_addr {
717
+ // Drop attempt to send packets to tun IP on the real interface
628
718
return ;
629
- } ;
719
+ }
720
+
721
+ fix_ipv4_checksums ( & mut ip, None , Some ( vpn_addr) ) ;
722
+
723
+ const BSD_LB_HEADER : & [ u8 ] = & ( AF_INET as u32 ) . to_be_bytes ( ) ;
724
+ if let Err ( error) = tun_writer
725
+ . write_vectored ( & [ IoSlice :: new ( BSD_LB_HEADER ) , IoSlice :: new ( ip. packet ( ) ) ] )
726
+ . await
727
+ {
728
+ log:: error!( "Failed to redirect incoming IPv4 packet: {error}" ) ;
729
+ }
730
+ }
630
731
631
- let dest = Some ( ip. get_destination ( ) ) ;
632
- if dest == vpn_v4 {
732
+ async fn handle_incoming_data_v6 (
733
+ tun_writer : & mut tokio:: io:: WriteHalf < tun:: AsyncDevice > ,
734
+ mut ip : MutableIpv6Packet < ' _ > ,
735
+ vpn_addr : Ipv6Addr ,
736
+ ) {
737
+ if ip. get_destination ( ) == vpn_addr {
633
738
// Drop attempt to send packets to tun IP on the real interface
634
739
return ;
635
740
}
636
741
637
- fix_ip_checksums ( & mut ip, None , vpn_v4 ) ;
742
+ fix_ipv6_checksums ( & mut ip, None , Some ( vpn_addr ) ) ;
638
743
639
- const BSD_LB_HEADER : & [ u8 ] = & [ 0 , 0 , 0 , AF_INET as u8 ] ;
744
+ const BSD_LB_HEADER : & [ u8 ] = & ( AF_INET6 as u32 ) . to_be_bytes ( ) ;
640
745
if let Err ( error) = tun_writer
641
746
. write_vectored ( & [ IoSlice :: new ( BSD_LB_HEADER ) , IoSlice :: new ( ip. packet ( ) ) ] )
642
747
. await
643
748
{
644
- log:: error!( "Failed to redirect incoming packet: {error}" ) ;
749
+ log:: error!( "Failed to redirect incoming IPv6 packet: {error}" ) ;
645
750
}
646
751
}
647
752
648
753
// Recalculate L3 and L4 checksums. Silently fail on error
649
- fn fix_ip_checksums (
754
+ fn fix_ipv4_checksums (
650
755
ip : & mut MutableIpv4Packet < ' _ > ,
651
756
new_source : Option < Ipv4Addr > ,
652
757
new_destination : Option < Ipv4Addr > ,
653
758
) {
654
759
// Update source and update checksums
655
-
656
760
if let Some ( source_ip) = new_source {
657
761
ip. set_source ( source_ip) ;
658
762
}
@@ -690,6 +794,48 @@ fn fix_ip_checksums(
690
794
ip. set_checksum ( pnet:: packet:: ipv4:: checksum ( & ip. to_immutable ( ) ) ) ;
691
795
}
692
796
797
+ // Recalculate L3 and L4 checksums. Silently fail on error
798
+ fn fix_ipv6_checksums (
799
+ ip : & mut MutableIpv6Packet < ' _ > ,
800
+ new_source : Option < Ipv6Addr > ,
801
+ new_destination : Option < Ipv6Addr > ,
802
+ ) {
803
+ // Update source and update checksums
804
+ if let Some ( source_ip) = new_source {
805
+ ip. set_source ( source_ip) ;
806
+ }
807
+ if let Some ( dest_ip) = new_destination {
808
+ ip. set_destination ( dest_ip) ;
809
+ }
810
+
811
+ let source_ip = ip. get_source ( ) ;
812
+ let destination_ip = ip. get_destination ( ) ;
813
+
814
+ match ip. get_next_header ( ) {
815
+ IpNextHeaderProtocols :: Tcp => {
816
+ if let Some ( mut tcp) = MutableTcpPacket :: new ( ip. payload_mut ( ) ) {
817
+ use pnet:: packet:: tcp:: ipv6_checksum;
818
+ tcp. set_checksum ( ipv6_checksum (
819
+ & tcp. to_immutable ( ) ,
820
+ & source_ip,
821
+ & destination_ip,
822
+ ) ) ;
823
+ }
824
+ }
825
+ IpNextHeaderProtocols :: Udp => {
826
+ if let Some ( mut udp) = MutableUdpPacket :: new ( ip. payload_mut ( ) ) {
827
+ use pnet:: packet:: udp:: ipv6_checksum;
828
+ udp. set_checksum ( ipv6_checksum (
829
+ & udp. to_immutable ( ) ,
830
+ & source_ip,
831
+ & destination_ip,
832
+ ) ) ;
833
+ }
834
+ }
835
+ _ => ( ) ,
836
+ }
837
+ }
838
+
693
839
/// This returns a stream of outbound packets on a utun tunnel.
694
840
///
695
841
/// * `utun_iface`- name of a utun interface to capture packets on. Note that if this does not
@@ -759,9 +905,8 @@ impl PacketCodec for PktapCodec {
759
905
return None ;
760
906
}
761
907
762
- // TODO: Wasteful. We could use a ring buffer for concurrency handling or just
763
- // a share single buffer if handling one frame at a time (assuming no concurrency is needed)
764
- // Allocating the frame here is purely done for efficiency reasons.
908
+ // TODO: Wasteful. Could share single buffer if handling one frame at a time (assuming no
909
+ // concurrency is needed). Allocating the frame here is purely done for efficiency reasons.
765
910
let mut frame = MutableEthernetPacket :: owned ( vec ! [ 0u8 ; 14 + data. len( ) - 4 ] ) . unwrap ( ) ;
766
911
767
912
let raw_family = i32:: from_ne_bytes ( data[ 0 ..4 ] . try_into ( ) . unwrap ( ) ) ;
0 commit comments