分享

linux下ioctl操作网络接口

 身子莫 2013-05-13
在linux下使用ioctl操作网络接口时,首先需要两个结构体结构ifconf和ifreq
ifconf用来保存所有网络接口的信息,其结构体为:
  1. struct ifconf   
  2. {   
  3.     int ifc_len;      /* size of buffer */  //存放接口信息所需的长度
  4.     union {              
  5.        char *ifc_buf; /* buffer address */   //存放接口信息的地址
  6.        struct ifreq *ifc_req; /* array of structures */  
  7.     };    
  8. };       
  9. #define ifc_buf ifc_ifcu.ifcu_buf   
  10. #define ifc_req ifc_ifcu.ifcu_req


ifreq用来保存某个接口的信息,用来获取Ip地址,MAC,子网掩码等网络接口信息,其结构体为:
  1. struct ifreq{  
  2. #define IFHWADDRLEN 6  
  3. #define IFNAMSIZ IF_NAMESIZE  
  4.     union{  
  5.         char ifrn_name[IFNAMSIZ];  //接口的名字  
  6. } ifr_ifrn;  
  7.   
  8.   
  9.     union{  
  10.         struct sockaddr ifru_addr;  //接口的IP地址
  11.         struct sockaddr ifru_dstaddr;  
  12.         struct sockaddr ifru_broadaddr;  //接口的广播地址
  13.         struct sockaddr ifru_netmask;    //接口的子网掩码
  14.         struct sockaddr ifru_hwaddr;     //接口的MAC地址
  15.         short int ifru_flags;  
  16.         int ifru_ivalue;  
  17.         int ifru_mtu;                   //接口传输单元的大小
  18.         struct ifmap ifru_map;  
  19.         char ifru_slave[IFNAMSIZ];   
  20.         char ifru_newname[IFNAMSIZ];  
  21.         __caddr_t ifru_data;  
  22. } ifr_ifru;  
  23. };  
  24. #define ifr_name ifr_ifrn.ifrn_name   
  25. #define ifr_hwaddr ifr_ifru.ifru_hwaddr   
  26. #define ifr_addr ifr_ifru.ifru_addr   
  27. #define ifr_dstaddr ifr_ifru.ifru_dstaddr   
  28. #define ifr_broadaddr ifr_ifru.ifru_broadaddr   
  29. #define ifr_netmask ifr_ifru.ifru_netmask   
  30. #define ifr_flags ifr_ifru.ifru_flags   
  31. #define ifr_metric ifr_ifru.ifru_ivalue   
  32. #define ifr_mtu ifr_ifru.ifru_mtu   
  33. #define ifr_map ifr_ifru.ifru_map   
  34. #define ifr_slave ifr_ifru.ifru_slave   
  35. #define ifr_data ifr_ifru.ifru_data   
  36. #define ifr_ifindex ifr_ifru.ifru_ivalue   
  37. #define ifr_bandwidth ifr_ifru.ifru_ivalue   
  38. #define ifr_qlen ifr_ifru.ifru_ivalue   
  39. #define ifr_newname ifr_ifru.ifru_newname   
  40. #define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)  
  41. #define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)  
  42. #define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)
  43.   
具体操作方法是:
1.通过ioctl获得本地所有接口的信息并保存在ifconf结构体中;
2.在从ifconf中获得具体某个ifreq的接口信息
其中:
ifc_len:表示用来存放所有接口信息的缓冲区长度

ifc_buf:表示存放接口信息的缓冲区

因此我们需要在程序开始时对ifconf的ifc_led和ifc_buf进行初始化
接下来使用ioctl获取所有接口信息,完成后ifc_len内存放实际获得的借口信息总长度
并且信息被存放在ifc_buf中。


实例:

