Linux环境下纯Python无第三方库读写Netlink
原文地址:http://shajunxing.tpddns.cn:8888/web/blog/2018-03-28-Linux环境下纯Python无第三方库读写Netlink/index.html基础知识Netlink使用套接字的方式提供了应用程序和内核之间信息交互的机制,用来替代传统的ioctl等机制。当前,Netlink主要涵盖了Linux网络相关的功能,注意,不要把Netlink和Lin
原文地址:http://shajunxing.tpddns.cn:8888/web/blog/2018-03-28-Linux环境下纯Python无第三方库读写Netlink/index.html
基础知识
Netlink使用套接字的方式提供了应用程序和内核之间信息交互的机制,用来替代传统的ioctl等机制。当前,Netlink主要涵盖了Linux网络相关的功能,注意,不要把Netlink和Linux网络本身混为一谈,它只是一种进程间通信(IPC)而已。例如,早期的net-tools工具集(ifconfig、netstat...)使用的是ioctl,现在取而代之的iproute2工具集(ip、ss...)则使用Netlink。
Netlink套接字的创建、发送和接收
socket函数用于创建套接字,同样也用与创建Netlink套接字。第一个参数af
填写AF_INET
,和普通的套接字一样;因为需要自己构造数据包的首部,所以第二个参数type
填写SOCK_RAW
;第三个参数protocol
填写Netlink专有的协议类型,比如NETLINK_ROUTE
用于读写网络相关信息,当然,这些在Python3的socket
模块中均有定义:
s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
数据收发必须指定自己和对端的地址,Netlink的地址结构为pid, groups
二元组,第一个参数相当于普通套接字的端口号,用来唯一标志一条“连接”,当然这里使用进程ID作为唯一标志是很方便的,第二个参数略,填0即可:
s.bind((os.getpid(), 0))
套接字的收发缓存要设置得尽量大一些,比如:
s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
然后就可以使用sendto()
和recvfrom()
函数收发数据了,因为Linux内核进程的PID是0,所以对端地址为0, 0
。注意,即便接收缓存设置得足够大,内核也很有可能会分批次发回数据,所以不能指望一次recvfrom()
就接收到所有的数据,以下代码演示了获取所有网络接口信息,使用无限循环接收,当然了,在接收完毕后recvfrom()
函数会阻塞,要手动结束程序,不过可以看到内核返回的结果了。
import os
import socket
s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
s.bind((os.getpid(), 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
s.sendto(b'\x14\x00\x00\x00\x12\x00\x01\x03\x00\x00\x00\x00\xd5\x1b\x00\x00\x11\x00\x00\x00', (0, 0))
while True:
print(s.recvfrom(65536))
细心的读者应该会在接收到的二进制数据中查找到网络接口的字符串名,比如“lo”、“eth0”等。
Netlink消息格式
Netlink报文包含格式首部和消息体,协议规定了统一的首部格式,如下图所示,而消息体又可以进一步扩充定义其它子协议,如果用TCP/IP协议作类比,那么Netlink类似于运输层,而子协议类似于应用层。
首先,是包含首部的消息总长度。规定首部和消息体必须以4字节的整数倍作为起始位置(4字节对齐),但是貌似结束位置没有此规定,也就是说消息体长度如果不足4的整数倍,不用填充。
然后是2字节的消息类型和16比特的标志位,配合起来描述消息的类型和各种状态,比如预定义、自定义类型;分片、结束;出错等,和TCP风格很像。
接下来是顺序号和端口号。对于顺序号,请求报文中所填写的值在响应报文中会原样返回,用途是让应用程序知道哪个请求对应哪个响应。而端口号的定义不是特别明确的,头文件中注释意思是发送端的端口ID,但是经过试验随便填都可以,而查看响应报文中的端口号,填的是应用程序的进程ID,也就是接收端的ID。WTF :(
Netlink协议规定统一采用主机字节序,所以可以使用Python的ctypes定义结构类型,因为ctypes是主机字节序的,当然也可以使用struct模块完成封包解包,或者两者配合使用都可以。
(待续)
参考文献
- Jean's tech blog: Howto get a list of network interfaces in Linux, part 3
- Understanding And Programming With Netlink Sockets
- Netlink Library (libnl)
- Routing Family Netlink Library (libnl-route)
- netlink(7) - Linux man page
- netlink(3) - Linux man page
- rtnetlink(7) - Linux man page
- rtnetlink(3) - Linux man page
- RFC 3549 - Linux Netlink as an IP Services Protocol
- netlink.h
- rtnetlink.h
更多推荐
所有评论(0)