Linux ifconfig(network interfaces configuring)
- Linux ifconfig命令用于显示或设置网络设备。ifconfig可设置网络设备的状态,或是显示目前的设置。
- 同netstat一样,ifconfig源码也位于net-tools中。
- 源码位于net-tools工具包中,这是linux网络的基本工具包,此外还有arp,hostname,route等命令。
PS
此文章不考虑ifconfig的具体功能介绍,而是侧重与驱动和内核的交互,下面简单描述下该工具的使用实例,并且代码并不包含对于ifconfig的标准实现,仅是注重实现过程。
实例
$ ifconfig
eth0 Link encap:Ethernet HWaddr 00:0c:29:e7:65:70
inet addr:192.168.101.128 Bcast:192.168.101.255 Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fee7:6570/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:17080658 errors:73 dropped:2 overruns:0 frame:0
TX packets:17346739 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:4038046913 (4.0 GB) TX bytes:2731849230 (2.7 GB)
Interrupt:19 Base address:0x2000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:58049 errors:0 dropped:0 overruns:0 frame:0
TX packets:58049 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:5540611 (5.5 MB) TX bytes:5540611 (5.5 MB)
启动关闭指定网卡
# ifconfig eth0 down
# ifconfig eth0 up
用ifconfig修改MAC地址
# ifconfig eth0 down //关闭网卡
# ifconfig eth0 hw ether 00:AA:BB:CC:DD:EE //修改MAC地址
# ifconfig eth0 up //启动网卡
配置IP地址
# ifconfig eth0 192.168.1.56
//给eth0网卡配置IP地址
# ifconfig eth0 192.168.1.56 netmask 255.255.255.0
// 给eth0网卡配置IP地址,并加上子掩码
# ifconfig eth0 192.168.1.56 netmask 255.255.255.0 broadcast 192.168.1.255
// 给eth0网卡配置IP地址,加上子掩码,加上个广播地址
启用和关闭ARP协议
# ifconfig eth0 arp //开启
# ifconfig eth0 -arp //关闭
/proc/net/dev
$ cat /proc/net/dev
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: 5558739 58239 0 0 0 0 0 0 5558739 58239 0 0 0 0 0 0
eth0: 4038107838 17081103 73 2 0 0 0 0 2731895950 17347146 0 0 0 0 0 0
- 这里列出了所有网络设备的其属性状态和收发包情况。ifconfig会open这个设备查找匹配信息。
那知道了如何使用该工具,那么具体是怎么实现的呢???
就到了下面的源码分析
代码分析
先来个简单的查看信息,如下是获取ip的流程
- 先通过ioctl获得本地所有接口的信息,并保存在ifconf中
- 再从ifconf中取出每一个ifreq中表示ip地址的信息
分析一:(获取ip地址)
接下来我们只需要从一个一个的接口信息获取ip地址信息即可。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <linux/if.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)
{
exit(1);
}
ioctl(sockfd, SIOCGIFCONF, &ifconf); //获取所有接口信息
//接下来一个一个的获取IP地址
ifreq = (struct ifreq*)buf;
for (i=(ifconf.ifc_len/sizeof (struct ifreq)); i>0; i--)
{
// if(ifreq->ifr_flags == AF_INET){ //for ipv4
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;
执行结果
ifconfig_achieve$ ./ifconfig_wlan_ip.o
name = [lo]
local addr = [127.0.0.1]
name = [ens33]
local addr = [192.168.101.137]
解析:
- ifc_len:表示用来存放所有接口信息的缓冲区长度
- ifc_buf:表示存放接口信息的缓冲区
- 用ioctl获得本地ip地址时要用到两个结构体ifconf和ifreq,我们需要在程序开始时对ifconf的ifc_len和ifc_buf进行初始化
- 使用ioctl获取所有接口信息,完成后ifc_len内存放实际获得的接口信息总长度
并且信息被存放在ifc_buf中。 - struct ifconf:通常是用来保存所有接口信息的
- struct ifreq:这个结构定义在include/net/if.h,用来配置ip地址,激活接口,配置MTU等接口信息的
- 想要获取当前网口网线插入状态,需要用到ifreq结构体,获取网卡的信息,然后socket结合网卡驱动的ioctl,就可以得到与网线插入状态相关的数据。
简单部分罗列下上面的结构体:
//ifconf通常是用来保存所有接口信息的
struct ifconf
{
int ifc_len; /* size of buffer */
union
{
char *ifcu_buf; /* input from user->kernel*/
struct ifreq *ifcu_req; /* return from kernel->user*/
} ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
#define ifc_req ifc_ifcu.ifcu_req /* array of structures */
//ifreq用来保存某个接口的信息
struct ifreq
{
char ifr_name[IFNAMSIZ];
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
} ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr
分析二(获取基础信息,并更改设置ip)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <linux/if.h>
#include <string.h>
#define INTERFACE "ens33"
void port_status(unsigned int flags)
{
if(flags & IFF_UP)
{
printf("is up\n");
}
if(flags & IFF_BROADCAST)
{
printf("is broadcast\n");
}
if(flags & IFF_LOOPBACK)
{
printf("is loop back\n");
}
if(flags & IFF_POINTOPOINT)
{
printf("is point to point\n");
}
if(flags & IFF_RUNNING)
{
printf("is running\n");
}
if(flags & IFF_PROMISC)
{
printf("is promisc\n");
}
}
int main()
{
int fd;
struct ifreq buf[5]; //这个结构定义在/usr/include/net/if.h,用来配置和获取ip地址,掩码,MTU等接口信息的。 ifreq用来保存某个接口的信息
struct ifconf ifc; //ifconf通常是用来保存所有接口信息的
int ret = 0,i=0,j=0;
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = (caddr_t) buf;
uint32_t ip = 0x8165a8c0;//192.168.101.129
struct sockaddr_in sin;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0))<0)
{
perror("socket" );
exit(1);
}
else
printf("----------fd:%d-----------\n",fd);
ret = ioctl(fd, SIOCGIFCONF, (char*)&ifc);
if(ret)
{
printf("get if config info failed");
return -1;
}
for (i=(ifc.ifc_len/sizeof (struct ifreq)); i>0; i--)
{
printf("--------------name = [%s]------------\n" , buf[j].ifr_name);
if(strcmp(buf[j].ifr_name,"ens33")==0)
{
printf("--------------ens33网卡---------------\n");
strncpy(buf[j].ifr_name, INTERFACE, 5);
memset(&sin, 0, sizeof(struct sockaddr));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ip;
memcpy(&buf[j].ifr_addr, &sin, sizeof(struct sockaddr));
ret = ioctl(fd, SIOCSIFADDR, (char*)&buf[j]);
if(ret)
continue;
}
ret = ioctl(fd, SIOCGIFADDR, (char*)&buf[j]);
if(ret)
continue;
printf("----------local addr = [%s]----------\n" ,inet_ntoa(((struct sockaddr_in*)&(buf[j].ifr_addr))->sin_addr));
ret = ioctl(fd, SIOCGIFFLAGS, (char*)&buf[j]);
if(ret)
continue;
port_status(buf[j].ifr_flags);
printf("--------------ifr_flags = [%d]------------\n" , buf[j].ifr_flags);
ret = ioctl(fd, SIOCGIFMTU, (char*)&buf[j]);
if(ret)
continue;
printf("-------mtu:%d----------\n",(unsigned int)(buf[j].ifr_ifru.ifru_mtu));
/* 获取当前网卡的mac 命令cat /sys/class/net/eth0/address*/
ret = ioctl(fd, SIOCGIFHWADDR, (char*)&buf[j]);
if(ret)
continue;
printf("%02x:%02x:%02x:%02x:%02x:%02x\n\n",
(unsigned char)buf[j].ifr_hwaddr.sa_data[0],
(unsigned char)buf[j].ifr_hwaddr.sa_data[1],
(unsigned char)buf[j].ifr_hwaddr.sa_data[2],
(unsigned char)buf[j].ifr_hwaddr.sa_data[3],
(unsigned char)buf[j].ifr_hwaddr.sa_data[4],
(unsigned char)buf[j].ifr_hwaddr.sa_data[5]
);
j++;
}
close(fd);
return 0;
}
运行结果:
./ifconfig_wlan.o
----------fd:3-----------
--------------name = [lo]------------
----------local addr = [127.0.0.1]----------
is up
is loop back
is running
--------------ifr_flags = [73]------------
-------mtu:65536----------
00:00:00:00:00:00
--------------name = [ens33]------------
--------------ens33网卡---------------
----------local addr = [192.168.101.129]----------
is up
is broadcast
is running
--------------ifr_flags = [4163]------------
-------mtu:1500----------
00:0c:29:24:11:8b
注意:
通过
gcc ifconfig_wlan.c -o ifconfig_wlan.o
命令编译出来的可执行文件本身是无法直接执行的,debug如下:
strace ./ifconfig_wlan.o
失败 log:
是由于编译的可执行文件权限不足导致ioctl设置失败,如下是解决流程:
1. gcc ifconfig_wlan_information.c -o ifconfig_wlan_information.o
2. sudo cp ifconfig_wlan_information.o ifconfig_wlan.o
3. sudo chmod u+s ifconfig_wlan.o
4. strace ./ifconfig_wlan.o
如下为再次执行后的结果:
结语
如上主要讲的是设置和获取,希望能够抛砖引玉,让大家学到更多