獲得Unix/Linux系統中的IP、MAC地址等信息


獲得Unix/Linux系統中的IP、MAC地址等信息

中高級  |  2010-07-13 16:03  |  分類:① C語言、  Unix/Linux、  網絡編程 ② 手冊  |  4,471 次閱讀

作者:diaoyf  |  文章來源:http://programmerdigest.cn

實際環境和特殊需求往往會將簡單問題復雜化,比如計算機IP地址,對於一個連接中socket,可以直接獲得本端和對端的IP、端口信息。但在一些特殊場合我們可能需要更多的信息,比如系統中有幾塊網卡,他們的Mac地址是多少,每塊網卡分配了幾個IP(一個網卡對應多個IP)等等。

這些信息往往需要通過ifconfig指令來獲得,對於程序員來說,在代碼中調用外部的shell指令可不是個最佳方案,因為沒人能保障不同平台、不同版本的ifconfig指令輸出的格式是一致的。本篇文章中將介紹通過ioctl函數實現上述需求。

 

#include <sys/ioctl.h>
int ioctl(int fd, int request, … /* void *arg */);
返回:成功返回0,失敗返回-1

ioctl函數的參數只有3個,但卻是Unix中少有的幾個“家族類”復雜函數,這里摘錄一段《Unix網絡編程》一書中對ioctl函數的描述:

在傳統上ioctl函數是用於那些普遍使用、但不適合歸入其他類別的任何特殊的系統接口……網絡程序(一般是服務器程序)中ioctl常用於在程序啟動時獲得主機上所有接口的信息:接口的地址、接口是否支持廣播、是否支持多播,等等。

ioctl函數的第一個參數fd,可以表示一個打開的文件(文件句柄)或網絡套接字,第二個和第三個參數體現了函數的家族特色,參數二request根據函數功能分類定義了多組宏,而參數三總是一個指針,指針的類型依賴於參數二request。因為ioctl的種類實在太多,這里只列出和本文相關的幾個參數定義:

分類 參數二(宏) 參數三 描述
接口 SIOCGIFCONF struct ifconf 獲得所有接口列表
  SIOCGIFADDR struct ifreq 獲得接口地址
  SIOCGIFFLAGS struct ifreq 獲得接口標志
  SIOCGIFBRDADDR struct ifreq 獲得廣播地址
  SIOCGIFNETMASK struct ifreq 獲得子網掩碼

 

上表中列出了兩個相關的結構體:struct ifconf 和 struct ifreq,要了解ioctl函數的具體運用,首先要了解這兩個結構:

 

 

[cpp]  view plain  copy
 
  1. /* net/if.h */  
  2. struct ifconf  
  3. {  
  4.     int ifc_len;            /* Size of buffer.  */  
  5.     union  
  6.     {  
  7.         __caddr_t ifcu_buf;  
  8.         struct ifreq *ifcu_req;  
  9.     } ifc_ifcu;  
  10. };  
  11.    
  12. struct ifreq  
  13. {  
  14. # define IFHWADDRLEN    6  
  15. # define IFNAMSIZ   IF_NAMESIZE  
  16.      
  17.     union  
  18.     {  
  19.         char ifrn_name[IFNAMSIZ];   /* Interface name, e.g. "en0".  */  
  20.     } ifr_ifrn;  
  21.      
  22.     union  
  23.     {  
  24.         struct sockaddr ifru_addr;  
  25.         struct sockaddr ifru_dstaddr;  
  26.         struct sockaddr ifru_broadaddr;  
  27.         struct sockaddr ifru_netmask;  
  28.         struct sockaddr ifru_hwaddr;  
  29.         short int ifru_flags;  
  30.         int ifru_ivalue;  
  31.         int ifru_mtu;  
  32.         struct ifmap ifru_map;  
  33.         char ifru_slave[IFNAMSIZ];  /* Just fits the size */  
  34.         char ifru_newname[IFNAMSIZ];  
  35.         __caddr_t ifru_data;  
  36.     } ifr_ifru;  
  37. };  



 

 

 

struct ifconf的第二個元素ifc_ifcu是一個聯合,是指向struct ifreq結構的地址,通常是一組struct ifreq結構空間(每一個描述一個接口),struct ifconf的第一個元素ifc_len描述了struct ifreq結構空間的大小;結構struct ifreq也有兩個元素,第一個元素ifr_ifrn內含一個字符串,用來描述接口的名稱,比如“eth0″、”wlan0”等,第二個元素是聯合,比較復雜,用來描述套接口的地址結構。

struct ifconf 和 struct ifreq的關系可以參考下圖:

ioctl函數中的struct ifconf 和 struct ifreq結構關系

ioctl函數中的struct ifconf 和 struct ifreq結構關系

通常運用ioctl函數的第一步是從內核獲取系統的所有接口,然后再針對每個接口獲取其地址信息。獲取所有接口通過SIOCGIFCONF請求來實現:

 

 

