第一部分:网卡状态变化监控实现     

       在 Linux 上实现一个监控网卡状态变化的 C++ 线程,可以通过使用 Linux 系统中的 netlink 套接字来实现。netlink 是一种与内核通信的机制,通过 NETLINK_ROUTE,我们可以实时监听网卡状态的变化,比如 UPDOWN 状态。

以下是一个示例代码,展示如何用 C++ 创建一个线程来监控网络接口的状态变化:

示例代码

#include <iostream>
#include <thread>
#include <atomic>
#include <unistd.h>
#include <netlink/netlink.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
​
// 全局变量用来控制线程退出
std::atomic<bool> running{true};
​
// 处理网络事件的函数
void handle_netlink_message(struct nlmsghdr *nlh) {
    struct ifinfomsg *ifi = (struct ifinfomsg *)NLMSG_DATA(nlh);
    
    // 提取网卡名称
    int len = nlh->nlmsg_len;
    struct rtattr *attr = IFLA_RTA(ifi);
    int attr_len = IFLA_PAYLOAD(nlh);
    
    char ifname[IF_NAMESIZE] = {0};
    for (; RTA_OK(attr, attr_len); attr = RTA_NEXT(attr, attr_len)) {
        if (attr->rta_type == IFLA_IFNAME) {
            strncpy(ifname, (char *)RTA_DATA(attr), RTA_PAYLOAD(attr));
            break;
        }
    }
    
    // 判断网卡状态
    if (ifi->ifi_flags & IFF_RUNNING) {
        std::cout << "Interface " << ifname << " is UP." << std::endl;
    } else {
        std::cout << "Interface " << ifname << " is DOWN." << std::endl;
    }
}
​
// 监控网卡状态变化的线程函数
void monitor_interface_status() {
    int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (sock_fd < 0) {
        std::cerr << "Failed to create socket." << std::endl;
        return;
    }
​
    struct sockaddr_nl addr = {};
    addr.nl_family = AF_NETLINK;
    addr.nl_groups = RTMGRP_LINK; // 监听网络链路的变化
​
    if (bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        std::cerr << "Failed to bind socket." << std::endl;
        close(sock_fd);
        return;
    }
​
    char buffer[4096];
    while (running) {
        ssize_t len = recv(sock_fd, buffer, sizeof(buffer), 0);
        if (len < 0) {
            if (running) {
                std::cerr << "Error receiving netlink message." << std::endl;
            }
            break;
        }
​
        struct nlmsghdr *nlh;
        for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, len);
             nlh = NLMSG_NEXT(nlh, len)) {
            if (nlh->nlmsg_type == NLMSG_DONE) {
                break;
            }
            if (nlh->nlmsg_type == RTM_NEWLINK) {
                handle_netlink_message(nlh);
            }
        }
    }
​
    close(sock_fd);
}
​
int main() {
    // 创建监控线程
    std::thread monitor_thread(monitor_interface_status);
​
    std::cout << "Monitoring network interface status. Press Enter to stop..." << std::endl;
    std::cin.get(); // 等待用户输入
​
    // 停止监控
    running = false;
    monitor_thread.join();
​
    std::cout << "Monitoring stopped." << std::endl;
    return 0;
}

代码说明

  1. netlink 套接字

    • 使用 AF_NETLINK 套接字与 Linux 内核通信。

    • 注册组 RTMGRP_LINK 来监听网络接口状态的改变。

  2. ifinfomsg 结构

    • 包含网络接口的状态信息,可以通过 ifi_flags 判断接口是否是 UPDOWN

  3. 线程的创建

    • 用 C++ 的 std::thread 创建独立的线程,用于运行网络状态监控逻辑。

  4. 优雅退出

    • 使用 std::atomic<bool> 变量 running 来通知线程安全地结束。

  5. 网卡名称提取

    • 当系统发送网络变化事件时,使用 IFLA_IFNAMErtattr 结构中提取网卡名称。

编译和运行

将代码保存为 monitor_net.cpp 并编译:

g++ -std=c++11 -o monitor_net monitor_net.cpp

运行代码:

./monitor_net

当有网卡状态变化(例如 ifconfig eth0 downifconfig eth0 up)时,程序会输出网卡的状态信息。

示例输出

Monitoring network interface status. Press Enter to stop...
Interface eth0 is DOWN.
Interface eth0 is UP.
Interface wlan0 is DOWN.
...

