获得Unix/Linux系统中的IP、MAC地址等信息
获得Unix/Linux系统中的IP、MAC地址等信息
作者:diaoyf | 文章来源:http://programmerdigest.cn
实际环境和特殊需求往往会将简单问题复杂化,比如计算机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的种类实在太多,这里只列出和本文相关的几个参数定义:
分类 | 参数二(宏) | 参数三 | 描述 |
接口 | SIOCGIFCONF | struct ifconf | 获得所有接口列表 |
SIOCGIFADDR | struct ifreq | 获得接口地址 | |
SIOCGIFFLAGS | struct ifreq | 获得接口标志 | |
SIOCGIFBRDADDR | struct ifreq | 获得广播地址 | |
SIOCGIFNETMASK | struct ifreq | 获得子网掩码 |
上表中列出了两个相关的结构体:struct ifconf 和 struct ifreq,要了解ioctl函数的具体运用,首先要了解这两个结构:
- /* net/if.h */
- struct ifconf
- {
- int ifc_len; /* Size of buffer. */
- union
- {
- __caddr_t ifcu_buf;
- struct ifreq *ifcu_req;
- } ifc_ifcu;
- };
- struct ifreq
- {
- # define IFHWADDRLEN 6
- # define IFNAMSIZ IF_NAMESIZE
- union
- {
- char ifrn_name[IFNAMSIZ]; /* Interface 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 int ifru_flags;
- int ifru_ivalue;
- int ifru_mtu;
- struct ifmap ifru_map;
- char ifru_slave[IFNAMSIZ]; /* Just fits the size */
- char ifru_newname[IFNAMSIZ];
- __caddr_t ifru_data;
- } ifr_ifru;
- };
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请求来实现:
- /* net/if.h */
- struct ifconf
- {
- int ifc_len; /* Size of buffer. */
- union
- {
- __caddr_t ifcu_buf;
- struct ifreq *ifcu_req;
- } ifc_ifcu;
- };
- struct ifreq
- {
- # define IFHWADDRLEN 6
- # define IFNAMSIZ IF_NAMESIZE
- union
- {
- char ifrn_name[IFNAMSIZ]; /* Interface 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 int ifru_flags;
- int ifru_ivalue;
- int ifru_mtu;
- struct ifmap ifru_map;
- char ifru_slave[IFNAMSIZ]; /* Just fits the size */
- char ifru_newname[IFNAMSIZ];
- __caddr_t ifru_data;
- } ifr_ifru;
- };
获得了接口列表,就可以通过struct ifconf结构中*ifcu_req的指针得到struct ifreq结构数组的地址,通过遍历获得每隔接口的详细地址信息:
- printf("接口名称:%s\n", ifrs[n].ifr_name); /* 接口名称 */
- /* 获得IP地址 */
- ioctl(fd, SIOCGIFADDR, (char *) &ifrs[n]);
- printf("IP地址:%s\n",
- (char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr));
- /* 获得子网掩码 */
- ioctl(fd, SIOCGIFNETMASK, (char *) &ifrs[n]);
- printf("子网掩码:%s\n",
- (char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr));
- /* 获得广播地址 */
- ioctl(fd, SIOCGIFBRDADDR, (char *) &ifrs[n]);
- printf("广播地址:%s\n",
- (char*)inet_ntoa(((struct sockaddr_in*) (&ifrs[n].ifr_addr))->sin_addr));
- /* 获得MAC地址 */
- ioctl(fd, SIOCGIFHWADDR, (char *) &ifrs[n]);
- printf("MAC地址:%02x:%02x:%02x:%02x:%02x:%02x\n",
- (unsigned char) ifrs[n].ifr_hwaddr.sa_data[0],
- (unsigned char) ifrs[n].ifr_hwaddr.sa_data[1],
- (unsigned char) ifrs[n].ifr_hwaddr.sa_data[2],
- (unsigned char) ifrs[n].ifr_hwaddr.sa_data[3],
- (unsigned char) ifrs[n].ifr_hwaddr.sa_data[4],
- (unsigned char) ifrs[n].ifr_hwaddr.sa_data[5]);
最后,给出一个参考程序代码。
ioctl函数没有纳入POXIS规范,各系统对ioctl的实现也不尽相同,下面的代码在我的Ubuntu10.04 linux上可执行通过,但在其他Unix系统上不一定能够通过编译,例如在Power AIX 5.3上需要将获得MAC地址的那段代码注释掉。
- #include <arpa/inet.h>
- #include <net/if.h>
- #include <net/if_arp.h>
- #include <netinet/in.h>
- #include <stdio.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #define MAXINTERFACES 16 /* 最大接口数 */
- int fd; /* 套接字 */
- int if_len; /* 接口数量 */
- struct ifreq buf[MAXINTERFACES]; /* ifreq结构数组 */
- struct ifconf ifc; /* ifconf结构 */
- int main(argc, argv)
- {
- /* 建立IPv4的UDP套接字fd */
- if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- {
- perror("socket(AF_INET, SOCK_DGRAM, 0)");
- return -1;
- }
- /* 初始化ifconf结构 */
- ifc.ifc_len = sizeof(buf);
- ifc.ifc_buf = (caddr_t) buf;
- /* 获得接口列表 */
- if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) == -1)
- {
- perror("SIOCGIFCONF ioctl");
- return -1;
- }
- if_len = ifc.ifc_len / sizeof(struct ifreq); /* 接口数量 */
- printf("接口数量:%d\n\n", if_len);
- while (if_len– > 0) /* 遍历每个接口 */
- {
- printf("接口:%s\n", buf[if_len].ifr_name); /* 接口名称 */
- /* 获得接口标志 */
- if (!(ioctl(fd, SIOCGIFFLAGS, (char *) &buf[if_len])))
- {
- /* 接口状态 */
- if (buf[if_len].ifr_flags & IFF_UP)
- {
- printf("接口状态: UP\n");
- }
- else
- {
- printf("接口状态: DOWN\n");
- }
- }
- else
- {
- char str[256];
- sprintf(str, "SIOCGIFFLAGS ioctl %s", buf[if_len].ifr_name);
- perror(str);
- }
- /* IP地址 */
- if (!(ioctl(fd, SIOCGIFADDR, (char *) &buf[if_len])))
- {
- printf("IP地址:%s\n",
- (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));
- }
- else
- {
- char str[256];
- sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);
- perror(str);
- }
- /* 子网掩码 */
- if (!(ioctl(fd, SIOCGIFNETMASK, (char *) &buf[if_len])))
- {
- printf("子网掩码:%s\n",
- (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));
- }
- else
- {
- char str[256];
- sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);
- perror(str);
- }
- /* 广播地址 */
- if (!(ioctl(fd, SIOCGIFBRDADDR, (char *) &buf[if_len])))
- {
- printf("广播地址:%s\n",
- (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));
- }
- else
- {
- char str[256];
- sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);
- perror(str);
- }
- /*MAC地址 */
- if (!(ioctl(fd, SIOCGIFHWADDR, (char *) &buf[if_len])))
- {
- printf("MAC地址:%02x:%02x:%02x:%02x:%02x:%02x\n\n",
- (unsigned char) buf[if_len].ifr_hwaddr.sa_data[0],
- (unsigned char) buf[if_len].ifr_hwaddr.sa_data[1],
- (unsigned char) buf[if_len].ifr_hwaddr.sa_data[2],
- (unsigned char) buf[if_len].ifr_hwaddr.sa_data[3],
- (unsigned char) buf[if_len].ifr_hwaddr.sa_data[4],
- (unsigned char) buf[if_len].ifr_hwaddr.sa_data[5]);
- }
- else
- {
- char str[256];
- sprintf(str, "SIOCGIFHWADDR ioctl %s", buf[if_len].ifr_name);
- perror(str);
- }
- }//–while end
- //关闭socket
- close(fd);
- return 0;
- }
在我的系统上,程序输出:
接口数量:4
接口:wlan0
接口状态: UP
IP地址:192.168.1.142
子网掩码:255.255.255.0
广播地址:192.168.1.255
MAC地址:00:14:a5:65:47:57接口:eth0:0
接口状态: UP
IP地址:192.168.4.113
子网掩码:255.255.255.0
广播地址:192.168.4.255
MAC地址:00:14:c2:e5:45:57接口:eth0
接口状态: UP
IP地址:192.168.4.111
子网掩码:255.255.255.0
广播地址:192.168.4.255
MAC地址:00:14:c2:e5:45:57接口:lo
接口状态: UP
IP地址:127.0.0.1
子网掩码:255.0.0.0
广播地址:0.0.0.0
MAC地址:00:00:00:00:00:00
从输出可以看出,系统有4个接口,”wlan0″表示第一块无线网卡接口,”eth0″(IP地址:192.168.4.111)表示第一块连线网卡接口(我们最长用的RJ45连接口网卡),”lo”是回路地址接口(我们常用的127.0.0.1)。
注意:”eth0:0″(IP地址:192.168.4.113)是有线网卡的别名******网卡绑定多个IP),这是为了测试这个参考程序特意在eth0上添加的一个IP地址。
参考资料:《Unix网络编程》第16章 ioctl操作
获得Unix/Linux系统中的IP、MAC地址等信息的更多相关文章
- Unix/Linux系统中僵尸进程是如何产生的?有什么危害?如何避免?
如题 Unix/Linux系统中僵尸进程是如何产生的?有什么危害?如何避免? 一个进程在调用exit命令结束自己的生命的时候,其实他并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结 ...
- 在Linux系统中修改IP地址
在Linux系统中,通过编辑网络配置文件,设置系统IP地址,当然要在root权限下执行,具体步骤如下: 1.切换路径到/etc/sysconfig/network-scripts [root@Comp ...
- java工具类,在Windows,Linux系统获取电脑的MAC地址、本地IP、电脑名
package com.cloudssaas.util; import java.io.BufferedReader; import java.io.IOException; import java. ...
- 在windows系统和linux系统中查询IP地址命令的不同
在linux和windows系统上查询IP地址的命令是不一样的. 在linux中的命令行模式下,输入ifconfig即可查询到IP.而在windows系统下要查询IP地址需要先打开do ...
- linux 系统获取网络ip, mask, gateway, dns信息小程序
net_util.c #define WIRED_DEV "eth0" #define WIRELESS_DEV ...
- 用户管理 之 Linux 系统中的超级权限的控制
在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者.普通用户无法执行的操作,root用户都能完成,所以也被称之为超级管理用户. 在系统中,每个文件.目录和进程,都归属于某一个用户 ...
- 在 Linux 系统中安装Load Generator ,并在windows 调用方法
在 Linux 系统中安装Load Generator ,并在windows 调用 由于公司需要测试系统的最大用户承受能力,所以需要学习使用loadrunner.在安装的时候碰到了不少问题,所以写下此 ...
- 在 Linux 系统中安装Load Generator ,并在windows 调用
原文地址:http://www.blogjava.net/qileilove/archive/2012/03/14/371861.html 由于公司需要测试系统的最大用户承受能力,所以需要学习使用lo ...
- Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名
Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名 转载自:http://b ...
随机推荐
- C# Form 非法字符
头部加上 EnableEventValidation="false" ValidateRequest="false"
- Win下循环进入目录启动执行某任务
cd tldlq for /D %%s in (*) do ( cd %%s start "" LoginGate.exe start "" GameGate. ...
- sencha 环境配置
sencha generate app MyApp MyApp 首先需要注意的是安装 sencha 并执行相关命令. 搞了老半天才知道 想要创建空项目首先 必须的在sdk 目录下才可以.好像据说第二次 ...
- 3-4 rpm包查询
概述:yum不能查询已经安装好的rpm包, 就算采用了yum来进行安装,查询方法还是依赖rpm包的查询, 因此rpm包的查询十分常用和重要 1.查询是否安装 <1>rpm -q 包名(不是 ...
- HDU 1231 最大连续子序列 --- 入门DP
HDU 1231 题目大意以及解题思路见: HDU 1003题解,此题和HDU 1003只是记录的信息不同,处理完全相同. /* HDU 1231 最大连续子序列 --- 入门DP */ #inclu ...
- HDU-3308 LCIS(区间合并)
题目大意:给一个整数序列,m次询问,每次询问某个区间中最长连续上升子序列的长度. 题目分析:线段树区间合并.维护以区间左端开头的.以区间右端点结尾的和区间最长的上升连续序列. 代码如下: # incl ...
- Python学习(1)
幂运算符比取反(一元运算符)的优先级要高. >>>-3**2 -9 >>>(-3)**2 9 >>>pow(2,3) 8 abs函数可以得到数的绝 ...
- poj2186 强连通
题意:有 n 头牛,以及一些喜欢关系,牛 A 喜欢牛 B,这种关系可以传递,问有多少头牛被牧场上所有牛喜欢. 首先强连通,因为在同一个强连通分量中牛是等价的,然后对于一个有向无环图看是否只有一个强连通 ...
- ARM1138@PWM例程分析
1. Buzzer例程控制原理 由ARM1138原理图可得:Buzzer使用CCP3(对应GPIO-G组0x40026000,4号引脚0x00000010)作为输入引脚: Buzzer例程结构: 涉及 ...
- Android TextView换行问题
本文转载于:http://niufc.iteye.com/blog/1729792 ndroid的TextView在显示文字的时候有个问题就是一行还没显示满就跳到下一行,原因是: 1) TextVie ...