前言
近期,发现判断网卡是否Ready的程序出了一些问题,有些网卡明明没有插线,依然会被判定为active的网卡。我去跟踪了下代码:
# struct ifreq { // FOR SIOCGIFFLAGS:
# char ifrn_name[IFNAMSIZ]
# short ifru_flags
# };
my $STRUCT_IFREQ_SIOCGIFFLAGS = 'Z' . IFNAMSIZ . 's1';
sub get_active_network_interfaces {
# Use the interface name list from /proc/net/dev
open my $fh, '<', '/proc/net/dev'
or die "failed to open /proc/net/dev: $!\n";
# And filter by IFF_UP flag fetched via a PF_INET6 socket ioctl:
my $sock;
socket($sock, PF_INET6, SOCK_DGRAM, &IPPROTO_IP)
or socket($sock, PF_INET, SOCK_DGRAM, &IPPROTO_IP)
or return [];
my $ifaces = [];
while(defined(my $line = <$fh>)) {
next if $line !~ /^\s*([^:\s]+):/;
my $ifname = $1;
my $ifreq = pack($STRUCT_IFREQ_SIOCGIFFLAGS, $ifname, 0);
if (!defined(ioctl($sock, SIOCGIFFLAGS, $ifreq))) {
warn "failed to get interface flags for: $ifname\n";
next;
}
my ($name, $flags) = unpack($STRUCT_IFREQ_SIOCGIFFLAGS, $ifreq);
push @$ifaces, $ifname if ($flags & IFF_UP);
}
close $fh;
close $sock;
return $ifaces;
}
发现源码使用通过调用 ioctl SIOCGIFFLAGS 来判定网卡是否已经ready。
关于SIOCGIFFLAGS
Linux ioctl支持一下两个标志位:
- SIOCGIFFLAGS: 获取设备的活动标志位
- SIOCSIFFLAGS: 修改设备的活动标志位
SIOCGIFFLAGS, SIOCSIFFLAGS
Get or set the active flag word of the device. ifr_flags contains a bit mask of the following values:
Device flags
IFF_UP Interface is running.
IFF_BROADCAST Valid broadcast address set.
IFF_DEBUG Internal debugging flag.
IFF_LOOPBACK Interface is a loopback interface.
IFF_POINTOPOINT Interface is a point-to-point link.
IFF_RUNNING Resources allocated.
IFF_NOARP No arp protocol, L2 destination address not set.
IFF_PROMISC Interface is in promiscuous mode.
IFF_NOTRAILERS Avoid use of trailers.
IFF_ALLMULTI Receive all multicast packets.
IFF_MASTER Master of a load balancing bundle.
IFF_SLAVE Slave of a load balancing bundle.
IFF_MULTICAST Supports multicast
IFF_PORTSEL Is able to select media type via ifmap.
IFF_AUTOMEDIA Auto media selection active.
IFF_DYNAMIC The addresses are lost when the interface goes down.
IFF_LOWER_UP Driver signals L1 up (since Linux 2.6.17)
IFF_DORMANT Driver signals dormant (since Linux 2.6.17)
IFF_ECHO Echo sent packets (since Linux 2.6.25)本次我们重点介绍IFF_UP和 IFF_RUNNING。
IFF_UP: 这个标志位表示的是从管理上讲,这个网口是UP的,是Ready的,但是并不代表连接状态。即网卡已经准备好了,但是并不意味着网线已插。
IFF_RUNNING: 这个标志位表示operational state,如果置位的话,表示CONNECTED的。
验证
做个简单验证:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <string.h>
#define BUFSIZE 1024
int main(int argc, char *argv[])
{
struct ifreq ifr;
int sfd ;
memset(&ifr, '\0' , sizeof(struct ifreq));
strcpy(ifr.ifr_name, argv[1]);
sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
int ret = ioctl(sfd, SIOCGIFFLAGS, (char *)&ifr);
if(ret !=0)
{
printf("failed to exec ioctl");
goto out;
}
printf("%10s IFF_UP: %d\n", argv[1], !!(ifr.ifr_flags & IFF_UP));
printf("%10s IFF_RUNNING: %d \n",argv[1], !!(ifr.ifr_flags & IFF_RUNNING));
out:
close(sfd);
exit(ret);
}
我的笔记本的网口情况如下:
manu-latitude3510 CODE/C » 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: 1503282 11818 0 0 0 0 0 0 1503282 11818 0 0 0 0 0 0
eno1: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
wlp1s0: 14947732561 40405691 0 822774 0 0 0 0 650855154 3591298 0 0 0 0 0 0
manu-latitude3510 CODE/C »
其中eno1 是有线,并未插线,wlp1s0是无线,已经连接的状态。
manu-latitude3510 CODE/C » ./nettool eno1
eno1 IFF_UP: 1
eno1 IFF_RUNNING: 0
manu-latitude3510 CODE/C » ./nettool wlp1s0
wlp1s0 IFF_UP: 1
wlp1s0 IFF_RUNNING: 1
manu-latitude3510 CODE/C » ./nettool lo
lo IFF_UP: 1
lo IFF_RUNNING: 1
manu-latitude3510 CODE/C