ifaddrs結構體定義如下:
- struct ifaddrs
- {
- struct ifaddrs *ifa_next; /* Next item in list */
- char *ifa_name; /* Name of interface */
- unsigned int ifa_flags; /* Flags from SIOCGIFFLAGS */
- struct sockaddr *ifa_addr; /* Address of interface */
- struct sockaddr *ifa_netmask; /* Netmask of interface */
- union
- {
- struct sockaddr *ifu_broadaddr; /* Broadcast address of interface */
- struct sockaddr *ifu_dstaddr; /* Point-to-point destination address */
- } ifa_ifu;
- #define ifa_broadaddr ifa_ifu.ifu_broadaddr
- #define ifa_dstaddr ifa_ifu.ifu_dstaddr
- void *ifa_data; /* Address-specific data */
- };
ifa_next指向鏈表的下一個成員;ifa_name是接口名稱,以0結尾的字符串,比如eth0,lo;ifa_flags是接口的標識位(比如當IFF_BROADCAST或IFF_POINTOPOINT設置到此標識位時,影響聯合體變量ifu_broadaddr存儲廣播地址或ifu_dstaddr記錄點對點地址);ifa_netmask存儲該接口的子網掩碼;結構體變量存儲廣播地址或點對點地址(見括弧介紹ifa_flags);ifa_data存儲了該接口協議族的特殊信息,它通常是NULL(一般不關注他)。
函數getifaddrs(int getifaddrs (struct ifaddrs **__ifap))獲取本地網絡接口信息,將之存儲於鏈表中,鏈表頭結點指針存儲於__ifap中帶回,函數執行成功返回0,失敗返回-1,且為errno賦值。
很顯然,函數getifaddrs用於獲取本機接口信息,比如最典型的獲取本機IP地址。
這 是一項不太清晰而且沒有多大意義的工作。一個原因是網絡地址的設置非常靈活而且都是允許用戶進行個性化設置的,比如一台計算機上可以有多塊物理網卡或者虛 擬網卡,一個網卡上可以綁定多個IP地址,用戶可以為網卡設置別名,可以重命名網卡,用戶計算機所在網絡拓撲結構未知,主機名設置是一個可選項並且同樣可 以為一個計算機綁定多個主機名等,這些信息都會有影響。脫離了網絡連接,單獨的網絡地址沒有任何意義。編程中遇到必須獲取計算機IP的場景,應該考慮將這 一選項放到配置文件中,由用戶自己來選擇。
通過google,編程獲取IP地址大約有以下三種思路:
1. 通過gethostname()和gethostbyname()
#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
char hname[128];
struct hostent *hent;
int i;
gethostname(hname, sizeof(hname));
//hent = gethostent();
hent = gethostbyname(hname);
printf("hostname: %s/naddress list: ", hent->h_name);
for(i = 0; hent->h_addr_list[i]; i++) {
printf("%s/t", inet_ntoa(*(struct in_addr*)(hent->h_addr_list[i])));
}
return 0;
}
運行:
[whb@jcwkyl c]$ ./local_ip
hostname: jcwkyl.jlu.edu.cn
address list: 10.60.56.90
2. 通過枚舉網卡,API接口可查看man 7 netdevice
/*代碼來自StackOverflow: http://stackoverflow.com/questions/212528/linux-c-get-the-ip-address-of-local-computer */
#include <stdio.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
int main (int argc, const char * argv[]) {
struct ifaddrs * ifAddrStruct=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
while (ifAddrStruct!=NULL) {
if (ifAddrStruct->ifa_addr->sa_family==AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printf("%s IP Address %s/n", ifAddrStruct->ifa_name, addressBuffer);
} else if (ifAddrStruct->ifa_addr->sa_family==AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printf("%s IP Address %s/n", ifAddrStruct->ifa_name, addressBuffer);
}
ifAddrStruct=ifAddrStruct->ifa_next;
}
return 0;
}
運行 :
[whb@jcwkyl c]$ ./local_ip2
lo IP Address 127.0.0.1
eth0 IP Address 10.60.56.90
eth0:1 IP Address 192.168.1.3
lo IP Address ::
eth0 IP Address ::2001:da8:b000:6213:20f:1fff
eth0 IP Address 0:0:fe80::20f:1fff
3. 打開一個對外界服務器的網絡連接,通過getsockname()反查自己的IP
在linux下 獲取,修改本機IP地址的兩個函數
//獲取本機IP地址函數
- QString GetLocalIp()
- {
- int sock_get_ip;
- char ipaddr[50];
- struct sockaddr_in *sin;
- struct ifreq ifr_ip;
- if ((sock_get_ip=socket(AF_INET, SOCK_STREAM, 0)) == -1)
- {
- printf("socket create failse...GetLocalIp!/n");
- return "";
- }
- memset(&ifr_ip, 0, sizeof(ifr_ip));
- strncpy(ifr_ip.ifr_name, "eth0", sizeof(ifr_ip.ifr_name) - 1);
- if( ioctl( sock_get_ip, SIOCGIFADDR, &ifr_ip) < 0 )
- {
- return "";
- }
- sin = (struct sockaddr_in *)&ifr_ip.ifr_addr;
- strcpy(ipaddr,inet_ntoa(sin->sin_addr));
- printf("local ip:%s /n",ipaddr);
- close( sock_get_ip );
- return QString( ipaddr );
- }
//修改本機IP地址的函數
- int SetLocalIp( const char *ipaddr )
- {
- int sock_set_ip;
- struct sockaddr_in sin_set_ip;
- struct ifreq ifr_set_ip;
- bzero( &ifr_set_ip,sizeof(ifr_set_ip));
- if( ipaddr == NULL )
- return -1;
- if(sock_set_ip = socket( AF_INET, SOCK_STREAM, 0 ) == -1);
- {
- perror("socket create failse...SetLocalIp!/n");
- return -1;
- }
- memset( &sin_set_ip, 0, sizeof(sin_set_ip));
- strncpy(ifr_set_ip.ifr_name, "eth0", sizeof(ifr_set_ip.ifr_name)-1);
- sin_set_ip.sin_family = AF_INET;
- sin_set_ip.sin_addr.s_addr = inet_addr(ipaddr);
- memcpy( &ifr_set_ip.ifr_addr, &sin_set_ip, sizeof(sin_set_ip));
- if( ioctl( sock_set_ip, SIOCSIFADDR, &ifr_set_ip) < 0 )
- {
- perror( "Not setup interface/n");
- return -1;
- }
- //設置激活標志
- ifr_set_ip.ifr_flags |= IFF_UP |IFF_RUNNING;
- //get the status of the device
- if( ioctl( sock_set_ip, SIOCSIFFLAGS, &ifr_set_ip ) < 0 )
- {
- perror("SIOCSIFFLAGS");
- return -1;
- }
- close( sock_set_ip );
- return 0;
- }
在linux下 獲取本機MAC地址的函數
獲取本機MAC地址函數
- QString GetLocalMac()
- {
- int sock_mac;
- struct ifreq ifr_mac;
- char mac_addr[30];
- sock_mac = socket( AF_INET, SOCK_STREAM, 0 );
- if( sock_mac == -1)
- {
- perror("create socket falise...mac/n");
- return "";
- }
- memset(&ifr_mac,0,sizeof(ifr_mac));
- strncpy(ifr_mac.ifr_name, "eth0", sizeof(ifr_mac.ifr_name)-1);
- if( (ioctl( sock_mac, SIOCGIFHWADDR, &ifr_mac)) < 0)
- {
- printf("mac ioctl error/n");
- return "";
- }
- sprintf(mac_addr,"%02x%02x%02x%02x%02x%02x",
- (unsigned char)ifr_mac.ifr_hwaddr.sa_data[0],
- (unsigned char)ifr_mac.ifr_hwaddr.sa_data[1],
- (unsigned char)ifr_mac.ifr_hwaddr.sa_data[2],
- (unsigned char)ifr_mac.ifr_hwaddr.sa_data[3],
- (unsigned char)ifr_mac.ifr_hwaddr.sa_data[4],
- (unsigned char)ifr_mac.ifr_hwaddr.sa_data[5]);
- printf("local mac:%s /n",mac_addr);
- close( sock_mac );
- return QString( mac_addr );
- }
在linux下 獲取,修改子網掩碼NETMASK的兩個函數
//獲取子網掩碼的函數
- QString GetLocalNetMask()
- {
- int sock_netmask;
- char netmask_addr[50];
- struct ifreq ifr_mask;
- struct sockaddr_in *net_mask;
- sock_netmask = socket( AF_INET, SOCK_STREAM, 0 );
- if( sock_netmask == -1)
- {
- perror("create socket failture...GetLocalNetMask/n");
- return "";
- }
- memset(&ifr_mask, 0, sizeof(ifr_mask));
- strncpy(ifr_mask.ifr_name, ifname, sizeof(ifr_mask.ifr_name )-1);
- if( (ioctl( sock_netmask, SIOCGIFNETMASK, &ifr_mask ) ) < 0 )
- {
- printf("mac ioctl error/n");
- return "";
- }
- net_mask = ( struct sockaddr_in * )&( ifr_mask.ifr_netmask );
- strcpy( netmask_addr, inet_ntoa( net_mask -> sin_addr ) );
- printf("local netmask:%s/n",netmask_addr);
- close( sock_netmask );
- return QString( netmask_addr );
- }
//修改子NETMASK的函數
- QString SetLocalNetMask(const char *szNetMask)
- {
- int sock_netmask;
- char netmask_addr[32];
- struct ifreq ifr_mask;
- struct sockaddr_in *sin_net_mask;
- sock_netmask = socket( AF_INET, SOCK_STREAM, 0 );
- if( sock_netmask == -1)
- {
- perror("Not create network socket connect/n");
- return "";
- }
- memset(&ifr_mask, 0, sizeof(ifr_mask));
- strncpy(ifr_mask.ifr_name, "eth0", sizeof(ifr_mask.ifr_name )-1);
- sin_net_mask = (struct sockaddr_in *)&ifr_mask.ifr_addr;
- sin_net_mask -> sin_family = AF_INET;
- inet_pton(AF_INET, szNetMask, &sin_net_mask ->sin_addr);
- if(ioctl(sock_netmask, SIOCSIFNETMASK, &ifr_mask ) < 0)
- {
- printf("sock_netmask ioctl error/n");
- return "";
- }
- }
//獲去GateWay
- QString GetGateWay()
- {
- FILE *fp;
- char buf[512];
- char cmd[128];
- char gateway[30];
- char *tmp;
- strcpy(cmd, "ip route");
- fp = popen(cmd, "r");
- if(NULL == fp)
- {
- perror("popen error");
- return "";
- }
- while(fgets(buf, sizeof(buf), fp) != NULL)
- {
- tmp =buf;
- while(*tmp && isspace(*tmp))
- ++ tmp;
- if(strncmp(tmp, "default", strlen("default")) == 0)
- break;
- }
- sscanf(buf, "%*s%*s%s", gateway);
- printf("default gateway:%s/n", gateway);
- pclose(fp);
- return QString(gateway);
- }
//設置網關
- int SetGateWay(const char *szGateWay)
- {
- int ret = 0;
- char cmd[128];
- QString DefGW = GetGateWay();
- const char *strGW = DefGW.latin1();
- strcpy(cmd, "route del default gw ");
- strcat(cmd, strGW);
- ret = system(cmd);
- if(ret < 0)
- {
- perror("route error");
- return -1;
- }
- strcpy(cmd, "route add default gw ");
- strcat(cmd, szGateWay);
- ret = system(cmd);
- if(ret < 0)
- {
- perror("route error");
- return -1;
- }
- return ret;
- }
有時候,寫程序的時候需要獲取計算機的網絡信息,比如IP地址、電腦名稱、DNS等信息。IP地址和電腦名稱是比較容易獲取到的,而要想獲取地址掩碼、DNS、網關等信息就有些麻煩了。
在Windows下我們一般都是通過從注冊表讀取這些信息。在Linux怎么做呢?其實,Linux下更加容易一些。因為我們可以拿現成的程序看它的源代碼。通過閱讀其源代碼找到解決該問題的方法。那么,看哪個程序的源代碼呢?如果你使用過Linux,並且比較熟悉的話就肯定知道一個命令ifconfig。這個命令和Windows下的ipconfig差不多,都可以輸出網卡的信息,其中就包含DNS、掩碼等信息。所以,我們可以通過看它的源代碼來找到解決該問題的方法。
獲取系統中的網卡數量
並沒有那個系統調用提供網卡數量的獲取。但是,我們可以通過強大的proc文件系統獲取網卡數量的信息。實際上,ifconfig也是這樣做的,請看示例代碼如下:
0001 #include <stdio.h>
0002 #include <string.h>
0003 #include <errno.h>
0004
0005 int GetNetCardCount()
0006 {
0007 int nCount = 0;
0008 FILE* f = fopen("/proc/net/dev", "r");
0009 if (!f)
0010 {
0011 fprintf(stderr, "Open /proc/net/dev failed!errno:%d\n", errno);
0012 return nCount;
0013 }
0014
0015 char szLine[512];
0016
0017 fgets(szLine, sizeof(szLine), f); /* eat line */
0018 fgets(szLine, sizeof(szLine), f);
0019
0020 while(fgets(szLine, sizeof(szLine), f))
0021 {
0022 char szName[128] = {0};
0023 sscanf(szLine, "%s", szName);
0024 int nLen = strlen(szName);
0025 if (nLen <= 0)continue;
0026 if (szName[nLen - 1] == ':') szName[nLen - 1] = 0;
0027 if (strcmp(szName, "lo") == 0)continue;
0028 nCount++;
0029 }
0030
0031 fclose(f);
0032 f = NULL;
0033 return nCount;
0034 }
0035
0036 int main(int argc, char* argv[])
0037 {
0038 printf("NetCardCount: %d\n", GetNetCardCount());
0039 return 0;
0040 }
獲取IP、掩碼、MAC及網關
獲取IP、掩碼、MAC和廣播地址是比較容易的,只需要調用對應的IOCTL即可。只是大家對Linux下的IOCTL可能不太熟悉。卻看示例代碼:
0001 void DispNetInfo(const char* szDevName)
0002 {
0003 int s = socket(AF_INET, SOCK_DGRAM, 0);
0004 if (s < 0)
0005 {
0006 fprintf(stderr, "Create socket failed!errno=%d", errno);
0007 return;
0008 }
0009
0010 struct ifreq ifr;
0011 unsigned char mac[6];
0012 unsigned long nIP, nNetmask, nBroadIP;
0013
0014 printf("%s:\n", szDevName);
0015
0016 strcpy(ifr.ifr_name, szDevName);
0017 if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
0018 {
0019 return;
0020 }
0021 memcpy(mac, ifr.ifr_hwaddr.sa_data, sizeof(mac));
0022 printf("\tMAC: %02x-%02x-%02x-%02x-%02x-%02x\n",
0023 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
0024
0025 strcpy(ifr.ifr_name, szDevName);
0026 if (ioctl(s, SIOCGIFADDR, &ifr) < 0)
0027 {
0028 nIP = 0;
0029 }
0030 else
0031 {
0032 nIP = *(unsigned long*)&ifr.ifr_broadaddr.sa_data[2];
0033 }
0034 printf("\tIP: %s\n", inet_ntoa(*(in_addr*)&nIP));
0035
0036 strcpy(ifr.ifr_name, szDevName);
0037 if (ioctl(s, SIOCGIFBRDADDR, &ifr) < 0)
0038 {
0039 nBroadIP = 0;
0040 }
0041 else
0042 {
0043 nBroadIP = *(unsigned long*)&ifr.ifr_broadaddr.sa_data[2];
0044 }
0045 printf("\tBroadIP: %s\n", inet_ntoa(*(in_addr*)&nBroadIP));
0046
0047 strcpy(ifr.ifr_name, szDevName);
0048 if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0)
0049 {
0050 nNetmask = 0;
0051 }
0052 else
0053 {
0054 nNetmask = *(unsigned long*)&ifr.ifr_netmask.sa_data[2];
0055 }
0056 printf("\tNetmask: %s\n", inet_ntoa(*(in_addr*)&nNetmask));
0057 close(s);
0058 }
那么如何獲取網關地址呢?更加容易,但是,好像很少有人知道。反正我在網上沒有找到有人知道。最后看了nslookup的源代碼以后才知道正確的做法。代碼如下:
res_init();
for (int i = 0; i < _res.nscount; i++)
{
struct sockaddr* server = (struct sockaddr*)&_res.nsaddr_list[i];
printf("Server: %s\n", inet_ntoa(*(in_addr*)&(server->sa_data[2])));
}
代碼很簡單,就不做解釋了。
怎么獲取網關呢?這個稍微有點麻煩一些,不過和獲取網卡數量相似,都是通過proc文件系統。這次分析的/proc/net/route文件。我就不再貼出示例代碼了。
最后,我把運行示例程序獲取到的信息附上,以供大家有個直觀的認識:
eth0:
MAC: 08-00-27-98-bf-f3
IP: 192.168.1.106
BroadIP: 255.255.255.255
Netmask: 255.255.255.0
Gateway: 192.168.1.1
eth1:
MAC: 08-00-27-16-f4-bf
IP: 192.168.1.108
BroadIP: 192.168.1.255
Netmask: 255.255.255.0
Gateway: 0.0.0.0
eth2:
MAC: 08-00-27-37-9c-91
IP: 0.0.0.0
BroadIP: 0.0.0.0
Netmask: 0.0.0.0
Gateway: 0.0.0.0
eth3:
MAC: 08-00-27-5a-d2-39
IP: 0.0.0.0
BroadIP: 0.0.0.0
Netmask: 0.0.0.0
Gateway: 0.0.0.0
NetCardCount: 4
DNS 0: 218.2.135.1
DNS 1: 61.147.37.1
Linux下的網絡配置包含三個要素,分別是IP地址、子網掩碼和網關。本文將介紹如何在C語言中進行網絡的配置和配置信息的獲取。
【配置】
方法一
使用system()或exec*()調用ifconfig和route命令進行配置。這種方法的優點是使用簡單,缺點是效率比較低,且依賴於ifconfig與route命令。
示例: 見所附代碼中的函數ip_config_system()和ip_config_exec()。
方法二
建立一個socket,用ioctl()進行配置。這種方法的優點是效率較高,缺點是程序實現起來比較麻煩。
示例: 見所附代碼中的函數ip_config_ioctl()。
【獲取】
方法一
用popen()建立一個管道,管道的一端執行命令ifconfig和route,管道的另一端讀取收到的數據並進行相應的解析。這種方法的優點是使用簡單,缺點是效率比較低,且依賴於ifconfig與route命令。
示例: 見所附代碼中的函數ip_get_pipe()。
方法二
用fopen()打開/proc/net/route,可以獲取網關(在/proc/net中尚未發現比較好的獲取IP地址和掩碼的方法,知道的請發郵件至cugfeng at gamil.com,謝謝)。這種方法的優點是使用簡單,效率比執行命令高,缺點是依賴於proc文件系統。
示例: 見所附代碼中的函數ip_get_proc()。
方法三
建立一個socket,用ioctl()進行獲取(用ioctl()尚未發現比較好的獲取網關的方法,知道的請發郵件至cugfeng at gamil.com,謝謝)。這種方法的優點是效率較高,缺點是程序實現起來比較麻煩。
示例: 見所附代碼中的函數ip_get_ioctl()。
BTW,用ioctl()的方法還可以獲取MAC地址,ioctl()命令為SIOCGIFHWADDR,具體用法與ioctl()獲取IP地址的方法相同,這里就不多說了。 |
在LINUX下獲取網卡信息需要用到IOCTL或者getifaddrs
而我在用getifaddrs的時候遇到了內存方面的問題
先看相關定義:
==========
函數定義:
/* Create a linked list of `struct ifaddrs' structures, one for each
network interface on the host machine. If successful, store the
list in *IFAP and return 0. On errors, return -1 and set `errno'.
The storage returned in *IFAP is allocated dynamically and can
only be properly freed by passing it to `freeifaddrs'. */
extern int getifaddrs (struct ifaddrs **__ifap) __THROW;
/* Reclaim the storage allocated by a previous `getifaddrs' call. */
extern void freeifaddrs (struct ifaddrs *__ifa) __THROW;
==============
此函數需要的結構體定義:
struct ifaddrs
{
struct ifaddrs *ifa_next; /* Pointer to the next structure. */
char *ifa_name; /* Name of this network interface. */
unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */
struct sockaddr *ifa_addr; /* Network address of this interface. */
struct sockaddr *ifa_netmask; /* Netmask of this interface. */
union
{
/* At most one of the following two is valid. If the IFF_BROADCAST
bit is set in `ifa_flags', then `ifa_broadaddr' is valid. If the
IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
It is never the case that both these bits are set at once. */
struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */
struct sockaddr *ifu_dstaddr; /* Point-to-point destination address. */
} ifa_ifu;
/* These very same macros are defined by <net/if.h> for `struct ifaddr'.
So if they are defined already, the existing definitions will be fine. */
# ifndef ifa_broadaddr
# define ifa_broadaddr ifa_ifu.ifu_broadaddr
# endif
# ifndef ifa_dstaddr
# define ifa_dstaddr ifa_ifu.ifu_dstaddr
# endif
void *ifa_data; /* Address-specific data (may be unused). */
};
=============
我在調用了getifaddrs()之后,正常地完成了需要的工作
但是最后如果用freeifaddrs,則出現運行時錯誤
*** glibc detected *** d: free(): invalid pointer: 0x0804a4d4 ***
======= Backtrace: =========
/lib/libc.so.6[0xb7eda911]
/lib/libc.so.6(__libc_free+0x84)[0xb7edbf84]
/lib/libc.so.6(freeifaddrs+0x1d)[0xb7f512dd]
d[0x8048989]
d[0x80486a5]
/lib/libc.so.6(__libc_start_main+0xdc)[0xb7e8c87c]
d[0x8048491]
======= Memory map: ========
08048000-08049000 r-xp 00000000 03:07 48637 /home/souldump/bin/d
08049000-0804a000 rw-p 00000000 03:07 48637 /home/souldump/bin/d
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
b7d00000-b7d21000 rw-p b7d00000 00:00 0
b7d21000-b7e00000 ---p b7d21000 00:00 0
b7e76000-b7e77000 rw-p b7e76000 00:00 0
b7e77000-b7f90000 r-xp 00000000 03:05 16184 /lib/libc-2.4.so
b7f90000-b7f92000 r--p 00118000 03:05 16184 /lib/libc-2.4.so
b7f92000-b7f94000 rw-p 0011a000 03:05 16184 /lib/libc-2.4.so
b7f94000-b7f98000 rw-p b7f94000 00:00 0
b7fab000-b7fb5000 r-xp 00000000 03:05 20108 /lib/libgcc_s.so.1
b7fb5000-b7fb6000 rw-p 00009000 03:05 20108 /lib/libgcc_s.so.1
b7fb6000-b7fb7000 rw-p b7fb6000 00:00 0
b7fb7000-b7fd1000 r-xp 00000000 03:05 16177 /lib/ld-2.4.so
b7fd1000-b7fd3000 rw-p 00019000 03:05 16177 /lib/ld-2.4.so
bfb2b000-bfb41000 rw-p bfb2b000 00:00 0 [stack]
ffffe000-fffff000 ---p 00000000 00:00 0 [vdso]
實際上也有人出現相同問題:
http://p.g.yupoo.com/nph-proxy.cgi/000110A/http/www.linuxdby.com/bbs/viewthread.php=3ftid=3d10756
此人說:"這說明不是真正的鏈表,指針非法"
但是又沒有進一步說明怎么解決
他干脆沒有調用freeifaddrs,自然會內存泄漏.....
我去看了afaddrs.c
freeifaddrs的定義居然是:
void
freeifaddrs (struct ifaddrs *ifa)
{
free (ifa);
}
怎么樣,很囧吧,明明在頭文件里說"必須用freeifaddrs才能正確free..."
然后我看了一下getifaddrs的函數體
他在getifaddrs內部定義了一個結構
struct ifaddrs_storage
{
struct ifaddrs ifa;
union
{
/* Save space for the biggest of the four used sockaddr types and
avoid a lot of casts. */
struct sockaddr sa;
struct sockaddr_ll sl;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
} addr, netmask, broadaddr;
char name[IF_NAMESIZE + 1];
};
然后把獲取的各網卡信息一個個填充到此結構的struct ifaddrs ifa中,ifa的next值手動設置為下一個struct ifaddrs_storage中的ifa的地址...
這酒是所謂的"偽鏈表"吧?
這就是我無法正確free掉它的原因?
我究竟要怎么把它free掉?freeifaddrs一運行就運行時錯誤
LINUX取得本機IP的簡單C程序
注意這里用了兩個struct ifaddrs
//代碼根據UNP和man手冊編寫
//適用於LINUX/BSD(FreeBSD, MacOS X)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
int main(void)
{
struct ifaddrs *ifc, *ifc1;
char ip[64];
char nm[64];
if (0 != getifaddrs(&ifc)) return(-1);
ifc1 = ifc;
printf("Iface\tIP address\tNetmask\n");
for(; NULL != ifc; ifc = (*ifc).ifa_next) {
printf("%s", (*ifc).ifa_name);
if (NULL != (*ifc).ifa_addr) {
inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_addr))->sin_addr), ip, 64);
printf("\t%s", ip);
} else {
printf("\t\t");
}
if (NULL != (*ifc).ifa_netmask) {
inet_ntop(AF_INET, &(((struct sockaddr_in*)((*ifc).ifa_netmask))->sin_addr), nm, 64);
printf("\t%s", nm);
} else {
printf("\t\t");
}
printf("\n");
}
freeifaddrs(ifc1);
return(0);
}
--------------------------------------------------------------------------------
============原來的============
struct ifaddrs *ifap, *ifaphead, *ifaTmp;
getifaddrs(&ifap);
ifaphead = ifap;
while((ifapTmp = ifap) != NULL)
{
//實際任務代碼
ifap = ifapTmp->ifa_next;
}
freeifaddrs(ifaphead);
=========修改后========
struct ifaddrs *ifap, *ifaphead;
getifaddrs(&ifap);
ifaphead = ifap;
while(ifap != NULL)
{
//實際任務代碼
ifap = ifa_next;
}
freeifaddrs(ifaphead);
==================
僅僅是用了一個ifapTmp來代替ifap做事,區別僅此而已(而且我也忘了一開始為什么要用ifapTmp....)
但是最后都是用了freeifaddrs(ifaphead)啊,並沒有傳錯指針啊????
中間的代碼並沒有對這段數據做任何修改啊.....
請指教一下,這唯一的區別為什么會造成我原先的代碼freeifaddrs失敗?謝謝!
ifaphead = ifap; //這里用ifaphead保存ifap指針地址
while((ifapTmp = ifap) != NULL)
{
ifap = ifapTmp->ifa_next; //這里修改了ifap的地址
}
freeifaddrs(ifaphead); //由於ifap的地址修改,所以ifaphead已經是無效指針。

