Linux Netlink 编程

作者: louyang | 来源:发表于2017-11-29 10:23 被阅读404次

    Netlink 是内核与用户空间进程通信的一种机制,采用与 UDP socket 非常类似的编程风格。(关于 UDP socket 编程,请看这里

    与 UDP 类似,Netlink 通信也有服务器端和客户端。例如,Linux 内核作为服务器端,已经运行在那里。我们则编写 Netlink 客户端,给内核发消息,并获得我们想要的信息。

    例子 1

    通过 Netlink,从内核中得到所有网口的名字。

    link-list.c:

    /*
     *  Display all network interface names
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 8192
    
    struct nl_req_s {
      struct nlmsghdr hdr;
      struct rtgenmsg gen;
    };
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void rtnl_print_link(struct nlmsghdr * h)
    {
        struct ifinfomsg * iface;
        struct rtattr * attr;
        int len;
    
        iface = NLMSG_DATA(h);
        len = RTM_PAYLOAD(h);
    
        /* loop over all attributes for the NEWLINK message */
        for (attr = IFLA_RTA(iface); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
        {
            switch (attr->rta_type)
            {
            case IFLA_IFNAME:
                printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));
                break;
            default:
                break;
            }
        }
    }
    
    int main(void)
    {
        struct sockaddr_nl kernel;
        int s, end=0, len;
        struct msghdr msg;
        struct nl_req_s req;
        struct iovec io;
        char buf[BUFSIZE];
    
        //build kernel netlink address
        memset(&kernel, 0, sizeof(kernel));
        kernel.nl_family = AF_NETLINK;
        kernel.nl_groups = 0;
    
        //create a Netlink socket
        if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        //build netlink message
        memset(&req, 0, sizeof(req));
        req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
        req.hdr.nlmsg_type = RTM_GETLINK;
        req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
        req.hdr.nlmsg_seq = 1;
        req.hdr.nlmsg_pid = getpid();
        req.gen.rtgen_family = AF_INET;
    
        memset(&io, 0, sizeof(io));
        io.iov_base = &req;
        io.iov_len = req.hdr.nlmsg_len;
    
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = &io;
        msg.msg_iovlen = 1;
        msg.msg_name = &kernel;
        msg.msg_namelen = sizeof(kernel);
    
        //send the message
        if (sendmsg(s, &msg, 0) < 0)
        {
            die("sendmsg");
        }
    
        //parse reply
        while (!end)
        {
            memset(buf, 0, BUFSIZE);
            msg.msg_iov->iov_base = buf;
            msg.msg_iov->iov_len = BUFSIZE;
            if ((len=recvmsg(s, &msg, 0)) < 0)
            {
                die("recvmsg");
            }
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case NLMSG_DONE:
                    end++;
                    break;
                case RTM_NEWLINK:
                    rtnl_print_link(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    # gcc link-list.c -o link-list && ./link-list
    Interface 1 : lo
    Interface 2 : eth0
    
    例子 2

    通过 Netlink,从内核中得到所有网口的 IP 地址。

    /*
     *  Display all IPv4 addresses
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 8192
    
    struct nl_req_s {
      struct nlmsghdr hdr;
      struct rtgenmsg gen;
    };
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void rtnl_print_addr(struct nlmsghdr * h)
    {
        struct ifaddrmsg * addr;
        struct rtattr * attr;
        int len;
    
        addr = NLMSG_DATA(h);
        len = RTM_PAYLOAD(h);
    
        /* loop over all attributes for the NEWLINK message */
        for (attr = IFLA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
        {
            switch (attr->rta_type)
            {
            case IFA_LABEL:
                printf("Interface  : %s\n", (char *)RTA_DATA(attr));
                break;
            case IFA_LOCAL:
            {
                int ip = *(int*)RTA_DATA(attr);
                unsigned char bytes[4];
                bytes[0] = ip & 0xFF;
                bytes[1] = (ip >> 8) & 0xFF;
                bytes[2] = (ip >> 16) & 0xFF;
                bytes[3] = (ip >> 24) & 0xFF;
                printf("IP Address : %d.%d.%d.%d\n", bytes[0], bytes[1], bytes[2], bytes[3]);
                break;
            }
            default:
                break;
            }
        }
    }
    
    int main(void)
    {
        struct sockaddr_nl kernel;
        int s, end=0, len;
        struct msghdr msg;
        struct nl_req_s req;
        struct iovec io;
        char buf[BUFSIZE];
    
        //build kernel netlink address
        memset(&kernel, 0, sizeof(kernel));
        kernel.nl_family = AF_NETLINK;
        kernel.nl_groups = 0;
    
        //create a Netlink socket
        if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        //build netlink message
        memset(&req, 0, sizeof(req));
        req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
        req.hdr.nlmsg_type = RTM_GETADDR;
        req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
        req.hdr.nlmsg_seq = 1;
        req.hdr.nlmsg_pid = getpid();
        req.gen.rtgen_family = AF_INET;
    
        memset(&io, 0, sizeof(io));
        io.iov_base = &req;
        io.iov_len = req.hdr.nlmsg_len;
    
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = &io;
        msg.msg_iovlen = 1;
        msg.msg_name = &kernel;
        msg.msg_namelen = sizeof(kernel);
    
        //send the message
        if (sendmsg(s, &msg, 0) < 0)
        {
            die("sendmsg");
        }
    
        //parse reply
        while (!end)
        {
            memset(buf, 0, BUFSIZE);
            msg.msg_iov->iov_base = buf;
            msg.msg_iov->iov_len = BUFSIZE;
            if ((len=recvmsg(s, &msg, 0)) < 0)
            {
                die("recvmsg");
            }
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case NLMSG_DONE:
                    end++;
                    break;
                case RTM_NEWADDR:
                    rtnl_print_addr(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    # gcc addr-list.c -o addr-list && ./addr-list
    IP Address : 127.0.0.1
    Interface  : lo
    IP Address : 10.254.115.149
    Interface  : eth0
    
    例子 3

    通过 Netlink 列出当前系统中主路由表中的所有路由。

    /*
     *  Display all routes
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl,NLMSG_DATA
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 8192
    
    struct nl_req_s {
      struct nlmsghdr hdr;
      struct rtgenmsg gen;
    };
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void rtnl_print_route(struct nlmsghdr * h)
    {
        struct rtmsg * rte = NLMSG_DATA(h);
        struct rtattr * attr = RTM_RTA(rte);
        int len = RTM_PAYLOAD(h);
        char dest[32] = {0};
        char gway[32] = {"unspecified"};
    
        if (rte->rtm_table != RT_TABLE_MAIN) {
            return;
        }
    
        for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
        {
            switch (attr->rta_type)
            {
            case RTA_DST:
                inet_ntop(AF_INET, RTA_DATA(attr), dest, sizeof(dest));
                break;
            case RTA_GATEWAY:
                inet_ntop(AF_INET, RTA_DATA(attr), gway, sizeof(gway));
                break;
            default:
                break;
            }
        }
    
        printf("%s/%d gateway %s\n", dest, rte->rtm_dst_len, gway);
    }
    
    int main(void)
    {
        struct sockaddr_nl kernel;
        int s, end=0, len;
        struct msghdr msg;
        struct nl_req_s req;
        struct iovec io;
        char buf[BUFSIZE];
    
        //build kernel netlink address
        memset(&kernel, 0, sizeof(kernel));
        kernel.nl_family = AF_NETLINK;
        kernel.nl_groups = 0;
    
        //create a Netlink socket
        if ((s=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        //build netlink message
        memset(&req, 0, sizeof(req));
        req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
        req.hdr.nlmsg_type = RTM_GETROUTE;
        req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
        req.hdr.nlmsg_seq = 1;
        req.hdr.nlmsg_pid = getpid();
        req.gen.rtgen_family = AF_INET;
    
        memset(&io, 0, sizeof(io));
        io.iov_base = &req;
        io.iov_len = req.hdr.nlmsg_len;
    
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = &io;
        msg.msg_iovlen = 1;
        msg.msg_name = &kernel;
        msg.msg_namelen = sizeof(kernel);
    
        //send the message
        if (sendmsg(s, &msg, 0) < 0)
        {
            die("sendmsg");
        }
    
        // parse reply
        while (!end)
        {
            memset(buf, 0, BUFSIZE);
            msg.msg_iov->iov_base = buf;
            msg.msg_iov->iov_len = BUFSIZE;
    
            if ((len=recvmsg(s, &msg, 0)) < 0)
            {
                die("recvmsg");
            }
    
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case NLMSG_DONE:
                    end++;
                    break;
                case RTM_NEWROUTE:
                    rtnl_print_route(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d, len=%d\n", msg_ptr->nlmsg_type, msg_ptr->nlmsg_len);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    # gcc route-list.c -o route-list && ./route-list
    /0 gateway 10.254.96.136
    10.254.96.0/19 gateway unspecified
    
    例子 4

    通过 Netlink 监测主路由表中路由的变化。

    /*
     *  Monitor route change
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl,NLMSG_DATA
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 4096
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void print_route(struct nlmsghdr * h)
    {
        struct rtmsg * rte = NLMSG_DATA(h);
        struct rtattr * attr = RTM_RTA(rte);
        int len = RTM_PAYLOAD(h);
        char dest[32] = {0};
        char gway[32] = {"unspecified"};
    
        if (rte->rtm_table != RT_TABLE_MAIN) {
            return;
        }
    
        for (; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
        {
            switch (attr->rta_type)
            {
            case RTA_DST:
                inet_ntop(AF_INET, RTA_DATA(attr), dest, sizeof(dest));
                break;
            case RTA_GATEWAY:
                inet_ntop(AF_INET, RTA_DATA(attr), gway, sizeof(gway));
                break;
            default:
                break;
            }
        }
    
        if (h->nlmsg_type == RTM_NEWROUTE) {
            printf("add ");
        }
        else if (h->nlmsg_type == RTM_DELROUTE) {
            printf("del ");
        }
        else {
            printf("nlmsg_type=%d ", h->nlmsg_type);
        }
        printf("%s/%d gateway %s\n", dest, rte->rtm_dst_len, gway);
    }
    
    int main(void)
    {
        int s, len;
        struct sockaddr_nl me;
        char buf[BUFSIZE];
    
        memset(&me, 0, sizeof(me));
    
        if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        me.nl_family = AF_NETLINK;
        me.nl_groups = RTMGRP_IPV4_ROUTE;
    
        if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
        {
            die("bind");
        }
    
        while (1)
        {
            memset(buf, 0, BUFSIZE);
            if ((len=recv(s, buf, BUFSIZE, 0)) < 0)
            {
                die("recv");
            }
    
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case RTM_NEWROUTE:
                case RTM_DELROUTE:
                    print_route(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    Terminal 1:
    # gcc route-chg.c -o route-chg && ./route-chg
    
    Terminal 2:
    # ip route add 199.1.1.0/24 via 10.254.96.136
    # ip route del 199.1.1.0/24 via 10.254.96.136
    
    Terminal 1:
    # gcc route-chg.c -o route-chg && ./route-chg
    add 199.1.1.0/24 gateway 10.254.96.136
    del 199.1.1.0/24 gateway 10.254.96.136
    
    例子 5

    通过 Netlink 监测网口的变化。

    /*
     *  Monitor network interface change
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl,NLMSG_DATA
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 4096
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void print_link(struct nlmsghdr * h)
    {
        struct ifinfomsg * iface;
        struct rtattr * attr;
        int len;
    
        iface = NLMSG_DATA(h); //point to payload
        len = RTM_PAYLOAD(h);
    
        /* loop over all attributes for the NEWLINK message */
        for (attr = IFLA_RTA(iface); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
        {
            switch (attr->rta_type)
            {
            case IFLA_IFNAME:
                if (h->nlmsg_type == RTM_NEWLINK) {
                    printf("add ");
                }
                else if (h->nlmsg_type == RTM_DELLINK) {
                    printf("del ");
                }
                else {
                    printf("nlmsg_type=%d ", h->nlmsg_type);
                }
    
                printf("Interface %d : %s\n", iface->ifi_index, (char *)RTA_DATA(attr));
                break;
            default:
                break;
            }
        }
    }
    
    int main(void)
    {
        int s, len;
        struct sockaddr_nl me;
        char buf[BUFSIZE];
    
        memset(&me, 0, sizeof(me));
    
        if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        int on = 1;
        if (setsockopt(s, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, sizeof(on)) < 0)
        {
            die("setsockopt");
        }
    
        me.nl_family = AF_NETLINK;
        me.nl_groups = RTMGRP_LINK;
    
        if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
        {
            die("bind");
        }
    
        while (1)
        {
            memset(buf, 0, BUFSIZE);
            if ((len=recv(s, buf, BUFSIZE, 0)) < 0)
            {
                die("recv");
            }
    
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case RTM_NEWLINK:
                case RTM_DELLINK:
                    print_link(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    Terminal 1:
    # gcc link-chg.c -o link-chg && ./link-chg
    
    Terminal 2:
    # ip link add veth1 type veth
    # ip link del veth1 type veth
    
    Terminal 1:
    # gcc link-chg.c -o link-chg && ./link-chg
    add Interface 8 : veth0
    add Interface 9 : veth1
    del Interface 9 : veth1
    del Interface 8 : veth0
    
    例子 6

    通过 Netlink 监测 IP 地址的变化。

    /*
     *  Monitor IP address change
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl,NLMSG_DATA
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 4096
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void print_addr(struct nlmsghdr * h)
    {
        struct ifaddrmsg * addr;
        struct rtattr * attr;
        int len;
    
        addr = NLMSG_DATA(h);
        len = RTM_PAYLOAD(h);
    
        /* loop over all attributes for the NEWLINK message */
        for (attr = IFLA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
        {
            switch (attr->rta_type)
            {
            case IFA_LABEL:
                printf("Interface  : %s\n", (char *)RTA_DATA(attr));
                break;
            case IFA_LOCAL:
            {
                if (h->nlmsg_type == RTM_NEWADDR) {
                    printf("add ");
                }
                else if (h->nlmsg_type == RTM_DELADDR) {
                    printf("del ");
                }
                else {
                    printf("nlmsg_type=%d ", h->nlmsg_type);
                }
    
                int ip = *(int*)RTA_DATA(attr);
                unsigned char bytes[4];
                bytes[0] = ip & 0xFF;
                bytes[1] = (ip >> 8) & 0xFF;
                bytes[2] = (ip >> 16) & 0xFF;
                bytes[3] = (ip >> 24) & 0xFF;
                printf("IP Address : %d.%d.%d.%d\n", bytes[0], bytes[1], bytes[2], bytes[3]);
                break;
            }
            default:
                break;
            }
        }
    }
    
    int main(void)
    {
        int s, len;
        struct sockaddr_nl me;
        char buf[BUFSIZE];
    
        memset(&me, 0, sizeof(me));
    
        if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        int on = 1;
        if (setsockopt(s, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, sizeof(on)) < 0)
        {
            die("setsockopt");
        }
    
        me.nl_family = AF_NETLINK;
        me.nl_groups = RTMGRP_IPV4_IFADDR;
    
        if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
        {
            die("bind");
        }
    
        while (1)
        {
            memset(buf, 0, BUFSIZE);
            if ((len=recv(s, buf, BUFSIZE, 0)) < 0)
            {
                die("recv");
            }
    
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case RTM_NEWADDR:
                case RTM_DELADDR:
                    print_addr(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    Terminal 1:
    # gcc addr-chg.c -o addr-chg && ./addr-chg
    
    Terminal 2:
    # ip address add 20.20.20.2/24 dev eth0
    # ip address del 20.20.20.2/24 dev eth0
    
    Terminal 1:
    # gcc addr-chg.c -o addr-chg && ./addr-chg
    add IP Address : 20.20.20.2
    Interface  : eth0
    del IP Address : 20.20.20.2
    Interface  : eth0
    
    例子 7

    通过 Netlink 监测所有 namespace 中的 IP 地址的变化。

    /*
     *  Monitor IP address change
     */
    #include <stdio.h>            //printf, perror
    #include <string.h>           //memset, strlen
    #include <stdlib.h>           //exit
    #include <unistd.h>           //close
    #include <sys/socket.h>       //msghdr
    #include <arpa/inet.h>        //inet_ntop
    #include <linux/netlink.h>    //sockaddr_nl,NLMSG_DATA
    #include <linux/rtnetlink.h>  //rtgenmsg,ifinfomsg
    
    #define BUFSIZE 4096
    
    void die(char *s)
    {
        perror(s);
        exit(1);
    }
    
    void print_addr(struct nlmsghdr * h)
    {
        struct ifaddrmsg * addr;
        struct rtattr * attr;
        int len;
    
        addr = NLMSG_DATA(h);
        len = RTM_PAYLOAD(h);
    
        /* loop over all attributes for the NEWLINK message */
        for (attr = IFLA_RTA(addr); RTA_OK(attr, len); attr = RTA_NEXT(attr, l)
        {
            switch (attr->rta_type)
            {
            case IFA_LABEL:
                printf("Interface  : %s\n", (char *)RTA_DATA(attr));
                break;
            case IFA_LOCAL:
            {
                if (h->nlmsg_type == RTM_NEWADDR) {
                    printf("ADD ");
                }
                else if (h->nlmsg_type == RTM_DELADDR) {
                    printf("DEL ");
                }
                else {
                    printf("nlmsg_type=%d ", h->nlmsg_type);
                }
    
                int ip = *(int*)RTA_DATA(attr);
                unsigned char bytes[4];
                bytes[0] = ip & 0xFF;
                bytes[1] = (ip >> 8) & 0xFF;
                bytes[2] = (ip >> 16) & 0xFF;
                bytes[3] = (ip >> 24) & 0xFF;
                printf("IP Address : %d.%d.%d.%d\n", bytes[0], bytes[1], bytes, bytes[3]);
                break;
            }
            default:
                break;
            }
        }
        printf("\n");
    }
    
    int main(void)
    {
        int s, len;
        struct sockaddr_nl me;
        struct msghdr msg;
        struct iovec io;
        char buf[BUFSIZE];
        char cmsgbuf[BUFSIZE];
    
        if ((s = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
        {
            die("socket");
        }
    
        int on = 1;
        if (setsockopt(s, SOL_NETLINK, NETLINK_LISTEN_ALL_NSID, &on, sizeof(on< 0)
        {
            die("setsockopt");
        }
    
        memset(&me, 0, sizeof(me));
        me.nl_family = AF_NETLINK;
        me.nl_groups = RTMGRP_IPV4_IFADDR;
    
        if (bind(s,(struct sockaddr *)&me, sizeof(me)) < 0)
        {
            die("bind");
        }
    
        memset(&msg, 0, sizeof(msg));
        msg.msg_iov = &io;
        msg.msg_iovlen = 1;
        msg.msg_name = &me;
        msg.msg_namelen = sizeof(me);
        msg.msg_control = &cmsgbuf;
        msg.msg_controllen = BUFSIZE;
    
        memset(&io, 0, sizeof(io));
        msg.msg_iov->iov_base = buf;
        msg.msg_iov->iov_len = BUFSIZE;
    
        while (1)
        {
            memset(buf, 0, BUFSIZE);
            memset(cmsgbuf, 0, BUFSIZE);
            msg.msg_iov->iov_len = BUFSIZE;
            msg.msg_controllen = BUFSIZE;
    
            if ((len=recvmsg(s, &msg, 0)) < 0)
            {
                die("recvmsg");
            }
    
            for (struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSXTHDR(&msg, cmsg))
            {
                if (cmsg->cmsg_level == SOL_NETLINK &&
                    cmsg->cmsg_type == NETLINK_LISTEN_ALL_NSID &&
                    cmsg->cmsg_len == CMSG_LEN(sizeof(int)))
                {
                    int * data = (int *)CMSG_DATA(cmsg);
                    printf("Namespace ID : %d\n", *data);
                }
            }
    
            for (struct nlmsghdr * msg_ptr = (struct nlmsghdr *)buf;
                 NLMSG_OK(msg_ptr, len); msg_ptr = NLMSG_NEXT(msg_ptr, len))
            {
                switch (msg_ptr->nlmsg_type)
                {
                case RTM_NEWADDR:
                case RTM_DELADDR:
                    print_addr(msg_ptr);
                    break;
                default:
                    printf("Ignored msg: type=%d\n", msg_ptr->nlmsg_type);
                    break;
                }
            }
        }
    
        close(s);
        return 0;
    }
    
    Terminal 1:
    # gcc addr-chg.c -o addr-chg && ./addr-chg
    
    Terminal 2:
    # ip netns add red
    # ip netns set red 123
    # ip netns
    red (id: 123)
    # ip address add 20.20.20.2/24 dev lo
    # ip address del 20.20.20.2/24 dev lo
    # ip netns exec red ip address add 20.20.20.3/24 dev lo
    # ip netns exec red ip address del 20.20.20.3/24 dev lo
    
    Terminal 1:
    # gcc addr-chg.c -o addr-chg && ./addr-chg
    ADD IP Address : 20.20.20.2
    Interface  : lo
    
    DEL IP Address : 20.20.20.2
    Interface  : lo
    
    Namespace ID : 123
    ADD IP Address : 20.20.20.3
    Interface  : lo
    
    Namespace ID : 123
    DEL IP Address : 20.20.20.3
    Interface  : lo
    

    参考文献

    1 http://man7.org/linux/man-pages/man7/rtnetlink.7.html
    2 http://man7.org/linux/man-pages/man7/netlink.7.html
    3 https://gist.github.com/cl4u2/5204374

    相关文章

      网友评论

        本文标题:Linux Netlink 编程

        本文链接:https://www.haomeiwen.com/subject/nwmavxtx.html