1. 程式人生 > >ioctl 函式與網路介面

ioctl 函式與網路介面

本文只討論ioctl 在 linux網路程式設計中的應用

#include<unistd.h>

int ioctl( int fd, int request, .../* void *arg */ );                               返回0——成功, -1——出錯

第一個引數 fd 指示某個檔案描述符(當然也包括 套介面描述符)

第二個引數 request 指示要ioctl執行的操作

第三個引數 總是某種指標,具體的指向型別依賴於 request 引數

我們可以把和網路相關的請求(request)劃分為6 類:

      套介面操作

      檔案操作

      介面操作

      ARP 快取記憶體操作

      路由表操作

      流系統

下表列出了網路相關ioctl 請求的request 引數以及arg 地址必須指向的資料型別:

 
類別                     request                              說明                                   (第三個引數)資料型別

                        SIOCATMARK                 是否位於帶外標記                              int

套介面               SIOCSPGRP                   設定陶介面的程序ID或組ID                 int

                        SIOCGPGRP                   獲取陶介面的程序ID或組ID                 int

 檔案                  FIONBIO                        設定/清除非阻塞IO標識                      int

                        FIOASYNC                      設定/清除訊號驅動非同步IO標識             int

                        FIONREAD                      獲取接收緩衝區中的位元組數                  int

                        ……

                         SIOCGIFCONF                獲取所有介面的清單                           struct ifconf

介面                  SIOCSIFADDR                 設定介面的ip地址                              struct ifreq

                        SIOCGIFADDR                 獲取介面地址                                    struct ifreq

                        SIOCSIFFLAGS                設定介面標識                                    struct ifreq

                        SIOCGIFFLAGS                獲取介面標識                                    struct ifreq

                        SIOCSIFDSTADDR           設定點到點地址                                 struct ifreq

                        SIOCGIFDSTADDR           獲取點到點地址                                 struct ifreq

                        SICSIFBRDADDR              設定廣播地址                                    struct ifreq

                        SICGIFBRDADDR              獲取廣播地址                                    struct ifreq

                        ……

                        SIOCSARP                       建立/修改ARP表項                            struct arpreq

 ARP                 SIOCGARP                       獲取ARP表項                                   struct arpreq

                        SIOCDARP                       刪除ARP表項                                   struct arpreq

路由                  ……

有很多request請求沒有列出,而且不同的系統所提供的request也有所不同,比如linux就不提供SIOCGSIZIFCONF請求)

上面的表中,用ioctl執行介面操作的請求時,要用到結構體 struct ifconf 與 struct ifreq

struct ifconf{

    int ifc_len;                 // 緩衝區ifcu_buf的大小

    union{

        caddr_t ifcu_buf;            // 其實就是char *型別。

        struct ifreq *ifcu_req;    // 為ifconf分配空間時我們用ifcu_buf指標;當要取得或設定該緩衝區中的ifreq型別時,則用這個指標

    }ifc_ifcu;

};

#define  ifc_buf  ifc_ifcu.ifcu_buf    //buffer address

#define  ifc_req  ifc_ifcu.ifcu_req    //array of structures returned

struct ifreq
{
    char ifrn_name[IFNAMSIZ];  /* if name, e.g. "eth0" */ 
    union {
         struct sockaddr ifru_addr;
         struct sockaddr ifru_dstaddr;
         struct sockaddr ifru_broadaddr;
         struct sockaddr ifru_netmask;
         struct  sockaddr ifru_hwaddr;
         short ifru_flags;
         int ifru_ivalue;
         int ifru_mtu;
         struct  ifmap ifru_map;
         char ifru_slave[IFNAMSIZ]; /* Just fits the size */
         char ifru_newname[IFNAMSIZ];
         void * ifru_data;
         struct if_settings ifru_settings;
    } ifr_ifru;
};

