tproxy_config/
linux.rs

1#![cfg(target_os = "linux")]
2
3use futures::stream::TryStreamExt;
4use std::fs;
5use std::fs::Permissions;
6use std::net::IpAddr;
7use std::os::fd::{AsFd, AsRawFd};
8use std::os::unix::fs::PermissionsExt;
9use std::str::FromStr;
10
11use std::collections::HashMap;
12use std::fs::File;
13use std::io::BufRead;
14use std::path::Path;
15
16use rtnetlink::packet_route::route::RouteMessage;
17use rtnetlink::{Handle, IpVersion, LinkMessageBuilder, LinkUnspec, RouteMessageBuilder, new_connection};
18
19use cidr::IpCidr;
20
21type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
22
23use crate::{ETC_RESOLV_CONF_FILE, TproxyArgs, TproxyStateInner, run_command};
24
25static IPV6_DEFAULT_ROUTE: std::sync::LazyLock<IpCidr> = std::sync::LazyLock::new(|| IpCidr::from_str("::/0").unwrap());
26static IPV6_SPACE_LOWER: std::sync::LazyLock<IpCidr> = std::sync::LazyLock::new(|| IpCidr::from_str("::/1").unwrap());
27static IPV6_SPACE_UPPER: std::sync::LazyLock<IpCidr> = std::sync::LazyLock::new(|| IpCidr::from_str("8000::/1").unwrap());
28
29static IPV4_DEFAULT_ROUTE: std::sync::LazyLock<IpCidr> = std::sync::LazyLock::new(|| IpCidr::from_str("0.0.0.0/0").unwrap());
30static IPV4_SPACE_LOWER: std::sync::LazyLock<IpCidr> = std::sync::LazyLock::new(|| IpCidr::from_str("0.0.0.0/1").unwrap());
31static IPV4_SPACE_UPPER: std::sync::LazyLock<IpCidr> = std::sync::LazyLock::new(|| IpCidr::from_str("128.0.0.0/1").unwrap());
32
33static ROUTING_TABLE_MAIN: u32 = 254;
34
35fn bytes_to_string(bytes: Vec<u8>) -> Result<String> {
36    match String::from_utf8(bytes) {
37        Ok(content) => Ok(content),
38        Err(e) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("error converting bytes to string: {e}")).into()),
39    }
40}
41
42async fn netlink_do<F, T, R>(f: F) -> Result<R>
43where
44    F: Fn(Handle) -> T,
45    T: Future<Output = Result<R>>,
46{
47    let action = async || {
48        let (connection, handle, _) = new_connection()?;
49        tokio::spawn(connection);
50        f(handle).await
51    };
52    action().await
53}
54
55async fn ip_route_add_msg(msg: &RouteMessage) -> Result<()> {
56    netlink_do(async |handle| Ok(handle.route().add(msg.clone()).execute().await?)).await
57}
58
59async fn ip_link_get_index(dev: &str) -> Result<u32> {
60    netlink_do(async |handle| {
61        let mut interfaces = handle.link().get().match_name(String::from(dev)).execute();
62        match interfaces.try_next().await? {
63            Some(link) => Ok(link.header.index),
64            None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Interface not found").into()),
65        }
66    })
67    .await
68}
69
70async fn ip_route_add(dest: &IpCidr, dev: &str, table: u32) -> Result<RouteMessage> {
71    let index = ip_link_get_index(dev).await?;
72    netlink_do(async |handle| {
73        let route = RouteMessageBuilder::<std::net::IpAddr>::new()
74            .destination_prefix(dest.first_address(), dest.network_length())?
75            .table_id(table)
76            .output_interface(index)
77            .build();
78        handle.route().add(route.clone()).execute().await?;
79        Ok(route)
80    })
81    .await
82}
83
84async fn ip_route_flush(table: u32, ip_version: IpVersion) -> Result<()> {
85    netlink_do(async |handle| {
86        let route = match ip_version {
87            IpVersion::V4 => RouteMessageBuilder::<std::net::Ipv4Addr>::new().table_id(table).build(),
88            IpVersion::V6 => RouteMessageBuilder::<std::net::Ipv6Addr>::new().table_id(table).build(),
89        };
90        let mut routes = handle.route().get(route).execute();
91
92        while let Some(route) = routes.try_next().await? {
93            let msg = route.clone();
94            handle.route().del(msg).execute().await?
95        }
96
97        Ok(())
98    })
99    .await
100}
101
102async fn ip_route_del_msg(msg: &RouteMessage) -> Result<()> {
103    netlink_do(async |handle| Ok(handle.route().del(msg.clone()).execute().await?)).await
104}
105
106async fn ip_link_set_up(dev: &str) -> Result<()> {
107    netlink_do(async |handle| {
108        let msg = LinkMessageBuilder::<LinkUnspec>::default().name(String::from(dev)).up().build();
109        Ok(handle.link().set(msg).execute().await?)
110    })
111    .await
112}
113
114async fn ip_link_del(dev: &str) -> Result<()> {
115    let index = ip_link_get_index(dev).await?;
116    netlink_do(async |handle| Ok(handle.link().del(index).execute().await?)).await
117}
118
119async fn ip_route_show(ip_version: IpVersion, table: u32) -> Result<Vec<RouteMessage>> {
120    netlink_do(async |handle| {
121        let route = match ip_version {
122            IpVersion::V4 => RouteMessageBuilder::<std::net::Ipv4Addr>::new().table_id(table).build(),
123            IpVersion::V6 => RouteMessageBuilder::<std::net::Ipv6Addr>::new().table_id(table).build(),
124        };
125        let mut routes = handle.route().get(route).execute();
126        let mut route_messages = Vec::new();
127        while let Some(route) = routes.try_next().await? {
128            route_messages.push(route);
129        }
130
131        // Sort routes by prefix length, the most specific route comes first.
132        route_messages.sort_by(|entry1: &RouteMessage, entry2: &RouteMessage| {
133            // If the prefix lengths are equal, we compare the priority of the routes.
134
135            let mut prio1 = 0;
136            let mut prio2 = 0;
137
138            for nla in &entry1.attributes {
139                if let rtnetlink::packet_route::route::RouteAttribute::Priority(prio) = nla {
140                    prio1 = *prio
141                }
142            }
143
144            for nla in &entry1.attributes {
145                if let rtnetlink::packet_route::route::RouteAttribute::Priority(prio) = nla {
146                    prio2 = *prio
147                }
148            }
149
150            let prio_cmp = prio1.cmp(&prio2);
151            if prio_cmp != std::cmp::Ordering::Equal {
152                return prio_cmp;
153            }
154
155            entry2
156                .header
157                .destination_prefix_length
158                .cmp(&entry1.header.destination_prefix_length)
159        });
160
161        Ok(route_messages)
162    })
163    .await
164}
165
166async fn ip_rule_add(ip_version: IpVersion, fwmark: u32, table: u32) -> Result<()> {
167    netlink_do(async |handle| {
168        if ip_version == IpVersion::V6 {
169            Ok(handle.rule().add().v6().fw_mark(fwmark).table_id(table).execute().await?)
170        } else {
171            Ok(handle.rule().add().v4().fw_mark(fwmark).table_id(table).execute().await?)
172        }
173    })
174    .await
175}
176
177async fn ip_rule_del(ip_version: IpVersion, fwmark: u32) -> Result<()> {
178    netlink_do(async |handle| {
179        let mut rules = handle.rule().get(ip_version.clone()).execute();
180        while let Some(rule) = rules.try_next().await? {
181            if rule.attributes.iter().any(|nla| {
182                if let rtnetlink::packet_route::rule::RuleAttribute::FwMark(mark) = nla {
183                    *mark == fwmark
184                } else {
185                    false
186                }
187            }) {
188                return Ok(handle.rule().del(rule).execute().await?);
189            }
190        }
191        Ok(())
192    })
193    .await
194}
195
196fn route_msg_to_cidr(msg: &rtnetlink::packet_route::route::RouteMessage) -> Result<IpCidr> {
197    let mut net_addr: Option<IpAddr> = match msg.header.address_family {
198        rtnetlink::packet_route::AddressFamily::Inet => Some(IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED)),
199        rtnetlink::packet_route::AddressFamily::Inet6 => Some(IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED)),
200        _ => None,
201    };
202    let prefix_len = msg.header.destination_prefix_length;
203    let attrs = &msg.attributes;
204    for nla in attrs.iter() {
205        if let rtnetlink::packet_route::route::RouteAttribute::Destination(addr) = nla {
206            if let rtnetlink::packet_route::route::RouteAddress::Inet(ip) = addr {
207                net_addr = Some(IpAddr::V4(*ip));
208            } else if let rtnetlink::packet_route::route::RouteAddress::Inet6(ip) = addr {
209                net_addr = Some(IpAddr::V6(*ip));
210            }
211        }
212    }
213
214    if let Some(addr) = net_addr {
215        return create_cidr(addr, prefix_len);
216    }
217
218    Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "failed to find destination in message attributes").into())
219}
220
221fn bool_to_ip_version(is_ipv6: bool) -> IpVersion {
222    if is_ipv6 { IpVersion::V6 } else { IpVersion::V4 }
223}
224
225fn get_table_id(table_name: String) -> Result<u32> {
226    if let Ok(table_id) = u32::from_str(table_name.as_str()) {
227        return Ok(table_id);
228    }
229
230    for path in ["/etc/iproute2/rt_tables", "/usr/share/iproute2/rt_tables"] {
231        let rt_tables = parse_rt_tables(String::from(path))?;
232        if let Some(&id) = rt_tables.get(&table_name) {
233            return Ok(id);
234        }
235    }
236
237    Err(std::io::Error::new(std::io::ErrorKind::NotFound, format!("Routing table '{table_name}' not found")).into())
238}
239
240/// Parses the /etc/iproute2/rt_tables file and returns a map of table ID to name.
241fn parse_rt_tables<P: AsRef<Path>>(path: P) -> Result<HashMap<String, u32>> {
242    let file = File::open(path)?;
243    let reader = std::io::BufReader::new(file);
244
245    let mut table_map = HashMap::new();
246    // Defaults cf. https://github.com/iproute2/iproute2/blob/d30f38d5d752abe12174b1ea05707bcf86f3d305/lib/rt_names.c#L508
247    table_map.insert("default".to_string(), 253);
248    table_map.insert("main".to_string(), 254);
249    table_map.insert("local".to_string(), 255);
250
251    for line in reader.lines() {
252        let line = line?;
253        let line = line.trim();
254
255        // Skip empty lines and comments
256        if line.is_empty() || line.starts_with('#') {
257            continue;
258        }
259
260        let parts: Vec<&str> = line.split_whitespace().collect();
261        if parts.len() != 2 {
262            log::debug!("Warning: skipping malformed line: {line}");
263            continue;
264        }
265
266        match parts[0].parse::<u32>() {
267            Ok(id) => {
268                table_map.insert(parts[1].to_string(), id);
269            }
270            Err(_) => {
271                log::debug!("Warning: invalid table ID in line: {line}");
272            }
273        }
274    }
275
276    Ok(table_map)
277}
278
279/// Retrieves a route message for the given CIDR and routing table.
280async fn ip_route_get(search_cidr: &IpCidr, table: u32, strict: bool) -> Result<Option<RouteMessage>> {
281    let ip_version = bool_to_ip_version(search_cidr.is_ipv6());
282    let route_messages = ip_route_show(ip_version, table).await?;
283    for msg in route_messages {
284        let route_cidr = route_msg_to_cidr(&msg)?;
285        if strict && route_cidr == *search_cidr
286            || !strict && route_cidr.contains(&search_cidr.first_address()) && route_cidr.contains(&search_cidr.last_address())
287        {
288            return Ok(Some(msg));
289        }
290    }
291    Ok(None)
292}
293
294async fn route_exists(route: &IpCidr, table: u32, strict: bool) -> Result<bool> {
295    Ok(ip_route_get(route, table, strict).await?.is_some())
296}
297
298fn create_cidr(addr: IpAddr, len: u8) -> Result<IpCidr> {
299    match IpCidr::new(addr, len) {
300        Ok(cidr) => Ok(cidr),
301        Err(_) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("failed to convert {addr}/{len} to CIDR")).into()),
302    }
303}
304
305fn write_buffer_to_fd(fd: std::os::fd::BorrowedFd<'_>, data: &[u8]) -> Result<()> {
306    let mut written = 0;
307    loop {
308        if written >= data.len() {
309            break;
310        }
311        written += nix::unistd::write(fd, &data[written..])?;
312    }
313    Ok(())
314}
315
316fn write_nameserver(fd: std::os::fd::BorrowedFd<'_>, tun_gateway: Option<IpAddr>) -> Result<()> {
317    let tun_gateway = tun_gateway.unwrap_or_else(|| "198.18.0.1".parse().unwrap());
318    let data = format!("nameserver {tun_gateway}\n");
319    nix::sys::stat::fchmod(fd.as_fd(), nix::sys::stat::Mode::from_bits(0o444).unwrap())?;
320    write_buffer_to_fd(fd, data.as_bytes())?;
321    Ok(())
322}
323
324fn ip_forwarding_file_path(ipv6: bool) -> &'static str {
325    if ipv6 {
326        "/proc/sys/net/ipv6/conf/all/forwarding"
327    } else {
328        "/proc/sys/net/ipv4/ip_forward"
329    }
330}
331
332fn ip_fowarding_enabled(ipv6: bool) -> Result<bool> {
333    let path = ip_forwarding_file_path(ipv6);
334    Ok(bytes_to_string(fs::read(path)?)?.trim() == "1")
335}
336
337fn configure_ip_forwarding(ipv6: bool, enable: bool) -> Result<()> {
338    let path = ip_forwarding_file_path(ipv6);
339    fs::write(path, if enable { "1\n" } else { "0\n" })?;
340    Ok(())
341}
342
343fn setup_resolv_conf(restore: &mut TproxyStateInner) -> Result<()> {
344    let tun_gateway = restore.tproxy_args.as_ref().map(|args| args.tun_gateway);
345    // We use a mount here because software like NetworkManager is known to fiddle with the
346    // resolv.conf file and restore it to its original state.
347    // Example: https://stackoverflow.com/q/51784208
348    // Using a readonly bind mount, we can pin our configuration without having the user update
349    // the NetworkManager configuration.
350    let file = tempfile::Builder::new()
351        .permissions(Permissions::from_mode(0o644))
352        .rand_bytes(32)
353        .tempfile()?;
354    write_nameserver(file.as_fd(), tun_gateway)?;
355    let source = format!("/proc/self/fd/{}", file.as_raw_fd());
356    let flags = nix::mount::MsFlags::MS_BIND;
357    let mount1 = nix::mount::mount(source.as_str().into(), ETC_RESOLV_CONF_FILE, "".into(), flags, "".into());
358    if mount1.is_ok() {
359        restore.umount_resolvconf = true;
360        let flags = nix::mount::MsFlags::MS_REMOUNT | nix::mount::MsFlags::MS_RDONLY | nix::mount::MsFlags::MS_BIND;
361        if nix::mount::mount("".into(), ETC_RESOLV_CONF_FILE, "".into(), flags, "".into()).is_err() {
362            log::warn!("failed to remount /etc/resolv.conf as readonly");
363        }
364    }
365    drop(file);
366    if mount1.is_err() {
367        log::warn!("failed to bind mount custom resolv.conf onto /etc/resolv.conf, resorting to direct write");
368
369        restore.restore_resolvconf_content = Some(fs::read(ETC_RESOLV_CONF_FILE)?);
370
371        let flags = nix::fcntl::OFlag::O_WRONLY | nix::fcntl::OFlag::O_CLOEXEC | nix::fcntl::OFlag::O_TRUNC;
372        let fd = nix::fcntl::open(ETC_RESOLV_CONF_FILE, flags, nix::sys::stat::Mode::from_bits(0o644).unwrap())?;
373        write_nameserver(fd.as_fd(), tun_gateway)?;
374    }
375    Ok(())
376}
377
378async fn do_bypass_ip(state: &mut TproxyStateInner, ip: &IpCidr) -> Result<bool> {
379    let route_info = ip_route_show(bool_to_ip_version(ip.is_ipv6()), ROUTING_TABLE_MAIN).await?;
380
381    let cidr = *ip;
382
383    for route_message in route_info {
384        let route_cidr = match route_msg_to_cidr(&route_message) {
385            Ok(cidr) => cidr,
386            Err(_) => {
387                log::debug!("failed to convert route message to CIDR: {route_message:?}");
388                continue;
389            }
390        };
391
392        // If the route does not contain the target CIDR, it is not interesting for us.
393        if !route_cidr.contains(&cidr.first_address()) || !route_cidr.contains(&cidr.last_address()) {
394            continue;
395        }
396
397        // The IP address is routed through a more specific route than the default route.
398        // In this case, there is nothing to do.
399        if route_cidr.network_length() != 0 {
400            break;
401        }
402
403        let mut new_route_message = route_message.clone();
404        let dst_attr = rtnetlink::packet_route::route::RouteAttribute::Destination(match cidr.first_address() {
405            IpAddr::V4(ip) => rtnetlink::packet_route::route::RouteAddress::Inet(ip),
406            IpAddr::V6(ip) => rtnetlink::packet_route::route::RouteAddress::Inet6(ip),
407        });
408        new_route_message.header.destination_prefix_length = cidr.network_length();
409
410        let mut dst_index = None;
411        new_route_message.attributes.iter().enumerate().for_each(|(i, nla)| {
412            if let rtnetlink::packet_route::route::RouteAttribute::Destination(_) = nla {
413                dst_index = Some(i);
414            }
415        });
416
417        match dst_index {
418            Some(index) => new_route_message.attributes[index] = dst_attr,
419            None => new_route_message.attributes.push(dst_attr),
420        }
421
422        let result = ip_route_add_msg(&new_route_message).await;
423        let mut route_exists = false;
424
425        if let Err(err) = &result {
426            if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
427                route_exists = io_err.kind() == std::io::ErrorKind::AlreadyExists;
428            }
429            result?;
430        }
431
432        if !route_exists {
433            log::debug!("added bypass route: {new_route_message:?}");
434            state.remove_routes.push(new_route_message);
435        } else {
436            log::debug!("bypass route already exists: {new_route_message:?}");
437        }
438
439        return Ok(true);
440    }
441    Ok(false)
442}
443
444fn setup_gateway_mode(state: &mut TproxyStateInner, tun_name: &String) -> Result<()> {
445    // sudo iptables -t nat -A POSTROUTING -o "tun_name" -j MASQUERADE
446    let args = &["-t", "nat", "-A", "POSTROUTING", "-o", tun_name.as_str(), "-j", "MASQUERADE"];
447    run_command("iptables", args)?;
448
449    // sudo iptables -A FORWARD -o "tun_name" -j ACCEPT
450    let args = &["-A", "FORWARD", "-o", tun_name.as_str(), "-j", "ACCEPT"];
451    run_command("iptables", args)?;
452
453    // sudo iptables -A FORWARD -i "tun_name" -o "default_interface" -m state --state RELATED,ESTABLISHED -j ACCEPT
454    let args = &[
455        "-A",
456        "FORWARD",
457        "-i",
458        tun_name.as_str(),
459        "-m",
460        "state",
461        "--state",
462        "RELATED,ESTABLISHED",
463        "-j",
464        "ACCEPT",
465    ];
466    run_command("iptables", args)?;
467
468    if !ip_fowarding_enabled(false)? {
469        log::debug!("IP forwarding not enabled");
470        configure_ip_forwarding(false, true)?;
471
472        state.restore_ip_forwarding = true;
473    }
474
475    let mut restore_gateway_mode = Vec::new();
476
477    // sudo iptables -t nat -D POSTROUTING -o "tun_name" -j MASQUERADE
478    restore_gateway_mode.push(format!("-t nat -D POSTROUTING -o {tun_name} -j MASQUERADE"));
479
480    // sudo iptables -D FORWARD -o "tun_name" -j ACCEPT
481    restore_gateway_mode.push(format!("-D FORWARD -o {tun_name} -j ACCEPT"));
482
483    // sudo iptables -D FORWARD -i "tun_name" -m state --state RELATED,ESTABLISHED -j ACCEPT
484    restore_gateway_mode.push(format!("-D FORWARD -i {tun_name} -m state --state RELATED,ESTABLISHED -j ACCEPT"));
485
486    state.restore_gateway_mode = Some(restore_gateway_mode);
487    log::debug!("restore gateway mode: {:?}", state.restore_gateway_mode);
488
489    Ok(())
490}
491
492async fn setup_fwmark_table(state: &mut TproxyStateInner, tproxy_args: &TproxyArgs) -> Result<()> {
493    let Some(fwmark) = tproxy_args.socket_fwmark else {
494        return Err(Box::new(std::io::Error::other("fwmark is None")));
495    };
496
497    let table = get_table_id(tproxy_args.socket_fwmark_table.clone())?;
498
499    // sudo ip rule add fwmark "mark" table "table"
500    ip_rule_add(IpVersion::V4, fwmark, table).await?;
501    ip_rule_add(IpVersion::V6, fwmark, table).await?;
502
503    // Flush the fwmark table. We just claim that table.
504    ip_route_flush(table, IpVersion::V4).await?;
505    ip_route_flush(table, IpVersion::V6).await?;
506
507    let ipv4_routes = ip_route_show(IpVersion::V4, table).await?;
508    let ipv6_routes = ip_route_show(IpVersion::V6, table).await?;
509
510    for route in ipv4_routes.iter().chain(ipv6_routes.iter()) {
511        let mut cloned_route = route.clone();
512
513        cloned_route.header.table = 0;
514
515        let mut tbl_index = None;
516        cloned_route.attributes.iter().enumerate().for_each(|(i, nla)| {
517            if let rtnetlink::packet_route::route::RouteAttribute::Table(_) = nla {
518                tbl_index = Some(i);
519            }
520        });
521
522        if let Some(tbl_index) = tbl_index {
523            cloned_route.attributes[tbl_index] = rtnetlink::packet_route::route::RouteAttribute::Table(table);
524        } else {
525            cloned_route
526                .attributes
527                .push(rtnetlink::packet_route::route::RouteAttribute::Table(table));
528        }
529
530        ip_route_add_msg(route).await?;
531    }
532
533    state.restore_socket_fwmark = Vec::from([
534        crate::FwmarkRestore {
535            ip_version: IpVersion::V4,
536            fwmark,
537            table,
538        },
539        crate::FwmarkRestore {
540            ip_version: IpVersion::V6,
541            fwmark,
542            table,
543        },
544    ]);
545    log::debug!("restore socket fwmark: {:?}", state.restore_socket_fwmark);
546
547    Ok(())
548}
549
550async fn detect_routing_loop(proxy_cidr: &IpCidr, tun_name: &str) -> Result<()> {
551    let tun_interface_id = ip_link_get_index(tun_name).await?;
552    log::debug!("tun ({tun_name}) interface id: {tun_interface_id}");
553    let mut routing_loop = false;
554
555    let proxy_route = ip_route_get(proxy_cidr, ROUTING_TABLE_MAIN, false).await?;
556    if let Some(route) = proxy_route {
557        route.attributes.iter().for_each(|nla| {
558            if let rtnetlink::packet_route::route::RouteAttribute::Oif(index) = nla {
559                log::debug!("proxy route output interface index: {index}");
560                if *index == tun_interface_id {
561                    routing_loop = true;
562                }
563            }
564        });
565        if routing_loop {
566            return Err(std::io::Error::other("routing loop detected".to_string()).into());
567        }
568    } else {
569        return Err(std::io::Error::new(std::io::ErrorKind::NotFound, format!("route to proxy {proxy_cidr} not found")).into());
570    }
571    Ok(())
572}
573
574pub(crate) async fn _tproxy_setup_inner(tproxy_args: &TproxyArgs, state: &mut TproxyStateInner) -> Result<()> {
575    let tun_name = &tproxy_args.tun_name;
576
577    let proxy_cidr = IpCidr::new_host(tproxy_args.proxy_addr.ip());
578
579    flush_dns_cache()?;
580
581    // check for gateway mode
582    if tproxy_args.gateway_mode {
583        setup_gateway_mode(state, tun_name)?;
584    }
585
586    // check for socket fwmark
587    if tproxy_args.socket_fwmark.is_some() {
588        setup_fwmark_table(state, tproxy_args).await?;
589    }
590
591    // sudo ip link set tun0 up
592    ip_link_set_up(tun_name).await?;
593
594    for ip in tproxy_args.bypass_ips.iter() {
595        do_bypass_ip(state, ip).await?;
596    }
597
598    if tproxy_args.bypass_ips.is_empty() {
599        do_bypass_ip(state, &proxy_cidr).await?;
600    }
601
602    if tproxy_args.ipv4_default_route {
603        if !route_exists(&IPV4_DEFAULT_ROUTE, ROUTING_TABLE_MAIN, true).await? {
604            state
605                .remove_routes
606                .push(ip_route_add(&IPV4_DEFAULT_ROUTE, tun_name, ROUTING_TABLE_MAIN).await?);
607        } else {
608            state
609                .remove_routes
610                .push(ip_route_add(&IPV4_SPACE_LOWER, tun_name, ROUTING_TABLE_MAIN).await?);
611            state
612                .remove_routes
613                .push(ip_route_add(&IPV4_SPACE_UPPER, tun_name, ROUTING_TABLE_MAIN).await?);
614        }
615    } else {
616        // If IPv4 is not enabled, we do not want IPv4 traffic to bypass the proxy if a route
617        // already exists.
618        let default_route = ip_route_get(&IPV4_DEFAULT_ROUTE, ROUTING_TABLE_MAIN, true).await?;
619
620        if let Some(msg) = default_route {
621            ip_route_del_msg(&msg).await?;
622            state.restore_routes.push(msg.clone());
623        } else {
624            log::debug!("no IPv4 default route found");
625        }
626    }
627
628    if tproxy_args.ipv6_default_route {
629        if !route_exists(&IPV6_DEFAULT_ROUTE, ROUTING_TABLE_MAIN, true).await? {
630            state
631                .remove_routes
632                .push(ip_route_add(&IPV6_DEFAULT_ROUTE, tun_name, ROUTING_TABLE_MAIN).await?);
633        } else {
634            state
635                .remove_routes
636                .push(ip_route_add(&IPV6_SPACE_LOWER, tun_name, ROUTING_TABLE_MAIN).await?);
637            state
638                .remove_routes
639                .push(ip_route_add(&IPV6_SPACE_UPPER, tun_name, ROUTING_TABLE_MAIN).await?);
640        }
641    } else {
642        // If IPv6 is not enabled, we do not want IPv6 traffic to bypass the proxy if a route
643        // already exists.
644        let default_route = ip_route_get(&IPV6_DEFAULT_ROUTE, ROUTING_TABLE_MAIN, true).await?;
645
646        if let Some(msg) = default_route {
647            ip_route_del_msg(&msg).await?;
648            state.restore_routes.push(msg.clone());
649        } else {
650            log::debug!("no IPv6 default route found");
651        }
652    }
653
654    setup_resolv_conf(state)?;
655
656    detect_routing_loop(&proxy_cidr, tun_name).await?;
657
658    Ok(())
659}
660
661pub(crate) async fn _tproxy_setup(tproxy_args: &TproxyArgs) -> Result<TproxyStateInner> {
662    let mut state: TproxyStateInner = TproxyStateInner {
663        tproxy_args: Some(tproxy_args.clone()),
664        ..Default::default()
665    };
666    let result = _tproxy_setup_inner(tproxy_args, &mut state).await;
667    if result.is_err() {
668        let remove_result = _tproxy_remove(&mut state).await;
669        if let Err(remove_result) = &remove_result {
670            log::error!("tproxy removal failed: {remove_result}");
671        }
672        result?;
673    }
674    Ok(state)
675}
676
677pub(crate) async fn _tproxy_remove(state: &mut TproxyStateInner) -> Result<()> {
678    if state.tproxy_removed_done {
679        return Ok(());
680    }
681
682    state.tproxy_removed_done = true;
683    let tproxy_args = state
684        .tproxy_args
685        .as_ref()
686        .ok_or(std::io::Error::new(std::io::ErrorKind::InvalidData, "tproxy_args is None"))?;
687
688    for route in &state.restore_routes {
689        log::debug!("restoring route: {route:?}");
690        if let Err(err) = ip_route_add_msg(route).await {
691            log::debug!("ip route add {route:?} error: {err}");
692        }
693    }
694
695    for route in &state.remove_routes {
696        log::debug!("removing route: {route:?}");
697        if let Err(err) = ip_route_del_msg(route).await {
698            log::debug!("ip route del {route:?} error: {err}");
699        }
700    }
701
702    if let Some(gateway_restore) = &state.restore_gateway_mode {
703        for restore in gateway_restore {
704            log::debug!("restore gateway mode: iptables {restore}");
705
706            if let Err(_err) = run_command("iptables", &restore.split(' ').collect::<Vec<&str>>()) {
707                log::debug!("command \"iptables {restore}\" error: {_err}");
708            }
709        }
710    }
711
712    for entry in &state.restore_socket_fwmark {
713        log::debug!(
714            "restore socket fwmark: ip rule del {} table {} (v: {:?})",
715            entry.fwmark,
716            entry.table,
717            entry.ip_version
718        );
719
720        if let Err(_err) = ip_rule_del(entry.ip_version.clone(), entry.fwmark).await {
721            log::debug!("ip_rule_del error: {_err}");
722        }
723
724        if let Err(err) = ip_rule_del(entry.ip_version.clone(), entry.fwmark).await {
725            log::debug!("ip rule del fwmark {} (v: {:?}) error: {err}", entry.fwmark, entry.ip_version);
726        }
727        if let Err(err) = ip_route_flush(entry.table, entry.ip_version.clone()).await {
728            log::debug!("ip route flush table {} (v: {:?}) error: {err}", entry.table, entry.ip_version);
729        }
730    }
731
732    if state.restore_ip_forwarding {
733        log::debug!("restore ip forwarding");
734
735        if let Err(_err) = configure_ip_forwarding(false, false) {
736            log::debug!("error restoring IP forwarding: {_err}");
737        }
738    }
739
740    log::debug!("deleting link: {}", tproxy_args.tun_name);
741    // sudo ip link del tun0
742    if let Err(err) = ip_link_del(&tproxy_args.tun_name).await {
743        log::debug!("ip link del {} error: {err}", tproxy_args.tun_name);
744    }
745
746    if state.umount_resolvconf {
747        log::debug!("unmounting {ETC_RESOLV_CONF_FILE}");
748        nix::mount::umount(ETC_RESOLV_CONF_FILE)?;
749    }
750
751    if let Some(data) = &state.restore_resolvconf_content {
752        fs::write(ETC_RESOLV_CONF_FILE, data)?;
753    }
754
755    flush_dns_cache()?;
756
757    Ok(())
758}
759
760pub(crate) fn flush_dns_cache() -> Result<()> {
761    // do nothing in linux
762    Ok(())
763}