使用ioctl的SIOCGIFCONF可以讀取所有網卡信息。ioctl調用后返回指向ifconf的結構鏈表,其中包含了指向ifreq的結構指針。ifconf及ifreq定義在net/if.h中。
《UNIX網絡編程》中提供了get_ifi_info函數的實現方法,使用這種方式來獲取網絡信息。在LINUX下,這種方式不能獲得IPV6的網卡信息。《UNIX網絡編程》中有如下描述:
在支持IPV6的系統中,沒有關於對SIOCGIFCONF請求是否返回IPV6地址的標准。我們給支持IPV6的新系統增加了一個case語句, 這是為了預防萬一。問題在於ifreq中的聯合把返回的地址定義成一個通用的16字節套接口地址結構,適合16字節的IPV4 socket_in結構,但對於24字節的IPV6 socket_in6結構太小了。如果返回IPV6地址,將可能破環現有的在每個ifreq結構中采用固定大小的套接口地址結構的代碼。
經測試,在fedor6-2.6.18kernel中無法返回ipv6地址,事實上,返回的地址簇總是AF_INET,而並非AF_INET6。
這種方法的實現代碼如下:
net_if.h
#ifndef __NET_INF_H #define __NET_INF_H #include #include #include #include #include #include #include #include #include #include #include #define IFI_NAME 16 #define IFI_HADDR 8 typedef struct ifi_info { char ifi_name[IFI_NAME]; u_char ifi_haddr[IFI_HADDR]; u_short ifi_hlen; short ifi_flags; short ifi_myflags; struct sockaddr *ifi_addr; struct sockaddr *ifi_brdaddr; struct sockaddr *ifi_dstaddr; struct ifi_info *ifi_next; }ifi_info; #define IFI_ALIAS 1 struct ifi_info *get_ifi_info(int, int); void free_ifi_info(struct ifi_info *); #endif
net_if.c
#include "net_if.h" ifi_info *get_ifi_info(int family, int doaliases) { ifi_info *ifi, *ifihead, **ifipnext; int sockfd, len, lastlen, flags, myflags; char *ptr, *buf, lastname[IFNAMSIZ], *cptr; struct ifconf ifc; struct ifreq *ifr, ifrcopy; struct sockaddr_in *sinptr; if ((sockfd=socket(family, SOCK_DGRAM, 0))<0) { printf("socket error.\n"); exit(1); } lastlen = 0; len = 10*sizeof(struct ifreq); while (1) { buf = (char*)malloc(len); ifc.ifc_len = len; ifc.ifc_buf = buf; if (ioctl(sockfd, SIOCGIFCONF, &ifc)<0) { if (errno!=EINVAL||lastlen!=0) { printf("ioctl error.\n"); } } else { if (ifc.ifc_len == lastlen) break; lastlen = ifc.ifc_len; } len += 10*sizeof(struct ifreq); free(buf); } ifihead = NULL; ifipnext = &ifihead; lastname[0] = 0; for (ptr = buf; ptrifr->ifr_addr.sa_len?sizeof(struct sockaddr):ifr->ifr_addr.sa_len; #else switch (ifr->ifr_addr.sa_family) { #ifdef IPV6 case AF_INET6: len = sizeof(struct sockaddr_in6); break; #endif case AF_INET: default: len = sizeof(struct sockaddr); break; } #endif ptr += sizeof(ifr->ifr_name) + len; if (ifr->ifr_addr.sa_family != family) continue; myflags = 0; if ((cptr=strchr(ifr->ifr_name, ':'))!=NULL) *cptr = 0; if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ)==0) { if (doaliases == 0) continue; myflags = IFI_ALIAS; } memcpy(lastname, ifr->ifr_name, IFNAMSIZ); ifrcopy = *ifr; ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy); flags = ifrcopy.ifr_flags; if ((flags&IFF_UP)==0) continue; /* if ((flags&IFF_BROADCAST)==0) continue; */ ifi = calloc(1, sizeof(struct ifi_info)); *ifipnext = ifi; ifipnext = &ifi->ifi_next; ifi->ifi_flags = flags; ifi->ifi_myflags = myflags; memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME); ifi->ifi_name[IFI_NAME-1] = '\0'; switch (ifr->ifr_addr.sa_family) { case AF_INET: sinptr = (struct sockaddr_in *)&ifr->ifr_addr; if (ifi->ifi_addr == NULL) { ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in)); memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); #ifdef SIOCGIFBRDADDR if (flags & IFF_BROADCAST) { ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy); sinptr = (struct sockaddr_in *)&ifrcopy.ifr_broadaddr; ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in)); memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in)); } #endif #ifdef SIOCGIFDSTADDR if (flags & IFF_POINTOPOINT) { ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy); sinptr = (struct sockaddr_in*)&ifrcopy.ifr_dstaddr; ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in)); memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in)); } #endif } break; default: break; } } free(buf); return(ifihead); } void free_ifi_info(ifi_info *ifihead) { ifi_info *ifi, *ifinext; for (ifi=ifihead; ifi!=NULL; ifi=ifinext) { if (ifi->ifi_addr!=NULL) free(ifi->ifi_addr); if (ifi->ifi_brdaddr!=NULL) free(ifi->ifi_brdaddr); if (ifi->ifi_dstaddr!=NULL) free(ifi->ifi_dstaddr); ifinext = ifi->ifi_next; free(ifi); } } char *sock_ntop(const struct sockaddr *sa, socklen_t salen) { char portstr[7]; static char str[128]; switch (sa->sa_family) { case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)sa; if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))==NULL) return NULL; if (ntohs(sin->sin_port)!=0) { snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin_port)); strcat(str, portstr); } return str; } break; case AF_INET6: { struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; if (inet_ntop(AF_INET6, &sin->sin6_addr, str, sizeof(str))==NULL) return NULL; if (ntohs(sin->sin6_port)!=0) { snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin6_port)); strcat(str, portstr); } return str; } break; default: return NULL; break; } } int main(int argc, char *argv[]) { ifi_info *ifi, *ifihead; struct sockaddr *sa; u_char *ptr; int i, family, doaliases; if (argc!=3) { printf("usage: ./prifinfo "); exit(1); } if (strcmp(argv[1], "inet4") == 0) family = AF_INET; #ifdef IPV6 else if (strcmp(argv[1], "inet6") == 0) family =AF_INET6; #endif else { printf("invalid "); exit(1); } doaliases = atoi(argv[2]); for(ifihead = ifi = get_ifi_info(family, doaliases); ifi!=NULL;ifi=ifi->ifi_next) { printf("%s:<", ifi->ifi_name); if (ifi->ifi_flags&IFF_UP) printf("UP"); if (ifi->ifi_flags&IFF_BROADCAST) printf("BCAST"); if (ifi->ifi_flags&IFF_MULTICAST) printf("MCAST"); if (ifi->ifi_flags&IFF_LOOPBACK) printf("LOOP"); if (ifi->ifi_flags&IFF_POINTOPOINT) printf("P2P"); printf(">\n"); if ((i=ifi->ifi_hlen)>0) { ptr = ifi->ifi_haddr; do { printf("%s%x", (i==ifi->ifi_hlen)?" ":":", *ptr++); }while(--i>0); printf("\n"); } if ((sa=ifi->ifi_addr)!=NULL) printf(" IP addr: %s\n", sock_ntop(sa, sizeof(*sa))); if ((sa=ifi->ifi_brdaddr)!=NULL) printf(" broadcast addr: %s\n", sock_ntop(sa, sizeof(*sa))); if ((sa=ifi->ifi_dstaddr)!=NULL) printf(" destnation addr: %s\n", sock_ntop(sa, sizeof(*sa))); } free_ifi_info(ifihead); exit(0); }
使用gcc net_if.c -o net_if -DIPV6編譯,在IPV4模式下運行輸出為:
[root@localhost net_if]./net_if inet4 1
lo:
IP addr: 127.0.0.1
eth1:
IP addr: 192.168.1.2
broadcast addr: 192.168.1.255
eth0:
IP addr: 192.168.125.99
broadcast addr: 192.168.125.255
執行./net_if inet6 1在輸出為空。
第二種方式是使用getifaddrs函數獲取,需要包含ifaddrs.h頭文件,這種方式可以獲得IPV6地址,改寫的《UNIX網絡編程》中的get_ifi_info函數如下所示:
znet.h
#ifndef __ZNET_H__ #define __ZNET_H__ #include #include #include #include #include #include #include #include #define IFI_NAME 16 /* same as IFNAMSIZ in */ #define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ struct ifi_info { char ifi_name[IFI_NAME]; /* interface name, null-terminated */ short ifi_index; /* interface index */ short ifi_flags; /* IFF_xxx constants from */ struct sockaddr *ifi_addr; /* primary address */ struct sockaddr *ifi_brdaddr;/* broadcast address */ struct ifi_info *ifi_next; /* next of these structures */ }; struct ifi_info* get_ifi_info(int, int); void free_ifi_info(struct ifi_info *); #endif
znet.c
#include "znet.h" struct ifi_info* get_ifi_info(int family, int doaliases) { struct ifi_info *ifi, *ifihead, **ifipnext,*p; struct sockaddr_in *sinptr; struct sockaddr_in6 *sin6ptr; struct ifaddrs *ifas; // char addr[128]; int sockfd; ifihead = NULL; ifipnext = &ifihead; if(getifaddrs(&ifas)!=0) return ; for(;ifas!=NULL;ifas=(*ifas).ifa_next) { if (((*ifas).ifa_addr)->sa_family != family) continue; // ignore if not desired address family /* printf("%s %d\n",(*ifas).ifa_name,((*ifas).ifa_addr)->sa_family); if(((*ifas).ifa_addr)->sa_family!=AF_INET6) inet_ntop(AF_INET,&(((struct sockaddr_in *)((*ifas).ifa_addr))->sin_addr),addr,sizeof(addr)); else inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)((*ifas).ifa_addr))->sin6_addr),addr,sizeof(addr)); printf("%s\t",addr); printf("\n"); */ ifi = (struct ifi_info*)calloc(1,sizeof(struct ifi_info)); *ifipnext = ifi; ifipnext = &ifi->ifi_next; ifi->ifi_flags = (*ifas).ifa_flags; memcpy(ifi->ifi_name, (*ifas).ifa_name, IFI_NAME); ifi->ifi_name[IFI_NAME-1] = '\0'; switch (((*ifas).ifa_addr)->sa_family) { case AF_INET: sinptr = (struct sockaddr_in *) (*ifas).ifa_addr; ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in)); #ifdef SIOCGIFBRDADDR if (ifi->ifi_flags & IFF_BROADCAST) { sinptr = (struct sockaddr_in *) (*ifas).ifa_broadaddr; ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in)); memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in)); } #endif break; case AF_INET6: sin6ptr = (struct sockaddr_in6 *) (*ifas).ifa_addr; ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6)); memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6)); break; default: break; } } freeifaddrs(ifas); return(ifihead); } int main(int argc, char *argv[]) { int family; if (argc!=2) { printf("usage: ./znet \n"); exit(1); } if (strcmp(argv[1], "inet4") == 0) family = AF_INET; else if (strcmp(argv[1], "inet6") == 0) family =AF_INET6; else { printf("invalid \n"); exit(1); } char addr[128]; struct ifi_info *ifi, *ifihead; printf("name\tflag\tIP\t\tbroadcastaddr\n"); for (ifihead = ifi = get_ifi_info(family,1); ifi != NULL; ifi = ifi->ifi_next) { printf("%s\t",ifi->ifi_name); printf("%d\t",ifi->ifi_flags); if((ifi->ifi_addr)->sa_family!=AF_INET6) inet_ntop(AF_INET,&(((struct sockaddr_in *)(ifi->ifi_addr))->sin_addr),addr,sizeof(addr)); else inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)(ifi->ifi_addr))->sin6_addr),addr,sizeof(addr)); printf("%s\t",addr); #ifdef SIOCGIFBRDADDR if ((ifi->ifi_flags & IFF_BROADCAST) && (ifi->ifi_addr)->sa_family!=AF_INET6) { inet_ntop(AF_INET,&(((struct sockaddr_in *) (ifi->ifi_brdaddr))->sin_addr),addr,sizeof(addr)); printf("%s\t",addr); } #endif printf("\n+++++++++++++++++++++++++++++++++++++++++++\n"); } return 0; }
這段代碼輸出如下:
[root@localhost net_if]./znet inet4
name flag IP broadcastaddr
lo 73 127.0.0.1
++++++++++++++++++++++++++++++
eth1 4099 192.168.1.2 192.168.1.255
++++++++++++++++++++++++++++++
eth0 4163 192.168.125.99 192.168.125.255
++++++++++++++++++++++++++++++
[root@localhost net_if]./znet inet6
name flag IP broadcastaddr
lo 73 ::1
++++++++++++++++++++++++++++++
eth1 4163 2001:250:1800:1::1
++++++++++++++++++++++++++++++
eth0 4163 2001:250:1888:1::1