C++ 如何在 Linux 中以编程方式检测 IP 地址更改?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/579783/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to detect IP address change programmatically in Linux?
提问by Amit Wadhwa
Is there a way to detect IP address changes on the local machine in Linux programmatically using C++?
有没有办法使用 C++ 以编程方式检测 Linux 中本地机器上的 IP 地址更改?
采纳答案by ste
In C, to get the current IP I use:
在 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\n",
inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
Replace "eth0" with the interface you're looking at. All you now need to do is poll for a change.
将“eth0”替换为您正在查看的界面。您现在需要做的就是轮询更改。
回答by Danny Dulai
here you go.. this does it without polling.
给你.. 无需轮询即可完成。
it only listens for RTM_NEWADDR but it should be easy to change to support RTM_DELADDR if you need
它只侦听 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\n", name, ip);
}
rth = RTA_NEXT(rth, rtl);
}
}
nlh = NLMSG_NEXT(nlh, len);
}
}
return 0;
}
回答by Diego Sevilla
It is not easy in any way. Each linux distribution uses different places to store IP addresses, etc. (more variation if you consider other UNIX variants). You canuse, for example, /sbin/ifconfig
to obtain the IP addresses of the interfaces, but you cannot even be sure if you'll find it at this place, or at all, etc.
无论如何都不容易。每个 linux 发行版使用不同的地方来存储 IP 地址等(如果您考虑其他 UNIX 变体,则会有更多变化)。例如,您可以使用/sbin/ifconfig
来获取接口的 IP 地址,但您甚至无法确定是否会在此位置找到它,或者根本无法找到它等等。
Also, given you have that executable, you have to set up a thread calling it to obtain the data with a given period (say 5 seconds), and interpret the output. It may vary, for example, if you have bridges, etc. etc. That is, it is not easy.
此外,如果您拥有该可执行文件,您必须设置一个线程来调用它以获取给定时间段(例如 5 秒)内的数据,并解释输出。它可能会有所不同,例如,如果您有桥梁等。也就是说,这并不容易。
A solution that comes to my mind is, if you have the opportunity of using GNOME or some other widespread distribution as KDE, you can rely on the messages/informations they give. For example, NetworkManager
outputs a signal to the DBUS standard buswhen a device changes. You have to implement a listener for those signal. Information here(not working right now, so here is a cache). Note the different messages when a new interface is added, or when one of them changes the IP address. This is the best way I can think of right now.
我想到的一个解决方案是,如果您有机会使用 GNOME 或其他一些广泛的发行版作为 KDE,您可以依赖它们提供的消息/信息。例如,当设备发生变化时NetworkManager
,向DBUS 标准总线输出信号。你必须为这些信号实现一个监听器。信息在这里(现在不工作,所以这里是一个缓存)。请注意添加新接口或其中之一更改 IP 地址时的不同消息。这是我目前能想到的最好的方法。
回答by Sean
If your users use NetworkManager, you can poll NetworkManager.Connection.Active and NetworkManager.IP4Config via D-Bus to get a more cross distribution way of determining this information.
如果您的用户使用 NetworkManager,您可以通过 D-Bus 轮询 NetworkManager.Connection.Active 和 NetworkManager.IP4Config 以获得确定此信息的更多交叉分布方式。
回答by Nicholas Knight
ste's suggestion to use ioctl SIOCGIFADDR used to be technically correct, unfortunately it is unreliable for modern Linux systems, where a single interface can have multiple addresses without using sub-interfaces (e.g. eth0:1) as was done with the now-obsolete ifconfig.
ste 使用 ioctl SIOCGIFADDR 的建议在技术上是正确的,不幸的是它对于现代 Linux 系统是不可靠的,在现代 Linux 系统中,单个接口可以有多个地址,而无需使用子接口(例如 eth0:1),就像现在已经过时的 ifconfig 所做的那样。
Your best bet is to use getifaddrs(3), which is present from glibc 2.3: http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html
最好的办法是使用 glibc 2.3 中的 getifaddrs(3):http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html
Unfortunately it's somewhat inefficient (you get back a linked list of all addresses on all interfaces and will have to iterate through to find the ones you're interested in), but in most cases you're probably not checking it more than once a minute or so, making the overhead tolerable.
不幸的是,它有点低效(您会返回所有接口上所有地址的链接列表,并且必须遍历以找到您感兴趣的地址),但在大多数情况下,您可能不会每分钟检查一次以上左右,使开销可以忍受。
回答by MarkR
If iproute2 is installed and you're on a 2.6 kernel,
如果安装了 iproute2 并且您使用的是 2.6 内核,
/sbin/ip monitor
Will output changes in local interface status and addresses to stdout. Your program can read this.
将本地接口状态和地址的变化输出到标准输出。您的程序可以读取此内容。
You could also use the same low level mechanism as the iproute2 tool does (I think it's a netlink socket).
您也可以使用与 iproute2 工具相同的低级机制(我认为它是一个 netlink 套接字)。
回答by Dana the Sane
One way would be to write a cron job which contains a call to one the gethost family of library functions. If you use gethostbyname() you can compare the return values of h_addr_list. See man gethostbyname.
一种方法是编写一个 cron 作业,其中包含对 gethost 系列库函数的调用。如果您使用 gethostbyname(),您可以比较 h_addr_list 的返回值。请参阅 man gethostbyname。
If you're want to do this from within your program, spawn a pthread which does the same thing, then sleeps for some arbitrary period of time.
如果您想在程序中执行此操作,请生成一个执行相同操作的 pthread,然后休眠一段时间。
回答by 0x6adb015
From man page of rtnetlink:
从 rtnetlink 的手册页:
DESCRIPTION
描述
Rtnetlink allows the kernel's routing tables to be read and altered. It is used within the kernel to communicate between various subsystems, though this usage is not documented here, and for communication with user-space programs. Network routes, ip addresses, link parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers may all be controlled through NETLINK_ROUTE sockets. It is based on netlink messages, see netlink(7) for more information.
Rtnetlink 允许读取和更改内核的路由表。它在内核中用于在各种子系统之间进行通信,尽管这里没有记录这种用法,以及用于与用户空间程序的通信。网络路由、IP 地址、链接参数、邻居设置、排队规则、流量类别和数据包分类都可以通过 NETLINK_ROUTE 套接字进行控制。它基于 netlink 消息,有关详细信息,请参阅 netlink(7)。
回答by bleater
Complete tested example in C with notifications watched for in separate thread:
在 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.\n");
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\n");
} else if (nlh->nlmsg_type == RTM_DELADDR) {
fprintf(stderr, "Netlink: RTM_DELADDR\n");
} else {
fprintf(stderr, "Netlink: nlmsg_type=%d\n", 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\n", 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.\n");
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.\n");
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;
}