***************************************************************************************************************************
作者:EasyWave 时间:2013.01.19
类别:Linux 应用实例源码 声明:转载,请保留链接
注意:如有错误,欢迎指正。这些是我学习的日志文章......
***************************************************************************************************************************
这段时间,因为项目的需要,了解了一下USBtoNet的驱动,同时采用IOCTL来设置MAC的地址,检测网卡的连接状态等等,因此,就从网络上了解了一下关于网络编程方面的知识.一般来说:Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的信息,所以,我们先来了解一下ioctl函数的具体实现.
函数形式:
#include <sys/ioctl.h>
int ioctl(int d, int 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 |
|
相关数据结构:
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_qlen ifr_ifru.ifru_ivalue;//传输单元长度 #define ifr_newname ifr_ifru.ifru_newname;//新名称 #define ifr_seeting ifr_ifru.ifru_settings;//设备协议设置
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
struct arpreq{ struct sockaddr arp_pa;//协议地址 struct sockaddr arp_ha;//硬件地址 int arp_flags;//标记 struct sockaddr arp_netmask;//协议地址的子网掩码 char arp_dev[16];//查询网络接口的名称 }
ARP高速缓存操作,包含IP地址和硬件地址的映射表, 操作ARP高速缓存的命令字 SIOCDARP,SIOCGARP,SIOCSARP分别是删除ARP高速缓存的一条记录,获得ARP高速缓存的一条记录和修改ARP高速缓存的一条记录
相关例子[以下为网络摘录:已经上机验证过]:
#include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <fcntl.h> #include <net/if.h> int main(int argc, char*argv[]) { int s,sv6; int err; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket error"); return -1; } 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); } err = ioctl(s, SIOCGIFMTU, &ifr); if (!err) { printf("SIOCGIFMTU:%d\n", ifr.ifr_mtu); } 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]); } 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); } 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); } 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); } 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); }
实例二:[修改代码后,已经上机验证过] MAC地址的设置和获取
#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <net/if.h> typedef unsigned char u8 #define LOGD(...) do {printf(__VA_ARGS__); printf("\n");} while(0) int set_mac(u8* addr, int len); int get_mac(u8* addr, int len); int main(int argc, char*argv[]) { int ret = 0; u8 addr[6] = {0x00, 0x00, 0x00, 0x61, 0x20, 0x58}; ret = set_mac(addr, 6); if (ret < 0) { LOGD("set_mac() error"); return 0; } LOGD("set_mac() done"); ret = get_mac(addr, 6); if (ret < 0) { LOGD("get_mac() error"); return 0; } LOGD("get_mac() done"); char buf[32] = {0}; snprintf(buf, 32, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); LOGD("%s", buf); return 0; } int set_mac(u8* addr, int len) { int s; int ret; struct ifreq ifr; if (len < 6) { LOGD("set_mac(), invalid length"); return -1; } s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { LOGD("socket() error: %s", strerror(errno)); return -1; } strcpy(ifr.ifr_ifrn.ifrn_name, "usb0"); ifr.ifr_ifru.ifru_hwaddr.sa_family = 1; memcpy(ifr.ifr_ifru.ifru_hwaddr.sa_data, addr, 6); ret = ioctl(s, SIOCSIFHWADDR, &ifr); if (ret != 0) { LOGD("ioctl(SIOCSIFHWADDR) error: %d(%s)", errno, strerror(errno)); return -1; } return 0; } int get_mac(u8* addr, int len) { int s; int ret; struct ifreq ifr; if (len < 6) { LOGD("get_mac(), invalid length"); return -1; } s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { LOGD("socket() error: %s", strerror(errno)); return -1; } strcpy(ifr.ifr_ifrn.ifrn_name, "usb0"); ret = ioctl(s, SIOCGIFHWADDR, &ifr); if (ret != 0) { LOGD("ioctl(SIOCSIFHWADDR) error: %s", strerror(errno)); return -1; } memcpy(addr, ifr.ifr_ifru.ifru_hwaddr.sa_data, 6); return 0; }
实例三:网卡连接状态[修改后,已经上机验证]
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/socket.h> #include <linux/if.h> typedef unsigned short u16; typedef unsigned int u32; typedef unsigned char u8; typedef unsigned long long u64 #include <linux/sockios.h> #include <linux/ethtool.h> int get_netlink_status(const char *if_name); int main(int argc, char* argv[]) { if(argc != 2) { fprintf(stderr, "usage: %s <ethname>.\n", argv[0]); return -1; } if(getuid() != 0) { fprintf(stderr, "Netlink Status Check Need Root Power.\n"); return 1; } printf("Net link status: %s.\n", get_netlink_status(argv[1])==1?"up":"down"); return 0; } // if_name like "ath0", "eth0". Notice: call this function // return value: // -1 -- error , details can check errno // 1 -- interface link up // 0 -- interface link down. int get_netlink_status(const char *if_name) { int skfd; struct ifreq ifr; struct ethtool_value edata; edata.cmd = ETHTOOL_GLINK; edata.data = 0; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name) - 1); ifr.ifr_data = (char *) &edata; if (( skfd = socket( AF_INET, SOCK_DGRAM, 0 )) == 0) return -1; if(ioctl( skfd, SIOCETHTOOL, &ifr ) == -1) { close(skfd); return -1; } close(skfd); return edata.data; }