如何透過 Python 來呼叫 ioctl
首先,先來抄一段 wikipedia 的定義:
In computing, ioctl (an abbreviation of input/output control) is a system call for device-specific input/output operations and other operations which cannot be expressed by regular system calls. It takes a parameter specifying a request code; the effect of a call depends completely on the request code. Request codes are often device-specific.
簡單來說,ioctl 是針對 device 去做輸入輸出(白話一點,get/set)操作的一種方式,在 Linux 底下,所有的 kernel module 都可以被視做是一個 device,所以把 ioctl 當作一種 UK 的溝通機制也未嘗不可。不過如果要傳大量資料的話還是採用其他的方式比較妥當,像是 netlink。在溝通的時候,User Space 和 Kernel Space 必須先定義好雙方溝通的格式。最常見的範例基本上就是 ifconfig 這隻程式了,在這裡也不去看它,反正是一支很簡單的 C 程式。
本篇要紀錄的是,在 Python 裡要怎麼呼叫 ioctl。範例很簡單,,就是實作一支類似 ifconfig 的程式(不過只有 get,懶得寫 set)。直接放在下面:
In computing, ioctl (an abbreviation of input/output control) is a system call for device-specific input/output operations and other operations which cannot be expressed by regular system calls. It takes a parameter specifying a request code; the effect of a call depends completely on the request code. Request codes are often device-specific.
簡單來說,ioctl 是針對 device 去做輸入輸出(白話一點,get/set)操作的一種方式,在 Linux 底下,所有的 kernel module 都可以被視做是一個 device,所以把 ioctl 當作一種 UK 的溝通機制也未嘗不可。不過如果要傳大量資料的話還是採用其他的方式比較妥當,像是 netlink。在溝通的時候,User Space 和 Kernel Space 必須先定義好雙方溝通的格式。最常見的範例基本上就是 ifconfig 這隻程式了,在這裡也不去看它,反正是一支很簡單的 C 程式。
本篇要紀錄的是,在 Python 裡要怎麼呼叫 ioctl。範例很簡單,,就是實作一支類似 ifconfig 的程式(不過只有 get,懶得寫 set)。直接放在下面:
#!/usr/bin/python import socket import fcntl import sys import struct import array def get_if_list(): try: fd = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) # #define SIOCGIFCONF 0x8912 /* get iface list */ max_possible = 8 # initial value struct_size = 40 while True: # struct ifconf { # int ifc_len; /* size of buffer */ # union { # char *ifc_buf; /* buffer address */ # struct ifreq *ifc_req; /* array of structures */ # }; # }; # #define IFNAMSIZ 16 # struct ifreq { # char ifr_name[IFNAMSIZ]; /* Interface name */ # union { # struct sockaddr ifr_addr; # struct sockaddr ifr_dstaddr; # struct sockaddr ifr_broadaddr; # struct sockaddr ifr_netmask; # struct sockaddr ifr_hwaddr; # short ifr_flags; # int ifr_ifindex; # int ifr_metric; # int ifr_mtu; # struct ifmap ifr_map; # char ifr_slave[IFNAMSIZ]; # char ifr_newname[IFNAMSIZ]; # char *ifr_data; # }; # }; # Initialize ifc_buf bytes = max_possible * struct_size names = array.array( 'B' ) for i in range( 0, bytes ): names.append( 0 ) # names.buffer_info() is ( address, length ) # So we can treat names.buffer_info()[0] as a pointer in C input_buffer = struct.pack( 'iL', bytes, names.buffer_info()[0] ) # #define SIOCGIFCONF 0x8912 /* get iface list */ output_buffer = fcntl.ioctl( fd.fileno(), 0x8912, input_buffer ) output_size = struct.unpack( 'iL', output_buffer )[0] # If output_size == bytes, there may be more interfaces. if output_size == bytes: max_possible *= 2 else: break namestr = names.tostring() ifaces = [] for i in range( 0, output_size, struct_size ): iface_name = namestr[ i : i+16 ].split( '\0', 1 )[0] ifaces.append( iface_name ) fd.close() return ifaces except IOError: print "Unable to call ioctl with SIOCGIFCONF" def get_if_info( ifname ): print ifname + ":" try: fd = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) input_buffer = struct.pack( '256s', ifname ) # #define SIOCGIFADDR 0x8915 /* get PA address # #define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */ # #define SIOCGIFNETMASK 0x891b /* get network PA mask */ # #define SIOCGIFMTU 0x8921 /* get MTU size */ # #define SIOCGIFHWADDR 0x8927 /* Get hardware address */ output_buffer = fcntl.ioctl( fd.fileno(), 0x8915, input_buffer ) print "\tIP address: " + socket.inet_ntoa( output_buffer[20:24] ) output_buffer = fcntl.ioctl( fd.fileno(), 0x8919, input_buffer ) print "\tBroadcast: " + socket.inet_ntoa( output_buffer[20:24] ) output_buffer = fcntl.ioctl( fd.fileno(), 0x891b, input_buffer ) print "\tNetmask: " + socket.inet_ntoa( output_buffer[20:24] ) # I do not care the endian issue here output_buffer = fcntl.ioctl( fd.fileno(), 0x8921, input_buffer ) print "\tMTU: " + str( ord( output_buffer[16] ) + ord( output_buffer[17] ) * 256 ) output_buffer = fcntl.ioctl( fd.fileno(), 0x8927, input_buffer ) ether_addr = "" for c in output_buffer[18:23]: ether_addr = ether_addr + format( ord( c ), '02X' ) + ':' ether_addr = ether_addr + format( ord( output_buffer[23] ), '02X' ) print "\tEthernet address: " + ether_addr fd.close() except IOError: print "\tUnable to call ioctl with SIOCGIFADDR on " + ifname if len( sys.argv ) == 2: get_if_info( sys.argv[1] ) else: ifaces = get_if_list() if len( ifaces ) > 0: for iface in ifaces: get_if_info( iface ) else: print "No available interface"
留言
張貼留言