Lniux网络接口(ioctl)

概述

iotcl函数

在示例程序中多次使用到了ioctl函数,使用这个函数直接获取数据。ioctl用户函数实际上就是为驱动为用户程序提供一个get/set接口,其间是内核做一次转换,内核将驱动程序和用户程序连接起来,可以参考文章(https://blog.csdn.net/qq_19923217/article/details/82698787)中的图示很容易看懂。
函数原型为int ioctl(int fd, int cmd, ...) ;其中cmd在指的就是接口请求命令,在接下来一小节有介绍。

接口请求命令

SIOCGIFCONF请求为每个已经被配置的接口返回其名字以及一个套接字地址结构,也有许多其他的类似接口请求(如下表),这些请求的获取(get)版本(SIOCGxxx)通常由netstat程序发出,设置(set)版本(SIOCSxxx)通常由ifconfig程序发出。任何用户都可以获取接口信息,设置接口信息却要求具备超级用户权限。以下是一些通用接口请求,许多实现中都加入了其他的请求。

程序示例

本程序主要展示ioctl的使用方法,使用ioctl获取网络设备的数据。
程序参考以下两篇文章:
https://blog.csdn.net/baicong9439/article/details/101089278
https://blog.csdn.net/q_l_s/article/details/52716730


#include<stdlib.h>  
#include<stdio.h>  
#include<unistd.h>  
#include<net/if.h>  
#include<net/if_arp.h>  
#include<arpa/inet.h>  
#include<sys/ioctl.h>  
#include<errno.h>  
   
#define ETH_NAME  "eth0"  
#define IFNAMSIZ  32

static void get_ifconfig(int sock_fd)  
{
    int i=0;
    struct ifconf _ifconf;
    unsigned char buf[512];
    struct ifreq *_ifreq;
    //初始化ifconf
    _ifconf.ifc_len = 512;
    _ifconf.ifc_buf = buf;

    ioctl(sock_fd, SIOCGIFCONF, &_ifconf); //获取所有接口信息

    /* 接下来一个一个的获取IP地址 */
    _ifreq = (struct _ifreq*)buf;
	
    /* 一个网络设备可能会有多张网卡多个网络接口 */
    for (i = (_ifconf.ifc_len/sizeof (struct _ifreq)); i > 0; i --)
    {
        /* for ipv4 */
        if(_ifreq->ifr_addr.sa_family == AF_INET)
	{ 
	        printf("name = [%s]/n" , _ifreq->ifr_name);
	        printf("local addr = [%s]/n" , inet_ntoa(((struct sockaddr_in*)&(_ifreq->ifr_addr))->sin_addr));

			_ifreq ++;
        }
    }
	
    return ;
}

static void get_network_interface_params(int sock_fd, char *net_interface, int cmd)  
{  
	strncpy(ifr.ifr_name, net_interface, IFNAMSIZ);  
	ifr.ifr_name[IFNAMSIZ - 1] = 0;  
	if(ioctl(sock_fd, cmd, &ifr) == 0)  
	{  
		memcpy(&sin,&ifr.ifr_addr,sizeof(sin));  
		printf("eth0: %s\n",inet_ntoa(sin.sin_addr));  
	}  
}  

int main()  
{  
        int sockfd;  
	struct sockaddr_in  sin;  
	struct sockaddr_in  netmask;  
	struct sockaddr_in  broad;  
	struct ifreq ifr;  
	unsigned char had[6];  

	
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
	if(sockfd == -1)  
	{  
		perror("create socket failed !");  
		return -1;  
	}  
	
	/* get a specific network interface info */
	/* get_local_ip */
	get_network_interface_params(sockfd, ETH_NAME, SIOCGIFADDR);
	/* get_mac_addr */
	get_network_interface_params(sockfd, ETH_NAME, SIOCGIFHWADDR);
	/* get_netmask_addr */
	get_network_interface_params(sockfd, ETH_NAME, SIOCGIFNETMASK);
	/* get_broad_addr */  
	get_network_interface_params(sockfd, ETH_NAME, SIOCGIFBRDADDR);

	/* get all network interface info */
	get_ifconfig(sockfd);
	
	return 0;  
}  


其中关于ifreq和ifconf的部分结构以下给出作为参考,事实上这些结构内容更多一些,这里只作为示例。如果对ifconf结构与ifreq结构的联合使用不太清楚,在文章(http://www.360doc.com/content/17/0510/17/8335678_652752795.shtml)的图示中可以看的很明白。


struct ifconf
{
	long ifc_len;
	union
	{
	    char  *ifcu_buf;
	    struct ifreq *ifcu_req;
	}ifc_ifcu;
};

#define ifc_buf ifc_ifcu.ifcu_buf
#define ifc_req ifc_ifcu.ifcu_req


struct ifreq
{
        char ifr_name[IFNAMSIZ];
	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_metric;
	    char    *ifru_data;
	}ifr_ifru;
};

#define ifr_addr        ifr_ifru.ifru_addr
#define ifr_broadaddr   ifr_ifru.ifru_broadaddr
#define ifr_hwaddr      ifr_ifru.ifru_hwaddr

补充知识

以下都参考自(https://blog.csdn.net/will130/article/details/53326740)

sockaddr和sockaddr_in详解

sockaddr_in可以强制转换成sockaddr结构,sockaddr结构多用于bing,connect,recvfrom,sendto等操作
struct sockaddr_in mysock;
(struct sockaddr *)&mysock

网络字节序与主机字节序转换

在数据传输过程中使用网络字节序,在数据表示的时候多使用主机字节序字符串

编号 函数名称 操作对象 转换
1 htons() 端口号 主机字节序->网络字节序
2 inet_addr() IP字符串 主机字节序->网络字节序
3 inet_ntoa() sin_addr结构 网络字节序->主机字节序

以上

posted @ 2021-04-29 18:30  哎呀是不是疯啦  阅读(1128)  评论(0编辑  收藏  举报