Linux應用層如何監測網線的插拔狀態

方法一:

參考:https://blog.csdn.net/hbk320/article/details/47300067

由於linux下的ifconfig命令就能夠實現在應用層監控網線插拔狀態,例如當網線連接正常時,使用ifconfig eth0命令,打印的信息中會有RUNNING,而拔掉網線後,再使用ifconfig eth0命令,RUNNING就不見了。所以,實現Linux應用層監控網線插入狀態就相當於自己寫一個ifconfig函數。

#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <arpa/inet.h>


int net_get_gateway(char *gateway)
{
    FILE *fp = NULL;
    char buf[256];
    char cmd[64];
    char *tmp;

    strcpy(cmd, "ip route");
    fp = popen(cmd, "r");
    if(NULL == fp)
    {
        perror("popen error");
        return -1;
    }
    while(fgets(buf, sizeof(buf), fp) != NULL)
    {
        tmp = buf;
        // 例如: default via 192.168.2.1 dev eth0
        // 找到default的起始點
        while(*tmp && isspace(*tmp))
        {
            ++ tmp;
        }
        if(strncmp(tmp, "default", strlen("default")) == 0)
            break;
    }
    // %*s表示以空格爲分隔符, 字符串將被忽略掉
    sscanf(buf, "%*s%*s%s", gateway);
    pclose(fp);

    return 0;
}

int net_detect(char* net_name)
{
    int skfd = 0;
    struct ifreq ifr;
    struct sockaddr_in *pAddr = NULL;

    skfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(skfd < 0)
    {
        printf("Open socket error\n");
        return -1;
    }

    // get ifr_flags
    strcpy(ifr.ifr_name, net_name);
    if(ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
    {
        printf("SIOCGIFFLAGS IOCTL error, %s is not valid\n", ifr.ifr_name);
        close(skfd);
        return -1;
    }

    if(ifr.ifr_flags & IFF_RUNNING)
    {
        // 先判斷running再判斷up還是down
        // 因爲即使拔掉網線, IFF_UP依然是up
        if(ifr.ifr_flags & IFF_UP)
        {
            printf("%s: is up\n", ifr.ifr_name);
        }
        printf("%s: is running\n", ifr.ifr_name);
    }
    else
    {   // 如果是not running, 那就直接判斷爲down
        printf("%s: is down\n", ifr.ifr_name);
        printf("%s: is NOT running\n", ifr.ifr_name);
    }

    // get HW address
    if(ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
    {
        printf("SIOCGIFHWADDR IOCTL error\n");
        close(skfd);
        return -1;
    }
    printf("%s: HW_ADDR: %02X:%02X:%02X:%02X:%02X:%02X\n",
        net_name,
        (unsigned char)ifr.ifr_hwaddr.sa_data[0],
        (unsigned char)ifr.ifr_hwaddr.sa_data[1],
        (unsigned char)ifr.ifr_hwaddr.sa_data[2],
        (unsigned char)ifr.ifr_hwaddr.sa_data[3],
        (unsigned char)ifr.ifr_hwaddr.sa_data[4],
        (unsigned char)ifr.ifr_hwaddr.sa_data[5]);

    // get IP address
    if(ioctl(skfd, SIOCGIFADDR, &ifr) < 0)
    {
        printf("SIOCGIFADDR IOCTL error\n");
        close(skfd);
        return -1;
    }
    pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);
    printf("%s: IP_ADDR: %s\n", net_name, inet_ntoa(pAddr->sin_addr));

    // get broadcast address
    if(ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0)
    {
        printf("SIOCGIFBRDADDR IOCTL error\n");
        close(skfd);
        return -1;
    }
    pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);
    printf("%s: BROADCAST: %s\n", net_name, inet_ntoa(pAddr->sin_addr));

    // get netmask address
    if(ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0)
    {
        printf("SIOCGIFNETMASK IOCTL error\n");
        close(skfd);
        return -1;
    }
    pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);
    printf("%s: NET_MASK: %s\n", net_name, inet_ntoa(pAddr->sin_addr));

    // get gateway address
    char gateway[16] = {0};
    net_get_gateway(gateway);
    printf("%s: GATEWAY: %s\n", net_name, gateway);

    close(skfd);
    return 0;
}

int main(int argc, char *argv[])
{
    char net_name[8] = {0};

    if(argc != 2)
    {
        printf("arg error\n");
        return -1;
    }

    memcpy(net_name, argv[1], strlen(argv[1]));

    return net_detect(net_name);
}

