Linux: netlink 内核网络数据变化通知应用

最近在看一个性能的问题,说,程序周期性的检查内核的IP的状态,最近又加了一个新功能,导致现在系统里的IP地址非常多。这个时候的周期性操作,感觉就不合适了。

就需要做一些优化,比如,怎么获取到系统在删除添加时的通知信息。以此来做audit操作,效率更好。

下面是一个监测变化的一个小程序。最好是在新的线程里做这件事,以免出现阻塞的情况。

c 复制代码
/*
 * test_netlink_listen_ip_changes.c
 * 
 * Test program to listen to kernel IP address change notifications
 * via Netlink multicast
 *
 * Compile: gcc -o test_netlink_listen_ip_changes test_netlink_listen_ip_changes.c
 * Run:     ./test_netlink_listen_ip_changes
 *
 * This program will listen for IP address add/delete events from kernel
 * and print them in real-time.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <net/if.h>

#define BUFSIZE 8192

static volatile int running = 1;

void signal_handler(int sig) {
    (void)sig;
    running = 0;
    printf("\nReceived signal, exiting...\n");
}

void print_timestamp(void) {
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    printf("[%ld.%09ld] ", ts.tv_sec, ts.tv_nsec);
}

void print_ip_address(struct ifaddrmsg* ifa, struct rtattr* rta) {
    char ip_str[INET6_ADDRSTRLEN] = {0};
    char ifname[IFNAMSIZ] = {0};
    char local_str[INET6_ADDRSTRLEN] = {0};
    
    int rta_len = IFA_PAYLOAD((struct nlmsghdr*)rta);
    struct rtattr* attr = IFA_RTA(ifa);
    
    // Parse attributes
    for (; RTA_OK(attr, rta_len); attr = RTA_NEXT(attr, rta_len)) {
        switch (attr->rta_type) {
            case IFA_ADDRESS:
                if (ifa->ifa_family == AF_INET) {
                    inet_ntop(AF_INET, RTA_DATA(attr), ip_str, sizeof(ip_str));
                } else if (ifa->ifa_family == AF_INET6) {
                    inet_ntop(AF_INET6, RTA_DATA(attr), ip_str, sizeof(ip_str));
                }
                break;
                
            case IFA_LOCAL:
                if (ifa->ifa_family == AF_INET) {
                    inet_ntop(AF_INET, RTA_DATA(attr), local_str, sizeof(local_str));
                } else if (ifa->ifa_family == AF_INET6) {
                    inet_ntop(AF_INET6, RTA_DATA(attr), local_str, sizeof(local_str));
                }
                break;
                
            case IFA_LABEL:
                strncpy(ifname, (char*)RTA_DATA(attr), sizeof(ifname) - 1);
                break;
        }
    }
    
    // Print IP information
    if (strlen(ip_str) > 0) {
        printf("IP: %s", ip_str);
        if (strlen(local_str) > 0 && strcmp(ip_str, local_str) != 0) {
            printf(" (local: %s)", local_str);
        }
        printf("/%d", ifa->ifa_prefixlen);
    }
    
    if (strlen(ifname) > 0) {
        printf(" on interface: %s", ifname);
    }
    printf(" (ifindex: %d)", ifa->ifa_index);
    
    // Print flags
    if (ifa->ifa_flags & IFA_F_TENTATIVE) {
        printf(" [TENTATIVE]");
    }
    if (ifa->ifa_flags & IFA_F_DADFAILED) {
        printf(" [DAD_FAILED]");
    }
    if (ifa->ifa_flags & IFA_F_DEPRECATED) {
        printf(" [DEPRECATED]");
    }
    if (ifa->ifa_flags & IFA_F_HOMEADDRESS) {
        printf(" [HOMEADDRESS]");
    }
    
    printf("\n");
}

int main(int argc, char* argv[]) {
    struct sockaddr_nl nladdr;
    char buffer[BUFSIZE];
    struct nlmsghdr* nlh;
    struct iovec iov;
    struct msghdr msg;
    int nl_socket;
    int retval;
    
    (void)argc;
    (void)argv;
    
    printf("========================================\n");
    printf("Netlink IP Change Listener Test Program\n");
    printf("========================================\n");
    printf("Listening for IP address changes from kernel...\n");
    printf("(Press Ctrl+C to exit)\n\n");
    
    // Setup signal handler
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    // Create netlink socket
    nl_socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
    if (nl_socket < 0) {
        perror("socket(AF_NETLINK)");
        return 1;
    }
    
    // Set socket to non-blocking mode
    int flags = fcntl(nl_socket, F_GETFL, 0);
    if (flags < 0) {
        perror("fcntl(F_GETFL)");
        close(nl_socket);
        return 1;
    }
    
    retval = fcntl(nl_socket, F_SETFL, flags | O_NONBLOCK);
    if (retval < 0) {
        perror("fcntl(F_SETFL)");
        close(nl_socket);
        return 1;
    }
    
    // Bind socket and subscribe to multicast groups
    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_pid = getpid();  // Use process ID
    
    // Subscribe to IP address change notifications
    // RTMGRP_IPV4_IFADDR: IPv4 address changes
    // RTMGRP_IPV6_IFADDR: IPv6 address changes
    nladdr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
    
    retval = bind(nl_socket, (struct sockaddr*)&nladdr, sizeof(nladdr));
    if (retval < 0) {
        perror("bind(netlink)");
        close(nl_socket);
        return 1;
    }
    
    printf("Successfully subscribed to IP address change notifications\n");
    printf("Multicast groups: RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR\n");
    printf("Waiting for events...\n\n");
    
    // Prepare message structure for receiving
    iov.iov_base = buffer;
    iov.iov_len = sizeof(buffer);
    
    msg.msg_name = (void*)&nladdr;
    msg.msg_namelen = sizeof(nladdr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_flags = 0;
    
    // Main loop: receive and process netlink messages
    while (running) {
        ssize_t len = recvmsg(nl_socket, &msg, 0);
        
        if (len < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // No data available, sleep a bit
                usleep(100000);  // 100ms
                continue;
            }
            if (errno == EINTR) {
                // Interrupted by signal
                continue;
            }
            perror("recvmsg");
            break;
        }
        
        if (len == 0) {
            printf("Connection closed\n");
            break;
        }
        
        // Parse netlink messages
        for (nlh = (struct nlmsghdr*)buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
            // Check for end of message
            if (nlh->nlmsg_type == NLMSG_DONE) {
                break;
            }
            
            // Check for error message
            if (nlh->nlmsg_type == NLMSG_ERROR) {
                struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(nlh);
                if (err->error != 0) {
                    printf("Netlink error: %s (%d)\n", strerror(-err->error), -err->error);
                }
                continue;
            }
            
            // Handle IP address change events
            switch (nlh->nlmsg_type) {
                case RTM_NEWADDR:  // New IP address added
                    {
                        struct ifaddrmsg* ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh);
                        print_timestamp();
                        printf(">>> NEW IP ADDRESS ADDED - ");
                        if (ifa->ifa_family == AF_INET) {
                            printf("IPv4 ");
                        } else if (ifa->ifa_family == AF_INET6) {
                            printf("IPv6 ");
                        } else {
                            printf("Unknown family(%d) ", ifa->ifa_family);
                        }
                        print_ip_address(ifa, (struct rtattr*)IFA_RTA(ifa));
                    }
                    break;
                    
                case RTM_DELADDR:  // IP address deleted
                    {
                        struct ifaddrmsg* ifa = (struct ifaddrmsg*)NLMSG_DATA(nlh);
                        print_timestamp();
                        printf(">>> IP ADDRESS DELETED - ");
                        if (ifa->ifa_family == AF_INET) {
                            printf("IPv4 ");
                        } else if (ifa->ifa_family == AF_INET6) {
                            printf("IPv6 ");
                        } else {
                            printf("Unknown family(%d) ", ifa->ifa_family);
                        }
                        print_ip_address(ifa, (struct rtattr*)IFA_RTA(ifa));
                    }
                    break;
                    
                default:
                    // Ignore other message types
                    break;
            }
        }
        
        // Check if message was truncated
        if (msg.msg_flags & MSG_TRUNC) {
            printf("Warning: Message was truncated!\n");
        }
    }
    
    printf("\nClosing socket and exiting...\n");
    close(nl_socket);
    return 0;
}

输出结果:

可以看到通知消息带有的信息还是非常的多,可以作为audit的依据。

c 复制代码
[1769060018.496720332] >>> IP ADDRESS DELETED - IPv4 IP: 10.0.32.3/24 on interface: eth0.1552 (ifindex: 7)
相关推荐
小王努力学编程2 小时前
LangChain—AI应用开发框架(认识模型)
linux·服务器·人工智能·机器学习·容器·langchain
qq_381338502 小时前
1Panel 快速部署 Node+MongoDB+Nginx 项目实操流程(附避坑指南)
linux·运维·服务器
廋到被风吹走2 小时前
gRPC 深度解析:Protocol Buffers、HTTP/2、流式传输与拦截器设计
网络·网络协议·http
双层吉士憨包2 小时前
如何注册Talkatone免费美国号码?实测有效方案
大数据·运维·网络
BHXDML2 小时前
TCP/IP 协议栈深度解析:互联网的神经系统
网络·网络协议·tcp/ip
空空空空空空空空空空空空如也2 小时前
QT编译程序在Ubuntu下不进桌面直接启动和避免重复启动的方法
linux·运维·ubuntu
辰尘_星启2 小时前
解决幽灵容器
linux·docker·容器·机器人·嵌入式
CS创新实验室2 小时前
《计算机网络》深入学:点对点协议 (PPP)
网络·计算机网络·php·ppp协议·点对点协议
呱呱巨基3 小时前
Linux Ext系列文件系统
linux·c++·笔记·学习