#include <string.h>

 #include <net/if.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <netinet/in.h>  
 int main()
 {
 	int i=0;
 	int sockfd;
 	struct ifconf ifconf;
 	unsigned char buf[512];
 	struct ifreq *ifreq;
 
 	//初始化ifconf 
 	ifconf.ifc_len = 512;
 	ifconf.ifc_buf = buf;
 
 	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
 	{
 		perror("socket");
 		exit(1);
 	}
 	ioctl(sockfd, SIOCGIFCONF, &ifconf); //获取所有接口信息 
 
 	//接下来一个一个的获取IP地址 
 	ifreq = (struct ifreq*)buf;
 	for(i=(ifconf.ifc_len/sizeof(struct ifreq)); i>0; i--)
 	{
 		printf("name = [%s]n", ifreq->ifr_name);
 		printf("local addr = [%s]n",
 		inet_ntoa(((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr));
 		ifreq++; 
 	}
 return 0;
 }

此参考程序是通过ioctl获得本地接口的信息,我们也可以通过操作socket的函数写一些小的应用程序,下面的例子是实现ifconfig的源代码(自测试,可通过)
#include <stdio.h> //printf()
#include <unistd.h> //ioctl()
#include <sys/ioctl.h> //ioctl
#include <sys/socket.h> //socket()
#include <net/if.h> //struct ifconf{} & struct ifreq{}
#include <string.h> //strcpy()
#include <arpa/inet.h> //inet_ntoa()
#include <stdlib.h> //malloc() & free()
int print_if_addr(int fd, char *interface_name); //打印接口的ip地址
int print_if_mac(int fd, char *interface_name); //打印接口的mac地址
int print_if_broadaddr(int fd, char *interface_name); //打印接口的广播地址
int print_if_mask(int fd, char *interface_name); //打印接口的掩码
int print_if_mtu(int fd, char *interface_name); //打印接口的mtu
int print_all_interface(); //打印所有接口的基本信息
int print_if_addr6(char *interface_name); //打印接口的ipv6地址
int print_interface_info(char *interface_name); //打印接口的以上所有信息
int set_if_up(char *interface_name); //启动接口
int set_if_down(char *interface_name); //关闭接口
int set_if_ip(char *interface_name, char *ip_str); //设置接口的ip地址
void usage(); //打印该程序的使用手册
int main(int argc, char **argv)
{
int sockfd;
switch(argc)
{
case 1:
print_all_interface();
break;
case 2:
print_interface_info(argv[1]);
break;
case 3:
if(strcmp(argv[2], "up") == 0)
set_if_up(argv[1]);
else if(strcmp(argv[2], "down") == 0)
set_if_down(argv[1]);
else
set_if_ip(argv[1], argv[2]);
break;
default:
usage();
break;
}
return 0;
}
void usage()
{
printf("usage: ./myifconfig [interface [down|up|ip]]\n");
}
int print_if_addr(int fd, char *if_name)
{
struct sockaddr_in *ip;
struct ifreq ifr;
strcpy(ifr.ifr_name, if_name);
if(ioctl(fd, SIOCGIFADDR, &ifr) < 0)
{
perror("ioctl SIOCGIFADDR error");
return -1;
}
ip = (struct sockaddr_in *)&ifr.ifr_addr; //获得ipv4地址
printf(" IP: %s\n", inet_ntoa(ip->sin_addr)); //将ipv4地址转换为主机字节序的字符串并输出
return 0;
}
int print_if_broadaddr(int fd, char *if_name)
{
struct sockaddr_in *ip;
struct ifreq ifr;
strcpy(ifr.ifr_name, if_name);
if(ioctl(fd, SIOCGIFBRDADDR, &ifr) < 0)
{
perror("ioctl SIOCGIFBRDADDR error");
return -1;
}
ip = (struct sockaddr_in *)&ifr.ifr_broadaddr; //获得广播地址
printf(" Broadcast: %s\n", inet_ntoa(ip->sin_addr));
return 0;
}
int print_if_mask(int fd, char *if_name)
{
struct sockaddr_in *ip;
struct ifreq ifr;
strcpy(ifr.ifr_name, if_name);
if(ioctl(fd, SIOCGIFNETMASK, &ifr) < 0)
{
perror("ioctl SIOCGIFNETMASK error");
return -1;
}
ip = (struct sockaddr_in *)&ifr.ifr_ifru.ifru_netmask; //获得子网掩码。注意!我们仍放在struct aockaddr_in结构中返回
printf(" Mask: %s\n", inet_ntoa(ip->sin_addr));
return 0;
}
int print_if_mac(int fd, char *if_name)
{
unsigned char *p; //注意! 这里要用unsigned char,而不是char!因为char要对[1xxx xxxx]这样的数进行补码运算的。
//但我们等下要打印的mac地址是不需要符号的数值
struct ifreq ifr;
strcpy(ifr.ifr_name, if_name);
if(ioctl(fd, SIOCGIFHWADDR, &ifr) < 0)
{
perror("ioctl SIOCGIFHWADDR error");
return -1;
}
p = (char *)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0]; //获得接口的MAC地址,用字符串指针返回
printf(" MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5));
//printf(" MAC:%02x:%02x:%02x:%02x:%02x:%02x/n", *p++, *p++, *p++, *p++, *p++, *p++);
//这么写会导致输出为倒序。这并不是p指针有什么问题,不信你可以用
// for(;;)
// printf(p++);
//来试验就是正确的,我猜倒序的原因是编译器的优化问题吧
return 0;
}
int print_if_mtu(int fd, char *if_name)
{
unsigned int mtu;
struct ifreq ifr;
strcpy(ifr.ifr_name, if_name);
if(ioctl(fd, SIOCGIFMTU, &ifr) < 0)
{
perror("ioctl SIOCGIFMTU error");
return -1;
}
mtu = ifr.ifr_ifru.ifru_mtu; //获得子网掩码。注意!我们仍放在struct aockaddr_in结构中返回
printf(" MTU: %d\n", mtu);
return 0;
}
int print_if_addr6(char *if_name)
{
unsigned int mtu;
struct ifreq ifr;
int sockfd;
if((sockfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
{
perror("Socket error");
return -1;
} // 创建用来检查网络接口的套接字
/* strcpy(ifr.ifr_name, if_name);
if(ioctl(fd, SIOCGIFMTU, &ifr) < 0)
{
perror("ioctl SIOCGIFMTU error");
return -1;
}
mtu = ifr.ifr_ifru.ifru_mtu; //获得子网掩码。注意!我们仍放在struct aockaddr_in结构中返回
printf(" ipv6: %d/n", mtu);
*/
//未写完,不知道怎么获得ipv6地址。。。
return 0;
}
int print_all_interface()
{
struct ifconf ifc;
struct ifreq *ifr_p;
int sockfd, len, old_len = 0, i;
char *buf;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Socket error");
return -1;
} // 创建用来检查网络接口的套接字
len = 10 * sizeof(struct ifreq);
for( ; ; )
{
if((buf = malloc(len)) == NULL)
{
perror("malloc error");
return -1;
}
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if(ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
{
perror("ioctl SIOCGIFCONF error");
return -1;
}
if(ifc.ifc_len == old_len)
break;
old_len = ifc.ifc_len;
len += 10 * sizeof(struct ifreq);
free(buf);
}
printf("we have %d Interfaces\n", ifc.ifc_len / sizeof(struct ifreq));
for(i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++)
{
ifr_p = &ifc.ifc_req[i];
printf(" Interface [%s]:\n", ifr_p->ifr_name);
print_if_addr(sockfd, ifr_p->ifr_name);
print_if_broadaddr(sockfd, ifr_p->ifr_name);
print_if_mask(sockfd, ifr_p->ifr_name);
print_if_mac(sockfd, ifr_p->ifr_name);
print_if_mtu(sockfd, ifr_p->ifr_name);
}
close(sockfd);
return 0;
}
int print_interface_info(char *if_name)
{
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Socket error");
return -1;
} // 创建用来检查网络接口的套接字
printf("%s:\n", if_name);
print_if_addr(sockfd, if_name);
print_if_broadaddr(sockfd, if_name);
print_if_mask(sockfd, if_name);
print_if_mac(sockfd, if_name);
print_if_mtu(sockfd, if_name);
close(sockfd);
return 0;
}
int set_if_up(char *if_name) //启动接口
{
struct ifreq ifr;
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Socket error");
return -1;
} // 创建用来检查网络接口的套接字
strcpy(ifr.ifr_name, if_name);
if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
{
perror("ioctl SIOCGIFFLAGS error");
return -1;
}
ifr.ifr_flags |= IFF_UP;
if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
{
perror("ioctl SIOCSIFFLAGS error");
return -1;
}
return 0;
}
int set_if_down(char *if_name) //关闭接口
{
struct ifreq ifr;
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Socket error");
return -1;
} // 创建用来检查网络接口的套接字
strcpy(ifr.ifr_name, if_name);
if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
{
perror("ioctl SIOCGIFFLAGS error");
return -1;
}
ifr.ifr_flags &= ~IFF_UP; //将IIF_UP取反后与原来的标志进行 与运算。
if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
{
perror("ioctl SIOCSIFFLAGS error");
return -1;
}
return 0;
}
int set_if_ip(char *if_name, char *ip_str) //设置接口的ip地址
{
struct ifreq ifr;
struct sockaddr_in ip_addr;
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("Socket error");
return -1;
} // 创建用来检查网络接口的套接字
ip_addr.sin_family = AF_INET;
if(inet_pton(AF_INET, ip_str, &ip_addr.sin_addr) < 1)
{
perror("error ipv4 addr:");
return -1;
}
strcpy(ifr.ifr_name, if_name);
memcpy(&ifr.ifr_addr, &ip_addr, sizeof(struct sockaddr_in));
if(ioctl(sockfd, SIOCSIFADDR, &ifr) < 0)
{
perror("ioctl SIOCSIFADDR error");
return -1;
}
return 0;
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多