Linux 检测网卡与网线连接状态

来源:互联网 发布:连续剧 容祖儿 知乎 编辑:程序博客网 时间:2024/06/05 17:41
检测网卡于网线到链接状态是使用ioctl向socket发送SIOCETHTOOL命令。 





#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;

#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 ", 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

// need root privilege.

// 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;
}



参考网络资料,经整理验证ok.

[cpp] view plaincopy
  1. #include <sys/socket.h>  
  2.   
  3. #include <sys/ioctl.h>  
  4. #include <linux/if.h>  
  5. #include <string.h>  
  6. #include <stdio.h>  
  7.   
  8. int net_detect(char* net_name)  
  9. {  
  10.     int skfd = 0;  
  11.     struct ifreq ifr;  
  12.     struct sockaddr_in *pAddr = NULL;  
  13.     skfd = socket(AF_INET, SOCK_DGRAM, 0);  
  14.     if(skfd < 0)  
  15.     {  
  16.         printf("%s:%d Open socket error!\n", __FILE__, __LINE__);  
  17.         return -1;  
  18.     }  
  19.     strcpy(ifr.ifr_name, net_name);  
  20.     if(ioctl(skfd, SIOCGIFFLAGS, &ifr) <0 )  
  21.     {  
  22.         printf("%s:%d IOCTL error!\n", __FILE__, __LINE__);  
  23.         printf("Maybe ethernet inferface %s is not valid!", ifr.ifr_name);  
  24.         close(skfd);  
  25.         return -1;  
  26.     }  
  27.     if(ifr.ifr_flags & IFF_RUNNING)  
  28.     {  
  29.         printf("%s is running :)\n", ifr.ifr_name);  
  30.     }  
  31.     else  
  32.     {  
  33.         printf("%s is not running :(\n", ifr.ifr_name);  
  34.     }  
  35.     if(ioctl(skfd,SIOCGIFADDR,&ifr)<0)  
  36.     {  
  37.         printf("SIOCGIFADDR IOCTL error!\n");  
  38.         close(skfd);  
  39.         return -1;  
  40.     }  
  41.     pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);  
  42.     printf("ip addr :[%s]\n", inet_ntoa(pAddr->sin_addr));  
  43.     if(ioctl(skfd,SIOCGIFHWADDR,&ifr)<0)  
  44.     {  
  45.         printf("SIOCGIFHWADDR IOCTL error!\n");  
  46.         close(skfd);  
  47.         return -1;  
  48.     }  
  49.     printf("mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n",  
  50.         (unsigned char)ifr.ifr_hwaddr.sa_data[0],  
  51.         (unsigned char)ifr.ifr_hwaddr.sa_data[1],  
  52.         (unsigned char)ifr.ifr_hwaddr.sa_data[2],  
  53.         (unsigned char)ifr.ifr_hwaddr.sa_data[3],  
  54.         (unsigned char)ifr.ifr_hwaddr.sa_data[4],  
  55.         (unsigned char)ifr.ifr_hwaddr.sa_data[5]);  
  56.     close(skfd);  
  57.     return 0;  
  58. }  
  59.   
  60. void main()  
  61. {  
  62.     net_detect("eth0");  
  63.   
  64. }  




参考网络资料:

1、对于基于linux2.4内核的uclinux系统如何实现在应用层监控网线插拔状态?

2、硬件环境:IPS100ARM7TDMI

3、实现过程

由于linux下的ifconfig命令就能够实现在应用层监控网线插拔状态,例如当网线连接正常时,使用ifconfig eth0命令,打印的信息中会有RUNNING,而拔掉网线后,再使用ifconfig eth0命令,RUNNING就不见了。所以,实现Linux应用层监控网线插入状态就相当于自己写一个ifconfig函数。

基于这个思路,首先参考ifconfig的源码,可以参考linux自身提供的ifconfig的源码(linux提供的有ifconfig.c函数),也可以在网上查找。首先找到linux自身提供的ifconfig.c函数,既然ifconfig通过RUNNING来判断网络的通断的状况的,首先找到RUNNING的出处,搜索一下发现这句话,

If(ptr->flags &IFF_RUNNIG)

{

Printf(__("RUNNING"));

}

以这个为切入点,层层向上找,分别是被些函数调用,最后我们进入了main函数(这是理所当然的),在这里的到RUNNING→ife_print()→if_pirint()→main().。先看ife_prinf()函数,这里没有和内核通信,这时我们再看if_print()函数,这里刚好有和内核通信的函数,else {                       
        struct interface *ife;

        ife = lookup_interface(ifname);
        res = do_if_fetch(ife);
        if (res >= 0)
            ife_print(ife);
    }

    这时需要看到lookup_interface(ifname)和do_if_fetch(ife)的原型,由于头文件很多,我没办法知道这两个函数在那个头文件中,所以干脆在网上找到这两个函数的原型,我们找到了一篇ifconfing源码分析的文章,http://viscar.blog.sohu.com/2574772.html,这里面找到了函数原型,我们看到 do_if_fetch()函数里面又调用了if_fetch()函数,太好了,这个函数正是我们所需要的,在这里面有个很重要的函数ioctl(),ioctl用于向设备发送控制和配置命令,驱动程序可以接收ioctl的数据,并返回数据,ioctl的原型为

    ioctl(int d, int cmd, ......),

