diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index 777e88dcf..46d5c0070 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -399,6 +399,10 @@ private: { return ((unsigned long)i * (unsigned long)0x9e3779b1); } + static inline unsigned long _hc(const int i) + { + return ((unsigned long)i * (unsigned long)0x9e3379b1); + } inline void _grow() { diff --git a/osdep/LinuxNetLink.cpp b/osdep/LinuxNetLink.cpp index 062144dcb..6251cb102 100644 --- a/osdep/LinuxNetLink.cpp +++ b/osdep/LinuxNetLink.cpp @@ -30,408 +30,608 @@ namespace ZeroTier { +struct nl_route_req { + struct nlmsghdr nl; + struct rtmsg rt; + char buf[8192]; +}; + +struct nl_if_req { + struct nlmsghdr nl; + struct ifinfomsg ifa; + char buf[8192]; +}; + LinuxNetLink::LinuxNetLink() - : _t() - , _running(false) - , _routes_ipv4() - , _routes_ipv6() - , _seq(0) - , _fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) - , _la({0}) + : _t() + , _running(false) + , _routes_ipv4() + , _routes_ipv6() + , _seq(0) + , _fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) + , _la({0}) { - // set socket timeout to 1 sec so we're not permablocking recv() calls - struct timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; - if(setsockopt(_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) != 0) { - fprintf(stderr, "setsockopt failed: %s\n", strerror(errno)); - } + // set socket timeout to 1 sec so we're not permablocking recv() calls + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + if(setsockopt(_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) != 0) { + fprintf(stderr, "setsockopt failed: %s\n", strerror(errno)); + } - _la.nl_family = AF_NETLINK; - _la.nl_pid = getpid(); - _la.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR|RTMGRP_IPV4_ROUTE|RTMGRP_IPV6_ROUTE|RTMGRP_NOTIFY; - if (bind(_fd, (struct sockaddr*)&_la, sizeof(_la))) { - fprintf(stderr, "Error connecting to RTNETLINK: %s\n", strerror(errno)); - ::exit(1); - } + _la.nl_family = AF_NETLINK; + _la.nl_pid = getpid(); + _la.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR|RTMGRP_IPV4_ROUTE|RTMGRP_IPV6_ROUTE|RTMGRP_NOTIFY; + if (bind(_fd, (struct sockaddr*)&_la, sizeof(_la))) { + fprintf(stderr, "Error connecting to RTNETLINK: %s\n", strerror(errno)); + ::exit(1); + } - _running = true; - _t = Thread::start(this); + _running = true; + _t = Thread::start(this); - fprintf(stderr, "Requesting IPV4 Routes\n"); - _requestIPv4Routes(); - Thread::sleep(10); - fprintf(stderr, "Requesting IPV6 Routes\n"); - _requestIPv6Routes(); + fprintf(stderr, "Requesting IPV4 Routes\n"); + _requestIPv4Routes(); + Thread::sleep(10); + fprintf(stderr, "Requesting IPV6 Routes\n"); + _requestIPv6Routes(); + Thread::sleep(10); + fprintf(stderr, "Requesting Interface List\n"); + _requestInterfaceList(); } LinuxNetLink::~LinuxNetLink() { - _running = false; - Thread::join(_t); + _running = false; + Thread::join(_t); - ::close(_fd); + ::close(_fd); } void LinuxNetLink::threadMain() throw() { - char buf[8192]; - char *p = NULL; - struct nlmsghdr *nlp; - int nll = 0; - int rtn = 0; - p = buf; + char buf[8192]; + char *p = NULL; + struct nlmsghdr *nlp; + int nll = 0; + int rtn = 0; + p = buf; - while(_running) { - rtn = recv(_fd, p, sizeof(buf) - nll, 0); + while(_running) { + rtn = recv(_fd, p, sizeof(buf) - nll, 0); - if (rtn > 0) { - nlp = (struct nlmsghdr *)p; + if (rtn > 0) { + nlp = (struct nlmsghdr *)p; - if(nlp->nlmsg_type == NLMSG_ERROR && (nlp->nlmsg_flags & NLM_F_ACK) != NLM_F_ACK) { - fprintf(stderr, "NLMSG_ERROR\n"); - struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nlp); - if (err->error != 0) { - fprintf(stderr, "rtnetlink error: %s\n", strerror(-(err->error))); - } - p = buf; - nll = 0; - continue; - } + if(nlp->nlmsg_type == NLMSG_ERROR && (nlp->nlmsg_flags & NLM_F_ACK) != NLM_F_ACK) { + fprintf(stderr, "NLMSG_ERROR\n"); + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nlp); + if (err->error != 0) { + fprintf(stderr, "rtnetlink error: %s\n", strerror(-(err->error))); + } + p = buf; + nll = 0; + continue; + } - if (nlp->nlmsg_type == NLMSG_NOOP) { - fprintf(stderr, "noop\n"); - continue; - } + if (nlp->nlmsg_type == NLMSG_NOOP) { + fprintf(stderr, "noop\n"); + continue; + } - if( (nlp->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI || (nlp->nlmsg_type == NLMSG_DONE)) - { - if (nlp->nlmsg_type == NLMSG_DONE) { - _processMessage(nlp, nll); - p = buf; - nll = 0; - continue; - } - p += rtn; - nll += rtn; - } + if( (nlp->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI || (nlp->nlmsg_type == NLMSG_DONE)) + { + if (nlp->nlmsg_type == NLMSG_DONE) { + _processMessage(nlp, nll); + p = buf; + nll = 0; + continue; + } + p += rtn; + nll += rtn; + } - if (nlp->nlmsg_type == NLMSG_OVERRUN) { - fprintf(stderr, "NLMSG_OVERRUN: Data lost\n"); - p = buf; - nll = 0; - continue; - } - - nll += rtn; + if (nlp->nlmsg_type == NLMSG_OVERRUN) { + fprintf(stderr, "NLMSG_OVERRUN: Data lost\n"); + p = buf; + nll = 0; + continue; + } + + nll += rtn; - _processMessage(nlp, nll); - p = buf; - nll = 0; - } - else { - Thread::sleep(100); - continue; - } - } + _processMessage(nlp, nll); + p = buf; + nll = 0; + } + else { + Thread::sleep(100); + continue; + } + } } void LinuxNetLink::_processMessage(struct nlmsghdr *nlp, int nll) { - for(; NLMSG_OK(nlp, nll); nlp=NLMSG_NEXT(nlp, nll)) - { - switch(nlp->nlmsg_type) - { - case RTM_NEWLINK: - _linkAdded(nlp); - break; - case RTM_DELLINK: - _linkDeleted(nlp); - break; - case RTM_NEWADDR: - _ipAddressAdded(nlp); - break; - case RTM_DELADDR: - _ipAddressDeleted(nlp); - break; - case RTM_NEWROUTE: - _routeAdded(nlp); - break; - case RTM_DELROUTE: - _routeDeleted(nlp); - break; - default: - fprintf(stderr, "ignore msgtype %d...\n", nlp->nlmsg_type); - } - } + for(; NLMSG_OK(nlp, nll); nlp=NLMSG_NEXT(nlp, nll)) + { + switch(nlp->nlmsg_type) + { + case RTM_NEWLINK: + _linkAdded(nlp); + break; + case RTM_DELLINK: + _linkDeleted(nlp); + break; + case RTM_NEWADDR: + _ipAddressAdded(nlp); + break; + case RTM_DELADDR: + _ipAddressDeleted(nlp); + break; + case RTM_NEWROUTE: + _routeAdded(nlp); + break; + case RTM_DELROUTE: + _routeDeleted(nlp); + break; + default: + fprintf(stderr, "ignore msgtype %d...\n", nlp->nlmsg_type); + } + } } void LinuxNetLink::_ipAddressAdded(struct nlmsghdr *nlp) { - struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap); - int ifal = IFA_PAYLOAD(nlp); + struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp); + struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap); + int ifal = IFA_PAYLOAD(nlp); - char addr[40] = {0}; - char local[40] = {0}; - char label[40] = {0}; - char bcast[40] = {0}; - - for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal)) - { - switch(rtap->rta_type) { - case IFA_ADDRESS: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40); - break; - case IFA_LOCAL: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40); - break; - case IFA_LABEL: - memcpy(label, RTA_DATA(rtap), 40); - break; - case IFA_BROADCAST: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40); - break; - } - } + char addr[40] = {0}; + char local[40] = {0}; + char label[40] = {0}; + char bcast[40] = {0}; + + for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal)) + { + switch(rtap->rta_type) { + case IFA_ADDRESS: + inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40); + break; + case IFA_LOCAL: + inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40); + break; + case IFA_LABEL: + memcpy(label, RTA_DATA(rtap), 40); + break; + case IFA_BROADCAST: + inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40); + break; + } + } - fprintf(stderr, "Added IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); + fprintf(stderr, "Added IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); } void LinuxNetLink::_ipAddressDeleted(struct nlmsghdr *nlp) { - struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap); - int ifal = IFA_PAYLOAD(nlp); + struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp); + struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap); + int ifal = IFA_PAYLOAD(nlp); - char addr[40] = {0}; - char local[40] = {0}; - char label[40] = {0}; - char bcast[40] = {0}; - - for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal)) - { - switch(rtap->rta_type) { - case IFA_ADDRESS: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40); - break; - case IFA_LOCAL: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40); - break; - case IFA_LABEL: - memcpy(label, RTA_DATA(rtap), 40); - break; - case IFA_BROADCAST: - inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40); - break; - } - } + char addr[40] = {0}; + char local[40] = {0}; + char label[40] = {0}; + char bcast[40] = {0}; + + for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal)) + { + switch(rtap->rta_type) { + case IFA_ADDRESS: + inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40); + break; + case IFA_LOCAL: + inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40); + break; + case IFA_LABEL: + memcpy(label, RTA_DATA(rtap), 40); + break; + case IFA_BROADCAST: + inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40); + break; + } + } - fprintf(stderr, "Removed IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); + fprintf(stderr, "Removed IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); } void LinuxNetLink::_routeAdded(struct nlmsghdr *nlp) { - char dsts[40] = {0}; - char gws[40] = {0}; - char ifs[16] = {0}; - char ms[24] = {0}; + char dsts[40] = {0}; + char gws[40] = {0}; + char ifs[16] = {0}; + char ms[24] = {0}; - struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp); - int rtl = RTM_PAYLOAD(nlp); + struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp); + struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp); + int rtl = RTM_PAYLOAD(nlp); - for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl)) - { - switch(rtap->rta_type) - { - case RTA_DST: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, rtp->rtm_family == AF_INET ? 24 : 40); - break; - case RTA_GATEWAY: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, rtp->rtm_family == AF_INET ? 24 : 40); - break; - case RTA_OIF: - sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); - break; - } - } - sprintf(ms, "%d", rtp->rtm_dst_len); + for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl)) + { + switch(rtap->rta_type) + { + case RTA_DST: + inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, rtp->rtm_family == AF_INET ? 24 : 40); + break; + case RTA_GATEWAY: + inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, rtp->rtm_family == AF_INET ? 24 : 40); + break; + case RTA_OIF: + sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); + break; + } + } + sprintf(ms, "%d", rtp->rtm_dst_len); - fprintf(stderr, "Route Added: dst %s/%s gw %s if %s\n", dsts, ms, gws, ifs); + fprintf(stderr, "Route Added: dst %s/%s gw %s if %s\n", dsts, ms, gws, ifs); } void LinuxNetLink::_routeDeleted(struct nlmsghdr *nlp) { - char dsts[40] = {0}; - char gws[40] = {0}; - char ifs[16] = {0}; - char ms[24] = {0}; + char dsts[40] = {0}; + char gws[40] = {0}; + char ifs[16] = {0}; + char ms[24] = {0}; - struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp); - int rtl = RTM_PAYLOAD(nlp); + struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp); + struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp); + int rtl = RTM_PAYLOAD(nlp); - for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl)) - { - switch(rtap->rta_type) - { - case RTA_DST: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, rtp->rtm_family == AF_INET ? 24 : 40); - break; - case RTA_GATEWAY: - inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, rtp->rtm_family == AF_INET ? 24 : 40); - break; - case RTA_OIF: - sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); - break; - } - } - sprintf(ms, "%d", rtp->rtm_dst_len); + for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl)) + { + switch(rtap->rta_type) + { + case RTA_DST: + inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, rtp->rtm_family == AF_INET ? 24 : 40); + break; + case RTA_GATEWAY: + inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, rtp->rtm_family == AF_INET ? 24 : 40); + break; + case RTA_OIF: + sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); + break; + } + } + sprintf(ms, "%d", rtp->rtm_dst_len); - fprintf(stderr, "Route Deleted: dst %s/%s gw %s if %s\n", dsts, ms, gws, ifs); + fprintf(stderr, "Route Deleted: dst %s/%s gw %s if %s\n", dsts, ms, gws, ifs); } void LinuxNetLink::_linkAdded(struct nlmsghdr *nlp) { - char mac[20] = {0}; - unsigned int mtu = 0; - char ifname[40] = {0}; + char mac[18] = {0}; + unsigned int mtu = 0; + char ifname[IFNAMSIZ] = {0}; - struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip); - int ifil = RTM_PAYLOAD(nlp); + struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp); + struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip); + int ifil = RTM_PAYLOAD(nlp); - const char *ptr; - unsigned char *ptr2; - for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil)) - { - switch(rtap->rta_type) { - case IFLA_ADDRESS: - ptr2 = (unsigned char*)RTA_DATA(rtap); - snprintf(mac, 20, "%02x:%02x:%02x:%02x:%02x:%02x", - ptr2[0], ptr2[1], ptr2[2], ptr2[3], ptr2[4], ptr2[5]); - break; - case IFLA_IFNAME: - ptr = (const char*)RTA_DATA(rtap); - memcpy(ifname, ptr, strlen(ptr)); - break; - case IFLA_MTU: - memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int)); - break; - } - } + const char *ptr; + unsigned char *ptr2; + for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil)) + { + switch(rtap->rta_type) { + case IFLA_ADDRESS: + ptr2 = (unsigned char*)RTA_DATA(rtap); + snprintf(mac, 20, "%02x:%02x:%02x:%02x:%02x:%02x", + ptr2[0], ptr2[1], ptr2[2], ptr2[3], ptr2[4], ptr2[5]); + break; + case IFLA_IFNAME: + ptr = (const char*)RTA_DATA(rtap); + memcpy(ifname, ptr, strlen(ptr)); + break; + case IFLA_MTU: + memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int)); + break; + } + } - fprintf(stderr, "Link Added: %s mac: %s, mtu: %d\n", ifname, mac, mtu); + struct iface_entry &entry = _interfaces[ifip->ifi_index]; + entry.index = ifip->ifi_index; + memcpy(entry.ifacename, ifname, sizeof(ifname)); + memcpy(entry.mac, mac, sizeof(mac)); + entry.mtu = mtu; + + fprintf(stderr, "Link Added: %s mac: %s, mtu: %d\n", ifname, mac, mtu); } void LinuxNetLink::_linkDeleted(struct nlmsghdr *nlp) { - char mac[20] = {0}; - unsigned int mtu = 0; - char ifname[40] = {0}; + char mac[18] = {0}; + unsigned int mtu = 0; + char ifname[40] = {0}; - struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp); - struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip); - int ifil = RTM_PAYLOAD(nlp); + struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp); + struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip); + int ifil = RTM_PAYLOAD(nlp); - const char *ptr; - unsigned char *ptr2; - for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil)) - { - switch(rtap->rta_type) { - case IFLA_ADDRESS: - ptr2 = (unsigned char*)RTA_DATA(rtap); - snprintf(mac, 20, "%02x:%02x:%02x:%02x:%02x:%02x", - ptr2[0], ptr2[1], ptr2[2], ptr2[3], ptr2[4], ptr2[5]); - break; - case IFLA_IFNAME: - ptr = (const char*)RTA_DATA(rtap); - memcpy(ifname, ptr, strlen(ptr)); - break; - case IFLA_MTU: - memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int)); - break; - } - } + const char *ptr; + unsigned char *ptr2; + for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil)) + { + switch(rtap->rta_type) { + case IFLA_ADDRESS: + ptr2 = (unsigned char*)RTA_DATA(rtap); + snprintf(mac, 20, "%02x:%02x:%02x:%02x:%02x:%02x", + ptr2[0], ptr2[1], ptr2[2], ptr2[3], ptr2[4], ptr2[5]); + break; + case IFLA_IFNAME: + ptr = (const char*)RTA_DATA(rtap); + memcpy(ifname, ptr, strlen(ptr)); + break; + case IFLA_MTU: + memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int)); + break; + } + } - fprintf(stderr, "Link Deleted: %s mac: %s, mtu: %d\n", ifname, mac, mtu); + fprintf(stderr, "Link Deleted: %s mac: %s, mtu: %d\n", ifname, mac, mtu); + if(_interfaces.contains(ifip->ifi_index)) { + _interfaces.erase(ifip->ifi_index); + } } void LinuxNetLink::_requestIPv4Routes() { - struct nl_req req; - bzero(&req, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_type = RTM_GETROUTE; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.rt.rtm_family = AF_INET; - req.rt.rtm_table = RT_TABLE_MAIN; + struct nl_route_req req; + bzero(&req, sizeof(req)); + req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nl.nlmsg_type = RTM_GETROUTE; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++_seq; + req.rt.rtm_family = AF_INET; + req.rt.rtm_table = RT_TABLE_MAIN; - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; + struct sockaddr_nl pa; + bzero(&pa, sizeof(pa)); + pa.nl_family = AF_NETLINK; - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); + struct msghdr msg; + bzero(&msg, sizeof(msg)); + msg.msg_name = (void*)&pa; + msg.msg_namelen = sizeof(pa); - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + struct iovec iov; + bzero(&iov, sizeof(iov)); + iov.iov_base = (void*)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - sendmsg(_fd, &msg, 0); + sendmsg(_fd, &msg, 0); } void LinuxNetLink::_requestIPv6Routes() { - struct nl_req req; - bzero(&req, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_type = RTM_GETROUTE; - req.nl.nlmsg_pid = 0; - req.nl.nlmsg_seq = ++_seq; - req.rt.rtm_family = AF_INET6; - req.rt.rtm_table = RT_TABLE_MAIN; + struct nl_route_req req; + bzero(&req, sizeof(req)); + req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nl.nlmsg_type = RTM_GETROUTE; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++_seq; + req.rt.rtm_family = AF_INET6; + req.rt.rtm_table = RT_TABLE_MAIN; - struct sockaddr_nl pa; - bzero(&pa, sizeof(pa)); - pa.nl_family = AF_NETLINK; + struct sockaddr_nl pa; + bzero(&pa, sizeof(pa)); + pa.nl_family = AF_NETLINK; - struct msghdr msg; - bzero(&msg, sizeof(msg)); - msg.msg_name = (void*)&pa; - msg.msg_namelen = sizeof(pa); + struct msghdr msg; + bzero(&msg, sizeof(msg)); + msg.msg_name = (void*)&pa; + msg.msg_namelen = sizeof(pa); - struct iovec iov; - bzero(&iov, sizeof(iov)); - iov.iov_base = (void*)&req.nl; - iov.iov_len = req.nl.nlmsg_len; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + struct iovec iov; + bzero(&iov, sizeof(iov)); + iov.iov_base = (void*)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - while((sendmsg(_fd, &msg, 0)) == -1) { - fprintf(stderr, "ipv6 waiting..."); - Thread::sleep(100); - } + sendmsg(_fd, &msg, 0); +} + +void LinuxNetLink::_requestInterfaceList() +{ + struct nl_if_req req; + bzero(&req, sizeof(req)); + req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nl.nlmsg_type = RTM_GETLINK; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++_seq; + req.ifa.ifi_family = AF_UNSPEC; + + struct sockaddr_nl pa; + bzero(&pa, sizeof(pa)); + pa.nl_family = AF_NETLINK; + + struct msghdr msg; + bzero(&msg, sizeof(msg)); + msg.msg_name = (void*)&pa; + msg.msg_namelen = sizeof(pa); + + struct iovec iov; + bzero(&iov, sizeof(iov)); + iov.iov_base = (void*)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + sendmsg(_fd, &msg, 0); } void LinuxNetLink::addRoute(const InetAddress &target, const InetAddress &via, const char *ifaceName) { + int rtl = sizeof(struct rtmsg); + struct nl_route_req req; + bzero(&req, sizeof(req)); + struct rtattr *rtap = (struct rtattr *)req.buf; + rtap->rta_type = RTA_DST; + if (target.isV4()) { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in_addr); + memcpy((void*)((char*)rtap+sizeof(struct rtattr)), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr)); + } else { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in6_addr); + memcpy((void*)((char*)rtap+sizeof(struct rtattr)), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr)); + } + rtl += rtap->rta_len; + + int interface_index = -1; + if (ifaceName != NULL) { + Hashtable::Iterator iter(_interfaces); + int *k = NULL; + iface_entry *v = NULL; + while(iter.next(k, v)) { + if(strcmp(ifaceName, v->ifacename) == 0) { + interface_index = v->index; + break; + } + } + if (interface_index != -1) { + rtap = (struct rtattr *) (((char*)rtap) + rtap->rta_len); + rtap->rta_type = RTA_OIF; + rtap->rta_len = sizeof(struct rtattr)+sizeof(int); + memcpy(((char*)rtap)+sizeof(rtattr), &interface_index, sizeof(int)); + rtl += rtap->rta_len; + } + } + + if(via) { + rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len); + rtap->rta_type = RTA_GATEWAY; + if(via.isV4()) { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in_addr); + memcpy((char*)rtap+sizeof(struct rtattr), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr)); + } else { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in6_addr); + memcpy((char*)rtap+sizeof(struct rtattr), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr)); + } + rtl += rtap->rta_len; + } + + req.nl.nlmsg_len = NLMSG_LENGTH(rtl); + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + req.nl.nlmsg_type = RTM_NEWROUTE; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++_seq; + req.rt.rtm_family = target.ss_family; + req.rt.rtm_table = RT_TABLE_MAIN; + req.rt.rtm_protocol = RTPROT_STATIC; + req.rt.rtm_scope = RT_SCOPE_UNIVERSE; + req.rt.rtm_type = RTN_UNICAST; + req.rt.rtm_dst_len = target.netmaskBits(); + + struct sockaddr_nl pa; + bzero(&pa, sizeof(pa)); + pa.nl_family = AF_NETLINK; + + struct msghdr msg; + bzero(&msg, sizeof(msg)); + msg.msg_name = (void*)&pa; + msg.msg_namelen = sizeof(pa); + + struct iovec iov; + bzero(&iov, sizeof(iov)); + iov.iov_base = (void*)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + sendmsg(_fd, &msg, 0); } void LinuxNetLink::delRoute(const InetAddress &target, const InetAddress &via, const char *ifaceName) { + int rtl = sizeof(struct rtmsg); + struct nl_route_req req; + bzero(&req, sizeof(req)); + struct rtattr *rtap = (struct rtattr *)req.buf; + rtap->rta_type = RTA_DST; + if (target.isV4()) { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in_addr); + memcpy((void*)((char*)rtap+sizeof(struct rtattr)), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr)); + } else { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in6_addr); + memcpy((void*)((char*)rtap+sizeof(struct rtattr)), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr)); + } + rtl += rtap->rta_len; + + int interface_index = -1; + if (ifaceName != NULL) { + Hashtable::Iterator iter(_interfaces); + int *k = NULL; + iface_entry *v = NULL; + while(iter.next(k, v)) { + if(strcmp(ifaceName, v->ifacename) == 0) { + interface_index = v->index; + break; + } + } + if (interface_index != -1) { + rtap = (struct rtattr *) (((char*)rtap) + rtap->rta_len); + rtap->rta_type = RTA_OIF; + rtap->rta_len = sizeof(struct rtattr)+sizeof(int); + memcpy(((char*)rtap)+sizeof(rtattr), &interface_index, sizeof(int)); + rtl += rtap->rta_len; + } + } + + if(via) { + rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len); + rtap->rta_type = RTA_GATEWAY; + if(via.isV4()) { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in_addr); + memcpy((char*)rtap+sizeof(struct rtattr), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr)); + } else { + rtap->rta_len = sizeof(struct rtattr)+sizeof(struct in6_addr); + memcpy((char*)rtap+sizeof(struct rtattr), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr)); + } + rtl += rtap->rta_len; + } + + req.nl.nlmsg_len = NLMSG_LENGTH(rtl); + req.nl.nlmsg_flags = NLM_F_REQUEST; + req.nl.nlmsg_type = RTM_DELROUTE; + req.nl.nlmsg_pid = 0; + req.nl.nlmsg_seq = ++_seq; + req.rt.rtm_family = target.ss_family; + req.rt.rtm_table = RT_TABLE_MAIN; + req.rt.rtm_protocol = RTPROT_STATIC; + req.rt.rtm_scope = RT_SCOPE_UNIVERSE; + req.rt.rtm_type = RTN_UNICAST; + req.rt.rtm_dst_len = target.netmaskBits(); + + struct sockaddr_nl pa; + bzero(&pa, sizeof(pa)); + pa.nl_family = AF_NETLINK; + + struct msghdr msg; + bzero(&msg, sizeof(msg)); + msg.msg_name = (void*)&pa; + msg.msg_namelen = sizeof(pa); + + struct iovec iov; + bzero(&iov, sizeof(iov)); + iov.iov_base = (void*)&req.nl; + iov.iov_len = req.nl.nlmsg_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + sendmsg(_fd, &msg, 0); } void LinuxNetLink::addInterface(const char *iface, unsigned int mtu) @@ -446,12 +646,12 @@ void LinuxNetLink::addAddress(const InetAddress &addr, const char *iface) RouteList LinuxNetLink::getIPV4Routes() const { - return _routes_ipv4; + return _routes_ipv4; } RouteList LinuxNetLink::getIPV6Routes() const { - return _routes_ipv6; + return _routes_ipv6; } } // namespace ZeroTier \ No newline at end of file diff --git a/osdep/LinuxNetLink.hpp b/osdep/LinuxNetLink.hpp index 8ee42ee4f..d0e831896 100644 --- a/osdep/LinuxNetLink.hpp +++ b/osdep/LinuxNetLink.hpp @@ -33,28 +33,23 @@ #include #include #include - +#include #include "../node/InetAddress.hpp" #include "Thread.hpp" +#include "../node/Hashtable.hpp" namespace ZeroTier { struct route_entry { - InetAddress target; - InetAddress via; - const char *iface; + InetAddress target; + InetAddress via; + int if_index; + char iface[IFNAMSIZ]; }; - typedef std::vector RouteList; -struct nl_req { - struct nlmsghdr nl; - struct rtmsg rt; - char buf[8192]; -}; - /** * Interface with Linux's RTNETLINK */ @@ -93,18 +88,27 @@ private: void _ipAddressAdded(struct nlmsghdr *nlp); void _ipAddressDeleted(struct nlmsghdr *nlp); - + void _requestInterfaceList(); void _requestIPv4Routes(); void _requestIPv6Routes(); Thread _t; bool _running; + RouteList _routes_ipv4; RouteList _routes_ipv6; uint32_t _seq; + struct iface_entry { + int index; + char ifacename[IFNAMSIZ]; + char mac[18]; + unsigned int mtu; + }; + Hashtable _interfaces; + // socket communication vars; int _fd; struct sockaddr_nl _la; diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp index 8d64fde32..fe7c62673 100644 --- a/osdep/ManagedRoute.cpp +++ b/osdep/ManagedRoute.cpp @@ -54,6 +54,7 @@ #include #include #include +#include "../osdep/LinuxNetLink.hpp" #endif #ifdef __BSD__ #include @@ -284,44 +285,14 @@ static void _routeCmd(const char *op,const InetAddress &target,const InetAddress #ifdef __LINUX__ // ---------------------------------------------------------- #define ZT_ROUTING_SUPPORT_FOUND 1 -static bool _hasRoute(const InetAddress &target, const InetAddress &via, const char *localInterface) -{ - if (target.ss_family == AF_INET) { - int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - - char *buf; - int nll; - struct rtmsg *rtp; - int rtl; - struct rtattr *rtap; - - struct sockaddr_nl la; - bzero(&la, sizeof(la)); - la.nl_family = AF_NETLINK; - la.nl_pad = 0; - la.nl_pid = (uint32_t)((ptrdiff_t)&target % getpid()); - la.nl_groups = 0; - int rtn = bind(fd, (struct sockaddr*)&la, sizeof(la)); - - - - close(fd); - return false; - } else { - - return false; - } -} - - static void _routeCmd(const char *op, const InetAddress &target, const InetAddress &via, const char *localInterface) { - bool hasRoute = _hasRoute(target, via, localInterface); - if (hasRoute && (strcmp(op, "add") == 0 || strcmp(op, "replace") == 0)) { - return; - } else if (!hasRoute && (strcmp(op, "remove") == 0 || strcmp(op, "del") == 0)) { - return; + if ((strcmp(op, "add") == 0 || strcmp(op, "replace") == 0)) { + LinuxNetLink::getInstance().addRoute(target, via, localInterface); + } else if ((strcmp(op, "remove") == 0 || strcmp(op, "del") == 0)) { + LinuxNetLink::getInstance().delRoute(target, via, localInterface); } + return; char targetStr[64] = {0}; char viaStr[64] = {0};