获得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. Polycarpus' Dice
在每个位置讨论一下最大值最小值的取值范围就行 #include<cstdio> #include<iostream> #define maxn 200003 using nam ...
- android布局中的divider(目前只知道TableLayout)
目前在genymotion中设置了之后显示不出来行与行之间的分割线,但是在真机上面是没有问题的 1.使用xml属性添加(3.0以上版本) 设置LinearLayout标签的 android:showD ...
- Java-->PrintStream
--> 用过之后都不想用read.write了,感觉还是挺方便的... package com.dragon.java.hwletter; import java.io.BufferedRead ...
- 基于Spring框架的Web应用开发笔记 - Outline
Motivation 最近的工作涉及Web框架搭建,在了解公司原有采用框架基础上对Web开发技术栈做了一次升级,在次做记录. Audience J2EE Web Application Develop ...
- linux安装phpredis扩展
1.下载扩展安装包 wget https://github.com/nicolasff/phpredis/downloads 2.解压 tar -zxvf nicolasff-phpredis-2 ...
- 全国信息学奥林匹克联赛 ( NOIP2014) 复赛 模拟题 Day1 长乐一中
题目名称 正确答案 序列问题 长途旅行 英文名称 answer sequence travel 输入文件名 answer.in sequence.in travel.in 输出文件名 answer. ...
- Codeforces Round #113 (Div. 2)
Codeforces Round #113 (Div. 2) B. Polygons 题意 给一个\(N(N \le 10^5)\)个点的凸包 \(M(M \le 2 \cdot 10^4)\)次询问 ...
- apache开启url rewrite模块
在把服务器数据转移到本地服务器之后,本地打开首页出现排版紊乱等问题,经过大神指点说是url rewrite的问题. 本篇文章主要写怎样开启apache的url rewrite功能. 打开Apache2 ...
- Oracle数据库五种约束
oracle 数据库 数据表的5个约束类型:1.主键约束2.外键约束3.唯一约束4.检查约束5.非空约束 主键约束:用来唯一标示表中的一个列,一个表中的主键约束只能有一个,但是可以在一个主键约束中包含 ...
- bootstrap 固定底部导航自适应
在使用bootstrap 底部导航的时候遇到了一个问题 -- 当我的内容超过一屏的时候,底部的部分内容会被固定的导航内容遮盖 自己写了一个JS脚本,解决自适应的问题 <nav class=&qu ...