如何在 Linux 中以编程方式检测 IP 地址变化?

2024-10-28 08:37:00
admin
原创
74
摘要:问题描述:有没有办法使用 C++ 以编程方式检测 Linux 中本地机器上的 IP 地址变化?解决方案 1:给你...这不需要轮询就可以完成。它只监听 RTM_NEWADDR,但如果你需要,可以很容易地更改为支持 RTM_DELADDR#include <stdio.h> #include...

问题描述:

有没有办法使用 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;
}
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用