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