#define ifr_name ifr_ifrn.ifrn_name                           /* interface name  */
#define ifr_hwaddr ifr_ifru.ifru_hwaddr                    /* MAC address   */
#define ifr_addr ifr_ifru.ifru_addr                              /* address  */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr                    /* other end of p-p lnk */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr            /* broadcast address */
#define ifr_netmask ifr_ifru.ifru_netmask                  /* interface net mask */
#define ifr_flags ifr_ifru.ifru_flags                              /* flags  */
#define ifr_metric ifr_ifru.ifru_ivalue                          /* metric  */
#define ifr_mtu  ifr_ifru.ifru_mtu                                /* mtu   */
#define ifr_map  ifr_ifru.ifru_map                               /* device map  */
#define ifr_slave ifr_ifru.ifru_slave                             /* slave device  */
#define ifr_data ifr_ifru.ifru_data                               /* for use by interface */
#define ifr_ifindex ifr_ifru.ifru_ivalue                          /* interface index */
#define ifr_bandwidth ifr_ifru.ifru_ivalue                   /* link bandwidth */
#define ifr_qlen ifr_ifru.ifru_ivalue                             /* Queue length  */
#define ifr_newname ifr_ifru.ifru_newname              /* New name  */
#define ifr_settings ifr_ifru.ifru_settings                    /* Device/proto settings*/

ifconf 結構包含了 ifreq 結構指標, 在使用ifconf之前,我們得先為其ifcu_buf指標(或者說ifcu_req指標)分配緩衝區

其他不知道該怎麼說。 

貼程式碼吧。  一個用ioctl實現的類似ifconfig命令的小程式。

