#include #include #include #include #include #include #include #include #include #include #include #include #include #define NL_BUFSIZE 64 * 1024 #include "netlink_watch.h" #include "nwstatus.h" #include "common.h" typedef struct { uint8_t buf[NL_BUFSIZE]; int fd; struct nlmsghdr *msg; int len; } netlink_t ; netlink_t *netlink = NULL; static void do_dump(int type, int seq) { struct { struct nlmsghdr nh; struct rtgenmsg gen; } req; req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); req.nh.nlmsg_flags = NLM_F_DUMP|NLM_F_REQUEST; req.nh.nlmsg_type = type; req.nh.nlmsg_pid = 0; req.nh.nlmsg_seq = seq; req.gen.rtgen_family = AF_UNSPEC; send(netlink->fd, &req, req.nh.nlmsg_len, 0); } static void handle_address(struct nlmsghdr *p) { if (p->nlmsg_type == RTM_NEWADDR || p->nlmsg_type == RTM_DELADDR) { struct ifaddrmsg *msg; int l; struct rtattr *r; int added = p->nlmsg_type == RTM_NEWADDR; msg = NLMSG_DATA(p); l = IFA_PAYLOAD(p); r = IFA_RTA(NLMSG_DATA(p)); for (;RTA_OK(r, l); r = RTA_NEXT(r,l)) { switch (r->rta_type) { case IFA_ADDRESS: if (added) { add_address(msg->ifa_index, msg->ifa_scope, msg->ifa_family, RTA_DATA(r)); } else { del_address(msg->ifa_index, msg->ifa_family, RTA_DATA(r)); } break; #if 0 case IFA_LOCAL: printf("Local: %s\n", inet_ntop(msg->ifa_family,RTA_DATA(r), namebuf, INET6_ADDRSTRLEN) ); break; case IFA_BROADCAST: printf("Broadcast: %s\n", inet_ntop(msg->ifa_family,RTA_DATA(r), namebuf, INET6_ADDRSTRLEN) ); break; case IFA_LABEL: printf("Label: %s\n", (char *)RTA_DATA(r)); break; case IFA_CACHEINFO: { struct ifa_cacheinfo *c = RTA_DATA(r); printf("Cache info: %d\n", c->ifa_prefered); } break; default: printf("-->%d\n", r->rta_type); #endif } } } } static void handle_link(struct nlmsghdr *p) { struct ifinfomsg *msg; int l; struct rtattr *r; msg = NLMSG_DATA(p); l = IFLA_PAYLOAD(p); r = IFLA_RTA(NLMSG_DATA(p)); if (p->nlmsg_type == RTM_DELLINK) { del_interface(msg->ifi_index); return; } interface_set_link(msg->ifi_index, msg->ifi_flags & IFF_UP); interface_set_flags(msg->ifi_index, msg->ifi_flags); for (;RTA_OK(r, l); r = RTA_NEXT(r,l)) { switch (r->rta_type) { case IFLA_IFNAME: interface_set_name(msg->ifi_index, (char *)RTA_DATA(r)); break; } } } static int netlink_next_message(void) { if (netlink->len <= 0) { return FALSE; } if (netlink->msg == NULL) { netlink->msg = (struct nlmsghdr *)netlink->buf; return TRUE; } netlink->msg = NLMSG_NEXT(netlink->msg, netlink->len); if (!NLMSG_OK(netlink->msg, netlink->len)) { /* No more messages in the queue */ netlink->len = 0; netlink->msg = NULL; return FALSE; } return TRUE; } int netlink_recv(void) { netlink->len = recv(netlink->fd, netlink->buf, NL_BUFSIZE, 0); if (netlink->len < 0) { fprintf(stderr, "Netlink receive failed\n"); exit(1); } return TRUE; } static void netlink_handle_message(void) { switch (netlink->msg->nlmsg_type) { case RTM_NEWADDR: case RTM_DELADDR: handle_address(netlink->msg); break; case RTM_NEWLINK: case RTM_DELLINK: handle_link(netlink->msg); break; } } void netlink_flush(void) { while (netlink_next_message()) { netlink_handle_message(); } } void watch_dump(int seq) { for (;;) { while (netlink_next_message()) { switch (netlink->msg->nlmsg_type) { case NLMSG_ERROR: if (seq == netlink->msg->nlmsg_seq) { struct nlmsgerr *e = NLMSG_DATA(netlink->msg); fprintf(stderr, "error seq: %d: %s\n", netlink->msg->nlmsg_seq, strerror(-e->error)); return; } break; case NLMSG_DONE: if (seq == netlink->msg->nlmsg_seq) { return; } break; default: netlink_handle_message(); break; } } netlink_recv(); } } void netlink_init(void) { struct sockaddr_nl addr; netlink = malloc(sizeof(netlink_t)); netlink->msg = NULL; netlink->len = 0; if ((netlink->fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { fprintf(stderr, "netlink socket failed\n"); exit(1); } memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR|RTMGRP_IPV6_IFADDR_BUG; addr.nl_pid = getpid(); if (bind(netlink->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { fprintf(stderr, "bind failed\n"); exit(1); } /* Dump all interfaces */ do_dump(RTM_GETLINK, 0xdeadbeef); watch_dump(0xdeadbeef); /* Dump all addresses */ do_dump(RTM_GETADDR, 0xdeadbeef); watch_dump(0xdeadbeef); } int netlink_get_fd(void) { return netlink->fd; }