1. 程式人生 > >Linux 獲取本機IP、MAC地址用法大全

Linux 獲取本機IP、MAC地址用法大全

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))獲取本地網路介面資訊,將之儲存於連結串列中,連結串列頭結點指標儲存於__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;
}

執行:
[[email protected] 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;
}

執行 :
[[email protected] 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等資訊函式

 

在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語言配置網路與獲取網路配置資訊的方法

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地址的方法相同,這裡就不多說了。