You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1207 lines
30 KiB
1207 lines
30 KiB
/* This Source Code Form is subject to the terms of the Mozilla Public |
|
* License, v. 2.0. If a copy of the MPL was not distributed with this |
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. |
|
* |
|
* (c) ZeroTier, Inc. |
|
* https://www.zerotier.com/ |
|
*/ |
|
|
|
#include "../node/Constants.hpp" |
|
|
|
#include <cerrno> |
|
|
|
// #define ZT_NETLINK_TRACE |
|
|
|
#ifdef __LINUX__ |
|
|
|
#include "LinuxNetLink.hpp" |
|
|
|
#include <linux/if_tun.h> |
|
#include <unistd.h> |
|
|
|
#ifndef IFNAMSIZ |
|
#define IFNAMSIZ 16 |
|
#endif |
|
const int ZT_RTE_METRIC = 5000; |
|
|
|
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]; |
|
}; |
|
|
|
struct nl_adr_req { |
|
struct nlmsghdr nl; |
|
struct ifaddrmsg ifa; |
|
char buf[8192]; |
|
}; |
|
|
|
LinuxNetLink::LinuxNetLink() : _t(), _running(false), _seq(0), _interfaces(), _if_m(), _fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)), _la({ 0 }) |
|
{ |
|
// set socket timeout to 1 sec so we're not permablocking recv() calls |
|
_setSocketTimeout(_fd, 1); |
|
|
|
_la.nl_family = AF_NETLINK; |
|
_la.nl_pid = 0; // getpid()+1; |
|
_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); |
|
} |
|
|
|
_requestIPv4Routes(); |
|
_requestIPv6Routes(); |
|
_requestInterfaceList(); |
|
|
|
_running = true; |
|
_t = Thread::start(this); |
|
} |
|
|
|
LinuxNetLink::~LinuxNetLink() |
|
{ |
|
_running = false; |
|
Thread::join(_t); |
|
::close(_fd); |
|
} |
|
|
|
void LinuxNetLink::_setSocketTimeout(int fd, int seconds) |
|
{ |
|
struct timeval tv; |
|
tv.tv_sec = seconds; |
|
tv.tv_usec = 0; |
|
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) != 0) { |
|
#ifdef ZT_NETLINK_TRACE |
|
fprintf(stderr, "setsockopt failed: %s\n", strerror(errno)); |
|
#endif |
|
} |
|
} |
|
|
|
#define ZT_NL_BUF_SIZE 16384 |
|
int LinuxNetLink::_doRecv(int fd) |
|
{ |
|
char* buf = nullptr; |
|
if (posix_memalign((void**)&buf, 16, ZT_NL_BUF_SIZE) != 0) { |
|
fprintf(stderr, "malloc failed!\n"); |
|
::exit(1); |
|
} |
|
if (! buf) { |
|
fprintf(stderr, "malloc failed!\n"); |
|
::exit(1); |
|
} |
|
|
|
char* p = NULL; |
|
struct nlmsghdr* nlp; |
|
int nll = 0; |
|
int rtn = 0; |
|
p = buf; |
|
|
|
for (;;) { |
|
rtn = recv(fd, p, ZT_NL_BUF_SIZE - nll, 0); |
|
|
|
if (rtn > 0) { |
|
nlp = (struct nlmsghdr*)p; |
|
|
|
if (nlp->nlmsg_type == NLMSG_ERROR && (nlp->nlmsg_flags & NLM_F_ACK) != NLM_F_ACK) { |
|
struct nlmsgerr* err = (struct nlmsgerr*)NLMSG_DATA(nlp); |
|
if (err->error != 0) { |
|
#ifdef ZT_NETLINK_TRACE |
|
fprintf(stderr, "rtnetlink error: %s\n", strerror(-(err->error))); |
|
#endif |
|
} |
|
p = buf; |
|
nll = 0; |
|
break; |
|
} |
|
|
|
if (nlp->nlmsg_type == NLMSG_NOOP) { |
|
break; |
|
} |
|
|
|
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; |
|
break; |
|
} |
|
p += rtn; |
|
nll += rtn; |
|
} |
|
|
|
if (nlp->nlmsg_type == NLMSG_OVERRUN) { |
|
#ifdef ZT_NETLINK_TRACE |
|
fprintf(stderr, "NLMSG_OVERRUN: Data lost\n"); |
|
#endif |
|
p = buf; |
|
nll = 0; |
|
break; |
|
} |
|
|
|
nll += rtn; |
|
|
|
_processMessage(nlp, nll); |
|
|
|
p = buf; |
|
nll = 0; |
|
break; |
|
} |
|
else { |
|
break; |
|
} |
|
} |
|
|
|
free(buf); |
|
|
|
return rtn; |
|
} |
|
|
|
void LinuxNetLink::threadMain() throw() |
|
{ |
|
int rtn = 0; |
|
while (_running) { |
|
rtn = _doRecv(_fd); |
|
if (rtn <= 0) { |
|
Thread::sleep(250); |
|
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: |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void LinuxNetLink::_ipAddressAdded(struct nlmsghdr* nlp) |
|
{ |
|
#ifdef ZT_NETLINK_TRACE |
|
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; |
|
} |
|
} |
|
|
|
fprintf(stderr, "Added IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); |
|
#endif |
|
} |
|
|
|
void LinuxNetLink::_ipAddressDeleted(struct nlmsghdr* nlp) |
|
{ |
|
#ifdef ZT_NETLINK_TRACE |
|
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; |
|
} |
|
} |
|
|
|
fprintf(stderr, "Removed IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast); |
|
#endif |
|
} |
|
|
|
void LinuxNetLink::_routeAdded(struct nlmsghdr* nlp) |
|
{ |
|
char dsts[40] = { 0 }; |
|
char gws[40] = { 0 }; |
|
char srcs[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); |
|
|
|
Route r; |
|
bool wecare = false; |
|
|
|
for (; RTA_OK(rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) { |
|
switch (rtap->rta_type) { |
|
case RTA_DST: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); |
|
r.target.set(RTA_DATA(rtap), 4, 0); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); |
|
r.target.set(RTA_DATA(rtap), 16, 0); |
|
wecare = true; |
|
break; |
|
} |
|
break; |
|
case RTA_SRC: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); |
|
r.src.set(RTA_DATA(rtap), 4, 0); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); |
|
r.src.set(RTA_DATA(rtap), 16, 0); |
|
wecare = true; |
|
break; |
|
} |
|
break; |
|
case RTA_GATEWAY: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); |
|
r.via.set(RTA_DATA(rtap), 4, 0); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); |
|
r.via.set(RTA_DATA(rtap), 16, 0); |
|
wecare = true; |
|
break; |
|
} |
|
break; |
|
case RTA_OIF: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
r.ifidx = *((int*)RTA_DATA(rtap)); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
r.ifidx = *((int*)RTA_DATA(rtap)); |
|
wecare = true; |
|
break; |
|
} |
|
sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); |
|
break; |
|
} |
|
} |
|
|
|
if (wecare) { |
|
Mutex::Lock rl(_routes_m); |
|
_routes[r.target].insert(r); |
|
} |
|
|
|
#ifdef ZT_NETLINK_TRACE |
|
sprintf(ms, "%d", rtp->rtm_dst_len); |
|
fprintf(stderr, "Route Added: dst %s/%s gw %s src %s if %s\n", dsts, ms, gws, srcs, ifs); |
|
#endif |
|
} |
|
|
|
void LinuxNetLink::_routeDeleted(struct nlmsghdr* nlp) |
|
{ |
|
char dsts[40] = { 0 }; |
|
char gws[40] = { 0 }; |
|
char srcs[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); |
|
|
|
Route r; |
|
bool wecare = false; |
|
|
|
for (; RTA_OK(rtap, rtl); rtap = RTA_NEXT(rtap, rtl)) { |
|
switch (rtap->rta_type) { |
|
case RTA_DST: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); |
|
r.target.set(RTA_DATA(rtap), 4, 0); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, 24); |
|
r.target.set(RTA_DATA(rtap), 16, 0); |
|
wecare = true; |
|
break; |
|
} |
|
break; |
|
case RTA_SRC: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); |
|
r.src.set(RTA_DATA(rtap), 4, 0); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, 24); |
|
r.src.set(RTA_DATA(rtap), 16, 0); |
|
wecare = true; |
|
break; |
|
} |
|
break; |
|
case RTA_GATEWAY: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); |
|
r.via.set(RTA_DATA(rtap), 4, 0); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, 24); |
|
r.via.set(RTA_DATA(rtap), 16, 0); |
|
wecare = true; |
|
break; |
|
} |
|
break; |
|
case RTA_OIF: |
|
switch (rtp->rtm_family) { |
|
case AF_INET: |
|
r.ifidx = *((int*)RTA_DATA(rtap)); |
|
wecare = true; |
|
break; |
|
case AF_INET6: |
|
r.ifidx = *((int*)RTA_DATA(rtap)); |
|
wecare = true; |
|
break; |
|
} |
|
sprintf(ifs, "%d", *((int*)RTA_DATA(rtap))); |
|
break; |
|
} |
|
} |
|
|
|
if (wecare) { |
|
Mutex::Lock rl(_routes_m); |
|
_routes[r.target].erase(r); |
|
} |
|
|
|
#ifdef ZT_NETLINK_TRACE |
|
sprintf(ms, "%d", rtp->rtm_dst_len); |
|
fprintf(stderr, "Route Deleted: dst %s/%s gw %s src %s if %s\n", dsts, ms, gws, srcs, ifs); |
|
#endif |
|
} |
|
|
|
void LinuxNetLink::_linkAdded(struct nlmsghdr* nlp) |
|
{ |
|
unsigned char mac_bin[6] = { 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); |
|
|
|
const char* ptr = (const char*)0; |
|
for (; RTA_OK(rtap, ifil); rtap = RTA_NEXT(rtap, ifil)) { |
|
switch (rtap->rta_type) { |
|
case IFLA_ADDRESS: |
|
ptr = (const char*)RTA_DATA(rtap); |
|
memcpy(mac_bin, ptr, 6); |
|
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; |
|
} |
|
} |
|
|
|
{ |
|
Mutex::Lock l(_if_m); |
|
struct iface_entry& entry = _interfaces[ifip->ifi_index]; |
|
entry.index = ifip->ifi_index; |
|
memcpy(entry.ifacename, ifname, sizeof(ifname)); |
|
snprintf( |
|
entry.mac, |
|
sizeof(entry.mac), |
|
"%.02x:%.02x:%.02x:%.02x:%.02x:%.02x", |
|
(unsigned int)mac_bin[0], |
|
(unsigned int)mac_bin[1], |
|
(unsigned int)mac_bin[2], |
|
(unsigned int)mac_bin[3], |
|
(unsigned int)mac_bin[4], |
|
(unsigned int)mac_bin[5]); |
|
memcpy(entry.mac_bin, mac_bin, 6); |
|
entry.mtu = mtu; |
|
} |
|
} |
|
|
|
void LinuxNetLink::_linkDeleted(struct nlmsghdr* nlp) |
|
{ |
|
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); |
|
|
|
const char* ptr = (const char*)0; |
|
for (; RTA_OK(rtap, ifil); rtap = RTA_NEXT(rtap, ifil)) { |
|
switch (rtap->rta_type) { |
|
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; |
|
} |
|
} |
|
|
|
{ |
|
Mutex::Lock l(_if_m); |
|
if (_interfaces.contains(ifip->ifi_index)) { |
|
_interfaces.erase(ifip->ifi_index); |
|
} |
|
} |
|
} |
|
|
|
void LinuxNetLink::_requestIPv4Routes() |
|
{ |
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
la.nl_groups = RTMGRP_IPV4_ROUTE; |
|
if (bind(fd, (struct sockaddr*)&la, sizeof(la))) { |
|
fprintf(stderr, "Error binding RTNETLINK (_requestIPv4Routes #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
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 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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
void LinuxNetLink::_requestIPv6Routes() |
|
{ |
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
la.nl_groups = RTMGRP_IPV6_ROUTE; |
|
if (bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { |
|
fprintf(stderr, "Error binding RTNETLINK (_requestIPv6Routes #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
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 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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
void LinuxNetLink::_requestInterfaceList() |
|
{ |
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
la.nl_groups = RTMGRP_LINK; |
|
if (bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { |
|
fprintf(stderr, "Error binding RTNETLINK (_requestInterfaceList #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
void LinuxNetLink::addRoute(const InetAddress& target, const InetAddress& via, const InetAddress& src, const char* ifaceName) |
|
{ |
|
if (! target) |
|
return; |
|
|
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
bzero(&la, sizeof(la)); |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
|
|
if (bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { |
|
fprintf(stderr, "Error binding RTNETLINK (addRoute #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
#ifdef ZT_NETLINK_TRACE |
|
char tmp[64]; |
|
char tmp2[64]; |
|
char tmp3[64]; |
|
fprintf(stderr, "Adding Route. target: %s via: %s src: %s iface: %s\n", target.toString(tmp), via.toString(tmp2), src.toString(tmp3), ifaceName); |
|
#endif |
|
|
|
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 = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr)); |
|
} |
|
else { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr)); |
|
} |
|
rtl += rtap->rta_len; |
|
|
|
if (via) { |
|
/* |
|
* Setting a metric keeps zerotier routes from taking priority over physical |
|
* At best the computer would use zerotier through the router instead of the LAN. |
|
* At worst it stops working at all. |
|
* |
|
* default via 192.168.82.1 dev eth0 proto dhcp src 192.168.82.169 metric 202 |
|
* 10.147.17.0/24 dev zt5u4uptmb proto kernel scope link src 10.147.17.94 |
|
* 192.168.82.0/24 dev eth0 proto dhcp scope link src 192.168.82.169 metric 202 |
|
* 192.168.82.0/24 via 10.147.17.1 dev zt5u4uptmb proto static metric 5000 |
|
* |
|
*/ |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = RTA_PRIORITY; |
|
rtap->rta_len = RTA_LENGTH(sizeof(ZT_RTE_METRIC)); |
|
memcpy(RTA_DATA(rtap), &ZT_RTE_METRIC, sizeof(ZT_RTE_METRIC)); |
|
rtl += rtap->rta_len; |
|
|
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = RTA_GATEWAY; |
|
if (via.isV4()) { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr)); |
|
} |
|
else { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr)); |
|
} |
|
rtl += rtap->rta_len; |
|
} |
|
else if (src) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = RTA_SRC; |
|
if (src.isV4()) { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&src)->sin_addr, sizeof(struct in_addr)); |
|
} |
|
else { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&src)->sin6_addr, sizeof(struct in6_addr)); |
|
} |
|
req.rt.rtm_src_len = src.netmaskBits(); |
|
} |
|
|
|
if (ifaceName != NULL) { |
|
int interface_index = _indexForInterface(ifaceName); |
|
if (interface_index != -1) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = RTA_OIF; |
|
rtap->rta_len = RTA_LENGTH(sizeof(int)); |
|
memcpy(RTA_DATA(rtap), &interface_index, sizeof(int)); |
|
rtl += rtap->rta_len; |
|
} |
|
} |
|
|
|
req.nl.nlmsg_len = NLMSG_LENGTH(rtl); |
|
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE | NLM_F_ACK; |
|
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(); |
|
req.rt.rtm_flags = 0; |
|
|
|
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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
void LinuxNetLink::delRoute(const InetAddress& target, const InetAddress& via, const InetAddress& src, const char* ifaceName) |
|
{ |
|
if (! target) |
|
return; |
|
|
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
|
|
if (bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { |
|
fprintf(stderr, "Error binding RTNETLINK (delRoute #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
#ifdef ZT_NETLINK_TRACE |
|
char tmp[64]; |
|
char tmp2[64]; |
|
char tmp3[64]; |
|
fprintf(stderr, "Removing Route. target: %s via: %s src: %s iface: %s\n", target.toString(tmp), via.toString(tmp2), src.toString(tmp3), ifaceName); |
|
#endif |
|
|
|
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 = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr)); |
|
} |
|
else { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr)); |
|
} |
|
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 = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr)); |
|
} |
|
else { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr)); |
|
} |
|
rtl += rtap->rta_len; |
|
} |
|
else if (src) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = RTA_SRC; |
|
if (src.isV4()) { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&src)->sin_addr, sizeof(struct in_addr)); |
|
} |
|
else { |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&src)->sin6_addr, sizeof(struct in6_addr)); |
|
} |
|
req.rt.rtm_src_len = src.netmaskBits(); |
|
} |
|
|
|
if (ifaceName != NULL) { |
|
int interface_index = _indexForInterface(ifaceName); |
|
if (interface_index != -1) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = RTA_OIF; |
|
rtap->rta_len = RTA_LENGTH(sizeof(int)); |
|
memcpy(RTA_DATA(rtap), &interface_index, sizeof(int)); |
|
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(); |
|
req.rt.rtm_flags = 0; |
|
|
|
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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
void LinuxNetLink::addAddress(const InetAddress& addr, const char* iface) |
|
{ |
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
memset(&la, 0, sizeof(la)); |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
if (addr.isV4()) { |
|
la.nl_groups = RTMGRP_IPV4_IFADDR; |
|
} |
|
else { |
|
la.nl_groups = RTMGRP_IPV6_IFADDR; |
|
} |
|
|
|
if (bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { |
|
fprintf(stderr, "Error binding RTNETLINK (addAddress #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
#ifdef ZT_NETLINK_TRACE |
|
char tmp[128]; |
|
fprintf(stderr, "Adding IP address %s to interface %s\n", addr.toString(tmp), iface); |
|
#endif |
|
|
|
int interface_index = _indexForInterface(iface); |
|
for (int reps = 0; interface_index == -1 && reps < 10; ++reps) { |
|
Thread::sleep(100); |
|
interface_index = _indexForInterface(iface); |
|
} |
|
|
|
if (interface_index == -1) { |
|
fprintf(stderr, "Unable to find index for interface %s\n", iface); |
|
close(fd); |
|
return; |
|
} |
|
|
|
int rtl = sizeof(struct ifaddrmsg); |
|
struct nl_adr_req req; |
|
bzero(&req, sizeof(struct nl_adr_req)); |
|
|
|
struct rtattr* rtap = (struct rtattr*)req.buf; |
|
; |
|
if (addr.isV4()) { |
|
struct sockaddr_in* addr_v4 = (struct sockaddr_in*)&addr; |
|
rtap->rta_type = IFA_ADDRESS; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); |
|
rtl += rtap->rta_len; |
|
|
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = IFA_LOCAL; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); |
|
rtl += rtap->rta_len; |
|
|
|
InetAddress broadcast = addr.broadcast(); |
|
if (broadcast) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
struct sockaddr_in* bcast = (struct sockaddr_in*)&broadcast; |
|
rtap->rta_type = IFA_BROADCAST; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &bcast->sin_addr, sizeof(struct in_addr)); |
|
rtl += rtap->rta_len; |
|
} |
|
} |
|
else { // V6 |
|
rtap->rta_type = IFA_ADDRESS; |
|
struct sockaddr_in6* addr_v6 = (struct sockaddr_in6*)&addr; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &addr_v6->sin6_addr, sizeof(struct in6_addr)); |
|
rtl += rtap->rta_len; |
|
} |
|
|
|
if (iface) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = IFA_LABEL; |
|
rtap->rta_len = RTA_LENGTH(strlen(iface)); |
|
memcpy(RTA_DATA(rtap), iface, strlen(iface)); |
|
rtl += rtap->rta_len; |
|
} |
|
|
|
req.nl.nlmsg_len = NLMSG_LENGTH(rtl); |
|
req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; |
|
req.nl.nlmsg_type = RTM_NEWADDR; |
|
req.nl.nlmsg_pid = 0; |
|
req.nl.nlmsg_seq = ++_seq; |
|
req.ifa.ifa_family = addr.ss_family; |
|
req.ifa.ifa_prefixlen = addr.port(); |
|
req.ifa.ifa_flags = IFA_F_PERMANENT; |
|
req.ifa.ifa_scope = 0; |
|
req.ifa.ifa_index = interface_index; |
|
|
|
struct sockaddr_nl pa; |
|
bzero(&pa, sizeof(sockaddr_nl)); |
|
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; |
|
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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
void LinuxNetLink::removeAddress(const InetAddress& addr, const char* iface) |
|
{ |
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
|
if (fd == -1) { |
|
fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno)); |
|
return; |
|
} |
|
|
|
_setSocketTimeout(fd); |
|
|
|
struct sockaddr_nl la; |
|
la.nl_family = AF_NETLINK; |
|
la.nl_pid = 0; // getpid(); |
|
if (addr.isV4()) { |
|
la.nl_groups = RTMGRP_IPV4_IFADDR; |
|
} |
|
else { |
|
la.nl_groups = RTMGRP_IPV6_IFADDR; |
|
} |
|
if (bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) { |
|
fprintf(stderr, "Error binding RTNETLINK (removeAddress #1): %s\n", strerror(errno)); |
|
close(fd); |
|
return; |
|
} |
|
|
|
#ifdef ZT_NETLINK_TRACE |
|
char tmp[128]; |
|
fprintf(stderr, "Removing IP address %s from interface %s\n", addr.toString(tmp), iface); |
|
#endif |
|
|
|
int interface_index = _indexForInterface(iface); |
|
|
|
if (interface_index == -1) { |
|
fprintf(stderr, "Unable to find index for interface %s\n", iface); |
|
close(fd); |
|
return; |
|
} |
|
|
|
int rtl = sizeof(struct ifaddrmsg); |
|
struct nl_adr_req req; |
|
bzero(&req, sizeof(struct nl_adr_req)); |
|
|
|
struct rtattr* rtap = (struct rtattr*)req.buf; |
|
if (addr.isV4()) { |
|
struct sockaddr_in* addr_v4 = (struct sockaddr_in*)&addr; |
|
rtap->rta_type = IFA_ADDRESS; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); |
|
rtl += rtap->rta_len; |
|
|
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = IFA_LOCAL; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr)); |
|
rtl += rtap->rta_len; |
|
|
|
InetAddress broadcast = addr.broadcast(); |
|
if (broadcast) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
struct sockaddr_in* bcast = (struct sockaddr_in*)&broadcast; |
|
rtap->rta_type = IFA_BROADCAST; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr)); |
|
memcpy(RTA_DATA(rtap), &bcast->sin_addr, sizeof(struct in_addr)); |
|
rtl += rtap->rta_len; |
|
} |
|
} |
|
else { // V6 |
|
rtap->rta_type = IFA_ADDRESS; |
|
struct sockaddr_in6* addr_v6 = (struct sockaddr_in6*)&addr; |
|
rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); |
|
memcpy(RTA_DATA(rtap), &addr_v6->sin6_addr, sizeof(struct in6_addr)); |
|
rtl += rtap->rta_len; |
|
} |
|
|
|
if (iface) { |
|
rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len); |
|
rtap->rta_type = IFA_LABEL; |
|
rtap->rta_len = RTA_LENGTH(strlen(iface)); |
|
memcpy(RTA_DATA(rtap), iface, strlen(iface)); |
|
rtl += rtap->rta_len; |
|
} |
|
|
|
req.nl.nlmsg_len = NLMSG_LENGTH(rtl); |
|
req.nl.nlmsg_flags = NLM_F_REQUEST; |
|
req.nl.nlmsg_type = RTM_DELADDR; |
|
req.nl.nlmsg_pid = 0; |
|
req.nl.nlmsg_seq = ++_seq; |
|
req.ifa.ifa_family = addr.ss_family; |
|
req.ifa.ifa_prefixlen = addr.port(); |
|
req.ifa.ifa_flags = IFA_F_PERMANENT; |
|
req.ifa.ifa_scope = 0; |
|
req.ifa.ifa_index = interface_index; |
|
|
|
struct sockaddr_nl pa; |
|
bzero(&pa, sizeof(sockaddr_nl)); |
|
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; |
|
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); |
|
|
|
_doRecv(fd); |
|
|
|
close(fd); |
|
} |
|
|
|
bool LinuxNetLink::routeIsSet(const InetAddress& target, const InetAddress& via, const InetAddress& src, const char* ifname) |
|
{ |
|
Mutex::Lock rl(_routes_m); |
|
const std::set<LinuxNetLink::Route>& rs = _routes[target]; |
|
for (std::set<LinuxNetLink::Route>::const_iterator ri(rs.begin()); ri != rs.end(); ++ri) { |
|
if ((ri->via == via) && (ri->src == src)) { |
|
if (ifname) { |
|
Mutex::Lock ifl(_if_m); |
|
const iface_entry* ife = _interfaces.get(ri->ifidx); |
|
if ((ife) && (! strncmp(ife->ifacename, ifname, IFNAMSIZ))) |
|
return true; |
|
} |
|
else { |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
int LinuxNetLink::_indexForInterface(const char* iface) |
|
{ |
|
Mutex::Lock l(_if_m); |
|
int interface_index = -1; |
|
Hashtable<int, iface_entry>::Iterator iter(_interfaces); |
|
int* k = NULL; |
|
iface_entry* v = NULL; |
|
while (iter.next(k, v)) { |
|
if (strcmp(iface, v->ifacename) == 0) { |
|
interface_index = v->index; |
|
break; |
|
} |
|
} |
|
return interface_index; |
|
} |
|
|
|
} // namespace ZeroTier |
|
|
|
#endif
|
|
|