网络ioctl实践1:获取网卡的MAC和IP

 网络ioctl实践1:获取网卡的MAC和IP

 网络ioctl实践2:获取网卡的广播地址和子网掩码

 网络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:~$

小结

 第二篇:网络ioctl实践2:获取网卡的广播地址和子网掩码

猜你喜欢

转载自blog.csdn.net/yueni_zhao/article/details/127804923