Bu eğlenceli bir problemdi.
Güncelleme: O kadar çok ben https://github.com/larsks/python-netns edinilebilir yüklenebilir bir Python modülü olarak çözümü, toplayıp sevdim.
setns()
sistem çağrısı kullanılarak başka bir ağ ad alanına erişebilirsiniz. Bu çağrı Python tarafından doğal olarak gösterilmiyor, bu yüzden onu kullanmak için (a) onu saran bir üçüncü taraf modülü bulmanız veya (b) ctypes
modülü gibi bir şeyi kullanmanız gerekir. Python kodu. İçeriye bir arayüz eklendi
# ip netns add red
# ip netns add blue
:
#!/usr/bin/python
import argparse
import os
import select
import socket
import subprocess
# Python doesn't expose the `setns()` function manually, so
# we'll use the `ctypes` module to make it available.
from ctypes import cdll
libc = cdll.LoadLibrary('libc.so.6')
setns = libc.setns
# This is just a convenience function that will return the path
# to an appropriate namespace descriptor, give either a path,
# a network namespace name, or a pid.
def get_ns_path(nspath=None, nsname=None, nspid=None):
if nsname:
nspath = '/var/run/netns/%s' % nsname
elif nspid:
nspath = '/proc/%d/ns/net' % nspid
return nspath
# This is a context manager that on enter assigns the process to an
# alternate network namespace (specified by name, filesystem path, or pid)
# and then re-assigns the process to its original network namespace on
# exit.
class Namespace (object):
def __init__(self, nsname=None, nspath=None, nspid=None):
self.mypath = get_ns_path(nspid=os.getpid())
self.targetpath = get_ns_path(nspath,
nsname=nsname,
nspid=nspid)
if not self.targetpath:
raise ValueError('invalid namespace')
def __enter__(self):
# before entering a new namespace, we open a file descriptor
# in the current namespace that we will use to restore
# our namespace on exit.
self.myns = open(self.mypath)
with open(self.targetpath) as fd:
setns(fd.fileno(), 0)
def __exit__(self, *args):
setns(self.myns.fileno(), 0)
self.myns.close()
# This is a wrapper for socket.socket() that creates the socket inside the
# specified network namespace.
def nssocket(ns, *args):
with Namespace(nsname=ns):
s = socket.socket(*args)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return s
def main():
# Create a socket inside the 'red' namespace
red = nssocket('red')
red.bind(('0.0.0.0', 7777))
red.listen(10)
# Create a socket inside the 'blue' namespace
blue = nssocket('blue')
blue.bind(('0.0.0.0', 7777))
blue.listen(10)
poll = select.poll()
poll.register(red, select.POLLIN)
poll.register(blue, select.POLLIN)
sockets = {
red.fileno(): {
'socket': red,
'label': 'red',
},
blue.fileno(): {
'socket': blue,
'label': 'blue',
}
}
while True:
events = poll.poll()
for fd, event in events:
sock = sockets[fd]['socket']
label = sockets[fd]['label']
if sock in [red, blue]:
newsock, client = sock.accept()
sockets[newsock.fileno()] = {
'socket': newsock,
'label': label,
'client': client,
}
poll.register(newsock, select.POLLIN)
elif event & select.POLLIN:
data = sock.recv(1024)
if not data:
print 'closing fd %d (%s)' % (fd, label)
poll.unregister(sock)
sock.close()
continue
print 'DATA %s [%d]: %s' % (label, fd, data)
if __name__ == '__main__':
main()
önce bu kodu çalıştırmadan, ben iki ağ ad alanlarını oluşturdu: ikinci seçeneği (ctypes
) kullanarak
, ben bu kod ile geldi
# ip netns exec red ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
816: virt-0-0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether f2:9b:6a:fd:87:77 brd ff:ff:ff:ff:ff:ff
inet 192.168.115.2/24 scope global virt-0-0
valid_lft forever preferred_lft forever
inet6 fe80::f09b:6aff:fefd:8777/64 scope link
valid_lft forever preferred_lft forever
# ip netns exec blue ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
817: virt-1-0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
link/ether 82:94:6a:1b:13:16 brd ff:ff:ff:ff:ff:ff
inet 192.168.113.2/24 scope global virt-1-0
valid_lft forever preferred_lft forever
inet6 fe80::8094:6aff:fe1b:1316/64 scope link
valid_lft forever preferred_lft forever
: her ad alanının, böylece nihai yapılandırması aşağıdaki gibi görünüyordu (Eğer
setns
çağrısının yapmak kullanımı için kök olmamız gerektiğinden
root
olarak,), ben
192.168.115.2:7777
(
red
ad) ya da
192.168.113.2:7777
(
blue
ad) ve işlerin ya bağlanabilir kodu çalıştırma
beklenildiği gibi.
Sorunuzu açıklayabilir misiniz? “Python ile bir tcp soketini nasıl açabilirim?” Diye soruyorsunuz, bunun için orada çok fazla örnek ve belge var. Python kodunuzu hedef ağ ad alanı içinde çalıştırdığınız sürece, kodunuzda ihtiyacınız olan özel bir şey yoktur; Küresel ağ ad alanında çalışan kod gibi çalışır. – larsks
Soru için teşekkürler. Benim sorum, tek bir program/süreç içinde birden fazla ağ ad alanına bağlanmak için daha fazla. Her bir tcp soketinin, belirli bir ad alanında süreci başlatarak her bir işleminde açık olabileceğimi anlıyorum, ancak bu durumda, ad alanından # gibi çok fazla işlem yapacağım ... bundan kaçınmaya çalışıyorum. – jackiesyu