注意事项

  1. 需要以 root 权限运行程序,否则可能没有权限监听 netlink 消息。

  2. 如果使用 CMake 构建系统,可以增加 target_link_libraries 选项链接相关库(如 netlink)。

  3. 程序能运行在基于 Linux 的系统上,但在非 Linux 系统上不可用。

通过这个实现,你可以实时监控网卡状态的改变,并在实际场景中根据状态变化执行不同的逻辑处理。  

第二部分:ifi_flags标志

        在 Linux 中,ifi->ifi_flagsifinfomsg 结构的一个字段,它表示网络接口的状态标志。其值由 IFF_* 宏定义,这些宏在头文件 <net/if.h> 中定义。

ifi_flags 是一个位标志,可以同时包含多个值(通过按位或的方式组合)。以下是常用的 IFF_* 标志及其含义的列表:

常用 IFF_* 标志

  1. IFF_UP (0x1)

    • 表示接口是激活的(已启动)。

    • 如果未设置此标志,接口被标记为关闭。

  2. IFF_BROADCAST (0x2)

    • 表示接口支持广播,并有有效的广播地址。

  3. IFF_DEBUG (0x4)

    • 用于启用调试信息(很少使用)。

  4. IFF_LOOPBACK (0x8)

    • 表示此接口是一个回环接口(如 lo 内置回环接口)。

  5. IFF_POINTOPOINT (0x10)

    • 表示接口是一个点对点连接(如 PPP 连接)。

  6. IFF_NOTRAILERS (0x20)

    • 禁止使用 trailer 协议(旧功能,现代设备中已很少使用)。

  7. IFF_RUNNING (0x40)

    • 表示接口已经就绪,能够正常工作(链路层状态是 UP)。

    • IFF_UP 的区别是:IFF_UP 表示接口被管理员标记为启用,而 IFF_RUNNING 表示接口实际上可以运行(例如网线已插入)。

  8. IFF_NOARP (0x80)

    • 表示接口不使用 ARP 协议。

  9. IFF_PROMISC (0x100)

    • 表示接口处于混杂模式(Promiscuous Mode):接收所有数据包,而不论目标是否是该接口(用于抓包工具如 Wireshark)。

  10. IFF_ALLMULTI (0x200)

    • 表示接口接收所有的多播数据包。

  11. IFF_MASTER (0x400)

    • 表示接口是一个主设备(例如,在网桥或绑定设备配置中)。

  12. IFF_SLAVE (0x800)

    • 表示接口是一个从设备(例如,在绑定中)。

  13. IFF_MULTICAST (0x1000)

    • 表示接口支持多播。

  14. IFF_PORTSEL (0x2000)

    • 接口能够选择媒介类型(指支持多种介质的网络接口)。

  15. IFF_AUTOMEDIA (0x4000)

    • 表示接口将自动检测媒介类型。

  16. IFF_DYNAMIC (0x8000)

    • 动态接口标志。

如何检查 ifi->ifi_flags

由于 ifi_flags 是位标志,可以通过按位与 (&) 操作检查特定标志。例如:

if (ifi->ifi_flags & IFF_RUNNING) {
    std::cout << "Interface is running (link is up)." << std::endl;
}
​
if (ifi->ifi_flags & IFF_UP) {
    std::cout << "Interface is enabled (administratively up)." << std::endl;
}

接口状态下的典型组合

  1. 接口已启用且运行

    • IFF_UP | IFF_RUNNING

    • 表示接口被管理员标记为启用 (IFF_UP),并且链路层链路是正常的(如网线已插入,IFF_RUNNING)。

  2. 接口已启用但未运行

    • IFF_UP(无 IFF_RUNNING

    • 表示接口被管理员启用,但可能未插入网线或连接有问题。

  3. 接口被禁用

    • IFF_UP 标志。

    • 表示接口处于关闭状态。

实际使用场景

通过检查这些状态标志,可以判断网络接口的当前状态,执行不同的逻辑。例如:

  • 检查是否网线断开:

    if (!(ifi->ifi_flags & IFF_RUNNING)) {
        std::cout << "Network cable is unplugged or link is down." << std::endl;
    }

  • 判断是否支持混杂模式:

    if (ifi->ifi_flags & IFF_PROMISC) {
        std::cout << "Interface is in promiscuous mode." << std::endl;
    }

总结

   ifi->ifi_flags 是描述网络接口状态的一个重要字段。通过位标志的形式,可以准确地判断接口的当前特性和状态,例如是否已启动、是否运行、多播、广播能力等。这在网络开发和调试中非常有用。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