[cpp]  view plain  copy
 
  1. /* net/if.h */  
  2. struct ifconf  
  3. {  
  4.     int ifc_len;            /* Size of buffer.  */  
  5.     union  
  6.     {  
  7.         __caddr_t ifcu_buf;  
  8.         struct ifreq *ifcu_req;  
  9.     } ifc_ifcu;  
  10. };  
  11.    
  12. struct ifreq  
  13. {  
  14. # define IFHWADDRLEN    6  
  15. # define IFNAMSIZ   IF_NAMESIZE  
  16.      
  17.     union  
  18.     {  
  19.         char ifrn_name[IFNAMSIZ];   /* Interface name, e.g. "en0".  */  
  20.     } ifr_ifrn;  
  21.      
  22.     union  
  23.     {  
  24.         struct sockaddr ifru_addr;  
  25.         struct sockaddr ifru_dstaddr;  
  26.         struct sockaddr ifru_broadaddr;  
  27.         struct sockaddr ifru_netmask;  
  28.         struct sockaddr ifru_hwaddr;  
  29.         short int ifru_flags;  
  30.         int ifru_ivalue;  
  31.         int ifru_mtu;  
  32.         struct ifmap ifru_map;  
  33.         char ifru_slave[IFNAMSIZ];  /* Just fits the size */  
  34.         char ifru_newname[IFNAMSIZ];  
  35.         __caddr_t ifru_data;  
  36.     } ifr_ifru;  
  37. };  



 

 

獲得了接口列表,就可以通過struct ifconf結構中*ifcu_req的指針得到struct ifreq結構數組的地址,通過遍歷獲得每隔接口的詳細地址信息:

 

[cpp]  view plain  copy
 
  1. printf("接口名稱:%s\n", ifrs[n].ifr_name); /* 接口名稱 */  
  2.    
  3. /* 獲得IP地址 */  
  4. ioctl(fd, SIOCGIFADDR, (char *) &ifrs[n]);  
  5. printf("IP地址:%s\n",  
  6.     (char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr));  
  7.    
  8. /* 獲得子網掩碼 */  
  9. ioctl(fd, SIOCGIFNETMASK, (char *) &ifrs[n]);  
  10. printf("子網掩碼:%s\n",  
  11.     (char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr));  
  12.    
  13. /* 獲得廣播地址 */  
  14. ioctl(fd, SIOCGIFBRDADDR, (char *) &ifrs[n]);  
  15. printf("廣播地址:%s\n",  
  16.     (char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr));  
  17.    
  18. /* 獲得MAC地址 */  
  19. ioctl(fd, SIOCGIFHWADDR, (char *) &ifrs[n]);  
  20. printf("MAC地址:%02x:%02x:%02x:%02x:%02x:%02x\n",  
  21.     (unsigned char) ifrs[n].ifr_hwaddr.sa_data[0],  
  22.     (unsigned char) ifrs[n].ifr_hwaddr.sa_data[1],  
  23.     (unsigned char) ifrs[n].ifr_hwaddr.sa_data[2],  
  24.     (unsigned char) ifrs[n].ifr_hwaddr.sa_data[3],  
  25.     (unsigned char) ifrs[n].ifr_hwaddr.sa_data[4],  
  26.     (unsigned char) ifrs[n].ifr_hwaddr.sa_data[5]);  



 

 

最后,給出一個參考程序代碼。

ioctl函數沒有納入POXIS規范,各系統對ioctl的實現也不盡相同,下面的代碼在我的Ubuntu10.04 linux上可執行通過,但在其他Unix系統上不一定能夠通過編譯,例如在Power AIX 5.3上需要將獲得MAC地址的那段代碼注釋掉。

 

 

