【问题标题】:Bind socket to local IP address blocks while OpenVPN client is running [closed]在 OpenVPN 客户端运行时将套接字绑定到本地 IP 地址块[关闭]
【发布时间】:2017-07-10 21:04:00
【问题描述】:

我的机器设置了一个正在运行的 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

我不明白为什么会这样,因为本地 NIC 仍应正确设置路由以连接到互联网:

>>> ip route
default via 10.200.195.1 dev tun0  proto static  metric 50 
default via 192.168.2.1 dev wlan0  proto static  metric 600 
10.200.195.0/24 dev tun0  proto kernel  scope link  src 10.200.195.233  metric 50 
62.113.253.4 via 192.168.2.1 dev wlan0  proto static  metric 600 
169.254.0.0/16 dev wlan0  scope link  metric 1000 
192.168.2.0/24 dev wlan0  proto kernel  scope link  src 192.168.2.33  metric 600 

指定到普通 IP 地址(而不是 google 主机)的连接同样失败,所以我想这不是 DNS 查找造成的问题。任何想法如何解决或至少进一步调查?

【问题讨论】:

  • 用任何网络/子网范围划分专用网络接口。

标签: python linux sockets networking openvpn


【解决方案1】:

在不会使用listen() 的客户端套接字上使用bind() 不会做任何事情。用于传出连接的接口完全取决于您的路由配置。

您已经设置了两个默认网关,但是连接到您的 wlan 接口的一个具有比您的隧道设备更高的metric。所以linux总是会使用tuning接口作为默认网关。

这里有一个很好的、更深入的解释:https://serverfault.com/questions/648276/routing-selection-specificity-vs-metric

[编辑 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]

如果你想通过隧道设备保持默认路由,你可以使用network namespaces:https://superuser.com/questions/241178/how-to-use-different-network-interfaces-for-different-processes/750412#750412

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

【讨论】:

  • 有没有办法在保留默认路由的同时仍然按需指定其他路由?我希望通过 VPN 保持默认设置。
  • 问题是:您的 vpn 的地址范围有限(10.200.195.0/24),因此您可以设置网络路由。互联网没有 :) 所以你通常将默认 gw 指向面向互联网的路由器。
  • 我的意思是,最后我想绕过机器的路由配置,并在不同的 VPN 及其网关之间选择如何以编程方式路由流量。依靠路由对我没有任何帮助。我曾希望我可以通过为套接字指定不同的传出 IP 地址来做到这一点。
  • 我也可以指定虚拟设备,例如ping -I dev
  • 好吧,如果您想按进程执行此操作,现代内核支持network namespaces。这与python无关,你可以将不同的进程绑定到不同的接口。看看这个答案:superuser.com/questions/241178/…。请注意,您不能从同一个进程访问两个网络。
【解决方案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 为我指明了正确的方向

【讨论】:

    猜你喜欢
    • 2012-06-29
    • 2017-06-06
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多