如何在 Linux 中以编程方式检测 IP 地址变化?
- 2024-10-28 08:37:00
- admin 原创
- 73
问题描述:
有没有办法使用 C++ 以编程方式检测 Linux 中本地机器上的 IP 地址变化?
解决方案 1:
给你...这不需要轮询就可以完成。
它只监听 RTM_NEWADDR,但如果你需要,可以很容易地更改为支持 RTM_DELADDR
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
int
main()
{
struct sockaddr_nl addr;
int sock, len;
char buffer[4096];
struct nlmsghdr *nlh;
if ((sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
perror("couldn't open NETLINK_ROUTE socket");
return 1;
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("couldn't bind");
return 1;
}
nlh = (struct nlmsghdr *)buffer;
while ((len = recv(sock, nlh, 4096, 0)) > 0) {
while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) {
if (nlh->nlmsg_type == RTM_NEWADDR) {
struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh);
struct rtattr *rth = IFA_RTA(ifa);
int rtl = IFA_PAYLOAD(nlh);
while (rtl && RTA_OK(rth, rtl)) {
if (rth->rta_type == IFA_LOCAL) {
char name[IFNAMSIZ];
if_indextoname(ifa->ifa_index, name);
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, RTA_DATA(rth), ip, sizeof(ip));
printf("interface %s ip: %s
", name, ip);
}
rth = RTA_NEXT(rth, rtl);
}
}
nlh = NLMSG_NEXT(nlh, len);
}
}
return 0;
}
解决方案 2:
在 C 语言中,为了获取当前 IP,我使用:
int s;
struct ifreq ifr = {};
s = socket(PF_INET, SOCK_DGRAM, 0);
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFADDR, &ifr) >= 0)
printf("%s
",
inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
将“eth0”替换为您要查看的接口。您现在需要做的就是轮询更改。
解决方案 3:
无论如何这都不容易。每个 Linux 发行版都使用不同的地方来存储 IP 地址等(如果考虑其他 UNIX 变体,则变化会更多)。例如,您可以/sbin/ifconfig
使用它来获取接口的 IP 地址,但您甚至不能确定您是否会在此处找到它,或者根本找不到它,等等。
此外,假设您有该可执行文件,则必须设置一个线程来调用它以获取给定时间段(例如 5 秒)内的数据,并解释输出。例如,如果您有桥梁等,它可能会有所不同。也就是说,这并不容易。
我想到的一个解决方案是,如果您有机会使用 GNOME 或其他一些广泛使用的发行版作为 KDE,您可以依赖它们提供的消息/信息。例如,当设备发生变化时,向DBUS 标准总线NetworkManager
输出信号。您必须为这些信号实现一个监听器。信息在这里(现在不起作用,所以这里是一个缓存)。注意添加新接口时或其中一个更改 IP 地址时的不同消息。这是我现在能想到的最好的方法。
解决方案 4:
如果您的用户使用 NetworkManager,您可以通过 D-Bus 轮询 NetworkManager.Connection.Active 和 NetworkManager.IP4Config,以获得一种跨分布的方式来确定此信息。
解决方案 5:
如果安装了 iproute2,并且你使用的是 2.6 内核,
/sbin/ip monitor
将本地接口状态和地址的变化输出到 stdout。您的程序可以读取此内容。
您还可以使用与 iproute2 工具相同的低级机制(我认为它是一个 netlink 套接字)。
解决方案 6:
ste 建议使用 ioctl SIOCGIFADDR 在技术上是正确的,但不幸的是它对于现代 Linux 系统来说并不可靠,因为在现代 Linux 系统中,单个接口可以有多个地址而无需使用子接口(例如 eth0:1),就像使用现已过时的 ifconfig 一样。
最好的选择是使用 getifaddrs(3),它从 glibc 2.3 开始提供:http ://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html
不幸的是,它有点低效(您会得到所有接口上的所有地址的链接列表,并且必须进行迭代才能找到您感兴趣的地址),但在大多数情况下,您可能不会每分钟检查一次以上,从而使开销可以忍受。
解决方案 7:
一种方法是编写一个 cron 作业,其中包含对 gethost 系列库函数之一的调用。如果您使用 gethostbyname(),则可以比较 h_addr_list 的返回值。请参阅 man gethostbyname。
如果您想在程序内部执行此操作,请生成一个执行相同操作的 pthread,然后休眠一段时间。
解决方案 8:
用 C 语言完成测试的示例,并在单独的线程中监视通知:
#include <sys/socket.h> // AF_INET, socket(), bind()
#include <ifaddrs.h> // struct ifaddrs, getifaddrs()
#include <netinet/in.h> // struct sockaddr_in
#include <arpa/inet.h> // inet_ntoa(), htonl()
#include <net/if.h> // if_indextoname()
#include <pthread.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <stdbool.h>
typedef enum {
IP_ADDR_ADD,
IP_ADDR_REMOVE
} ip_address_change_notification_type_t;
typedef void (*ip_address_change_notification_callback_t)(ip_address_change_notification_type_t type, uint32_t ipaddr, void *userdata);
static int ip_address_change_notification_socket = -1;
static pthread_t ip_address_change_notification_thread;
static ip_address_change_notification_callback_t ip_address_change_notification_callback;
static void *ip_address_change_notification_callback_userdata;
void *ip_address_change_notification_worker(void *arg)
{
fprintf(stderr, "ip_address_change_notification_worker entered.
");
if (ip_address_change_notification_socket == -1) {
goto done;
}
char buffer[4096];
struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;
int len;
while ((len = recv(ip_address_change_notification_socket, nlh, sizeof(buffer), 0)) > 0) {
for (; (NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == RTM_NEWADDR) {
fprintf(stderr, "Netlink: RTM_NEWADDR
");
} else if (nlh->nlmsg_type == RTM_DELADDR) {
fprintf(stderr, "Netlink: RTM_DELADDR
");
} else {
fprintf(stderr, "Netlink: nlmsg_type=%d
", nlh->nlmsg_type);
continue; // Some other kind of announcement.
}
struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
struct rtattr *rth = IFA_RTA(ifa);
int rtl = IFA_PAYLOAD(nlh);
for (; rtl && RTA_OK(rth, rtl); rth = RTA_NEXT(rth,rtl)) {
char name[IFNAMSIZ];
uint32_t ipaddr;
if (rth->rta_type != IFA_LOCAL) continue;
ipaddr = *((uint32_t *)RTA_DATA(rth)); // In network byte-order.
fprintf(stderr, "Interface %s %s has IP address %s
", if_indextoname(ifa->ifa_index, name), (nlh->nlmsg_type == RTM_NEWADDR ? "now" : "no longer"), inet_ntoa(*((struct in_addr *)&ipaddr)));
if (ip_address_change_notification_callback) (*ip_address_change_notification_callback)((nlh->nlmsg_type == RTM_NEWADDR ? IP_ADDR_ADD : IP_ADDR_REMOVE), ipaddr, ip_address_change_notification_callback_userdata);
}
}
}
done:
fprintf(stderr, "ip_address_change_notification_worker exited.
");
return (NULL);
}
bool begin_ip_address_change_notifications(ip_address_change_notification_callback_t callback, void *userdata)
{
if (ip_address_change_notification_socket != -1) return false;
ip_address_change_notification_callback = callback;
ip_address_change_notification_callback_userdata = userdata;
if ((ip_address_change_notification_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
perror("begin_ip_address_change_notifications socket");
return false;
}
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(ip_address_change_notification_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("begin_ip_address_change_notifications bind");
goto bail;
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, 1); // Preclude the need to do pthread_join on the thread after it exits.
int err = pthread_create(&ip_address_change_notification_thread, &attr, ip_address_change_notification_worker, NULL);
pthread_attr_destroy(&attr);
if (err != 0) {
fprintf(stderr, "Error creating ip address change notification thread.
");
goto bail;
}
return (true);
bail:
close(ip_address_change_notification_socket);
ip_address_change_notification_socket = -1;
ip_address_change_notification_callback = NULL;
ip_address_change_notification_callback_userdata = NULL;
return false;
}
void end_ip_address_change_notifications(void)
{
if (ip_address_change_notification_socket == -1) return;
pthread_cancel(ip_address_change_notification_thread);
close(ip_address_change_notification_socket);
ip_address_change_notification_socket = -1;
ip_address_change_notification_callback = NULL;
ip_address_change_notification_callback_userdata = NULL;
}
解决方案 9:
来自 rtnetlink 的手册页:
描述
Rtnetlink 允许读取和修改内核的路由表。它在内核中用于在各个子系统之间进行通信(尽管此处未记录此用法),也用于与用户空间程序进行通信。网络路由、IP 地址、链接参数、邻居设置、排队规则、流量类别和数据包分类器都可以通过 NETLINK_ROUTE 套接字进行控制。它基于 netlink 消息,有关更多信息,请参阅 netlink(7)。
解决方案 10:
使用libnl-3库,检测链接和ip4地址变化。
参考-https: //www.infradead.org/~tgr/libnl/doc/core.html#_introduction
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <arpa/inet.h>
#include <iostream>
static char ip4Addr[INET_ADDRSTRLEN];
static int parseAddress(struct nlmsghdr *hdr)
{
std::cout << "parseAddress" << std::endl;
struct ifaddrmsg *iface = (struct ifaddrmsg *)nlmsg_data(hdr);
struct nlattr *attrs[IFA_MAX + 1];
if (nlmsg_parse(hdr, sizeof(struct ifaddrmsg), attrs, IFA_MAX, nullptr) < 0)
{
std::cerr << "problem parsing Netlink response" << std::endl;
return -1;
}
if (attrs[IFA_ADDRESS] == nullptr)
{
std::cerr << "Address Never Received "
<< std::endl;
return -1;
}
inet_ntop(iface->ifa_family, nla_data(attrs[IFA_ADDRESS]), ip4Addr, sizeof(ip4Addr));
if ((hdr->nlmsg_type == RTM_NEWADDR) && (iface->ifa_family == AF_INET))
{
std::cout << "IPv4 Address added : " << ip4Addr << std::endl;
}
if ((hdr->nlmsg_type == RTM_DELADDR) && (iface->ifa_family == AF_INET))
{
std::cout << "IPv4 Address deleted : " << ip4Addr << std::endl;
}
return 0;
}
static int parseLink(struct nlmsghdr *hdr)
{
std::cout << "parseLink" << std::endl;
struct ifinfomsg *iface = (struct ifinfomsg *)nlmsg_data(hdr);
struct nlattr *attrs[IFLA_MAX + 1];
if (nlmsg_parse(hdr, sizeof(struct ifinfomsg), attrs, IFLA_MAX, nullptr) < 0)
{
std::cerr << "problem parsing Netlink response" << std::endl;
return -1;
}
if (attrs[IFLA_IFNAME] != nullptr)
{
if (hdr->nlmsg_type == RTM_NEWLINK)
{
std::cout << (char *)nla_data(attrs[IFLA_IFNAME]) << std::endl;
}
else if (hdr->nlmsg_type == RTM_DELLINK)
{
std::cout << (char *)nla_data(attrs[IFLA_IFNAME]) << std::endl;
}
}
return 0;
}
static int receiveNewMsg(struct nl_msg *msg, void *arg)
{
struct nlmsghdr *nlh = nlmsg_hdr(msg);
int len = nlh->nlmsg_len;
int type = nlh->nlmsg_type;
while (nlmsg_ok(nlh, len))
{
if (type != RTM_NEWLINK && type != RTM_DELLINK && type != RTM_NEWADDR && type != RTM_DELADDR)
{
if (nlh->nlmsg_type == NLMSG_DONE)
{
std::cout << "message complete" << std::endl;
}
nlh = nlmsg_next(nlh, &len);
continue;
}
if ((nlh->nlmsg_type == RTM_NEWLINK) || (nlh->nlmsg_type == RTM_DELLINK))
{
parseLink(nlh);
}
if ((nlh->nlmsg_type == RTM_NEWADDR) || (nlh->nlmsg_type == RTM_DELADDR))
{
parseAddress(nlh);
}
nlh = nlmsg_next(nlh, &len);
}
return 1;
}
int main(int argc, char const *argv[])
{
struct nl_sock *sk;
/* Allocate a new socket */
sk = nl_socket_alloc();
/*
* Notifications do not use sequence numbers, disable sequence number checking.
*/
nl_socket_disable_seq_check(sk);
/*
* Define a callback function, which will be called for each notification received
*/
nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, receiveNewMsg, nullptr);
nl_socket_modify_cb(sk, NL_CB_FINISH, NL_CB_CUSTOM, receiveNewMsg, nullptr);
/* Connect to routing netlink protocol */
nl_connect(sk, NETLINK_ROUTE);
/* Subscribe to link notifications group */
nl_socket_add_memberships(sk, RTNLGRP_LINK, 0);
nl_socket_add_memberships(sk, RTNLGRP_IPV4_IFADDR, 0);
/*
* Start receiving messages. The function nl_recvmsgs_default() will block
* until one or more netlink messages (notification) are received which
* will be passed on to my_func().
*/
while (1)
nl_recvmsgs_default(sk);
return 0;
}
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件