我可以从Python代码中打开多个网络名称空间中的套接字吗?

我可以从Python代码中打开多个网络名称空间中的套接字吗?,python,sockets,namespaces,Python,Sockets,Namespaces,我正在多网络名称空间中运行一些应用程序。我需要创建到环回地址+每个名称空间中的特定端口的套接字连接。请注意,“特定端口”在所有网络名称空间中都是相同的。有没有办法在python中创建这样的套接字连接 感谢你的指点 这是一个有趣的问题 更新:我非常喜欢它,因此我将该解决方案打包为一个可安装的Python模块,可从中获得 您可以通过使用setns()系统调用访问另一个网络名称空间。这个调用不是Python本机公开的,因此为了使用它,您需要(a)找到一个封装它的第三方模块,或者(b)使用类似于ctyp

我正在多网络名称空间中运行一些应用程序。我需要创建到环回地址+每个名称空间中的特定端口的套接字连接。请注意,“特定端口”在所有网络名称空间中都是相同的。有没有办法在python中创建这样的套接字连接


感谢你的指点

这是一个有趣的问题

更新:我非常喜欢它,因此我将该解决方案打包为一个可安装的Python模块,可从中获得

您可以通过使用
setns()
系统调用访问另一个网络名称空间。这个调用不是Python本机公开的,因此为了使用它,您需要(a)找到一个封装它的第三方模块,或者(b)使用类似于
ctypes
模块的东西使它在Python代码中可用

使用第二个选项(
ctypes
),我产生了以下代码:

#!/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()
在运行此代码之前,我创建了两个网络名称空间:

# ip netns add red
# ip netns add blue
我在每个名称空间中添加了一个接口,以便 配置如下所示:

# 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
#ip网络执行器红色ip a
1:lo:mtu 65536 qdisc noop状态关闭组默认值
链接/环回00:00:00:00:00 brd 00:00:00:00:00:00:00
816:virt-0-0:mtu 1500 qdisc noqueue状态未知组默认值
链路/乙醚f2:9b:6a:fd:87:77 brd ff:ff:ff:ff:ff:ff:ff:ff:ff
inet 192.168.115.2/24范围全局virt-0-0
永远有效\u lft首选\u lft永远有效
inet6 fe80::f09b:6aff:fefd:8777/64范围链接
永远有效\u lft首选\u lft永远有效
#ip网络执行器蓝色ip a
1:lo:mtu 65536 qdisc noop状态关闭组默认值
链接/环回00:00:00:00:00 brd 00:00:00:00:00:00:00
817:virt-1-0:mtu 1500 qdisc noqueue状态未知组默认值
链接/以太82:94:6a:1b:13:16 brd ff:ff:ff:ff:ff:ff:ff
inet 192.168.113.2/24范围全局virt-1-0
永远有效\u lft首选\u lft永远有效
inet6 fe80::8094:6aff:fe1b:1316/64范围链接
永远有效\u lft首选\u lft永远有效
以root用户身份运行代码,因为您需要成为root用户才能 使用
setns
调用),我可以连接到
192.168.115.2:7777
(红色的
名称空间)或
192.168.113.2:7777

(蓝色的
名称空间)一切正常。

我是在研究网络名称空间并使用python与之交互时看到这篇文章的。关于您关于非root用户运行setns()或类似函数的问题,我相信这是可以实现的。在创建本文中提到的
红色
蓝色
名称空间的小脚本中,您还可以在新名称空间中设置linux功能,允许非根用户附加和绑定。直接从手册页中,我们可以看到以下描述:
callsetns(2)(需要在目标命名空间中使用CAP_SYS_ADMIN)

功能可以添加到二进制文件中,如python2.7,也可以添加到systemd进程中。例如,如果您查看centos 7或RHEL 7上的默认openvpn服务器服务文件,您可以看到添加的功能,以便它可以在没有根权限的情况下运行:
CapabilityBoundingSet=CAP\u IPC\u LOCK CAP\u NET\u ADMIN…..


我知道这不是对最初问题的回答,但我目前没有足够的声誉来回复评论。如果您有安全意识并希望以非root用户身份运行代码,我建议您查看功能和提供的所有选项。

您能澄清您的问题吗?听起来像是在问“如何用Python打开tcp套接字?”,已经有很多例子和文档了。只要您在目标网络名称空间中运行Python代码,代码中就没有什么特别的需要;它将像在全局网络名称空间中运行的代码一样运行。谢谢你的提问。我的问题更多的是在单个程序/进程中连接到多个网络名称空间。我知道我可以通过在特定名称空间中启动进程,在每个进程中打开每个tcp套接字,但在这种情况下,我将拥有与名称空间的#一样多的进程…试图避免这种情况。非常感谢!这对我有用!有没有办法只以root用户身份运行setns()或类似的函数,而其他所有函数都以用户身份运行?非常感谢。在上,您提到可以通过
sock=netns.socket(netns.get\ns\u path(nsname='myns'))
创建命名空间内的套接字。我可以使用套接字,但当我尝试将此套接字连接到服务器端口和地址时,它会显示“网络无法访问”。你能分享一个如何从这个套接字连接发送流量的例子吗?你在你的命名空间中创建了网络接口吗?他们的地址是什么?您是否有一个从名称空间内部到您试图连接的地址的路由?我已经添加了。