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结构 | 网络字节序->主机字节序 |
以上