使用者工具

網站工具


program:c:linux編程獲取本機ip地址



linux編程獲取本機ip地址

使用getifaddrs()和struct ifaddrs的使用,獲取本機IP

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)

函數 getifaddrs(int getifaddrs (struct ifaddrs **__ifap) 獲取本地網絡接口信息,將之存儲於鏈表中,鏈表頭結點指針存儲於__ifap中帶回,函數執行成功返回0,失敗返回-1,且為errno賦值。

很顯然,函數getifaddrs用於獲取本機接口信息,比如最典型的獲取本機IP地址。

linux編程獲取本機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;  
}  

Linux下如何獲取網卡信息

有時候,寫程序的時候需要獲取計算機的網絡信息,比如IP地址、電腦名稱、DNS等信息。IP地址和電腦名稱是比較容易獲取到的,而要想獲取地址掩碼、DNS、網關等信息就有些麻煩了。

在Windows下我們一般都是通過從註冊表讀取這些信息。在Linux怎麼做呢?其實,Linux下更加容易一些。因為我們可以拿現成的程序看它的源代碼。通過閱讀其源代碼找到解決該問題的方法。那麼,看哪個程序的源代碼呢?如果你使用過Linux,並且比較熟悉的話就肯定知道一個命令ifconfig。這個命令和Windows下的ipconfig差不多,都可以輸出網卡的信息,其中就包含DNS、掩碼等信息。所以,我們可以通過看它的源代碼來找到解決該問題的方法。

獲取系統中的網卡數量並沒有那個系統調用提供網卡數量的獲取。但是,我們可以通過強大的proc文件系統獲取網卡數量的信息。實際上,ifconfig也是這樣做的,請看示例代碼如下:

#include <stdio.h>
#include <string.h>
#include <errno.h>
 
int GetNetCardCount()
{
    int nCount = 0;
    FILE* f = fopen("/proc/net/dev", "r");
    if (!f)
    {
        fprintf(stderr, "Open /proc/net/dev failed!errno:%d\n", errno);
        return nCount;
    }
 
    char szLine[512];
 
    fgets(szLine, sizeof(szLine), f);    /* eat line */
    fgets(szLine, sizeof(szLine), f);
 
    while(fgets(szLine, sizeof(szLine), f))
    {
        char szName[128] = {0};
        sscanf(szLine, "%s", szName);
        int nLen = strlen(szName);
        if (nLen <= 0)continue;
        if (szName[nLen - 1] == ':') szName[nLen - 1] = 0;
        if (strcmp(szName, "lo") == 0)continue;
        nCount++;
    }
 
    fclose(f);
    f = NULL;
    return nCount;
}
 
int main(int argc, char* argv[])
{
    printf("NetCardCount: %d\n", GetNetCardCount());
    return 0;
}

獲取IP、掩碼、MAC及網關

獲取IP、掩碼、MAC和廣播地址是比較容易的,只需要調用對應的IOCTL即可。只是大家對Linux下的IOCTL可能不太熟悉。卻看示例代碼:

void DispNetInfo(const char* szDevName)
{
    int s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0)
    {
        fprintf(stderr, "Create socket failed!errno=%d", errno);
        return;
    }
 
    struct ifreq ifr;
    unsigned char mac[6];
    unsigned long nIP, nNetmask, nBroadIP;
 
    printf("%s:\n", szDevName);
 
    strcpy(ifr.ifr_name, szDevName);
    if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
    {
        return;
    }
    memcpy(mac, ifr.ifr_hwaddr.sa_data, sizeof(mac));
    printf("\tMAC: %02x-%02x-%02x-%02x-%02x-%02x\n",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 
    strcpy(ifr.ifr_name, szDevName);
    if (ioctl(s, SIOCGIFADDR, &ifr) < 0)
    {
        nIP = 0;
    }
    else
    {
        nIP = *(unsigned long*)&ifr.ifr_broadaddr.sa_data[2];
    }
    printf("\tIP: %s\n", inet_ntoa(*(in_addr*)&nIP));
 
    strcpy(ifr.ifr_name, szDevName);
    if (ioctl(s, SIOCGIFBRDADDR, &ifr) < 0)
    {
        nBroadIP = 0;
    }
    else
    {
        nBroadIP = *(unsigned long*)&ifr.ifr_broadaddr.sa_data[2];
    }
    printf("\tBroadIP: %s\n", inet_ntoa(*(in_addr*)&nBroadIP));
 
    strcpy(ifr.ifr_name, szDevName);
    if (ioctl(s, SIOCGIFNETMASK, &ifr) < 0)
    {
        nNetmask = 0;
    }
    else
    {
        nNetmask = *(unsigned long*)&ifr.ifr_netmask.sa_data[2];
    }
    printf("\tNetmask: %s\n", inet_ntoa(*(in_addr*)&nNetmask));
    close(s);
}
那麼如何獲取網關地址呢?更加容易,但是,好像很少有人知道。反正我在網上沒有找到有人知道。最後看了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下C語言配置網絡與獲取網絡配置信息的方法

2009-01-19 21:06
下的網絡配置包含三個要素,分別是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下的getifaddrs()函數的內存釋放問題 
在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及getifaddrs讀取IPv4,IPv6網卡信息

2011-05-08 21:18:30


使用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
program/c/linux編程獲取本機ip地址.txt · 上一次變更: 2013/01/18 07:17 (外部編輯)