[cpp]  view plain  copy
 
  1. #include <arpa/inet.h>  
  2. #include <net/if.h>  
  3. #include <net/if_arp.h>  
  4. #include <netinet/in.h>  
  5. #include <stdio.h>  
  6. #include <sys/ioctl.h>  
  7. #include <sys/socket.h>  
  8. #include <unistd.h>  
  9.    
  10. #define MAXINTERFACES 16    /* 最大接口數 */  
  11.    
  12. int fd;         /* 套接字 */  
  13. int if_len;     /* 接口數量 */  
  14. struct ifreq buf[MAXINTERFACES];    /* ifreq結構數組 */  
  15. struct ifconf ifc;                  /* ifconf結構 */  
  16.    
  17. int main(argc, argv)  
  18. {  
  19.     /* 建立IPv4的UDP套接字fd */  
  20.     if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)  
  21.     {  
  22.         perror("socket(AF_INET, SOCK_DGRAM, 0)");  
  23.         return -1;  
  24.     }  
  25.    
  26.     /* 初始化ifconf結構 */  
  27.     ifc.ifc_len = sizeof(buf);  
  28.     ifc.ifc_buf = (caddr_t) buf;  
  29.    
  30.     /* 獲得接口列表 */  
  31.     if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) == -1)  
  32.     {  
  33.         perror("SIOCGIFCONF ioctl");  
  34.         return -1;  
  35.     }  
  36.    
  37.     if_len = ifc.ifc_len / sizeof(struct ifreq); /* 接口數量 */  
  38.     printf("接口數量:%d\n\n", if_len);  
  39.    
  40.     while (if_len– > 0) /* 遍歷每個接口 */  
  41.     {  
  42.         printf("接口:%s\n", buf[if_len].ifr_name); /* 接口名稱 */  
  43.    
  44.         /* 獲得接口標志 */  
  45.         if (!(ioctl(fd, SIOCGIFFLAGS, (char *) &buf[if_len])))  
  46.         {  
  47.             /* 接口狀態 */  
  48.             if (buf[if_len].ifr_flags & IFF_UP)  
  49.             {  
  50.                 printf("接口狀態: UP\n");  
  51.             }  
  52.             else  
  53.             {  
  54.                 printf("接口狀態: DOWN\n");  
  55.             }  
  56.         }  
  57.         else  
  58.         {  
  59.             char str[256];  
  60.             sprintf(str, "SIOCGIFFLAGS ioctl %s", buf[if_len].ifr_name);  
  61.             perror(str);  
  62.         }  
  63.    
  64.    
  65.         /* IP地址 */  
  66.         if (!(ioctl(fd, SIOCGIFADDR, (char *) &buf[if_len])))  
  67.         {  
  68.             printf("IP地址:%s\n",  
  69.                     (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));  
  70.         }  
  71.         else  
  72.         {  
  73.             char str[256];  
  74.             sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);  
  75.             perror(str);  
  76.         }  
  77.    
  78.         /* 子網掩碼 */  
  79.         if (!(ioctl(fd, SIOCGIFNETMASK, (char *) &buf[if_len])))  
  80.         {  
  81.             printf("子網掩碼:%s\n",  
  82.                     (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));  
  83.         }  
  84.         else  
  85.         {  
  86.             char str[256];  
  87.             sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);  
  88.             perror(str);  
  89.         }  
  90.    
  91.         /* 廣播地址 */  
  92.         if (!(ioctl(fd, SIOCGIFBRDADDR, (char *) &buf[if_len])))  
  93.         {  
  94.             printf("廣播地址:%s\n",  
  95.                     (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));  
  96.         }  
  97.         else  
  98.         {  
  99.             char str[256];  
  100.             sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);  
  101.             perror(str);  
  102.         }  
  103.    
  104.         /*MAC地址 */  
  105.         if (!(ioctl(fd, SIOCGIFHWADDR, (char *) &buf[if_len])))  
  106.         {  
  107.             printf("MAC地址:%02x:%02x:%02x:%02x:%02x:%02x\n\n",  
  108.                     (unsigned char) buf[if_len].ifr_hwaddr.sa_data[0],  
  109.                     (unsigned char) buf[if_len].ifr_hwaddr.sa_data[1],  
  110.                     (unsigned char) buf[if_len].ifr_hwaddr.sa_data[2],  
  111.                     (unsigned char) buf[if_len].ifr_hwaddr.sa_data[3],  
  112.                     (unsigned char) buf[if_len].ifr_hwaddr.sa_data[4],  
  113.                     (unsigned char) buf[if_len].ifr_hwaddr.sa_data[5]);  
  114.         }  
  115.         else  
  116.         {  
  117.             char str[256];  
  118.             sprintf(str, "SIOCGIFHWADDR ioctl %s", buf[if_len].ifr_name);  
  119.             perror(str);  
  120.         }  
  121.     }//–while end  
  122.    
  123.     //關閉socket  
  124.     close(fd);  
  125.     return 0;  
  126. }  



 

 

 

在我的系統上,程序輸出:

接口數量:4

接口:wlan0
接口狀態: UP
IP地址:192.168.1.142
子網掩碼:255.255.255.0
廣播地址:192.168.1.255
MAC地址:00:14:a5:65:47:57

接口:eth0:0
接口狀態: UP
IP地址:192.168.4.113
子網掩碼:255.255.255.0
廣播地址:192.168.4.255
MAC地址:00:14:c2:e5:45:57

接口:eth0
接口狀態: UP
IP地址:192.168.4.111
子網掩碼:255.255.255.0
廣播地址:192.168.4.255
MAC地址:00:14:c2:e5:45:57

接口:lo
接口狀態: UP
IP地址:127.0.0.1
子網掩碼:255.0.0.0
廣播地址:0.0.0.0
MAC地址:00:00:00:00:00:00

從輸出可以看出,系統有4個接口,”wlan0″表示第一塊無線網卡接口,”eth0″(IP地址:192.168.4.111)表示第一塊連線網卡接口(我們最長用的RJ45連接口網卡),”lo”是回路地址接口(我們常用的127.0.0.1)。

注意:”eth0:0″(IP地址:192.168.4.113)是有線網卡的別名******網卡綁定多個IP),這是為了測試這個參考程序特意在eth0上添加的一個IP地址。


參考資料:《Unix網絡編程》第16章 ioctl操作


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM