#include #include #include #include #include #include #include #include #include #include uint32_t seq = 0; #define IPV4_ADDR_LEN 4 int init_nl_sock() { /* TODO: error handling */ struct sockaddr_nl local; int bufsz =1024 * 1024; int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsz, sizeof(bufsz)); setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsz, sizeof(bufsz)); local.nl_family = AF_NETLINK; local.nl_groups = RTMGRP_IPV4_ROUTE; bind(fd, (struct sockaddr*)&local, sizeof(local)); return fd; } int handle(struct nlmsghdr* n) { fprintf(stderr, "got nlmsghdr seq: %u\n", n->nlmsg_seq); if (n->nlmsg_type == RTM_NEWROUTE) { /* it is the answer for our route request */ if (n->nlmsg_seq == seq) { struct rtmsg* r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr* rta = RTM_RTA(r); int alen; char* addr; int index; fprintf(stderr, "got answer\n"); len -= NLMSG_LENGTH(sizeof(*r)); assert(len >= 0); while (RTA_OK(rta, len)) { /* may not have gateway attr */ if (rta->rta_type == RTA_GATEWAY) { alen = RTA_PAYLOAD(rta); /* IPv4, 4 byte */ assert(alen == IPV4_ADDR_LEN); addr = RTA_DATA(rta); fprintf(stderr, "via len: %d, addr: %d.%d.%d.%d\n", alen, addr[0], addr[1], addr[2], addr[3]); } if (rta->rta_type == RTA_OIF) { alen = RTA_PAYLOAD(rta); /* int */ assert(alen == sizeof(index)); index = *(int*)RTA_DATA(rta); fprintf(stderr, "out device index: %d\n", index); } rta = RTA_NEXT(rta, len); } assert(len == 0); } else { fprintf(stderr, "got route addition broadcast\n"); } } if (n->nlmsg_type == RTM_DELROUTE) { fprintf(stderr, "got route deletion broadcast\n"); } return 0; } int r(int fd) { struct iovec iov; char buf[8192]; int status; struct msghdr msg = { .msg_name = NULL, .msg_namelen = 0, .msg_iov = &iov, .msg_iovlen = 1 }; struct nlmsghdr *n; iov.iov_base = buf; iov.iov_len = 8192; /* TODO: error handling, EINTR, EAGAIN */ status = recvmsg(fd, &msg, 0); assert(status > 0); /* may return multi nlmsgs once recvmsg */ for (n = (struct nlmsghdr*)buf; status >= sizeof(*n); ) { int len = n->nlmsg_len; int l = len - sizeof(*n); assert(l >= 0 && len <= status); handle(n); status -= NLMSG_ALIGN(len); n = (struct nlmsghdr*)((void*)n + NLMSG_ALIGN(len)); } return 0; } /* send route request */ int s(int fd, char* addr) { struct { struct nlmsghdr n; struct rtmsg r; char attr[1024]; } req; struct rtattr* rta; struct iovec iov; struct msghdr msg; struct nlmsghdr* n = &req.n; struct sockaddr_nl dst_addr; int len; req.r.rtm_family = AF_INET; req.r.rtm_table = 0; req.r.rtm_protocol = 0; req.r.rtm_scope = 0; req.r.rtm_type = 0; req.r.rtm_src_len = 0; req.r.rtm_tos = 0; n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); n->nlmsg_flags = NLM_F_REQUEST; n->nlmsg_type = RTM_GETROUTE; /* * route request seq id * used to trace route request */ n->nlmsg_seq = ++seq; rta = (struct rtattr*)((void*)n + NLMSG_ALIGN(n->nlmsg_len)); len = RTA_LENGTH(IPV4_ADDR_LEN); rta->rta_type = RTA_DST; rta->rta_len = len; memcpy(RTA_DATA(rta), addr, IPV4_ADDR_LEN); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); /* address mask 32 */ req.r.rtm_dst_len = 32; iov.iov_base = (void*) n; iov.iov_len = n->nlmsg_len; memset(&msg, 0, sizeof(msg)); msg.msg_name = &dst_addr; msg.msg_namelen = sizeof(dst_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; memset(&dst_addr, 0, sizeof(dst_addr)); dst_addr.nl_family = AF_NETLINK; /* TODO: error handling */ return sendmsg(fd, &msg, 0); } int main() { int fd = init_nl_sock(); /* get route for 7.7.7.7/32 */ char a[4] = {7, 7, 7, 7}; s(fd, a); while (1) { r(fd); } return 0; }