以下程序運行示例:./a.out eth0

顯然,以上程序要使用者用輪詢的方式去監測網線的狀態

 

 

方法二:

參考:https://blog.csdn.net/muclenerd/article/details/49950257

參考:https://blog.csdn.net/al86866365/article/details/79066227

參考:https://blog.csdn.net/gt945/article/details/45315911

cat /sys/class/net/eth0/carrier

如果carrier爲1表示connect,否則disconnect。

平時沒有狀態變化時,以下程序會每隔5秒(由select函數控制)循環一次,一旦監測到可讀,則read()函數就去讀數據,之後的程序就解析各個事件的數據

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>

#define BUFLEN 20480

int main(int argc, char *argv[])
{
    int fd, retval;
    char buf[BUFLEN] = {0};
    int len = BUFLEN;
    struct sockaddr_nl addr;
    struct nlmsghdr *nh;
    struct rtattr *attr;

    fd_set rd_set;
    struct timeval timeout;
    int select_r;

    // 1. 打開 NetLink Socket
    // 第一個參數必須是AF_NETLINK或PF_NETLINK, 在Linux中, 它們倆實際爲一個東西, 它表示要使用netlink
    // 第二個參數必須是SOCK_RAW或SOCK_DGRAM
    // 第三個參數指定netlink協議類型, NETLINK_ROUTE 意爲“路由守護進程”
    // 綁定該協議所創建出來的fd可以接收到來自內核的路由通知事件(如網路接口eth0上線)
    fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len));

    // 2. 設定接收類型並綁定Socket
    memset(&addr, 0, sizeof(addr));
    addr.nl_family = AF_NETLINK;
    // 指定接收路由多播組消息 + IPV4消息
    addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
    // bind()用於把一個打開的netlink socket與netlink源socket地址綁定在一起
    bind(fd, (struct sockaddr*)&addr, sizeof(addr));

    while(1)
    {
        //每次循環都要清空集合,否則不能檢測描述符變化
        FD_ZERO(&rd_set);
        //添加描述符
        FD_SET(fd, &rd_set);
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        // read()函數讀不到東西會一直阻塞, 用select()函數可以達到不阻塞的效果
        // select()能夠監視我們需要監視的文件描述符的變化情況——讀寫或是異常
        // 此處select()監視到fd可讀之後, 返回值select_r大於0; fd不可讀, 則返回0
        select_r = select(fd + 1, &rd_set, NULL, NULL, &timeout);
        if(select_r < 0)
        {
            printf("select() return error\n");
        }
        else if(select_r > 0)
        {
            if(FD_ISSET(fd, &rd_set))
            {
                retval = read(fd, buf, BUFLEN);

                for(nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval))
                {
                    if(nh->nlmsg_type == NLMSG_DONE)
                        break;
                    else if(nh->nlmsg_type == NLMSG_ERROR)
                        return;
                    else if(nh->nlmsg_type == RTM_NEWLINK)
                    {
                        struct ifinfomsg *ifinfo;
                        ifinfo = NLMSG_DATA(nh);
                        printf("NEWLINK: %s",  (ifinfo->ifi_flags & IFF_LOWER_UP) ? "up" : "down");

                        attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifinfo)));
                        len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
                        for(; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
                        {
                            if(attr->rta_type == IFLA_IFNAME)
                            {
                                printf(" %s", (char*)RTA_DATA(attr));
                                break;
                            }
                        }
                        printf("\n");
                    }
                    else if(nh->nlmsg_type == RTM_NEWADDR || nh->nlmsg_type == RTM_DELADDR)
                    {
                        struct ifaddrmsg *ifaddr;
                        ifaddr = NLMSG_DATA(nh);
                        printf("%s:", (nh->nlmsg_type==RTM_NEWADDR)?"NEWADDR":"DELADDR");

                        attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifaddr)));
                        len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifaddr));
                        for(; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
                        {
                            if(attr->rta_type == IFA_ADDRESS)
                            {
                                char tmp[32] = {0};
                                inet_ntop(ifaddr->ifa_family, RTA_DATA(attr), tmp, sizeof(tmp));
                                printf(" %s", tmp);
                            }

                            if(attr->rta_type == IFA_LABEL)
                            {
                                printf(" %s", (char*)RTA_DATA(attr));
                                break;
                            }
                        }
                        printf("\n");
                    }
                }
            }
        }
    }

    close(fd);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章