Skip to content

Commit 4243419

Browse files
committed
Add IPv6 for ST utun
1 parent 3104038 commit 4243419

File tree

1 file changed

+202
-57
lines changed
  • talpid-core/src/split_tunnel/macos

1 file changed

+202
-57
lines changed

talpid-core/src/split_tunnel/macos/tun.rs

+202-57
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use pnet::{
1414
ethernet::{EtherTypes, MutableEthernetPacket},
1515
ip::IpNextHeaderProtocols,
1616
ipv4::MutableIpv4Packet,
17+
ipv6::MutableIpv6Packet,
1718
tcp::MutableTcpPacket,
1819
udp::MutableUdpPacket,
1920
MutablePacket, Packet,
@@ -437,7 +438,7 @@ async fn redirect_packets_for_pktap_stream(
437438
mut pktap_stream: PktapStream,
438439
default_interface: DefaultInterface,
439440
vpn_interface: Option<VpnInterface>,
440-
classify: Box<dyn Fn(&PktapPacket) -> RoutingDecision + Send + 'static>,
441+
mut classify: Box<dyn Fn(&PktapPacket) -> RoutingDecision + Send + 'static>,
441442
) -> Result<RedirectHandle, Error> {
442443
let default_dev = bpf::Bpf::open().map_err(Error::CreateDefaultBpf)?;
443444
let buffer_size = default_dev
@@ -479,6 +480,7 @@ async fn redirect_packets_for_pktap_stream(
479480
};
480481

481482
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);
482484

483485
let ingress_task: tokio::task::JoinHandle<(
484486
tokio::io::ReadHalf<tun::AsyncDevice>,
@@ -519,7 +521,7 @@ async fn redirect_packets_for_pktap_stream(
519521
let bpf_payload = &mut read_data[header.bh_hdrlen as usize
520522
..(header.bh_hdrlen as usize + header.bh_caplen as usize)];
521523

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;
523525

524526
if new_offset < read_data.len() {
525527
let read_len = read_data.len();
@@ -543,17 +545,9 @@ async fn redirect_packets_for_pktap_stream(
543545
});
544546

545547
let default_interface_clone = default_interface.clone();
548+
let vpn_interface_clone = vpn_interface.clone();
546549

547550
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-
557551
loop {
558552
tokio::select! {
559553
packet = pktap_stream.next() => {
@@ -562,35 +556,13 @@ async fn redirect_packets_for_pktap_stream(
562556
Error::PktapStreamStopped
563557
})??;
564558

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;
594566
}
595567
Ok(()) | Err(_) = egress_abort_rx.recv() => {
596568
log::debug!("stopping packet processing");
@@ -605,55 +577,187 @@ async fn redirect_packets_for_pktap_stream(
605577
ingress_task,
606578
classify_task,
607579
default_interface: default_interface_clone,
608-
vpn_interface,
580+
vpn_interface: vpn_interface_clone,
609581
})
610582
}
611583

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+
612672
async fn handle_incoming_data(
613673
tun_writer: &mut tokio::io::WriteHalf<tun::AsyncDevice>,
614674
payload: &mut [u8],
615675
vpn_v4: Option<Ipv4Addr>,
676+
vpn_v6: Option<Ipv6Addr>,
616677
) {
617678
let Some(mut frame) = MutableEthernetPacket::new(payload) else {
618679
log::trace!("discarding non-Ethernet frame");
619680
return;
620681
};
621682

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+
}
625709
}
710+
}
626711

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
629719
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+
}
631732

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 {
634739
// Drop attempt to send packets to tun IP on the real interface
635740
return;
636741
}
637742

638-
fix_ip_checksums(&mut ip, None, vpn_v4);
743+
fix_ipv6_checksums(&mut ip, None, Some(vpn_addr));
639744

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();
641746
if let Err(error) = tun_writer
642747
.write_vectored(&[IoSlice::new(BSD_LB_HEADER), IoSlice::new(ip.packet())])
643748
.await
644749
{
645-
log::error!("Failed to redirect incoming packet: {error}");
750+
log::error!("Failed to redirect incoming IPv6 packet: {error}");
646751
}
647752
}
648753

649754
// Recalculate L3 and L4 checksums. Silently fail on error
650-
fn fix_ip_checksums(
755+
fn fix_ipv4_checksums(
651756
ip: &mut MutableIpv4Packet<'_>,
652757
new_source: Option<Ipv4Addr>,
653758
new_destination: Option<Ipv4Addr>,
654759
) {
655760
// Update source and update checksums
656-
657761
if let Some(source_ip) = new_source {
658762
ip.set_source(source_ip);
659763
}
@@ -691,6 +795,48 @@ fn fix_ip_checksums(
691795
ip.set_checksum(pnet::packet::ipv4::checksum(&ip.to_immutable()));
692796
}
693797

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+
694840
/// This returns a stream of outbound packets on a utun tunnel.
695841
///
696842
/// * `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 {
760906
return None;
761907
}
762908

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.
766911
let mut frame = MutableEthernetPacket::owned(vec![0u8; 14 + data.len() - 4]).unwrap();
767912

768913
let raw_family = i32::from_ne_bytes(data[0..4].try_into().unwrap());

0 commit comments

Comments
 (0)