繁体   English   中英

使用 netlink 过滤 IPv6 本地地址

[英]Filter IPv6 local address using netlink

我使用 netlink 编写 C 代码来获取有关 IP 路由表的信息并检测给定接口的新 IP 地址。 使用 IPv4,我可以使用“IFA_LOCAL”过滤本地地址。 但是使用 IPv6,我无法获得本地地址: rta_type永远不会等于IFA_LOCAL ,即使对于本地地址也是如此。

使用的代码如下:

int main(void)
{
    struct {
        struct nlmsghdr hdr;
        struct ifaddrmsg msg;
    } req;
    struct sockaddr_nl addr;
    int sock[2];

    memset(&addr, 0, sizeof(addr));
    memset(&req, 0, sizeof(req));

    if ((sock[0] = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
        CWMP_LOG(ERROR,"couldn't open NETLINK_ROUTE socket");
        return -1;
    }

    addr.nl_family = AF_NETLINK;
    addr.nl_groups = RTMGRP_IPV6_IFADDR;// |RTMGRP_IPV6_IFADDR;
    if ((bind(sock[0], (struct sockaddr_in6 *)&addr, sizeof(addr))) == -1) {
        CWMP_LOG(ERROR,"couldn't bind netlink socket");
        return -1;
    }

    netlink_event.fd = sock[0];
    if ((sock[1] = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) == -1) {
        CWMP_LOG(ERROR,"couldn't open NETLINK_ROUTE socket");
        return -1;
    }

    req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
    req.hdr.nlmsg_type = RTM_GETADDR;
    req.msg.ifa_family = AF_INET6;

    if ((send(sock[1], &req, req.hdr.nlmsg_len, 0)) == -1) {
        CWMP_LOG(ERROR,"couldn't send netlink socket");
        return -1;
    }

    struct nlmsghdr *nlh;
    char buffer[BUFSIZ];
    int msg_size;

    memset(&buffer, 0, sizeof(buffer));

    nlh = (struct nlmsghdr *)buffer;
    if ((msg_size = recv(sock[1], nlh, BUFSIZ, 0)) == -1) {
        CWMP_LOG(ERROR,"error receiving netlink message");
        return;
    }

    while (msg_size > sizeof(*nlh)) {
        int len = nlh->nlmsg_len;
        int req_len = len - sizeof(*nlh);

        if (req_len < 0 || len > msg_size) {
            CWMP_LOG(ERROR,"error reading netlink message");
            return;
        }

        if (!NLMSG_OK(nlh, msg_size)) {
            CWMP_LOG(ERROR,"netlink message is not NLMSG_OK");
            return;
        }

        if (nlh->nlmsg_type == RTM_NEWADDR) {
            printf("new addr \n");
            struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh);
            struct rtattr *rth = IFA_RTA(ifa);
            int rtl = IFA_PAYLOAD(nlh);
            char if_name[IFNAMSIZ], if_addr[INET6_ADDRSTRLEN];
            char *c;
            struct in6_addr *in6p;

            memset(&if_name, 0, sizeof(if_name));
            memset(&if_addr, 0, sizeof(if_addr));

            while (rtl && RTA_OK(rth, rtl)) {
                printf("rth->rta_type %d \n", rth->rta_type);
                in6p = (struct in6_addr *)RTA_DATA(rth);
                printf("addr1: " NIP6_FMT "\n",NIP6(*in6p));
                if (rth->rta_type != IFA_LOCAL) {
                    printf("########NOT IFA_LOCAL ############\n");
                    rth = RTA_NEXT(rth, rtl);
                    continue;
                }
                printf("======IFA_LOCAL======\n");
                rth = RTA_NEXT(rth, rtl);                   
            }
        }   
        msg_size -= NLMSG_ALIGN(len);
        nlh = (struct nlmsghdr*)((char*)nlh + NLMSG_ALIGN(len));
    }
    return 0;
}

执行的痕迹如下:

rth->rta_type 6 
addr1: 0000:0e0e:0000:1c1e:0031:15c7:0031:15c7
########NOT IFA_LOCAL ############
new addr 
rth->rta_type 1 
addr1: fd59:ca3e:bd63:0000:0222:07ff:fe41:b9d6
########NOT IFA_LOCAL ############
rth->rta_type 6 
addr1: 0000:068c:0000:1ba4:0030:e60b:0030:e60b
########NOT IFA_LOCAL ############
new addr 
rth->rta_type 1 
addr1: 2001:1338:000a:0000:0000:0000:0000:0196
########NOT IFA_LOCAL ############
rth->rta_type 6 
addr1: ffff:ffff:ffff:ffff:0005:a828:0005:a828
########NOT IFA_LOCAL ############
new addr 
rth->rta_type 1 
addr1: fe80:0000:0000:0000:0222:07ff:fe41:b9d6
########NOT IFA_LOCAL ############
rth->rta_type 6 
addr1: ffff:ffff:ffff:ffff:0005:a826:0005:a826
########NOT IFA_LOCAL ############
new addr 
rth->rta_type 1 
addr1: fe80:0000:0000:0000:0222:07ff:fe41:b9d7
########NOT IFA_LOCAL ############

尝试寻找“IFA_ADDRESS”。 对于广播接口,IFA_LOCAL 和 IFA_ADDRESS 含义相同。 这是内核if_addr.h的代码片段。

/*
 * Important comment:
 * IFA_ADDRESS is prefix address, rather than local interface address.
 * It makes no difference for normally configured broadcast interfaces,
 * but for point-to-point IFA_ADDRESS is DESTINATION address,
 * local address is supplied in IFA_LOCAL attribute.
 */
enum {
    IFA_UNSPEC,
    IFA_ADDRESS,
    IFA_LOCAL,
    IFA_LABEL,
    IFA_BROADCAST,
    IFA_ANYCAST,
    IFA_CACHEINFO,
    IFA_MULTICAST,
    __IFA_MAX,
};

对于 inet6 RTM_GETADDR 请求,内核以 IFA_ADDRESS (addrconf.c:inet6_fill_ifaddr()) 响应:-

nla_put(skb, IFA_ADDRESS, 16, &ifa->addr)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM