获得Linux系统中的IP、MAC地址,广播地址,子网掩码等信息详解

http://blog.csdn.net/tianmohust/article/details/6533301
转:http://z515256164.blog.163.com/blog/static/324430292012324111040897/
获得Unix/Linux系统中的IP、MAC地址等信息 
实际环境和特殊需求往往会将简单问题复杂化,比如计算机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的种类实在太多,这里只列出和本文相关的几个参数定义: 
类别  Request  说明  数据类型 
口  SIOCATMARK  
SIOCSPGRP  
SIOCGPGRP  是否位于带外标记  
设置套接口的进程ID 或进程组ID  
获取套接口的进程ID 或进程组ID  int  
int  
int 
件  FIONBIN  
FIOASYNC  
FIONREAD  
FIOSETOWN  
FIOGETOWN  设置/ 清除非阻塞I/O 标志  
设置/ 清除信号驱动异步I/O 标志  
获取接收缓存区中的字节数  
设置文件的进程ID 或进程组ID  
获取文件的进程ID 或进程组ID  int  
int  
int  
int  
int 
口  SIOCGIFCONF  
SIOCSIFADDR  
SIOCGIFADDR  
SIOCSIFFLAGS  
SIOCGIFFLAGS  
SIOCSIFDSTADDR  
SIOCGIFDSTADDR  
SIOCGIFBRDADDR  
SIOCSIFBRDADDR  
SIOCGIFNETMASK  
SIOCSIFNETMASK  
SIOCGIFMETRIC  
SIOCSIFMETRIC  
SIOCGIFMTU  
SIOCxxx  获取所有接口的清单  
设置接口地址  
获取接口地址  
设置接口标志  
获取接口标志  
设置点到点地址  
获取点到点地址  
获取广播地址  
设置广播地址  
获取子网掩码  
设置子网掩码  
获取接口的测度  
设置接口的测度  
获取接口MTU  
(还有很多取决于系统的实现)  struct ifconf  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq  
struct ifreq 
ARP  SIOCSARP  
SIOCGARP  
SIOCDARP  创建/ 修改ARP 表项  
获取ARP 表项  
删除ARP 表项  struct arpreq  
struct arpreq  
struct arpreq 
由  SIOCADDRT  
SIOCDELRT  增加路径  
删除路径  struct rtentry  
struct rtentry 
流  I_xxx     
上表中列出了两个相关的结构体:struct ifconf 和 struct ifreq,要了解ioctl函数的具体运用,首先要了解这两个结构: 
/* 
 * Interface request structure used for socket 
 * ioctl's.  All interface ioctl's must have parameter 
 * definitions which begin with ifr_name.  The 
 * remainder may be interface specific. 
 */ 
struct ifreq  
{ 
#define IFHWADDRLEN  6 
  union 
  { 
    char  ifrn_name[IFNAMSIZ];    /* if name, e.g. "en0" */ 
  } ifr_ifrn; 
   
  union { 
    struct  sockaddr ifru_addr; 
    struct  sockaddr ifru_dstaddr; 
    struct  sockaddr ifru_broadaddr; 
    struct  sockaddr ifru_netmask; 
    struct  sockaddr ifru_hwaddr; 
    short  ifru_flags; 
    int  ifru_ivalue; 
    int  ifru_mtu; 
    struct  ifmap ifru_map; 
    char  ifru_slave[IFNAMSIZ];  /* Just fits the size */ 
    char  ifru_newname[IFNAMSIZ]; 
    void __user *  ifru_data; 
    struct  if_settings ifru_settings; 
  } ifr_ifru; 
}; 
#define ifr_name  ifr_ifrn.ifrn_name  /* interface name   */ 
#define ifr_hwaddr  ifr_ifru.ifru_hwaddr  /* MAC address     */ 
#define  ifr_addr  ifr_ifru.ifru_addr  /* address    */ 
#define  ifr_dstaddr  ifr_ifru.ifru_dstaddr  /* other end of p-p lnk  */ 
#define  ifr_broadaddr  ifr_ifru.ifru_broadaddr  /* broadcast address  */ 
#define  ifr_netmask  ifr_ifru.ifru_netmask  /* interface net mask  */ 
#define  ifr_flags  ifr_ifru.ifru_flags  /* flags    */ 
#define  ifr_metric  ifr_ifru.ifru_ivalue  /* metric    */ 
#define  ifr_mtu    ifr_ifru.ifru_mtu  /* mtu      */ 
#define ifr_map    ifr_ifru.ifru_map  /* device map    */ 
#define ifr_slave  ifr_ifru.ifru_slave  /* slave device    */ 
#define  ifr_data  ifr_ifru.ifru_data  /* for use by interface  */ 
#define ifr_ifindex  ifr_ifru.ifru_ivalue  /* interface index  */ 
#define ifr_bandwidth  ifr_ifru.ifru_ivalue    /* link bandwidth  */ 
#define ifr_qlen  ifr_ifru.ifru_ivalue  /* Queue length   */ 
#define ifr_newname  ifr_ifru.ifru_newname  /* New name    */ 
#define ifr_settings  ifr_ifru.ifru_settings  /* Device/proto settings*/ 
/* 
 * Structure used in SIOCGIFCONF request. 
 * Used to retrieve interface configuration 
 * for machine (useful for programs which 
 * must know all networks accessible). 
 */ 
struct ifconf  
{ 
  int  ifc_len;      /* size of buffer  */ 
  union  
  { 
    char __user *ifcu_buf; 
    struct ifreq __user *ifcu_req; 
  } ifc_ifcu; 
}; 
#define  ifc_buf  ifc_ifcu.ifcu_buf    /* buffer address  */ 
#define  ifc_req  ifc_ifcu.ifcu_req    /* array of structures  */ 
#endif /* _LINUX_IF_H */ 
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请求来实现: 
1.  struct ifconf ifc;  /* ifconf结构 */ 
2.  struct ifreq ifrs[16];  /* ifreq结构数组(这里估计了接口的最大数量16) */ 
3.    
4.  /* 初始化ifconf结构 */ 
5.  ifc.ifc_len = sizeof(ifrs); 
6.  ifc.ifc_buf = (caddr_t) ifrs; 
7.    
8.  /* 获得接口列表 */ 
9.  ioctl(fd, SIOCGIFCONF, (char *) &ifc); 
获得了接口列表,就可以通过struct ifconf结构中*ifcu_req的指针得到struct ifreq结构数组的地址,通过遍历获得每隔接口的详细地址信息: 
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]); 

参考程序:

/*
mode time:20120727
LINUX下获取IP地址和MAC地址.程序相关结构体在程序后面。
 打印网卡的ip地址 子网掩码  广播地址 mac地址
环境:本程序测试没有错误
  [root@bogon temp]# uname -a
  Linux bogon 2.6.31.5-127.fc12.i686.PAE #1 SMP Sat Nov 7 21:25:57 EST 2009 i686 i686 i386 GNU/Linux
*/
#include <stdio.h> 
#include <sys/types.h> 
#include <sys/param.h> 
#include <sys/ioctl.h> 
#include <sys/socket.h> 
#include <net/if.h> 
#include <netinet/in.h> 
#include <net/if_arp.h> 
#ifdef SOLARIS 
#include <sys/sockio.h> 
#endif 
#define MAXINTERFACES 16 /* 最大接口数 */
int  main(int argc, char **argv)
{ 
  register int fd, intrface, retn = 0; 
  struct ifreq buf[MAXINTERFACES]; /* ifreq结构数组 */
  struct arpreq arp; 
  struct ifconf ifc; 
  if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0) 
  { 
    ifc.ifc_len = sizeof buf; 
    ifc.ifc_buf = (caddr_t) buf; 
    if (!ioctl (fd, SIOCGIFCONF, (char *) &ifc)) 
    { 
      //获取接口数量信息
      intrface = ifc.ifc_len / sizeof (struct ifreq); 
      printf("interface num is intrface=%d\n",intrface); 
      puts("");
      //根据借口信息循环获取设备IP和MAC地址
      while ( (intrface--) > 0) 
      { 
        //获取设备名称
        printf ("net device %s\n", buf[intrface].ifr_name); 
        //判断网卡类型 
        if (!(ioctl (fd, SIOCGIFFLAGS, (char *) &buf[intrface]))) 
        { 
          if (buf[intrface].ifr_flags & IFF_PROMISC) 
          { 
            puts ("the interface is PROMISC"); 
            retn++; 
          } 
        } 
        else 
        { 
          char str[256]; 
          sprintf (str, "cpm: ioctl device %s", buf[intrface].ifr_name); 
          perror (str); 
        } 
        //判断网卡状态 
        if (buf[intrface].ifr_flags & IFF_UP) 
        { 
          puts("the interface status is UP"); 
        } 
        else 
        { 
          puts("the interface status is DOWN"); 
        } 
        //获取当前网卡的IP地址 
        if (!(ioctl (fd, SIOCGIFADDR, (char *) &buf[intrface]))) 
        { 
          printf("IP address is:"); 
          puts((char *)inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr)); 
          //printf("\n%d\n"buf[intrface].ifr_addr))->sin_addr.s_addr); 
          //puts (buf[intrface].ifr_addr.sa_data); 
        } 
        else 
        { 
          char str[256]; 
          sprintf (str, "cpm: ioctl device %s", buf[intrface].ifr_name); 
          perror (str); 
        } 
        /* this section can't get Hardware Address,I don't know whether the reason is module driver*/ 
    #ifdef SOLARIS 
        //获取MAC地址
        arp.arp_pa.sa_family = AF_INET; 
        arp.arp_ha.sa_family = AF_INET; 
        ((struct sockaddr_in*)&arp.arp_pa)->sin_addr.s_addr=((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr.s_addr; 
        if (!(ioctl (fd, SIOCGARP, (char *) &arp))) 
        { 
          printf("HW address is:"); 
          //以十六进制显示MAC地址
          printf("%02x:%02x:%02x:%02x:%02x:%02x\n", 
              (unsigned char)arp.arp_ha.sa_data[0], 
              (unsigned char)arp.arp_ha.sa_data[1], 
              (unsigned char)arp.arp_ha.sa_data[2], 
              (unsigned char)arp.arp_ha.sa_data[3], 
              (unsigned char)arp.arp_ha.sa_data[4], 
              (unsigned char)arp.arp_ha.sa_data[5]); 
          puts(""); 
          puts(""); 
        } 
#else 
#if 0 
        /*Get HW ADDRESS of the net card */ 
        if (!(ioctl (fd, SIOCGENADDR, (char *) &buf[intrface]))) 
        { 
          printf("HW address is:"); 
          printf("%02x:%02x:%02x:%02x:%02x:%02x\n", 
              (unsigned char)buf[intrface].ifr_enaddr[0], 
              (unsigned char)buf[intrface].ifr_enaddr[1], 
              (unsigned char)buf[intrface].ifr_enaddr[2], 
              (unsigned char)buf[intrface].ifr_enaddr[3], 
              (unsigned char)buf[intrface].ifr_enaddr[4], 
              (unsigned char)buf[intrface].ifr_enaddr[5]); 
          puts(""); 
        } 
#endif 
        if (!(ioctl (fd, SIOCGIFHWADDR, (char *) &buf[intrface]))) 
        { 
          printf("HW address is:"); 
          printf("%02x:%02x:%02x:%02x:%02x:%02x\n", 
              (unsigned char)buf[intrface].ifr_hwaddr.sa_data[0], 
              (unsigned char)buf[intrface].ifr_hwaddr.sa_data[1], 
              (unsigned char)buf[intrface].ifr_hwaddr.sa_data[2], 
              (unsigned char)buf[intrface].ifr_hwaddr.sa_data[3], 
              (unsigned char)buf[intrface].ifr_hwaddr.sa_data[4], 
              (unsigned char)buf[intrface].ifr_hwaddr.sa_data[5]); 
        } 
#endif 
        else 
        { 
          char str[256]; 
          sprintf (str, "cpm: ioctl device %s", buf[intrface].ifr_name); 
          perror (str); 
        } 
        //子网掩码
         if (!(ioctl(fd, SIOCGIFNETMASK, (char *) &buf[intrface])))
        {
              printf("MASK:%s",
                    (char*)inet_ntoa(((struct sockaddr_in*) (&buf[intrface].ifr_addr))->sin_addr));
              puts(""); 
        }
       else
        {
           char str[256];
            sprintf(str, "SIOCGIFADDR ioctl %s", buf[intrface].ifr_name);
            perror(str);
       }
       //广播地址
       if (! (ioctl(fd, SIOCGIFBRDADDR, (char *) &buf[intrface])))
        printf("Broadcast Address:%s\n",
          (char*)inet_ntoa(((struct sockaddr_in*) (&buf[intrface].ifr_addr))->sin_addr));
      puts(""); 
      puts(""); 
      } //while
    } else 
      perror ("cpm: ioctl"); 
  } else 
    perror ("cpm: socket"); 
  close (fd); 
  return retn; 
}
/*
#include <sys/ioctl.h>
int ioctl(int fd, int request, … * void *arg *);
返回:成功返回0,失败返回-1
char *inet_ntoa (struct in_addr);
  返回点分十进制的字符串在静态内存中的指针。
  所在头文件:<arpa/inet.h> 函数功能:将网络地址转换成“.”点隔的字符串格式。
  所需库: winsock.h
#include<linux.h>
struct ifconf 
{
  int  ifc_len;      /* size of buffer  *
  union 
  {
    char __user *ifcu_buf;
    struct ifreq __user *ifcu_req;
  } ifc_ifcu;
};
struct ifreq 
{
#define IFHWADDRLEN  6
  union
  {
    char  ifrn_name[IFNAMSIZ];    * if name, e.g. "en0" *
  } ifr_ifrn;
  
  union {
    struct  sockaddr ifru_addr;
    struct  sockaddr ifru_dstaddr;
    struct  sockaddr ifru_broadaddr;
    struct  sockaddr ifru_netmask;
    struct  sockaddr ifru_hwaddr;
    short  ifru_flags;
    int  ifru_ivalue;
    int  ifru_mtu;
    struct  ifmap ifru_map;
    char  ifru_slave[IFNAMSIZ];  /* Just fits the size *
    char  ifru_newname[IFNAMSIZ];
    void __user *  ifru_data;
    struct  if_settings ifru_settings;
  } ifr_ifru;
};
#define ifr_name  ifr_ifrn.ifrn_name  /* interface name   *
#define ifr_hwaddr  ifr_ifru.ifru_hwaddr  /* MAC address     *
#define  ifr_addr  ifr_ifru.ifru_addr  /* address    *
#define  ifr_dstaddr  ifr_ifru.ifru_dstaddr  /* other end of p-p lnk  *
#define  ifr_broadaddr  ifr_ifru.ifru_broadaddr  /* broadcast address  *
#define  ifr_netmask  ifr_ifru.ifru_netmask  /* interface net mask  *
#define  ifr_flags  ifr_ifru.ifru_flags  /* flags    *
#define  ifr_metric  ifr_ifru.ifru_ivalue  /* metric    *
#define  ifr_mtu    ifr_ifru.ifru_mtu  /* mtu      *
#define ifr_map    ifr_ifru.ifru_map  /* device map    
#define ifr_slave  ifr_ifru.ifru_slave  /* slave device    *
#define  ifr_data  ifr_ifru.ifru_data  /* for use by interface  *
#define ifr_ifindex  ifr_ifru.ifru_ivalue  /* interface index  
#define ifr_bandwidth  ifr_ifru.ifru_ivalue    /* link bandwidth  *
#define ifr_qlen  ifr_ifru.ifru_ivalue  /* Queue length   *
#define ifr_newname  ifr_ifru.ifru_newname  /* New name    *
#define ifr_settings  ifr_ifru.ifru_settings  /* Device/proto settings*
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值