网络ioctl实践3:设置网卡的mac、ip、子网掩码、广播地址
前言
如果设备控制没有好的解决办法,那么ioctl就可能是最终答案。如果要说IOCTL能干什么,那就是任何事情,都可以做。
一 ioctl函数原型
它是一个变参函数,第二个是命令类型,第三个是命令对应的参数。函数成功返回0,失败返回-1.
NAME
ioctl - control device
SYNOPSIS
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
参数fd表示文件描述符,如果要获取网卡信息,这个fd就是套接字描述符,就是socket函数的返回值。
NAME
socket - create an endpoint for communication
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain参数:我们只关注两个值,AF_UNIX和AF_INET,AF_UNIX用于本地通进程间信,AF_INET既可以用于本机进程间通信新,也可以用于主机间进程通信。
type 参数:我们只关注两个值,SOCK_STREAM和SOCK_DGRAM,SOCK_STREAM用于TCP,SOCK_DGRAM用于UDP。
protocol参数:值为0时,会自动选择type类型对应的默认协议。
获取网络描述符的语句如下所示:
int fd = socket(AF_INET,SOCK_DGRAM,0);
好了,万事具备了。就差给ioctl一个合适的命令和一个合适的参数了。
类别 |
Request |
说明 |
数据类型 |
套 接 口 |
SIOCATMARK SIOCSPGRP SIOCGPGRP |
是否位于带外标记 设置套接口的进程ID 或进程组ID 获取套接口的进程ID 或进程组ID |
int int int |
文 件 |
FIONBIO 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 |
二 网络相关的IOCTL命令-内核中的宏定义
直接把文件copy过来sockios.h (include\uapi\linux),如果能这些IOCTL挨个试一遍,那么网络控制这块,(⊙o⊙)…就算入魔了吧,这么多啊。下面这个宏定义和上面表格是一样的。
/*
* INET An implementation of the TCP/IP protocol suite for the LINUX
* operating system. INET is implemented using the BSD Socket
* interface as the means of communication with the user level.
*
* Definitions of the socket-level I/O control calls.
*
* Version: @(#)sockios.h 1.0.2 03/09/93
*
* Authors: Ross Biro
* Fred N. van Kempen, <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_SOCKIOS_H
#define _LINUX_SOCKIOS_H
#include <asm/sockios.h>
/* Linux-specific socket ioctls */
#define SIOCINQ FIONREAD
#define SIOCOUTQ TIOCOUTQ /* output queue size (not sent + not acked) */
/* Routing table calls. */
#define SIOCADDRT 0x890B /* add routing table entry */
#define SIOCDELRT 0x890C /* delete routing table entry */
#define SIOCRTMSG 0x890D /* call to routing system */
/* Socket configuration controls. */
#define SIOCGIFNAME 0x8910 /* get iface name */
#define SIOCSIFLINK 0x8911 /* set iface channel */
#define SIOCGIFCONF 0x8912 /* get iface list */
#define SIOCGIFFLAGS 0x8913 /* get flags */
#define SIOCSIFFLAGS 0x8914 /* set flags */
#define SIOCGIFADDR 0x8915 /* get PA address */
#define SIOCSIFADDR 0x8916 /* set PA address */
#define SIOCGIFDSTADDR 0x8917 /* get remote PA address */
#define SIOCSIFDSTADDR 0x8918 /* set remote PA address */
#define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
#define SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
#define SIOCGIFNETMASK 0x891b /* get network PA mask */
#define SIOCSIFNETMASK 0x891c /* set network PA mask */
#define SIOCGIFMETRIC 0x891d /* get metric */
#define SIOCSIFMETRIC 0x891e /* set metric */
#define SIOCGIFMEM 0x891f /* get memory address (BSD) */
#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */
#define SIOCGIFMTU 0x8921 /* get MTU size */
#define SIOCSIFMTU 0x8922 /* set MTU size */
#define SIOCSIFNAME 0x8923 /* set interface name */
#define SIOCSIFHWADDR 0x8924 /* set hardware address */
#define SIOCGIFENCAP 0x8925 /* get/set encapsulations */
#define SIOCSIFENCAP 0x8926
#define SIOCGIFHWADDR 0x8927 /* Get hardware address */
#define SIOCGIFSLAVE 0x8929 /* Driver slaving support */
#define SIOCSIFSLAVE 0x8930
#define SIOCADDMULTI 0x8931 /* Multicast address lists */
#define SIOCDELMULTI 0x8932
#define SIOCGIFINDEX 0x8933 /* name -> if_index mapping */
#define SIOGIFINDEX SIOCGIFINDEX /* misprint compatibility :-) */
#define SIOCSIFPFLAGS 0x8934 /* set/get extended flags set */
#define SIOCGIFPFLAGS 0x8935
#define SIOCDIFADDR 0x8936 /* delete PA address */
#define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */
#define SIOCGIFCOUNT 0x8938 /* get number of devices */
#define SIOCGIFBR 0x8940 /* Bridging support */
#define SIOCSIFBR 0x8941 /* Set bridging options */
#define SIOCGIFTXQLEN 0x8942 /* Get the tx queue length */
#define SIOCSIFTXQLEN 0x8943 /* Set the tx queue length */
/* SIOCGIFDIVERT was: 0x8944 Frame diversion support */
/* SIOCSIFDIVERT was: 0x8945 Set frame diversion options */
#define SIOCETHTOOL 0x8946 /* Ethtool interface */
#define SIOCGMIIPHY 0x8947 /* Get address of MII PHY in use. */
#define SIOCGMIIREG 0x8948 /* Read MII PHY register. */
#define SIOCSMIIREG 0x8949 /* Write MII PHY register. */
#define SIOCWANDEV 0x894A /* get/set netdev parameters */
#define SIOCOUTQNSD 0x894B /* output queue size (not sent only) */
/* ARP cache control calls. */
/* 0x8950 - 0x8952 * obsolete calls, don't re-use */
#define SIOCDARP 0x8953 /* delete ARP table entry */
#define SIOCGARP 0x8954 /* get ARP table entry */
#define SIOCSARP 0x8955 /* set ARP table entry */
/* RARP cache control calls. */
#define SIOCDRARP 0x8960 /* delete RARP table entry */
#define SIOCGRARP 0x8961 /* get RARP table entry */
#define SIOCSRARP 0x8962 /* set RARP table entry */
/* Driver configuration calls */
#define SIOCGIFMAP 0x8970 /* Get device parameters */
#define SIOCSIFMAP 0x8971 /* Set device parameters */
/* DLCI configuration calls */
#define SIOCADDDLCI 0x8980 /* Create new DLCI device */
#define SIOCDELDLCI 0x8981 /* Delete DLCI device */
#define SIOCGIFVLAN 0x8982 /* 802.1Q VLAN support */
#define SIOCSIFVLAN 0x8983 /* Set 802.1Q VLAN options */
/* bonding calls */
#define SIOCBONDENSLAVE 0x8990 /* enslave a device to the bond */
#define SIOCBONDRELEASE 0x8991 /* release a slave from the bond*/
#define SIOCBONDSETHWADDR 0x8992 /* set the hw addr of the bond */
#define SIOCBONDSLAVEINFOQUERY 0x8993 /* rtn info about slave state */
#define SIOCBONDINFOQUERY 0x8994 /* rtn info about bond state */
#define SIOCBONDCHANGEACTIVE 0x8995 /* update to a new active slave */
/* bridge calls */
#define SIOCBRADDBR 0x89a0 /* create new bridge device */
#define SIOCBRDELBR 0x89a1 /* remove bridge device */
#define SIOCBRADDIF 0x89a2 /* add interface to bridge */
#define SIOCBRDELIF 0x89a3 /* remove interface from bridge */
/* hardware time stamping: parameters in linux/net_tstamp.h */
#define SIOCSHWTSTAMP 0x89b0 /* set and get config */
#define SIOCGHWTSTAMP 0x89b1 /* get config */
/* Device private ioctl calls */
/*
* These 16 ioctls are available to devices via the do_ioctl() device
* vector. Each device should include this file and redefine these names
* as their own. Because these are device dependent it is a good idea
* _NOT_ to issue them to random objects and hope.
*
* THESE IOCTLS ARE _DEPRECATED_ AND WILL DISAPPEAR IN 2.5.X -DaveM
*/
#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */
/*
* These 16 ioctl calls are protocol private
*/
#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */
#endif /* _LINUX_SOCKIOS_H */
三 打印网卡MAC地址:SIOCGIFHWADDR命令
它对应的结构体是struct ifreq ifr;很多人搞不懂这个命令主要是不了解这个结构体,如果看到这个结构体的定义,如下所示,那就理解起来,(⊙o⊙)…,也不轻松。
#define IFNAMSIZ 16
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;
}
根据名字,ifru_hwaddr应该是用来表示硬件MAC地址的。看下struct sockaddr的结构体定义:
/*
* 1003.1g requires sa_family_t and that sa_data is char.
*/
typedef unsigned short __kernel_sa_family_t;
typedef __kernel_sa_family_t sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
sa_family_t 成员本质上是unsigned short,取值就是socket的第一个参数。
如果是取MAC地址,data中保存的就是mac地址值:原理如下:
int i = 0;
struct ifreq ifr;
ioctl(fd,SIOCGIFHWADDR,&ifr);
for(i = 0;i < IFHWADDRLEN;i++)
{
if(i == IFHWADDRLEN - 1){
printf("0x%02x",ifr.ifr_hwaddr.sa_data[i]);
}else{
printf("0x%02x:",ifr.ifr_hwaddr.sa_data[i]);
}
}
获取网卡实例1:ioctl.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <net/if.h>
int main(int argc,char *argv[])
{
int fd;
int i;
struct ifreq ifr;
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket");
exit(-1);
}
if(i = ioctl(fd,SIOCGIFHWADDR,&ifr)){
perror("ioctl");
close(fd);
exit(-2);
}
for(i = 0;i < 6;i++)
{
if(i == 55555){
printf("0x%02x",ifr.ifr_hwaddr.sa_data[i]);
}else{
printf("0x%02x:",ifr.ifr_hwaddr.sa_data[i]);
}
}
close(fd);
return 0;
}
编译gcc ioctl.c -o ioctl,运行./ioctl,结果如下所示:
csdn@ubuntu:~$ ./a.out
ioctl: No such device
csdn@ubuntu:~$
提示没有设备,这是因为它需要我们让内核知道,我们需要哪一个网卡的mac地址,假设现在你知道网卡的名字是ens33,根据结构体的struct ifreq 定义可知,成员ifrn_name用于保存网卡名字,且名字最大长度是16;如下所示:
sprintf(ifr.ifr_ifrn.ifrn_name,"%s","ens33");
修改后的代码:ioctl.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <net/if.h>
#include <stdint.h>
int get_mac_by_name(int fd,char *name)
{
int i = 0;
struct ifreq ifr;
sprintf(ifr.ifr_ifrn.ifrn_name,"%s",name);
if(i = ioctl(fd,SIOCGIFHWADDR,&ifr)){
perror("ioctl");
close(fd);
exit(-2);
}
for(i = 0;i < 6;i++)
{
if(i == 5){
printf("0x%02x\n",(uint8_t)ifr.ifr_ifru.ifru_hwaddr.sa_data[i]);
}else{
printf("0x%02x:",(uint8_t)ifr.ifr_ifru.ifru_hwaddr.sa_data[i]);
}
}
return 0;
}
int main(int argc,char *argv[])
{
int fd;
int i;
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket");
exit(-1);
}
get_mac_by_name(fd,"ens33");
close(fd);
return 0;
}
编译gcc ioctl.c -o app,运行./app,运行结果:与ifconfig结果相比,准确无误。
csdn@ubuntu:~$ ./app
0x00:0x0c:0x29:0x8c:0x05:0x15
csdn@ubuntu:~$ ifconfig | sed -n '/ens33/,+1p'
ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
inet 地址:192.168.0.11 广播:192.168.0.255 掩码:255.255.255.0
csdn@ubuntu:~$
三 遍历网卡的名字:SIOCGIFCONF命令
SIOCGIFCONF全名大概是Socket IO Control InterFace CONFigure。或者是 Socket IO Control Internet Family CONFigure。也可能是Socket IO Control Internet inteface Family CONFigure。这都不重要,咱又不考研,知道它是获取信息的就够了。
给它的ioctl的参数是struct ifconf结构体指针。
/*
* 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;
}
由定义可知,ifcu_req成员可以作为一个数组来看待,ifc_len返回的就是这个数组的长度。
遍历名字,就是下面这样的,如果不知道struct ifconf结构体的定义,这个代码是看不明白的。
struct ifconf ifconf;
ioctl(fd,SIOCGIFCONF,&ifconf);
for(i = 0;i < iconf.ifc_len;i++){
printf("%s",ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
}
实例验证:ioctl.c,所需头文件和前例相同。
int main(int argc,char *argv[])
{
int fd;
int i;
struct ifconf ifconf;
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket");
exit(-1);
}
if(i = ioctl(fd,SIOCGIFCONF,&ifconf)){
perror("ioctl");
printf("ret = %d\n",i);
close(fd);
exit(-1);
}
printf("ifconf.ifc_len = %d\n",ifconf.ifc_len);
for(i = 0;i < ifconf.ifc_len;i++){
printf("%d:%s\n",i,ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
}
close(fd);
return 0;
}
编译gcc ioctl.c -o app,运行./app,结果如下所示:
csdn@ubuntu:~$ ./app
ioctl: Bad address
ret = -1
csdn@ubuntu:~$
报错,这是为什么呢?上面的分析有误,ifc_len 表示的是 size of buffer ,char __user *ifcu_buf;
ifcu_buf成员需要指向用户空间等待缓存,所以要提前给它分配空间。我们暂且给他分配1024字节。并给这个内存清零,这样最多可以获取的接口数就是:1024/sizeof(struct ifreq)。为了简单,我们直接计算返回的网卡名字的长度,如果长度为0,说明已经获取信息完毕。
ifconf.ifc_len = 1024;
ifconf.ifc_ifcu.ifcu_buf = malloc(1024);
memset(ifconf.ifc_ifcu.ifcu_buf,0,1024);
if(i = ioctl(fd,SIOCGIFCONF,&ifconf)){
perror("ioctl");
printf("ret = %d\n",i);
close(fd);
exit(-1);
}
for(i = 0;i < ifconf.ifc_len/sizeof(struct ifreq);i++){
if(strlen(ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name) == 0)break;
printf("%d:%s\n",i,ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
}
完整的实例代码:ioctl.c
int main(int argc,char *argv[])
{
int fd;
int i;
struct ifconf ifconf;
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket");
exit(-1);
}
ifconf.ifc_len = 1024;
ifconf.ifc_ifcu.ifcu_buf = malloc(1024);
memset(ifconf.ifc_ifcu.ifcu_buf,0,1024);
if(i = ioctl(fd,SIOCGIFCONF,&ifconf)){
perror("ioctl");
printf("ret = %d\n",i);
close(fd);
exit(-1);
}
for(i = 0;i < ifconf.ifc_len/sizeof(struct ifreq);i++){
if(strlen(ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name) == 0)break;
printf("%d:%s\n",i,ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
}
free(ifconf.ifc_ifcu.ifcu_buf);
close(fd);
return 0;
}
编译gcc ioctl.c -o app,运行./app,结果如下所示:
csdn@ubuntu:~$ ./app
0:lo
1:ens33
2:ens38
csdn@ubuntu:~$
使用ifconfig获取网卡名字:和我们./app执行结果比较,完全正确是吧。
csdn@ubuntu:~$ ifconfig | sed -n '/Link encap/p'
ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
ens38 Link encap:以太网 硬件地址 00:0c:29:8c:05:1f
lo Link encap:本地环回
csdn@ubuntu:~$
四 获取IP地址:SIOCGIFADDR命令
理论上SIOCGIFCONF也可以获取IP地址,但是ifr_ifru这个共用体,我还不知道它里面哪个才是有效,至少还缺少理论知识支撑。和获取MAC地址思路相同,需要提供名字,从struct ifreq结构题定义可知,ifru_addr就是返回的IP地址信息。需要将它转换struct sockaddr_in 结构体。
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
在这里端口信息无效,所以只关系struct in_addr sin_addr,根据内存的布局特点,sin_addr成员对应ifru_addr.sa_data[2-5],好了,代码大概如下所示:
实例代码:ioctl.c
int get_ip_by_name(int fd,char *name)
{
int i = 0;
struct ifreq ifr;
sprintf(ifr.ifr_ifrn.ifrn_name,"%s",name);
if(i = ioctl(fd,SIOCGIFADDR,&ifr)){
perror("ioctl");
close(fd);
exit(-2);
}
for(i = 0;i < 4;i++)
{
if(i == 3){
printf("%d\n",(uint8_t)ifr.ifr_ifru.ifru_addr.sa_data[2 + i]);
}else{
printf("%d:",(uint8_t)ifr.ifr_ifru.ifru_addr.sa_data[2 + i]);
}
}
return 0;
}
int main(int argc,char *argv[])
{
int fd;
int i;
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket");
exit(-1);
}
get_ip_by_name(fd,"ens33");
close(fd);
return 0;
}
编译gcc ioctl.c -o app,运行./app,结果如下所示:
csdn@ubuntu:~$ ./app
192:168:0:11
csdn@ubuntu:~$
使用ifconfig验证结果:sed -n '/ens33/,+1p'表示匹配ens33所在的行,并多显示一行。
csdn@ubuntu:~$ ifconfig | sed -n '/ens33/,+1p'
ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
inet 地址:192.168.0.11 广播:192.168.0.255 掩码:255.255.255.0
csdn@ubuntu:~$
五 综合:遍历MAC+IP实例
借助本章学到的三个命令,写一个遍历网口名字、MAC地址和IP地址的小代码:ioctl.c
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <net/if.h>
#include <stdint.h>
int get_mac_by_name(int fd,char *name)
{
int i = 0;
struct ifreq ifr;
sprintf(ifr.ifr_ifrn.ifrn_name,"%s",name);
if(i = ioctl(fd,SIOCGIFHWADDR,&ifr)){
perror("ioctl");
close(fd);
exit(-2);
}
for(i = 0;i < 6;i++)
{
if(i == 5){
printf("0x%02x\n",(uint8_t)ifr.ifr_ifru.ifru_hwaddr.sa_data[i]);
}else{
printf("0x%02x:",(uint8_t)ifr.ifr_ifru.ifru_hwaddr.sa_data[i]);
}
}
return 0;
}
int get_ip_by_name(int fd,char *name);
int show_all_net_interface_name(int fd)
{
int i = 0;
struct ifconf ifconf;
ifconf.ifc_len = 1024;
ifconf.ifc_ifcu.ifcu_buf = malloc(1024);
memset(ifconf.ifc_ifcu.ifcu_buf,0,1024);
if(i = ioctl(fd,SIOCGIFCONF,&ifconf)){
perror("ioctl");
printf("ret = %d\n",i);
close(fd);
exit(-1);
}
for(i = 0;i < ifconf.ifc_len/sizeof(struct ifreq);i++){
if(strlen(ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name) == 0)break;
printf("%d:%s\n",i,ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
printf("ip:");
get_ip_by_name(fd,ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
printf("mac:");
get_mac_by_name(fd,ifconf.ifc_ifcu.ifcu_req[i].ifr_ifrn.ifrn_name);
printf("\n");
}
free(ifconf.ifc_ifcu.ifcu_buf);
return 0;
}
int get_ip_by_name(int fd,char *name)
{
int i = 0;
struct ifreq ifr;
sprintf(ifr.ifr_ifrn.ifrn_name,"%s",name);
if(i = ioctl(fd,SIOCGIFADDR,&ifr)){
perror("ioctl");
close(fd);
exit(-2);
}
for(i = 0;i < 4;i++)
{
if(i == 3){
printf("%d\n",(uint8_t)ifr.ifr_ifru.ifru_addr.sa_data[2 + i]);
}else{
printf("%d:",(uint8_t)ifr.ifr_ifru.ifru_addr.sa_data[2 + i]);
}
}
return 0;
}
int main(int argc,char *argv[])
{
int fd;
int i;
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd < 0){
perror("socket");
exit(-1);
}
show_all_net_interface_name(fd);
close(fd);
return 0;
}
编译gcc ioctl.c -o app,运行./app,结果如下所示:
csdn@ubuntu:~$ ./app
0:lo
ip:127:0:0:1
mac:0x00:0x00:0x00:0x00:0x00:0x00
1:ens33
ip:192:168:0:11
mac:0x00:0x0c:0x29:0x8c:0x05:0x15
2:ens38
ip:192:168:31:148
mac:0x00:0x0c:0x29:0x8c:0x05:0x1f
csdn@ubuntu:~$
使用ifconfig验证结果:ifconfig | sed -n '/Link encap/,+1p'
csdn@ubuntu:~$ ifconfig | sed -n '/Link encap/,+1p'
ens33 Link encap:以太网 硬件地址 00:0c:29:8c:05:15
inet 地址:192.168.0.11 广播:192.168.0.255 掩码:255.255.255.0
ens38 Link encap:以太网 硬件地址 00:0c:29:8c:05:1f
inet 地址:192.168.31.148 广播:192.168.31.255 掩码:255.255.255.0
lo Link encap:本地环回
inet 地址:127.0.0.1 掩码:255.0.0.0
csdn@ubuntu:~$