UNIX网络编程——ioctl 函数的用法详解
1.介绍
Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口。并且还能够查看,修改,删除ARP高速缓存的信息,所以,我们有必要了解一下ioctl函数的具体实现。
2.相关结构体与相关函数
#include <sys/ioctl.h> int ioctl(int d,int request,....);
参数:
d-文件描述符,这里是对网络套接字操作,显然是套接字描述符。
request-请求码
省略的部分对应不同的内存缓冲区,而具体的内存缓冲区是由请求码request来决定的,下面看一下具体都有哪些相关缓冲区。
(1)网络接口请求结构ifreq
struct ifreq
{
#define IFHWADDRLEN 6 //6个字节的硬件地址,即MAC
union{
char ifrn_name[IFNAMESIZ];//网络接口名称
}ifr_ifrn;
union{
struct sockaddr ifru_addr;//本地IP地址
struct sockaddr ifru_dstaddr;//目标IP地址
struct sockaddr ifru_broadaddr;//广播IP地址
struct sockaddr ifru_netmask;//本地子网掩码地址
struct sockaddr ifru_hwaddr;//本地MAC地址
short ifru_flags;//网络接口标记
int ifru_ivalue;//不同的请求含义不同
struct ifmap ifru_map;//网卡地址映射
int ifru_mtu;//最大传输单元
char ifru_slave[IFNAMSIZ];//占位符
char ifru_newname[IFNAMSIZE];//新名称
void __user* ifru_data;//用户数据
struct if_settings ifru_settings;//设备协议设置
}ifr_ifru;
}
#define ifr_name ifr_ifrn.ifrn_name;//接口名称
#define ifr_hwaddr ifr_ifru.ifru_hwaddr;//MAC
#define ifr_addr ifr_ifru.ifru_addr;//本地IP
#define ifr_dstaddr ifr_ifru.dstaddr;//目标IP
#define ifr_broadaddr ifr_ifru.broadaddr;//广播IP
#define ifr_netmask ifr_ifru.ifru_netmask;//子网掩码
#define ifr_flags ifr_ifru.ifru_flags;//标志
#define ifr_metric ifr_ifru.ifru_ivalue;//接口侧度
#define ifr_mtu ifr_ifru.ifru_mtu;//最大传输单元
#define ifr_map ifr_ifru.ifru_map;//设备地址映射
#define ifr_slave ifr_ifru.ifru_slave;//副设备
#define ifr_data ifr_ifru.ifru_data;//接口使用
#define ifr_ifrindex ifr_ifru.ifru_ivalue;//网络接口序号
#define ifr_bandwidth ifr_ifru.ifru_ivalue;//连接带宽
#define ifr_qlen ifr_ifru.ifru_ivalue;//传输单元长度
#define ifr_newname ifr_ifru.ifru_newname;//新名称
#define ifr_seeting ifr_ifru.ifru_settings;//设备协议设置
如果想获得网络接口的相关信息,就传入ifreq结构体。
(2)网卡设备属性ifmap
struct ifmap{//网卡设备的映射属性
unsigned long mem_start;//开始地址
unsigned long mem_end;//结束地址
unsigned short base_addr;//基地址
unsigned char irq;//中断号
unsigned char dma;//DMA
unsigned char port;//端口
}
(3)网络配置接口ifconf
struct ifconf{//网络配置结构体是一种缓冲区
int ifc_len;//缓冲区ifr_buf的大小
union{
char__user *ifcu_buf;//绘冲区指针
struct ifreq__user* ifcu_req;//指向ifreq指针
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf;//缓冲区地址
#define ifc_req ifc_ifcu.ifcu_req;//ifc_req地址
(4)ARP高速缓存操作arpreq
/* ARP高速缓存操作,包含IP地址和硬件地址的映射表,操作ARP高速缓存的命令字 SIOCDARP,SIOCGARP,SIOCSARP分别是删除ARP高速缓存的一条记录,获得ARP高速缓存的一条记录和修改ARP高速缓存的一条记录 */
struct arpreq{
struct sockaddr arp_pa;//协议地址
struct sockaddr arp_ha;//硬件地址
int arp_flags;//标记
struct sockaddr arp_netmask;//协议地址的子网掩码
char arp_dev[16];//查询网络接口的名称
}
3. 请求码request
|
类别 |
Request |
说明 |
数据类型 |
|
套 接 口 |
SIOCATMARK SIOCSPGRP SIOCGPGRP |
是否位于带外标记 设置套接口的进程ID或进程组ID 获取套接口的进程ID或进程组ID |
int int int |
|
文
件 |
FIONBIN 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 |
|
流 |
I_xxx |
4. 相关例子
(1)网络接口信息
选项获取填充struct ifreq的ifr_name:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if.h>
#include <arpa/inet.h>
#include <linux/sockios.h>
/**
ioctl函数是与内核交互的一种方法,使用ioctl函数与内核协议栈进行交互
ioctl函数可操作I/O请求,文件请求与网络接口请求
网络接口请求的几个结构体:
struct ifreq{
#define IFHWADDRLEN 6 //6个字节的硬件地址,即MAC
union{
char ifrn_name[IFNAMESIZ];//网络接口名称
}ifr_ifrn;
union{
struct sockaddr ifru_addr;//本地IP地址
struct sockaddr ifru_dstaddr;//目标IP地址
struct sockaddr ifru_broadaddr;//广播IP地址
struct sockaddr ifru_netmask;//本地子网掩码地址
struct sockaddr ifru_hwaddr;//本地MAC地址
short ifru_flags;//网络接口标记
int ifru_ivalue;//不同的请求含义不同
struct ifmap ifru_map;//网卡地址映射
int ifru_mtu;//最大传输单元
char ifru_slave[IFNAMSIZ];//占位符
char ifru_newname[IFNAMSIZE];//新名称
void __user* ifru_data;//用户数据
struct if_settings ifru_settings;//设备协议设置 }ifr_ifru;
}
#define ifr_name ifr_ifrn.ifrn_name;//接口名称
#define ifr_hwaddr ifr_ifru.ifru_hwaddr;//MAC
#define ifr_addr ifr_ifru.ifru_addr;//本地IP
#define ifr_dstaddr ifr_ifru.dstaddr;//目标IP
#define ifr_broadaddr ifr_ifru.broadaddr;//广播IP
#define ifr_netmask ifr_ifru.ifru_netmask;//子网掩码
#define ifr_flags ifr_ifru.ifru_flags;//标志
#define ifr_metric ifr_ifru.ifru_ivalue;//接口侧度
#define ifr_mtu ifr_ifru.ifru_mtu;//最大传输单元
#define ifr_map ifr_ifru.ifru_map;//设备地址映射
#define ifr_slave ifr_ifru.ifru_slave;//副设备
#define ifr_data ifr_ifru.ifru_data;//接口使用
#define ifr_ifrindex ifr_ifru.ifru_ivalue;//网络接口序号
#define ifr_bandwidth ifr_ifru.ifru_ivalue;//连接带宽
#define ifr_qlen ifr_ifru.ifru_ivalue;//传输单元长度
#define ifr_newname ifr_ifru.ifru_newname;//新名称
#define ifr_seeting ifr_ifru.ifru_settings;//设备协议设置 struct ifmap{//网卡设备的映射属性
unsigned long mem_start;//开始地址
unsigned long mem_end;//结束地址
unsigned short base_addr;//基地址
unsigned char irq;//中断号
unsigned char dma;//DMA
unsigned char port;//端口
} struct ifconf{//网络配置结构体是一种缓冲区
int ifc_len;//缓冲区ifr_buf的大小
union{
char__user *ifcu_buf;//绘冲区指针
struct ifreq__user* ifcu_req;//指向ifreq指针
}ifc_ifcu; };
#define ifc_buf ifc_ifcu.ifcu_buf;//缓冲区地址
#define ifc_req ifc_ifcu.ifcu_req;//ifc_req地址 (1)获得配置选项SIOCGIFCONF获得网络接口的配置情况 需要填充struct ifreq中ifr_name变量
(2)其它选项获取填充struct ifreq的ifr_name
**/ int main(int argc,char*argv[]){
int s;
int err;
s=socket(AF_INET,SOCK_DGRAM,0);
if(s<0){
perror("socket error");
return;
} //传入网络接口序号,获得网络接口的名称
struct ifreq ifr; ifr.ifr_ifindex=2;//获得第2个网络接口的名称
err=ioctl(s,SIOCGIFNAME,&ifr); if(err)
perror("index error");
else
printf("the %dst interface is:%s\n",ifr.ifr_ifindex,ifr.ifr_name); //传入网络接口名称,获得标志
memcpy(ifr.ifr_name,"eth0",5);
err=ioctl(s,SIOCGIFFLAGS,&ifr);
if(!err)
printf("SIOCGIFFLAGS:%d\n",ifr.ifr_flags); //获得MTU和MAC
err=ioctl(s,SIOCGIFMTU,&ifr);
if(!err)
printf("SIOCGIFMTU:%d\n",ifr.ifr_mtu); //获得MAC地址
err=ioctl(s,SIOCGIFHWADDR,&ifr);
if(!err){
unsigned char* hw=ifr.ifr_hwaddr.sa_data;
printf("SIOCGIFHWADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);
} //获得网卡映射参数 命令字SIOCGIFMAP
err=ioctl(s,SIOCGIFMAP,&ifr);
if(!err)
printf("SIOCGIFMAP,mem_start:%d,mem_end:%d,base_addr:%d,ifr_map:%d,dma:%d,port:%d\n",ifr.ifr_map.mem_start,ifr.ifr_map.mem_end,ifr.ifr_map.base_addr,ifr.ifr_map.irq,ifr.ifr_map.dma,ifr.ifr_map.port); //获得网卡序号
err=ioctl(s,SIOCGIFINDEX,&ifr);
if(!err)
printf("SIOCGIFINDEX:%d\n",ifr.ifr_ifindex); //获取发送队列的长度
err=ioctl(s,SIOCGIFTXQLEN,&ifr);
if(!err)
printf("SIOCGIFTXQLEN:%d\n",ifr.ifr_qlen); //获取网络接口IP struct sockaddr_in *sin=(struct sockaddr_in*)&ifr.ifr_addr;//保存的是二进制IP
char ip[16];//字符数组,存放字符串
memset(ip,0,16);
err=ioctl(s,SIOCGIFADDR,&ifr);
if(!err){
inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);//转换的字符串保存到ip数组中,第二个参数是要转换的二进制IP指针,第三个参数是转换完成存放IP的缓冲区,最后一个参数是缓冲区的长度
printf("SIOCGIFADDR:%s\n",ip);
} //查询目标IP地址
err=ioctl(s,SIOCGIFDSTADDR,&ifr);
if(!err){
inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);
printf("SIOCGIFDSTADDR:%s\n",ip);
} //查询子网掩码
err=ioctl(s,SIOCGIFNETMASK,&ifr);
if(!err){
inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);
printf("SIOCGIFNETMASK:%s\n",ip);
} //设置IP地址,设置网络接口
inet_pton(AF_INET,"222.27.253.108",&sin->sin_addr.s_addr);//将字符串IP转换成二进制
err=ioctl(s,SIOCSIFADDR,&ifr);//发送设置本机ip地址请求命令
if(!err){
printf("check IP-----");
memset(&ifr,0,sizeof(ifr));
memcpy(ifr.ifr_name,"eth0",5);
ioctl(s,SIOCGIFADDR,&ifr);
inet_ntop(AF_INET,&sin->sin_addr.s_addr,ip,16);
printf("%s\n",ip);
} //得到接口的广播地址
memset(&ifr,0,sizeof(ifr));
memcpy(ifr.ifr_name,"eth0",5);
ioctl(s,SIOCGIFBRDADDR,&ifr);
struct sockaddr_in *broadcast=(struct sockaddr_in*)&ifr.ifr_broadaddr;
//转换成字符串
inet_ntop(AF_INET,&broadcast->sin_addr.s_addr,ip,16);//inet_ntop将二进制IP转换成点分十进制的字符串
printf("BROADCAST IP:%s\n",ip);
close(s);
}
运行结果:
(2)查看arp高速缓存信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <string.h>
#include <stdlib.h>
#include <linux/sockios.h>
/**
ARP高速缓存操作,包含IP地址和硬件地址的映射表
操作ARP高速缓存的命令字 SIOCDARP,SIOCGARP,SIOCSARP分别是删除ARP高速缓存的一条记录,获得ARP高速缓存的一条记录和修改ARP高速缓存的一条记录
struct arpreq{
struct sockaddr arp_pa;//协议地址
struct sockaddr arp_ha;//硬件地址
int arp_flags;//标记
struct sockaddr arp_netmask;//协议地址的子网掩码
char arp_dev[16];//查询网络接口的名称
} **/ //根据IP地址查找硬件地址
int main(int argc,char*argv[]){
int s;
int err;
struct arpreq arpreq;
struct sockaddr_in *addr=(struct sockaddr_in*)&arpreq.arp_pa;//IP地址
s=socket(AF_INET,SOCK_DGRAM,0);
if(s<0)
perror("socket error"); addr->sin_family=AF_INET;
addr->sin_addr.s_addr=inet_addr(argv[1]);//转换成二进制IP
if(addr->sin_addr.s_addr==INADDR_NONE)
printf("IP地址格式错误\n"); strcpy(arpreq.arp_dev,"eth0");
err=ioctl(s,SIOCGARP,&arpreq);
if(err==-1){
perror("arp");
return -1;
} unsigned char* hw=(unsigned char*)&arpreq.arp_ha.sa_data;//硬件地址
printf("%s\n",argv[1]);
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);
close(s);
return 0;
}
运行结果:
查看网关的MAC。在查看ARP高速缓存时要传入IP地址与接口信息。而获得接口信息要传入接口名ifr_name,如eth0。
总结:
本文主要介绍了获得网络接口请求信息,获得网卡设备映射属性、配置网络接口、获得ARP高速缓存等。其它ioctl函数还能对操作文件,操作I/O、操作路由等。最后对于网络接口的操作与ARP高速缓存的操作分别给出了实例。
UNIX网络编程——ioctl 函数的用法详解的更多相关文章
- PHP截取字符串函数substr()函数实例用法详解
在PHP中有一项非常重要的技术,就是截取指定字符串中指定长度的字符.PHP对于字符串截取可以使用PHP预定义函数substr()函数来实现.下面就来介绍一下substr()函数的语法及其应用. sub ...
- UNIX网络编程——select函数的并发限制和 poll 函数应用举例
一.用select实现的并发服务器,能达到的并发数,受两方面限制 1.一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n来调整或者使用setrlimit函数设置, ...
- unix网络编程之listen()详解
转自于:http://blog.csdn.net/ordeder/article/details/21551567 Unix网络编程描述如下: #include <sys/socket.h> ...
- UNIX网络编程——fcntl函数
fcntl函数提供了与网络编程相关的如下特性: 非阻塞式I/O. 通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型. 信号驱动式I/O. 通过使用F ...
- UNIX网络编程--ioctl操作(十七)
一.概述 在本书中有两个地方都对这个函数进行了介绍,其实还有很多地方需要这个函数.ioclt函数传统上一直作为纳西而不适合归入其他精细定义类别的特性的系统接口.网络程序(特别是服务器程序)经常在程序启 ...
- Linux 网络编程三(socket代码详解)
//网络编程客户端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include < ...
- UNIX网络编程——sockatmark函数
每当收到一个带外数据时,就有一个与之关联的带外标记.这是发送进程发送带外字节时该字节在发送端普通数据流中的位置.在从套接字读入期间,接收进程通过调用sockatmark函数确定是否处于带外标记. #i ...
- python socket网络编程之粘包问题详解
一,粘包问题详情 1,只有TCP有粘包现象,UDP永远不会粘包 你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,其实是先把数据从用 ...
- Python中scatter函数参数用法详解
1.scatter函数原型 2.其中散点的形状参数marker如下: 3.其中颜色参数c如下: 4.基本的使用方法如下: #导入必要的模块 import numpy as np import matp ...
随机推荐
- 洛谷P2144 [FJOI2007]轮状病毒
可以用Matrix-Tree定理,然而被卡精度 #include<cstdio> #include<cstdlib> #include<algorithm> #in ...
- hdu 5919 主席树(区间不同数的个数 + 区间第k大)
Sequence II Time Limit: 9000/4500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Tot ...
- 【SDOI2009】学校食堂
Description 小F的学校在城市的一个偏僻角落,所有学生都只好在学校吃饭.学校有一个食堂,虽然简陋,但食堂大厨总能做出让同学们满意的菜肴.当然,不同的人口味也不一定相同,但每个人的口味都可以用 ...
- Spring MVC页面重定向
以下示例显示如何编写一个简单的基于Web的重定向应用程序,这个应用程序使用重定向将http请求传输到另一个页面. 基于Spring MVC - Hello World实例章节中代码,创建创建一个名称为 ...
- 8.QT-对话框(模态与非模态)
对话框介绍 对话框是于用户进行简易交互的顶层窗口 QDialog是Qt中所有对话框窗口的父类,是一种容器类型的组件 QDialog继承于QWidget类,如下图所示: QWidget和QDialog有 ...
- JAVA虚拟机:对象的创建过程
简要说明的话,Java对象的创建过程分为下面几步: 1.执行相关检查: 2.为对象分配内存,将分配到的内存空间都初始化为零值: 3.进行构造代码块和构造函数的初始化 下面详细介绍这几个步骤: 1.执行 ...
- Spring中<context:annotation-config/>
最近在研究Spring中<context:annotation-config/>配置的作用,现记录如下: <context:annotation-config/>的作用是向Sp ...
- typeAliases别名
<configuration> <typeAliases> <!-- 通过package, 可以直接指定package的名字, mybatis会自动扫描你指定包下面的ja ...
- 螺旋打印2D数组
//一破题付出血的代价 多思考!public static void offer(int [][]a){ ,right=a.length-,low=,high=a[].length-; while(l ...
- C++笔记--1
一.namespace 命名空间 //定义一个命名空间 namespace spaceA { ; } int main(void) { //调用方式一 using namespace spaceA; ...