2015-03-04 12 views
5

Birden çok ağ ad alanında bir uygulama çalıştırıyorum. Ve geridöngü adresi + ad alanının her birinde belirli bir bağlantı noktasına soket bağlantısı oluşturmam gerekiyor. "Belirli bağlantı noktasının" tüm ağ ad alanlarında aynı olduğunu unutmayın. Python'da böyle bir soket bağlantısı oluşturmamın bir yolu var mı?Soketleri Python kodumdan birden çok ağ adında açabilir miyim?

Herhangi bir işaretçiyi takdir edin!

+0

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

+0

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

cevap

8

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.

+0

Bunun için çok teşekkürler! Benim için çalıştı! Setns() veya benzeri işlevleri root olarak çalıştırmanın bir yolu var mı? – jackiesyu

İlgili konular