如何用Python Pyroute2将USB WiFi网卡移入Linux网络命名空间?
问题描述
我在Linux系统中使用Python的Pyroute2工具实现以太网接口向网络命名空间的迁移已经成功,但迁移USB WiFi网卡时遇到了问题。了解到WiFi接口需要通过PHY设备进行迁移,但官方文档中没有相关示例。以下是我的两次尝试代码及对应的报错信息,请问如何正确完成USB WiFi网卡的网络命名空间迁移?
初始尝试代码
# Inspect networking interfaces import netifaces # Setup netowrking namespaces from pyroute2 import NetNS, NSPopen, netns, IPDB from pyroute2 import IW from pyroute2 import IPRoute from pyroute2.netlink import NetlinkError # Get a list of all the network interfaces interfaces = netifaces.interfaces() # Manage interfaces ipdb = IPDB() # Loop over all the interfaces and print their details for iface in interfaces: iface_details = netifaces.ifaddresses(iface) if iface.find("wlxc") >= 0: print(f"Found USB Wi-Fi interface: {iface} with details: {iface_details}") # Truncate the long name truncated_name = iface[-8:-1] # Create a networking namespace ipdb_netns = IPDB(nl=NetNS(truncated_name)) # Command to run program_command = ["ifconfig"] # Get the interface print(f"Truncated name is: {truncated_name}") link_iface = ipdb.interfaces[iface] print(f"Link interface: {link_iface} ...") # Look for the wireless index ip = IPRoute() iw = IW() index = ip.link_lookup(ifname=iface)[0] try: wifi_if = iw.get_interface_by_ifindex(index) print(f"Wireless interface: {wifi_if}") with ipdb.interfaces[iface] as if_to_move: print(f"Moving interface: {if_to_move} to: {truncated_name}") if_to_move.net_ns_fd = truncated_name print(f"Done moving to: {truncated_name} ...") except NetlinkError as e: if e.code == 19: # 19 'No such device' print("not a wireless interface") finally: iw.close() ip.close()
初始报错
Found USB Wi-Fi interface: wlxc87f548d9633 with details: {17: [{'addr': 'c8:7f:54:8d:96:33', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]} Truncated name is: 548d963 Link interface: {'address': 'c8:7f:54:8d:96:33', 'broadcast': 'ff:ff:ff:ff:ff:ff', 'ifname': 'wlxc87f548d9633', 'mtu': 1500, 'qdisc': 'noqueue', 'txqlen': 1000, 'operstate': 'DOWN', 'linkmode': 1, 'group': 0, 'promiscuity': 0, 'num_tx_queues': 1, 'num_rx_queues': 1, 'carrier': 0, 'carrier_changes': 1, 'proto_down': 0, 'gso_max_segs': 65535, 'gso_max_size': 65536, 'xdp': {'attrs': [('IFLA_XDP_ATTACHED', None)]}, 'carrier_up_count': 0, 'carrier_down_count': 1, 'min_mtu': 256, 'max_mtu': 2304, 'perm_address': 'c8:7f:54:8d:96:33', 'parent_dev_name': '3-3:1.0', 'parent_dev_bus_name': 'usb', 'index': 10, 'flags': 4099, 'ipdb_scope': 'system', 'ipdb_priority': 0, 'vlans': (), 'ipaddr': (), 'ports': (), 'family': 0, 'ifi_type': 1, 'state': 'up', 'neighbours': ()} ... Wireless interface: ({'cmd': 7, 'version': 1, 'reserved': 0, 'attrs': [('NL80211_ATTR_IFINDEX', 10), ('NL80211_ATTR_IFNAME', 'wlxc87f548d9633'), ('NL80211_ATTR_WIPHY', 1), ('NL80211_ATTR_IFTYPE', 2), ('NL80211_ATTR_WDEV', 4294967297), ('NL80211_ATTR_MAC', 'c8:7f:54:8d:96:33'), ('NL80211_ATTR_GENERATION', 17), ('NL80211_ATTR_4ADDR', '00'), ('NL80211_ATTR_WIPHY_TX_POWER_LEVEL', 1600), ('NL80211_ATTR_TXQ_STATS', '08:00:01:00:00:00:00:00:08:00:02:00:00:00:00:00:08:00:03:00:00:00:00:00:08:00:04:00:00:00:00:00:08:00:05:00:00:00:00:00:08:00:06:00:00:00:00:00:08:00:08:00:00:00:00:00:08:00:09:00:00:00:00:00:08:00:0a:00:00:00:00:00')], 'header': {'length': 188, 'type': 34, 'flags': 0, 'sequence_number': 255, 'pid': 14840, 'error': None, 'target': 'localhost', 'stats': Stats(qsize=0, delta=0, delay=0)}, 'event': 'NL80211_CMD_NEW_INTERFACE'},) Moving interface: {'address': 'c8:7f:54:8d:96:33', 'broadcast': 'ff:ff:ff:ff:ff:ff', 'ifname': 'wlxc87f548d9633', 'mtu': 1500, 'qdisc': 'noqueue', 'txqlen': 1000, 'operstate': 'DOWN', 'linkmode': 1, 'group': 0, 'promiscuity': 0, 'num_tx_queues': 1, 'num_rx_queues': 1, 'carrier': 0, 'carrier_changes': 1, 'proto_down': 0, 'gso_max_segs': 65535, 'gso_max_size': 65536, 'xdp': {'attrs': [('IFLA_XDP_ATTACHED', None)]}, 'carrier_up_count': 0, 'carrier_down_count': 1, 'min_mtu': 256, 'max_mtu': 2304, 'perm_address': 'c8:7f:54:8d:96:33', 'parent_dev_name': '3-3:1.0', 'parent_dev_bus_name': 'usb', 'index': 10, 'flags': 4099, 'ipdb_scope': 'system', 'ipdb_priority': 0, 'vlans': (), 'ipaddr': (), 'ports': (), 'family': 0, 'ifi_type': 1, 'state': 'up', 'neighbours': ()} to: 548d963 fail <class 'NoneType'> fail <class 'NoneType'>
更新后代码
#!/usr/bin/python3 # Configure logging import logging logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO, format="%(asctime)s:%(levelname)s:%(message)s") # Inspect networking interfaces import netifaces # Setup netowrking namespaces from pyroute2 import NetNS, NSPopen, netns, IPDB, IPRoute # Get a list of all the network interfaces interfaces = netifaces.interfaces() # Managing network interfaces with IPDB() as ipdb: # Loop over all the interfaces and logger.info their details for iface in interfaces: # Determine interface information iface_details = netifaces.ifaddresses(iface) if iface.find("wlxc") >= 0: logger.info(f"Found USB Wi-Fi interface: {iface} with details: {iface_details}") # Attempt to remove a networking namespace by the same name iface_truncate = iface[-4:] net_namespace = f"{iface_truncate}-ns" try: netns.remove(net_namespace) except Exception as err: logger.warning(f"Namespace does not exist and we could not remove it: {net_namespace} ...") # Create a new network namespace or identify an existing one logger.info(f"Trying to create network namespace: {net_namespace} ...") netns.create(net_namespace) # Get the interface index with IPRoute() as ip: index = ip.link_lookup(ifname=iface)[0] logger.info(f"Index of USB WiFi is: {index} ...") logger.info(f"Interfaces: {ipdb.interfaces[iface]} ...") # Move the interface to the new network namespace with ipdb.interfaces[index] as interf: interf.net_ns_fd = net_namespace logger.info(f"Moved {iface} to {net_namespace}") logger.info("Done moving interface ...") # Close IPDB ipdb.release()
更新后报错
2023-11-13 21:05:36,676:WARNING:Deprecation warning https://docs.pyroute2.org/ipdb_toc.html 2023-11-13 21:05:36,676:WARNING:To remove this DeprecationWarning exception, start IPDB(deprecation_warning=False, ...) 2023-11-13 21:05:36,678:INFO:Found USB Wi-Fi interface: wlxc87f548d9633 with details: {17: [{'addr': 'c8:7f:54:8d:96:33', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]} 2023-11-13 21:05:36,680:INFO:Trying to create network namespace: 9633-ns ... 2023-11-13 21:05:36,686:INFO:Index of USB WiFi is: 10 ... 2023-11-13 21:05:36,700:INFO:Interfaces: {'address': 'c8:7f:54:8d:96:33', 'broadcast': 'ff:ff:ff:ff:ff:ff', 'ifname': 'wlxc87f548d9633', 'mtu': 1500, 'qdisc': 'noqueue', 'txqlen': 1000, 'operstate': 'DOWN', 'linkmode': 1, 'group': 0, 'promiscuity': 0, 'num_tx_queues': 1, 'num_rx_queues': 1, 'carrier': 0, 'carrier_changes': 1, 'proto_down': 0, 'gso_max_segs': 65535, 'gso_max_size': 65536, 'xdp': {'attrs': [('IFLA_XDP_ATTACHED', None)]}, 'carrier_up_count': 0, 'carrier_down_count': 1, 'min_mtu': 256, 'max_mtu': 2304, 'perm_address': 'c8:7f:54:8d:96:33', 'parent_dev_name': '3-3:1.0', 'parent_dev_bus_name': 'usb', 'index': 10, 'flags': 4099, 'ipdb_scope': 'system', 'ipdb_priority': 0, 'vlans': (), 'ipaddr': (), 'ports': (), 'family': 0, 'ifi_type': 1, 'state': 'up', 'neighbours': ()} ... 2023-11-13 21:05:36,700:INFO:Moved wlxc87f548d9633 to 9633-ns fail <class 'NoneType'> fail <class 'NoneType'> Traceback (most recent call last): File "/home/user/Desktop/network-stress-test/network-stress-test.py", line 52, in <module> with ipdb.interfaces[index] as interf: File "/usr/local/lib/python3.10/dist-packages/pyroute2/ipdb/transactional.py", line 206, in __exit__ self.commit() File "/usr/local/lib/python3.10/dist-packages/pyroute2/ipdb/interfaces.py", line 1136, in commit raise error File "/usr/local/lib/python3.10/dist-packages/pyroute2/ipdb/interfaces.py", line 1036, in commit run(nl.link, 'update', **request) File "/usr/local/lib/python3.10/dist-packages/pyroute2/ipdb/interfaces.py", line 515, in _run raise error File "/usr/local/lib/python3.10/dist-packages/pyroute2/ipdb/interfaces.py", line 510, in _run return cmd(*argv, **kwarg) File "/usr/local/lib/python3.10/dist-packages/pyroute2/iproute/linux.py", line 1672, in link ret = self.nlm_request(msg, msg_type=msg_type, msg_flags=msg_flags) File "/usr/local/lib/python3.10/dist-packages/pyroute2/netlink/nlsocket.py", line 870, in nlm_request return tuple(self._genlm_request(*argv, **kwarg)) File "/usr/local/lib/python3.10/dist-packages/pyroute2/netlink/nlsocket.py", line 1214, in nlm_request for msg in self.get( File "/usr/local/lib/python3.10/dist-packages/pyroute2/netlink/nlsocket.py", line 873, in get return tuple(self._genlm_get(*argv, **kwarg)) File "/usr/local/lib/python3.10/dist-packages/pyroute2/netlink/nlsocket.py", line 550, in get raise msg['header']['error'] pyroute2.netlink.exceptions.NetlinkError: (22, 'Invalid argument')
解决方案
WiFi接口不能直接像以太网接口那样迁移,因为它依赖对应的PHY(无线物理层)设备。正确的做法是先将PHY设备移动到目标命名空间,对应的WiFi接口会自动跟随进入。
实现步骤
- 找到WiFi接口对应的PHY编号
- 创建目标网络命名空间
- 将PHY设备移动到目标命名空间
- 在目标命名空间内启用WiFi接口
完整代码示例
#!/usr/bin/python3 import logging import netifaces import subprocess from pyroute2 import NetNS, netns, IPRoute, IW # 配置日志 logging.basicConfig(level=logging.INFO, format="%(asctime)s:%(levelname)s:%(message)s") logger = logging.getLogger(__name__) def move_wifi_to_ns(iface_name, ns_name): # 获取WiFi接口的PHY编号 with IPRoute() as ip, IW() as iw: ifindex = ip.link_lookup(ifname=iface_name)[0] wifi_info = iw.get_interface_by_ifindex(ifindex) # 从返回结果中提取PHY编号 phy_id = None for attr in wifi_info[0]['attrs']: if attr[0] == 'NL80211_ATTR_WIPHY': phy_id = attr[1] break if not phy_id: logger.error(f"无法获取接口 {iface_name} 的PHY编号") return logger.info(f"WiFi接口 {iface_name} 对应的PHY编号: {phy_id}") # 清理并创建命名空间 try: netns.remove(
相关产品推荐
相关产品推荐