d是某个设备的文件描述符,cmdioctl的命令,可变参数取决于cmd,是指向变量或结构体的指针。

这里面用到的设备文件描述符skfd = socketAF_INET,SOCK_DGRAM,0);这是一个套接字,作用是打开一个网络通讯端口,成功的话返回skfd,相当于一个文件描述符。

   有了这些之后我们就可以写一个自己的简洁版的ifconfig函数了,现在ubuntu10.04上编写代码,代码里面的ioctl函数这样写ioctl(skfd, SIOCGIFFLAGS, &ifr) ,其中SIOCGIGGLAGS表示得到sock i/oflags,这时因为,RUNNIGN的条件是ptr->flags &IFF_RUNNING 是否为真,代码里面直接体现eth0,函数为strcpy(ifr.ifr_name, “eth0”);完整的代码如下:
(以下代码在ubuntu 10.04下运行通过)

[cpp] view plaincopy
  1. #include "icconst.h"  
  2. #include <sys/socket.h>  
  3. #include <sys/ioctl.h>  
  4. #include <linux/if.h>  
  5. #include <string.h>  
  6. #include <stdio.h>  
  7. #include "net_detect.h"  
  8.   
  9. int net_detect(char* net_name)  
  10. {  
  11. int skfd = 0;  
  12. struct ifreq ifr;   
  13. skfd = socket(AF_INET, SOCK_DGRAM, 0);  
  14.   
  15. if(skfd < 0)  
  16. {  
  17. printf("%s:%d Open socket error!\n", __FILE__, __LINE__);  
  18. return -1;  
  19. }  
  20.   
  21. strcpy(ifr.ifr_name, net_name);  
  22.   
  23. if(ioctl(skfd, SIOCGIFFLAGS, &ifr) <0 )  
  24. {  
  25. printf("%s:%d IOCTL error!\n", __FILE__, __LINE__);  
  26. printf("Maybe ethernet inferface %s is not valid!", ifr.ifr_name);  
  27. close(skfd);  
  28. return -1;  
  29. }  
  30.   
  31. if(ifr.ifr_flags & IFF_RUNNING)  
  32. {  
[cpp] view plaincopy
  1. printf("%s is running :)\n", ifr.ifr_name);  
  2. }  
  3. else  
  4. {  
  5. printf("%s is not running :(\n", ifr.ifr_name);  
  6. }  
  7.   
  8. /*****************以下为识别ip地址,mac地址***************************************/  
  9. if(ioctl(skfd,SIOCGIFADDR,&ifr)<0)  
  10. {  
  11.   printf("SIOCGIFADDR IOCTL error!\n");  
  12.   close(skfd);  
  13.   return -1;  
  14. }  
[cpp] view plaincopy
  1. printf("ip addr :[%s]\n",inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));  
[cpp] view plaincopy
  1. if(ioctl(skfd,SIOCGIFHWADDR,&ifr)<0)  
  2. {  
  3.    printf("SIOCGIFHWADDR IOCTL error!\n");  
  4.   close(skfd);  
  5.   return -1;  
  6. }  
  7.   
  8. printf("mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n"  
  9.     (unsigned char)ifr.ifr_hwaddr.sa_data[0],  
  10.     (unsigned char)ifr.ifr_hwaddr.sa_data[1],  
  11.     (unsigned char)ifr.ifr_hwaddr.sa_data[2],  
  12.     (unsigned char)ifr.ifr_hwaddr.sa_data[3],  
  13.     (unsigned char)ifr.ifr_hwaddr.sa_data[4],  
  14.     (unsigned char)ifr.ifr_hwaddr.sa_data[5]  );  
  15.   
  16. /*************************************************************************************************************************************/  
  17.   
  18. close(skfd);  
  19.   
  20. return 0;  
  21. }  


 

代码里面的struct ifreq 是一个设备请求的结构体,在<linux/if.h>中定义,SIOCGIFFLAGS使用了ifreq结构,

  在ubuntu10.04环境下编译之后,运行#./a.out eth0  即可实现网线插拔的监控。

这时把代码加到uclinux下运行,发现并不能实现ubuntu的效果,这是为什么呢?

初步分析原因,可能是在驱动程序中没有将插拔状态的信息通知内核,所以我们使用ioctl实际上不能获得内核网络设备的状态的信息。我们在《linux设备驱动程序这本书上》看到有两个函数刚好做这件事情:

Void netif_carrier_off(struct net_device*dev);

Void netif_carrier_on(struct net_device*dev);

当驱动检测到设备没有连接好,可以调用netif_carrier_off通知内核这一事情;当设备再次连接好时,调用netif_carrier_on通知内核现在连接好了。

现在我们将这两个函数分别加到驱动程序中,放到监控网线插拔状态的位置,再在unlinux中插拔网线时,会在串口终端打印出相应的状态信息,这时的状态信息完全是在应用层实现的。

 

4.参考文档:uclinux内核网络驱动源码和ifconfig的源码

           《linux设备驱动程序》


0 0
原创粉丝点击