工作中用到了使用tap/tun裝置實現虛擬網路,看到網上的例子都是用C實現的。便想試著用python實現一把,主要就是要重新用ctypes定義相關的結構定義。
代碼github地址:
https://github.com/happyAnger6/network_programming
這個git庫裡會不斷的用python實現網路相關的編程。
if_tun.py: 主要是相關結構體定義。
import ctypesimport ioctl_defTUNSETIFF=ioctl_def._IOW(ord('T'), 202, ctypes.c_int)IFF_TUN=1IFF_TAP=2IFNAMSIZ=16class SockAddr(ctypes.Structure):_fields_=[("sa_family",ctypes.c_ushort),("sa_data",ctypes.c_char*14)]class IfMap(ctypes.Structure):_fields_=[("mem_start", ctypes.c_ulong), ("mem_end", ctypes.c_ulong), ("base_addr", ctypes.c_ushort), ("irq", ctypes.c_char), ("dma", ctypes.c_char), ("port", ctypes.c_char)]class Ifs_Ifsu(ctypes.Union):_fields_=[("raw_hdlc", ctypes.c_void_p), ("cisco", ctypes.c_void_p), ("fr", ctypes.c_void_p), ("fr_pvc", ctypes.c_void_p), ("fr_pvc_info", ctypes.c_void_p), ("sync", ctypes.c_void_p), ("te1", ctypes.c_void_p)]class IfSettings(ctypes.Structure):_fields_=[("type", ctypes.c_uint), ("size", ctypes.c_uint), ("ifs_ifsu", Ifs_Ifsu)]class Ifr_Ifru(ctypes.Union):_fields_=[("ifru_addr", SockAddr), ("ifru_dstaddr", SockAddr), ("ifru_broadaddr", SockAddr), ("ifru_netmask", SockAddr), ("ifru_hwaddr", SockAddr), ("ifru_flags", ctypes.c_ushort), ("ifru_ivalue", ctypes.c_int), ("ifru_mtu", ctypes.c_int), ("ifru_map", IfMap), ("ifru_slave", ctypes.c_char * IFNAMSIZ), ("ifru_newname", ctypes.c_char * IFNAMSIZ), ("ifru_data", ctypes.c_void_p), ("ifru_settings", IfSettings)]class Ifr_Ifrn(ctypes.Union):_fields_=[("ifrn_name", ctypes.c_char * IFNAMSIZ)]class IfReq(ctypes.Structure):_fields_=[("ifr_ifrn", Ifr_Ifrn),("ifr_ifru", Ifr_Ifru)]if __name__ == "__main__":ifreq = IfReq()print(ctypes.sizeof(ifreq.ifr_ifrn))print(ctypes.sizeof(ifreq.ifr_ifru))
ioctl_def.py:主要是ioctl命令字相關定義:
_IOC_NRBITS=8_IOC_TYPEBITS=8_IOC_SIZEBITS=14_IOC_DIRBITS=2_IOC_NRMASK=((1 << _IOC_NRBITS) - 1)_IOC_TYPEMASK=((1 << _IOC_TYPEBITS) - 1)_IOC_SIZEMASK=((1 << _IOC_SIZEBITS) - 1)_IOC_DIRMASK=((1 << _IOC_DIRBITS) - 1)_IOC_NRSHIFT=0_IOC_TYPESHIFT=(_IOC_NRSHIFT + _IOC_NRBITS)_IOC_SIZESHIFT=(_IOC_TYPESHIFT + _IOC_TYPEBITS)_IOC_DIRSHIFT=(_IOC_SIZESHIFT + _IOC_SIZEBITS)_IOC_NONE=0_IOC_WRITE=1_IOC_READ=2def _IOC(dir, type, nr, size):return (((dir) << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr) << _IOC_NRSHIFT) | \((size) << _IOC_SIZESHIFT))import ctypesdef _IOC_TYPECHECK(t):return ctypes.sizeof(t)def _IO(type, nr):return _IOC(_IOC_NONE, type, nr, 0)def _IOR(type, nr, size):return _IOC(_IOC_READ, type, nr, _IOC_TYPECHECK(size))def _IOW(type, nr, size):return _IOC(_IOC_WRITE, type, nr, _IOC_TYPECHECK(size))if __name__ == "__main__":print(_IOC_TYPESHIFT,_IOC_SIZESHIFT,_IOC_DIRSHIFT)print(_IOC())
tun_oper.py:建立tun裝置並監聽資料發送請求.
import fcntlimport osimport ctypesimport structfrom if_tun import IfReq, TUNSETIFF, IFF_TUNdef tun_create(devname, flags):fd = -1if not devname:return -1fd = os.open("/dev/net/tun", os.O_RDWR)if fd < 0:print("open /dev/net/tun err!")return fdr=IfReq()ctypes.memset(ctypes.byref(r), 0, ctypes.sizeof(r))r.ifr_ifru.ifru_flags |= flagsr.ifr_ifrn.ifrn_name = devname.encode('utf-8')try:err = fcntl.ioctl(fd, TUNSETIFF, r)except Exception as e:print("err:",e)os.close(fd)return -1return fdif __name__ == "__main__":fd = tun_create("tun3", IFF_TUN)if fd < 0:raise OSErrorMAXSIZE=4096while(True):buf = os.read(fd,MAXSIZE)if not buf:breakprint("read size:%d" % len(buf))
測試:
為tun3裝置設定ip:
#ifconfig tun3 10.0.0.1 up
添加靜態路由,通過tun3轉寄:
#route add -net 10.0.0.2 netmask 255.255.255.255 dev tun3
ping一個地址向tun3發送資料:
#ping 10.0.0.2
tun3收到了資料:
read size:88
read size:88
read size:88
read size:88
read size:88
read size:88
read size:88
read size:88