Python 在OpenVPN客户端运行时将套接字绑定到本地IP地址块

Python 在OpenVPN客户端运行时将套接字绑定到本地IP地址块,python,linux,sockets,networking,openvpn,Python,Linux,Sockets,Networking,Openvpn,我已将我的计算机设置为运行VPN客户端,并希望通过VPN隧道或通过指定套接字上的绑定地址直接通过本地接口连接到internet上的主机。考虑下面的代码示例: import socket, subprocess, re def get_ipv4_address(): ifc_resp = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE).communicate() patt = re.compile(r'inet\s*\

我已将我的计算机设置为运行VPN客户端,并希望通过VPN隧道或通过指定套接字上的绑定地址直接通过本地接口连接到internet上的主机。考虑下面的代码示例:

import socket, subprocess, re

def get_ipv4_address():
    ifc_resp = subprocess.Popen(["ifconfig"], stdout=subprocess.PIPE).communicate()
    patt = re.compile(r'inet\s*\w*\S*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
    return patt.findall(ifc_resp[0])

def check_sock(addr):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((addr, 0))
    s.connect(("www.google.com", 80))
    s.close()

def main():
    addrs = sorted(a for a in get_ipv4_address() if not a.startswith("127.0."))
    print "Checking addresses " + ", ".join(addrs)
    for addr in addrs:
        print "Connecting via " + addr
        check_sock(addr)

main()
当我运行此程序时,可以通过VPN IP地址毫无问题地建立连接。尽管程序挂起连接到本地网络的连接:

>>> python binddemo.py 
Checking addresses 10.200.195.233, 192.168.2.33
Connecting via 10.200.195.233
Connecting via 192.168.2.33
^CTraceback (most recent call last):
  File "binddemo.py", line 22, in <module>
    main()
  File "binddemo.py", line 20, in main
    check_sock(addr)
  File "binddemo.py", line 12, in check_sock
    s.connect(("www.google.com", 80))
  File "/usr/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
KeyboardInterrupt
指定给普通IP地址(而不是google主机)的连接同样会失败,所以我想这不是DNS查找造成的问题。有什么办法可以解决这个问题或者至少可以进一步研究吗?

在客户端套接字上使用
bind()
,而该客户端套接字不打算
listen()
也不起任何作用。用于传出连接的接口完全取决于路由配置

您已设置了两个默认网关,但连接到wlan接口的网关具有比隧道设备更高的
指标。因此linux总是将tuneling接口用作默认网关

下面是一个更深入的解释:

[编辑1]

仔细查看路由表后,删除隧道设备的默认网关应该可以解决问题,因为您还有一个网络路由设置,可以正确地将所有流量路由到vpn。因此,请尝试删除此项:

default via 10.200.195.1 dev tun0  proto static  metric 50 
因为这个将通过
tun0
路由您的vpn流量:

10.200.195.0/24 dev tun0  proto kernel  scope link  src 10.200.195.233  metric 50 
[编辑2]

如果希望通过隧道设备保留默认路由,可以使用
网络名称空间


但是,您仍然无法从一个程序访问两个网络。

最终的解决方案是:

echo "1 localwlan" >> /etc/iproute2/rt_tables
ip route add default table localwlan via 192.168.2.33 dev wlan0
ip rule add from 192.168.2.33 lookup localwlan

感谢@Christian Eichelman为我指明了正确的方向

是否无法保留默认路线,并根据需要指定其他路线?我希望通过VPN保留默认设置。问题是:您的VPN的地址范围有限(10.200.195.0/24),因此您可以设置网络路由。internet没有:)因此您通常将默认gw指向面向internet的路由器。我的意思是,最终我希望绕过计算机的路由配置,在不同的VPN及其网关之间选择如何以编程方式路由流量。依赖路由对我没有任何帮助。我曾希望可以通过为套接字指定不同的传出IP地址来实现这一点。我也可以指定虚拟设备,比如
ping-I dev
。如果您希望每个进程都这样做,现代内核支持
网络名称空间
。这与python无关,您可以将不同的进程绑定到不同的接口。请查看以下答案:。请注意,您不能从同一进程访问两个网络。请为具有任何网络/子网范围的专用网络划分一个接口。
echo "1 localwlan" >> /etc/iproute2/rt_tables
ip route add default table localwlan via 192.168.2.33 dev wlan0
ip rule add from 192.168.2.33 lookup localwlan