gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 21:31:24 24 4
gpt4 key购买 nike

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

感谢任何指点!

最佳答案

这是一个有趣的问题。

更新:我非常喜欢它,所以我将解决方案打包为可安装的 Python 模块,可从 https://github.com/larsks/python-netns 获得.

您可以通过使用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

我在每个命名空间内添加了一个接口(interface),这样最终的配置看起来像这样:

# 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

运行代码(作为root,因为你需要是root才能利用 setns 调用),我可以连接到192.168.115.2:7777(red 命名空间)或 192.168.113.2:7777(blue 命名空间)并且一切按预期工作。

关于python - 我可以从我的 Python 代码打开多个网络命名空间中的套接字吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28846059/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com