Найти - Пользователи
Полная версия: Выбор сетевого интерфейса из скрипта.
Начало » Network » Выбор сетевого интерфейса из скрипта.
1 2
slivlen
demoriz
Ну неужели у такого популярного языка нет способа самостоятельно выбирать сетевой интерфейс для выхода в интернет?
Язык здесь вообще не причем. Это делается при помощи сокетов(см bind). Только вот к urllib это проблематично будет применить.
tombird
интересно, если на хосте расположено с десяток интерфейсов,
значит должны быть настроены протоколы маршрутизации,
и таблица маршрутов является динамической.

значит, распределением нагрузки по сетевым интерфейсам занимается операционная система,
а не приложение - ведь в метриках и текущая загрузка маршрутов учитывается.

по всей видимости прибиндить пакеты к определенному интерфейсу возможно лишь в случае
использования raw sockets, и этот вариант вряд ли подойдет топикстартеру.
(есть еще маньяки - пишут хаки для ядра или используют netlink sockets чтобы избежать роутинга

bind и SO_BINDTODEVICE - поменяют адрес отправителя в заголовках IP, а не интерфейс.
так что настраивайте роутинг.
slivlen
tombird
bind и SO_BINDTODEVICE - поменяют адрес отправителя в заголовках IP, а не интерфейс.
так что настраивайте роутинг.
Бред! Забайнди TCP сокет на 127.0.0.1 и попробуй законнектиться к серверу во внешней сети.
По твоему получается что если сервер(н-р апач) делает bind, то это только для того чтобы сменить ip отправителя?
tombird
slivlen
попробовал - и вправду чушь я написал про подмену адреса.
в случае bind-а к локалхосту вылетает connect с ошибкой параметра, а случае бинда к локальному адресу сетевухи - вызов bind игнорируется (адреса пакетов действительно нормальные).

А так, почему-то всегда считал, что вызов bind предназначен для слушающих сокетов, но никак не отправляющих.
кстати - любопытно, есть ли программы, которая позволяет указывать интерфейс для исходящих соединений (ну кроме ping).

С другой стороны, setsockopt с SO_BINDTODEVICE показал интересные результаты:
в случае с интерфейсом lo - уходят безответные SYN пакеты с адресом от eth0 (неясно)
Capturing on lo
0.000000 192.168.34.2 -> 91.121.46.245 TCP 43804 > http [SYN] Seq=0 Win=32792 Len=0 MSS=16396 TSV=730162486 TSER=0 WS=2
11.999955 192.168.34.2 -> 91.121.46.245 TCP 43804 > http [SYN] Seq=0 Win=32792 Len=0 MSS=16396 TSV=730165486 TSER=0 WS=2
35.999951 192.168.34.2 -> 91.121.46.245 TCP 43804 > http [SYN] Seq=0 Win=32792 Len=0 MSS=16396 TSV=730171486 TSER=0 WS=2
ну а в случае биндинга к eth0 - сетевой стек не может определить физический адрес destination хоста
Capturing on eth0
0.000000 Internet_03:45:e8 -> Broadcast ARP Who has 91.121.46.245? Tell 192.168.34.2
1.000000 Internet_03:45:e8 -> Broadcast ARP Who has 91.121.46.245? Tell 192.168.34.2
2.000057 Internet_03:45:e8 -> Broadcast ARP Who has 91.121.46.245? Tell 192.168.34.2
demoriz
Попробуйте запускать следующий скрипт на вашем многодомном хосте, подменяя параметр
IFACE на нужный интерфейс - и отпишите результат - очень интересно!
команда, которой я проверял наличие пакетов на интерфейсе - tshark -i ppp0 -f “host coding.debuntu.org

В случае, если все сработает, можно просто сделать копию httplib.py и добавить в нужные методы параметр интерфейса и вызов setsockopt

#!/usr/bin/env python

import socket
import sys
import IN
import struct

HOST = 'coding.debuntu.org'
GET = '/system/files/socket_client.py.txt'
PORT = 80
IFACE = 'eth0'

try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg:
sys.stderr.write("1 [ERROR] %s\n" % msg[1])
sys.exit(1)

sock.setsockopt(socket.SOL_SOCKET,IN.SO_BINDTODEVICE,struct.pack("%ds" % (len(IFACE)+1,), IFACE))

try:
sock.connect((HOST, PORT))
except socket.error, msg:
sys.stderr.write("4 [ERROR] %s\n" % msg[1])
sys.exit(2)

sock.send("GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" % (GET, HOST))

data = sock.recv(1024)
string = ""
while len(data):
string = string + data
data = sock.recv(1024)
sock.close()

print string

sys.exit(0)
demoriz
ситуация с интерфейсами такая:
ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

venet0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:127.0.0.1 P-t-P:127.0.0.1 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1
RX packets:67382 errors:0 dropped:0 overruns:0 frame:0
TX packets:73879 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:6758018 (6.4 MiB) TX bytes:12516020 (11.9 MiB)

venet0:0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:77.221.155.141 P-t-P:77.221.155.141 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1

venet0:1 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:77.221.155.142 P-t-P:77.221.155.142 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1

venet0:2 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:77.221.159.146 P-t-P:77.221.159.146 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1

venet0:3 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:77.221.159.148 P-t-P:77.221.159.148 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1

venet0:4 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:77.221.159.149 P-t-P:77.221.159.149 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1

venet0:5 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:77.221.159.150 P-t-P:77.221.159.150 Bcast:0.0.0.0 Mask:255.255.255.255
UP BROADCAST POINTOPOINT RUNNING NOARP MTU:1500 Metric:1
скрипт работает только при
IFACE = 'venet0'
если же сделать
IFACE = 'venet0:1'
то вылетает с ошибкой
Traceback (most recent call last):
File "/srv/bot/bla.py", line 19, in <module>
sock.setsockopt(socket.SOL_SOCKET,IN.SO_BINDTODEVICE,struct.pack("%ds" % (len(IFACE)+1,), IFACE))
File "<string>", line 1, in setsockopt
socket.error: (19, 'No such device')
В одном месте посоветовали подменить opener от urllib2
попытался сделать так
#!/usr/bin/python
# _*_ coding: utf-8 _*_
import urllib2
import httplib
def geturl():
txheaders = {
"User-Agent" : "Opera/9.64 (Windows NT 5.1; U; en) Presto/2.1.1",
'Accept-Language': 'en-us',
'Cache-Control': 'max-age=0'
}
req = urllib2.Request('http://xxxx.xx', None, txheaders)
return urllib2.urlopen(req).read()
class MyHTTPConnection(httplib.HTTPConnection):
def __init__(self, host, port=None, strict=None):
httplib.HTTPConnection.__init__(self, host, port, strict)

def connect(self):
"""Connect to the host and port specified in __init__."""
msg = "getaddrinfo returns an empty list"
for res in socket.getaddrinfo(self.host, self.port, 0,
socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
self.sock = socket.socket(af, socktype, proto)
if self.debuglevel > 0:
print "connect: (%s, %s)" % (self.host, self.port)
self.sock.connect(sa)
self.sock.bind(('77.221.159.148', 80))

except socket.error, msg:
if self.debuglevel > 0:
print 'connect fail:', (self.host, self.port)
if self.sock:
self.sock.close()
self.sock = None
continue
break
if not self.sock:
raise socket.error, msg
class MyHTTPHandler(urllib2.AbstractHTTPHandler):
def http_open(self, req):
return self.do_open(MyHTTPConnection, req)
http_request = urllib2.AbstractHTTPHandler.do_request_
my_opener = urllib2.build_opener(MyHTTPHandler)
urllib2.install_opener(my_opener)
print geturl()
в итоге апач сайта зарегестрировал коннект с 77.221.155.141
хотя выставил
self.sock.bind(('77.221.159.148', 80))
где я туплю…
demoriz
Так работает :)
#!/usr/bin/python
# _*_ coding: utf-8 _*_
import socket
host = '77.221.155.141'
port = 80
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))
s.connect(("xxxx.xx", 80))
mes = "GET / HTTP/1.1\r\n"
mes += "Host: www.ventra.su\r\n"
mes += "User-Agent: Opera/9.64 (Windows NT 5.1; U; en) Presto/2.1.1\r\n"
mes += "Accept: text/html\r\n"
mes += "Connection: close\r\n\r\n"
s.send(mes)
buff = ""
result = ""
while 1:
buff = s.recv(1024)
if buff: result += buff
else: break
s.close()
print result
только если апачь на этом же сервере запущен ошибку выкидывает что порт занят.
Traceback (most recent call last):
File "/srv/bot/bla.py", line 10, in <module>
s.bind((host, port))
File "<string>", line 1, in bind
socket.error: (98, 'Address already in use')
tombird
спасибо за тест. Удивительно, мягко говоря.
хотя логично - интерфейс то один (venet0 - просто адресов у него много)

А с вашими скриптами - раз последний пример с биндом сработал, значит сработает и с build_opener-ом,
если вызов bind выполнить до connect (у вас в тексте он позже).
caribbean_snow
если еще актуально - вот такое решение. Работает отлично.
http://stackoverflow.com/questions/1150332/source-interface-with-python-and-urllib2
import urllib2, httplib, socket

class BindableHTTPConnection(httplib.HTTPConnection):
def connect(self):
"""Connect to the host and port specified in __init__."""
self.sock = socket.socket()
self.sock.bind((self.source_ip, 0))
if isinstance(self.timeout, float):
self.sock.settimeout(self.timeout)
self.sock.connect((self.host,self.port))

def BindableHTTPConnectionFactory(source_ip):
def _get(host, port=None, strict=None, timeout=0):
bhc=BindableHTTPConnection(host, port=port, strict=strict, timeout=timeout)
bhc.source_ip=source_ip
return bhc
return _get

class BindableHTTPHandler(urllib2.HTTPHandler):
def http_open(self, req):
return self.do_open(BindableHTTPConnectionFactory('127.0.0.1'), req)

opener = urllib2.build_opener(BindableHTTPHandler)
opener.open("http://google.com/").read() # Will fail, 127.0.0.1 can't reach google.com.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB