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,失败返回-1ioctl函数的参数只有3个,但却是Unix中少有的几个“家族类”复杂函数,这里摘录一段《Unix网络编程》一书中对ioctl函数的描述:在传统上ioctl函数是用于那些普遍使用、但不适合归入其他类别的任何特殊的系统接口……网络程序(一般是服务器程序)中ioctl常用于在程序启动时获得主机上所有接口的信息:接口的地址、接口是否支持广播、是否支持多播,等等。ioctl函数的第一个参数fd,可以表示一个打开的文件(文件句柄)或网络套接字,第二个和第三个参数体现了函数的家族特色,参数二request根据函数功能分类定义了多组宏,而参数三总是一个指针,指针的类型依赖于参数二request。因为ioctl的种类实在太多,这里只列出和本文相关的几个参数定义:类别 Request 说明 数据类型套接口 SIOCATMARKSIOCSPGRPSIOCGPGRP 是否位于带外标记设置套接口的进程ID 或进程组ID获取套接口的进程ID 或进程组ID int
int
int
文件 FIONBINFIOASYNCFIONREADFIOSETOWNFIOGETOWN 设置/ 清除非阻塞I/O 标志设置/ 清除信号驱动异步I/O 标志获取接收缓存区中的字节数设置文件的进程ID 或进程组ID获取文件的进程ID 或进程组ID int
int
int
int
int
接口 SIOCGIFCONFSIOCSIFADDRSIOCGIFADDRSIOCSIFFLAGSSIOCGIFFLAGSSIOCSIFDSTADDRSIOCGIFDSTADDRSIOCGIFBRDADDRSIOCSIFBRDADDRSIOCGIFNETMASKSIOCSIFNETMASKSIOCGIFMETRICSIOCSIFMETRICSIOCGIFMTUSIOCxxx 获取所有接口的清单设置接口地址获取接口地址设置接口标志获取接口标志设置点到点地址获取点到点地址获取广播地址设置广播地址获取子网掩码设置子网掩码获取接口的测度设置接口的测度获取接口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 SIOCSARPSIOCGARPSIOCDARP 创建/ 修改ARP 表项获取ARP 表项删除ARP 表项 struct arpreq
struct arpreq
struct arpreq
路由 SIOCADDRTSIOCDELRT 增加路径删除路径 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 6union
{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:20120727LINUX下获取IP地址和MAC地址.程序相关结构体在程序后面。打印网卡的ip地址 子网掩码 广播地址 mac地址环境:本程序测试没有错误
[root@bogon temp]# uname -aLinux 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("");}#endifif (!(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]);}#endifelse
{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,失败返回-1char *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 6union{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**/