最近在看一个性能的问题,说,程序周期性的检查内核的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)