背景

背景

如何强制 Chrome 使用特定的网络接口(例如 tun1)?

某些程序例如curl具有选择特定网络接口的选项。

在 Windows 中,强制绑定IP允许您强制应用程序使用特定的网络接口/ IP 地址。

我想知道 Ubuntu/Linux 中是否有解决方案。

或者,由于我们可以在 Chrome 中设置代理;我们可以将本地 IP 重定向到网络接口或反之亦然吗?

答案1

这是网络命名空间的完美用例,网络命名空间自 2016 年或更早以来就已成为 Linux 的一部分。以下是带有注释的示例,可帮助您入门:

# enable forwarding
sysctl -w net.ipv4.ip_forward=1

# create the network namespace
ip netns add chrome

# create the virtual nic and it's peer
ip link add chrome type veth peer name chrome-peer

# assign the peer to the network namespace
ip link set chrome-peer netns chrome

# assign an ip address
ip addr add 192.0.2.1/24 dev chrome

# bring up interface
ip link set chrome up

# similar network setup for network namespace
ip netns exec chrome ip link set lo up
ip netns exec chrome ip addr add 192.0.2.2/24 dev chrome-peer
ip netns exec chrome ip route add default via 192.0.2.1
ip netns exec chrome ip link set chrome-peer up

# allow forwarding and add enable NAT
iptables -I FORWARD -s 192.0.2.0/24 -j ACCEPT
iptables -t nat -I POSTROUTING -s 192.0.2.0/24 -o tun1 -j MASQUERADE

# pop a shell in the namespace
ip netns exec chrome bash

# check that you're in the namespace
ip netns identify

# run the browser as your local user
runuser -u Barry google-chrome

答案2

背景

Linux 可以SO_BINDTODEVICE选择将套接字绑定到特定的网络接口。

主意

Chromium 使用 libc 函数来调用系统调用,包括connect()用于执行网络请求的系统调用。我们可以在执行系统调用之前修补connect()要调用的函数。这可以通过环境变量预加载共享库来实现。setsockopt(SO_BINDTODEVICE)connect()LD_PRELOAD

最小工作示例:

连接.c

#define _GNU_SOURCE
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/syscall.h>

#define IFACE_NAME "tun0"
#define IPV6_TO_BIND "fddd:bbbb::2"

static void set_bind_addr6(int fd)
{
        struct sockaddr_in6 addr;

        memset(&addr, 0, sizeof(addr));
        addr.sin6_family = AF_INET6;
        inet_pton(AF_INET6, IPV6_TO_BIND, &addr.sin6_addr);
        bind(fd, (struct sockaddr *)&addr, sizeof(addr));
}

int connect(int fd, const struct sockaddr *daddr, socklen_t addrlen)
{
        int family = daddr->sa_family;

        if (family == AF_INET || family == AF_INET6) {

                if (family == AF_INET6)
                        set_bind_addr6(fd);

                setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, IFACE_NAME, sizeof(IFACE_NAME));
        }

        return syscall(__NR_connect, fd, daddr, addrlen);
}

调整IFACE_NAMEIPV6_TO_BIND您的机器设置。如果您不想使用 IPv6,则可以省略 IPv6 绑定代码。

如果您希望使用 IPv6,则需要 IPv6 绑定,因为调用者设置了错误的绑定地址。它还可以帮助您选择要用于传出连接的 IPv6 地址。

IPv4 不需要绑定。

用法

ammarfaizi2@integral2:/tmp$ gcc -fpic -fPIC -Wall -Wextra -shared -O3 connect.c -o connect.so
ammarfaizi2@integral2:/tmp$ export LD_PRELOAD="/tmp/connect.so"
ammarfaizi2@integral2:/tmp$ /opt/google/chrome/chrome

在 Ubuntu 22.04 上使用 IPv4 和 IPv6 网络测试并运行良好

更新

2023 年 6 月 30 日:

  • 添加 IPv6 支持。
  • 删除内联汇编(使其可移植到其他架构)。

相关内容