原文出自: http://blog.sina.com.cn/s/blog_5d0e8d0d0101604k.html
其實你可以
>find /usr/include/ -name "*.h" -exec grep -l "ifconf" {} \;
用ioctl獲得本地ip地址時要用到兩個結構體ifconf和ifreq,它們對於大多數人
來說都是比較陌生的,這裏給大家一種比較簡單的理解方法,當然只一種幫助
理解的方法,在描述中可能會有一些地方與真實定義有所出入,僅供參考.
首先先認識一下ifconf和ifreq:
//ifconf通常是用來保存所有接口信息的
//if.h
struct
ifconf {
*ifcu_buf;
ifreq *ifcu_req;
#define
//ifreq用來保存某個接口的信息
//if.h
struct
ifreq
{
ifr_name[IFNAMSIZ];
sockaddr ifru_addr;
sockaddr ifru_dstaddr;
sockaddr ifru_broadaddr;
ifru_flags;
ifru_metric;
ifr_addr ifr_ifru.ifru_addr#define
ifr_dstaddr ifr_ifru.ifru_dstaddr#define
ifr_broadaddr ifr_ifru.ifru_broadaddr
上邊這兩個結構看起來比較複雜,我們現在把它們簡單化一些:
比如說現在我們向實現獲得本地IP的功能。
我們的做法是:
1. 先通過ioctl獲得本地所有接口的信息,並保存在ifconf中
2. 再從ifconf中取出每一個ifreq中表示ip地址的信息
具體使用時我們可以認爲ifconf就有兩個成員:
ifc_len 和 ifc_buf, 如圖一所示:
ifc_len:表示用來存放所有接口信息的緩衝區長度
ifc_buf:表示存放接口信息的緩衝區
所以我們需要在程序開始時對ifconf的ifc_len和ifc_buf進行初始化
接下來使用ioctl獲取所有接口信息,完成後ifc_len內存放實際獲得的藉口信息總長度
並且信息被存放在ifc_buf中。
如下圖示:(假設讀到兩個接口信息)
接下來我們只需要從一個一個的接口信息獲取ip地址信息即可。
下面有一個簡單的參考:
#include #include #include #include #include in
.h>#include .h>#include if
.h>#include int
main(){
i=0;
sockfd; struct
ifconf ifconf; unsigned char
buf[512]; struct
ifreq *ifreq;
ifconf.ifc_len = 512; ifconf.ifc_buf = buf;
((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
);
ifreq = (struct
ifreq*)buf;
(i=(ifconf.ifc_len/sizeof
(struct
ifreq)); i>0; i--) {//
, ifreq->ifr_name);
,inet_ntoa(((struct
sockaddr_in*)&(ifreq->ifr_addr))->sin_addr));
}
0;}
Linux C 獲取本機IP地址的方法,排除127.0.0.1
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//獲取地址
//返回IP地址字符串
//返回:0=成功,-1=失敗
int getlocalip(char* outip)
{
int i=0;
int sockfd;
struct ifconf ifconf;
char buf[512];
struct ifreq *ifreq;
char* ip;
//初始化ifconf
ifconf.ifc_len = 512;
ifconf.ifc_buf = buf;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
{
return -1;
}
ioctl(sockfd, SIOCGIFCONF, &ifconf);
close(sockfd);
//接下來一個一個的獲取IP地址
ifreq = (struct ifreq*)buf;
for(i=(ifconf.ifc_len/sizeof(struct ifreq)); i>0; i–)
{
ip = inet_ntoa(((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr);
if(strcmp(ip,”127.0.0.1″)==0)
{
ifreq++;
continue;
}
strcpy(outip,ip);
return 0;
}
return -1;
}
//——————————-函數的調用方式————————————-
char ip[20];
if ( getlocalip( ip ) == 0 )
{
printf( “
}
else
{
printf( ” 無法獲取本機IP地址
}
----------------------------
ifconf和ifreq
獲得Unix/Linux系統中的IP、MAC地址等信息
實際環境和特殊需求往往會將簡單問題複雜化,比如計算機IP地址,對於一個連接中socket,可以直接獲得本端和對端的IP、端口信息。但在一些特殊場合我們可能需要更多的信息,比如系統中有幾塊網卡,他們的Mac地址是多少,每塊網卡分配了幾個IP(一個網卡對應多個IP)等等。
這些信息往往需要通過ifconfig指令來獲得,對於程序員來說,在代碼中調用外部的shell指令可不是個最佳方案,因爲沒人能保障不同平臺、不同版本的ifconfig指令輸出的格式是一致的。本篇文章中將介紹通過ioctl函數實現上述需求。
#include
int ioctl(int fd, int request, … );
返回:成功返回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函數的具體運用,首先要了解這兩個結構:
-
struct
ifconf - {
-
int ifc_len; -
union -
{ -
__caddr_t ifcu_buf; -
struct ifreq *ifcu_req; -
} ifc_ifcu; - };
-
-
struct
ifreq - {
-
# define IFHWADDRLEN
6 -
# define IFNAMSIZ
IF_NAMESIZE -
-
union -
{ -
char ifrn_name[IFNAMSIZ]; -
} ifr_ifrn; -
-
union -
{ -
struct sockaddr ifru_addr; -
struct sockaddr ifru_dstaddr; -
struct sockaddr ifru_broadaddr; -
struct sockaddr ifru_netmask; -
struct sockaddr ifru_hwaddr; -
short int ifru_flags; -
int ifru_ivalue; -
int ifru_mtu; -
struct ifmap ifru_map; -
char ifru_slave[IFNAMSIZ]; -
char ifru_newname[IFNAMSIZ]; -
__caddr_t ifru_data; -
} ifr_ifru; - };
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函數的第一步是從內核獲取系統的所有接口,然後再針對每個接口獲取其地址信息。獲取所有接口通過SIOCGIFCONF請求來實現:
-
struct
ifconf ifc; -
struct
ifreq ifrs[16]; -
-
ifc.ifc_len
= sizeof(ifrs); -
ifc.ifc_buf
= (caddr_t) ifrs; -
-
ioctl(fd, SIOCGIFCONF,
(char *) &ifc);
獲得了接口列表,就可以通過struct ifconf結構中*ifcu_req的指針得到struct ifreq結構數組的地址,通過遍歷獲得每隔接口的詳細地址信息:
-
printf("接口名稱:%s/n",
ifrs[n].ifr_name);
-
-
ioctl(fd, SIOCGIFADDR,
(char *) &ifrs[n]); - printf("IP地址:%s/n",
-
(char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr)); -
-
ioctl(fd, SIOCGIFNETMASK,
(char *) &ifrs[n]); - printf("子網掩碼:%s/n",
-
(char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr)); -
-
ioctl(fd, SIOCGIFBRDADDR,
(char *) &ifrs[n]); - printf("廣播地址:%s/n",
-
(char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr)); -
-
ioctl(fd, SIOCGIFHWADDR,
(char *) &ifrs[n]); - printf("MAC地址:x:x:x:x:x:x/n",
-
(unsigned char) ifrs[n].ifr_hwaddr.sa_data[0], -
(unsigned char) ifrs[n].ifr_hwaddr.sa_data[1], -
(unsigned char) ifrs[n].ifr_hwaddr.sa_data[2], -
(unsigned char) ifrs[n].ifr_hwaddr.sa_data[3], -
(unsigned char) ifrs[n].ifr_hwaddr.sa_data[4], -
(unsigned char) ifrs[n].ifr_hwaddr.sa_data[5]);
最後,給出一個參考程序代碼。
ioctl函數沒有納入POXIS規範,各系統對ioctl的實現也不盡相同,下面的代碼在我的Ubuntu10.04 linux上可執行通過,但在其他Unix系統上不一定能夠通過編譯,例如在Power AIX 5.3上需要將獲得MAC地址的那段代碼註釋掉。
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
-
#define MAXINTERFACES 16
-
-
int
fd; -
int
if_len; -
struct
ifreq buf[MAXINTERFACES]; -
struct
ifconf ifc; -
-
int
main(argc, argv) - {
-
-
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) -
{ -
perror("socket(AF_INET, SOCK_DGRAM, 0)"); -
return -1; -
} -
-
-
ifc.ifc_len = sizeof(buf); -
ifc.ifc_buf = (caddr_t) buf; -
-
-
if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) == -1) -
{ -
perror("SIOCGIFCONF ioctl"); -
return -1; -
} -
-
if_len = ifc.ifc_len / sizeof(struct ifreq); -
printf("接口數量:%d/n/n", if_len); -
-
while (if_len– > 0) -
{ -
printf("接口:%s/n", buf[if_len].ifr_name); -
-
-
if (!(ioctl(fd, SIOCGIFFLAGS, (char *) &buf[if_len]))) -
{ -
-
if (buf[if_len].ifr_flags & IFF_UP) -
{ -
printf("接口狀態: UP/n"); -
} -
else -
{ -
printf("接口狀態: DOWN/n"); -
} -
} -
else -
{ -
char str[256]; -
sprintf(str, "SIOCGIFFLAGS ioctl %s", buf[if_len].ifr_name); -
perror(str); -
} -
-
-
-
if (!(ioctl(fd, SIOCGIFADDR, (char *) &buf[if_len]))) -
{ -
printf("IP地址:%s/n", -
(char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr)); -
} -
else -
{ -
char str[256]; -
sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name); -
perror(str); -
} -
-
-
if (!(ioctl(fd, SIOCGIFNETMASK, (char *) &buf[if_len]))) -
{ -
printf("子網掩碼:%s/n", -
(char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr)); -
} -
else -
{ -
char str[256]; -
sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name); -
perror(str); -
} -
-
-
if (!(ioctl(fd, SIOCGIFBRDADDR, (char *) &buf[if_len]))) -
{ -
printf("廣播地址:%s/n", -
(char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr)); -
} -
else -
{ -
char str[256]; -
sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name); -
perror(str); -
} -
-
-
if (!(ioctl(fd, SIOCGIFHWADDR, (char *) &buf[if_len]))) -
{ -
printf("MAC地址:x:x:x:x:x:x/n/n", -
(unsigned char) buf[if_len].ifr_hwaddr.sa_data[0], -
(unsigned char) buf[if_len].ifr_hwaddr.sa_data[1], -
(unsigned char) buf[if_len].ifr_hwaddr.sa_data[2], -
(unsigned char) buf[if_len].ifr_hwaddr.sa_data[3], -
(unsigned char) buf[if_len].ifr_hwaddr.sa_data[4], -
(unsigned char) buf[if_len].ifr_hwaddr.sa_data[5]); -
} -
else -
{ -
char str[256]; -
sprintf(str, "SIOCGIFHWADDR ioctl %s", buf[if_len].ifr_name); -
perror(str); -
} -
}//–while end -
-
//關閉socket -
close(fd); -
return 0; - }
在我的系統上,程序輸出:
接口數量: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地址。