[cpp] view plaincopyprint?
  1. #include <stdio.h>            //printf()
  2. #include <unistd.h>           //ioctl()
  3. #include <sys/ioctl.h>        //ioctl
  4. #include <sys/socket.h>       //socket()
  5. #include <net/if.h>           //struct ifconf{} & struct ifreq{}
  6. #include <string.h>           //strcpy()
  7. #include <arpa/inet.h>        //inet_ntoa()
  8. #include <stdlib.h>           //malloc() & free()
  9. int print_if_addr(int fd, char *interface_name);    //列印介面的ip地址
  10. int print_if_mac(int fd, char *interface_name); //列印介面的mac地址
  11. int print_if_broadaddr(int fd, char *interface_name);   //列印介面的廣播地址
  12. int print_if_mask(int fd, char *interface_name);    //列印介面的掩碼
  13. int print_if_mtu(int fd, char *interface_name); //列印介面的mtu
  14. int print_all_interface();  //列印所有介面的基本資訊
  15. int print_if_addr6(char *interface_name);   //列印介面的ipv6地址
  16. int print_interface_info(char *interface_name); //列印介面的以上所有資訊
  17. int set_if_up(char *interface_name);        //啟動介面
  18. int set_if_down(char *interface_name);      //關閉介面
  19. int set_if_ip(char *interface_name, char *ip_str);  //設定介面的ip地址
  20. void usage();   //列印該程式的使用手冊
  21. int main(int argc, char **argv)  
  22. {  
  23. int sockfd;  
  24.     printf("/n  **********funway:用ioctl函式來實現ifconfig命令的效果**********/n");  
  25. switch(argc)  
  26.     {  
  27. case 1:  
  28.         print_all_interface();        
  29. break;  
  30. case 2:  
  31.         print_interface_info(argv[1]);  
  32. break;  
  33. case 3:  
  34. if(strcmp(argv[2], "up") == 0)  
  35.             set_if_up(argv[1]);  
  36. elseif(strcmp(argv[2], "down") == 0)  
  37.             set_if_down(argv[1]);  
  38. else
  39.             set_if_ip(argv[1], argv[2]);  
  40. break;  
  41. default:  
  42.         usage();  
  43. break;  
  44.     }  
  45. return 0;  
  46. }  
  47. void usage()  
  48. {  
  49.     printf("usage: ./myifconfig [interface [down|up|ip]]/n");  
  50. }  
  51. int print_if_addr(int fd, char *if_name)  
  52. {  
  53. struct sockaddr_in *ip;  
  54. struct ifreq ifr;  
  55.     strcpy(ifr.ifr_name, if_name);  
  56. if(ioctl(fd, SIOCGIFADDR, &ifr) < 0)  
  57.     {  
  58.         perror("ioctl SIOCGIFADDR error");  
  59. return -1;  
  60.     }  
  61.     ip = (struct sockaddr_in *)&ifr.ifr_addr;       //獲得ipv4地址
  62.     printf("    IP: %s/n", inet_ntoa(ip->sin_addr)); //將ipv4地址轉換為主機位元組序的字串並輸出
  63. return 0;  
  64. }  
  65. int print_if_broadaddr(int fd, char *if_name)  
  66. {  
  67. struct sockaddr_in *ip;  
  68. struct ifreq ifr;  
  69.     strcpy(ifr.ifr_name, if_name);  
  70. if(ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)  
  71.     {  
  72.         perror("ioctl SIOCGIFBRDADDR error");  
  73. return -1;  
  74.     }  
  75.     ip = (struct sockaddr_in *)&ifr.ifr_broadaddr;  //獲得廣播地址
  76.     printf("    Broadcast: %s/n", inet_ntoa(ip->sin_addr));  
  77. return 0;  
  78. }  
  79. int print_if_mask(int fd, char *if_name)  
  80. {  
  81. struct sockaddr_in *ip;  
  82. struct ifreq ifr;  
  83.     strcpy(ifr.ifr_name, if_name);  
  84. if(ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)  
  85.     {  
  86.         perror("ioctl SIOCGIFNETMASK error");  
  87. return -1;  
  88.     }  
  89.     ip = (struct sockaddr_in *)&ifr.ifr_ifru.ifru_netmask;  //獲得子網掩碼。注意!我們仍放在struct aockaddr_in結構中返回
  90.     printf("    Mask: %s/n", inet_ntoa(ip->sin_addr));  
  91. return 0;  
  92. }  
  93. int print_if_mac(int fd, char *if_name)  
  94. {  
  95.     unsigned char *p;   //注意! 這裡要用unsigned char,而不是char!因為char要對[1xxx xxxx]這樣的數進行補碼運算的。
  96. //但我們等下要列印的mac地址是不需要符號的數值
  97. struct ifreq ifr;  
  98.     strcpy(ifr.ifr_name, if_name);  
  99. if(ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)  
  100.     {  
  101.         perror("ioctl SIOCGIFHWADDR error");  
  102. return -1;  
  103.     }  
  104.     p = (char *)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0];   //獲得介面的MAC地址,用字串指標返回
  105.     printf("    MAC: %02x:%02x:%02x:%02x:%02x:%02x/n", *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5));  
  106. //printf(" MAC:%02x:%02x:%02x:%02x:%02x:%02x/n", *p++, *p++, *p++, *p++, *p++, *p++);
  107. //這麼寫會導致輸出為倒序。這並不是p指標有什麼問題,不信你可以用
  108. // for(;;) 
  109. //  printf(p++);
  110. //來試驗就是正確的,我猜倒序的原因是編譯器的優化問題吧
  111. return 0;  
  112. }  
  113. int print_if_mtu(int fd, char *if_name)  
  114. {  
  115.     unsigned int mtu;  
  116. struct ifreq ifr;  
  117.     strcpy(ifr.ifr_name, if_name);  
  118. if(ioctl(fd, SIOCGIFMTU, &ifr) < 0)  
  119.     {  
  120.         perror("ioctl SIOCGIFMTU error");  
  121. return -1;  
  122.     }  
  123.     mtu = ifr.ifr_ifru.ifru_mtu;    //獲得子網掩碼。注意!我們仍放在struct aockaddr_in結構中返回
  124.     printf("    MTU: %d/n", mtu);  
  125. return 0;  
  126. }  
  127. int print_if_addr6(char *if_name)  
  128. {  
  129.     unsigned int mtu;  
  130. struct ifreq ifr;  
  131. int sockfd;  
  132. if((sockfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)  
  133.     {  
  134.         perror("Socket error");  
  135. return -1;  
  136.     }   // 建立用來檢查網路介面的套接字
  137. /*  strcpy(ifr.ifr_name, if_name); 
  138.     if(ioctl(fd, SIOCGIFMTU, &ifr) < 0) 
  139.     { 
  140.         perror("ioctl SIOCGIFMTU error"); 
  141.         return -1; 
  142.     } 
  143.     mtu = ifr.ifr_ifru.ifru_mtu;    //獲得子網掩碼。注意!我們仍放在struct aockaddr_in結構中返回 
  144.     printf("    ipv6: %d/n", mtu); 
  145. */
  146. //未寫完,不知道怎麼獲得ipv6地址。。。
  147. return 0;  
  148. }  
  149. int print_all_interface()  
  150. {  
  151. struct ifconf ifc;  
  152. struct ifreq *ifr_p;  
  153. int sockfd, len, old_len = 0, i;  
  154. char *buf;  
  155. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)  
  156.     {  
  157.         perror("Socket error");  
  158. return -1;  
  159.     }   // 建立用來檢查網路介面的套接字
  160.     len = 10 * sizeof(struct ifreq);  
  161. for( ; ; )  
  162.     {  
  163. if((buf = malloc(len)) == NULL)  
  164.         {  
  165.             perror("malloc error");  
  166. return -1;  
  167.         }  
  168.         ifc.ifc_len = len;  
  169.         ifc.ifc_buf = buf;  
  170. if(ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)  
  171.         {  
  172.             perror("ioctl SIOCGIFCONF error");  
  173. return -1;  
  174.         }     
  175. if(ifc.ifc_len == old_len)  
  176. break;  
  177.         old_len = ifc.ifc_len;  
  178.         len += 10 * sizeof(struct ifreq);  
  179.         free(buf);  
  180.     }     
  181.     printf("we have %d interfaces/n", ifc.ifc_len / sizeof(struct ifreq));  
  182. for(i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++)  
  183.     {  
  184.         ifr_p = &ifc.ifc_req[i];  
  185.         printf("/ninterface [%s]:/n", ifr_p->ifr_name);  
  186.         print_if_addr(sockfd, ifr_p->ifr_name);  
  187.         print_if_broadaddr(sockfd, ifr_p->ifr_name);  
  188.         print_if_mask(sockfd, ifr_p->ifr_name);  
  189.         print_if_mac(sockfd, ifr_p->ifr_name);  
  190.         print_if_mtu(sockfd, ifr_p->ifr_name);  
  191.     }  
  192.     close(sockfd);  
  193. return 0;  
  194. }  
  195. int print_interface_info(char *if_name)  
  196. {  
  197. int sockfd;  
  198. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)  
  199.     {  
  200.         perror("Socket error");  
  201. return -1;  
  202.     }   // 建立用來檢查網路介面的套接字
  203.     printf("%s:/n", if_name);  
  204.     print_if_addr(sockfd, if_name);  
  205.     print_if_broadaddr(sockfd, if_name);  
  206.     print_if_mask(sockfd, if_name);  
  207.     print_if_mac(sockfd, if_name);  
  208.     print_if_mtu(sockfd, if_name);    
  209.     close(sockfd);  
  210. return 0;  
  211. }  
  212. int set_if_up(char *if_name)        //啟動介面
  213. {  
  214. struct ifreq ifr;  
  215. int sockfd;  
  216. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)  
  217.     {  
  218.         perror("Socket error");  
  219. return -1;  
  220.     }   // 建立用來檢查網路介面的套接字
  221.     strcpy(ifr.ifr_name, if_name);  
  222. if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)  
  223.     {  
  224.         perror("ioctl SIOCGIFFLAGS error");  
  225. return -1;  
  226.     }  
  227.     ifr.ifr_flags |= IFF_UP;  
  228. if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)  
  229.     {  
  230.         perror("ioctl SIOCSIFFLAGS error");  
  231. return -1;  
  232.     }  
  233. return 0;  
  234. }  
  235. int set_if_down(char *if_name)      //關閉介面
  236. {  
  237. struct ifreq ifr;  
  238. int sockfd;  
  239. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)  
  240.     {  
  241.         perror("Socket error");  
  242. return -1;  
  243.     }   // 建立用來檢查網路介面的套接字
  244.     strcpy(ifr.ifr_name, if_name);  
  245. if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)  
  246.     {  
  247.         perror("ioctl SIOCGIFFLAGS error");  
  248. return -1;  
  249.     }  
  250.     ifr.ifr_flags &= ~IFF_UP;   //將IIF_UP取反後與原來的標誌進行 與運算。
  251. if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)  
  252.     {  
  253.         perror("ioctl SIOCSIFFLAGS error");  
  254. return -1;  
  255.     }  
  256. return 0;  
  257. }  
  258. int set_if_ip(char *if_name, char *ip_str)  //設定介面的ip地址
  259. {  
  260. struct ifreq ifr;  
  261. struct sockaddr_in ip_addr;  
  262. int sockfd;  
  263. if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)  
  264.     {  
  265.         perror("Socket error");  
  266. return -1;  
  267.     }   // 建立用來檢查網路介面的套接字
  268.     ip_addr.sin_family = AF_INET;     
  269. if(inet_pton(AF_INET, ip_str, &ip_addr.sin_addr) < 1)  
  270.     {  
  271.         perror("error ipv4 addr:");  
  272. return -1;  
  273.     }     
  274.     strcpy(ifr.ifr_name, if_name);  
  275.     memcpy(&ifr.ifr_addr, &ip_addr, sizeof(struct sockaddr_in));      
  276. if(ioctl(sockfd, SIOCSIFADDR, &ifr) < 0)  
  277.     {  
  278.         perror("ioctl SIOCSIFADDR error");  
  279. return -1;  
  280.     }  
  281. return 0;     
  282. }