我有两个网络接口(wifi和以太网),都可以上网。假设我的接口是eth (以太网)和wlp2 (wifi)。我需要通过eth接口和其他通过wpl2的具体要求。
类似于:
// Through "eth"
request.post(url="http://myapi.com/store_ip", iface="eth")
// Through "wlp2"
request.post(url="http://myapi.com/log", iface="wlp2")我使用的是requests,但如果无法用requests实现,我也可以使用pycurl或urllib。
How to specify source interface in python requests module?引用了Requests, bind to an ip,但它不起作用。
发布于 2018-02-27 05:39:37
我找到了一种使用pycurl的方法。这就像一个护身符。
import pycurl
from io import BytesIO
import json
def curl_post(url, data, iface=None):
c = pycurl.Curl()
buffer = BytesIO()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.POST, True)
c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json'])
c.setopt(pycurl.TIMEOUT, 10)
c.setopt(pycurl.WRITEFUNCTION, buffer.write)
c.setopt(pycurl.POSTFIELDS, data)
if iface:
c.setopt(pycurl.INTERFACE, iface)
c.perform()
# Json response
resp = buffer.getvalue().decode('UTF-8')
# Check response is a JSON if not there was an error
try:
resp = json.loads(resp)
except json.decoder.JSONDecodeError:
pass
buffer.close()
c.close()
return resp
if __name__ == '__main__':
dat = {"id": 52, "configuration": [{"eno1": {"address": "192.168.1.1"}}]}
res = curl_post("http://127.0.0.1:5000/network_configuration/", json.dumps(dat), "wlp2")
print(res)我把这个问题留在一边,希望有人能用requests给出答案。
发布于 2020-05-04 04:19:36
这是一个不需要修补任何东西的请求库的解决方案。
此函数将创建绑定到给定IP地址的会话。由您决定所需网络接口的IP地址。
经测试可与requests==2.23.0配合使用。
import requests
def session_for_src_addr(addr: str) -> requests.Session:
"""
Create `Session` which will bind to the specified local address
rather than auto-selecting it.
"""
session = requests.Session()
for prefix in ('http://', 'https://'):
session.get_adapter(prefix).init_poolmanager(
# those are default values from HTTPAdapter's constructor
connections=requests.adapters.DEFAULT_POOLSIZE,
maxsize=requests.adapters.DEFAULT_POOLSIZE,
# This should be a tuple of (address, port). Port 0 means auto-selection.
source_address=(addr, 0),
)
return session
# usage example:
s = session_for_src_addr('192.168.1.12')
s.get('https://httpbin.org/ip')不过要注意,这种方法与curl的--interface选项相同,在某些情况下不会有帮助。根据你的路由配置,即使你绑定到特定的IP地址,请求也会通过其他接口。所以如果这个答案对你不起作用,那么首先检查curl http://httpbin.org/ip --interface myinterface是否会像预期的那样工作。
发布于 2021-08-24 11:31:44
如果你想在Linux上这样做,你可以对setsockopt使用SO_BINDTODEVICE标志(更多细节请查看man 7 socket )。事实上,如果你在linux上使用--interface option,它就是使用by curl的工具。但请记住,SO_BINDTODEVICE需要根权限(CAP_NET_RAW,尽管有some attempts可以更改这一点),如果SO_BINDTODEVICE失败,curl会退回到常规的bind技巧。
下面是失败时的curl代码示例:
strace -f -e setsockopt,bind curl --interface eth2 https://ifconfig.me/
strace: Process 18208 attached
[pid 18208] +++ exited with 0 +++
setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
setsockopt(3, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = -1 EPERM (Operation not permitted)
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.8.1")}, 16) = 0 # curl fallbacks to regular bind
127.0.0.1+++ exited with 0 +++另外,我想说的是,使用常规bind并不总是保证流量会通过指定的接口(@MarSoft answer使用普通的bind)。在linux上,只有SO_BINDTODEVICE可以保证流量通过指定的设备。
这里有一个如何在requests和requests-toolbelt中使用SO_BINDTODEVICE的例子(正如我所说的,它需要CAP_NET_RAW permissions)。
import socket
import requests
from requests_toolbelt.adapters.socket_options import SocketOptionsAdapter
session = requests.Session()
# set interface here
options = [(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, b"eth0")]
for prefix in ('http://', 'https://'):
session.mount(prefix, SocketOptionsAdapter(socket_options=options))
print(session.get("https://ifconfig.me/").text)或者,如果您不想使用requests-toolbelt,您可以自己实现适配器类:
import socket
import requests
from requests import adapters
from urllib3.poolmanager import PoolManager
class InterfaceAdapter(adapters.HTTPAdapter):
def __init__(self, **kwargs):
self.iface = kwargs.pop('iface', None)
super(InterfaceAdapter, self).__init__(**kwargs)
def _socket_options(self):
if self.iface is None:
return []
else:
return [(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, self.iface)]
def init_poolmanager(self, connections, maxsize, block=False):
self.poolmanager = PoolManager(
num_pools=connections,
maxsize=maxsize,
block=block,
socket_options=self._socket_options()
)
session = requests.Session()
for prefix in ('http://', 'https://'):
session.mount(prefix, InterfaceAdapter(iface=b'eth0'))
print(session.get("https://ifconfig.me/").text)https://stackoverflow.com/questions/48996494
复制相似问题