Tying up default route and route mgmt loose ends. It now periodically updates shadow routes so hopefully your link will stay up as you move around.

This commit is contained in:
Adam Ierymenko 2016-06-17 13:14:48 -07:00
parent 1bf1c38b30
commit 3ee15e65aa
2 changed files with 167 additions and 154 deletions

View file

@ -527,6 +527,7 @@ public:
NetworkState() : tap((EthernetTap *)0),managedIps(),managedRoutes(),allowManaged(true),allowGlobal(true),allowDefault(true) {}
EthernetTap *tap;
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
std::vector<InetAddress> managedIps;
std::list<ManagedRoute> managedRoutes;
bool allowManaged; // allow managed addresses and routes
@ -851,7 +852,7 @@ public:
restarted = true;
}
// Refresh bindings in case device's interfaces have changed
// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default)
if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
lastBindRefresh = now;
for(int i=0;i<3;++i) {
@ -859,6 +860,13 @@ public:
_bindings[i].refresh(_phy,_ports[i],*this);
}
}
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
if (n->second.tap)
syncManagedStuff(n->second,false,true);
}
}
}
uint64_t dl = _nextBackgroundTaskDeadline;
@ -985,6 +993,118 @@ public:
// Begin private implementation methods
// Checks if a managed IP or route target is allowed
bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &addr)
{
if (!n.allowManaged)
return false;
if (addr.isDefaultRoute())
return n.allowDefault;
switch(addr.ipScope()) {
case InetAddress::IP_SCOPE_NONE:
case InetAddress::IP_SCOPE_MULTICAST:
case InetAddress::IP_SCOPE_LOOPBACK:
case InetAddress::IP_SCOPE_LINK_LOCAL:
return false;
case InetAddress::IP_SCOPE_GLOBAL:
return n.allowGlobal;
default:
return true;
}
}
// Apply or update managed IPs for a configured network (be sure n.tap exists)
void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes)
{
if (syncIps) {
std::vector<InetAddress> newManagedIps;
newManagedIps.reserve(n.config.assignedAddressCount);
for(unsigned int i=0;i<n.config.assignedAddressCount;++i) {
const InetAddress *ii = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[i]));
if (checkIfManagedIsAllowed(n,*ii))
newManagedIps.push_back(*ii);
}
std::sort(newManagedIps.begin(),newManagedIps.end());
newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end());
for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
if (!n.tap->addIp(*ip))
fprintf(stderr,"ERROR: unable to add ip address %s"ZT_EOL_S, ip->toString().c_str());
}
}
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
if (!n.tap->removeIp(*ip))
fprintf(stderr,"ERROR: unable to remove ip address %s"ZT_EOL_S, ip->toString().c_str());
}
}
n.managedIps.swap(newManagedIps);
}
if (syncRoutes) {
const std::string tapdev(n.tap->deviceName());
// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
bool haveRoute = false;
if (checkIfManagedIsAllowed(n,mr->target())) {
for(unsigned int i=0;i<n.config.routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
if ( (mr->target() == *target) && ( ((via->ss_family == target->ss_family)&&(mr->via() == *via)) || (tapdev == mr->device()) ) ) {
haveRoute = true;
break;
}
}
}
if (haveRoute) {
++mr;
} else {
n.managedRoutes.erase(mr++);
}
}
// Apply routes in n.config.routes[] that we haven't applied yet, and sync those we have in case shadow routes need to change
for(unsigned int i=0;i<n.config.routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
if (!checkIfManagedIsAllowed(n,*target))
continue;
bool haveRoute = false;
// Ignore routes implied by local managed IPs since adding the IP adds the route
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) {
haveRoute = true;
break;
}
}
if (haveRoute)
continue;
// If we've already applied this route, just sync it and continue
for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
if ( (mr->target() == *target) && ( ((via->ss_family == target->ss_family)&&(mr->via() == *via)) || (tapdev == mr->device()) ) ) {
haveRoute = true;
mr->sync();
break;
}
}
if (haveRoute)
continue;
// Add and apply new routes
n.managedRoutes.push_back(ManagedRoute());
if (!n.managedRoutes.back().set(*target,*via,tapdev.c_str()))
n.managedRoutes.pop_back();
}
}
}
inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
{
#ifdef ZT_ENABLE_CLUSTER
@ -1256,129 +1376,9 @@ public:
// After setting up tap, fall through to CONFIG_UPDATE since we also want to do this...
case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig));
if (n.tap) { // sanity check
if (n.allowManaged) {
{ // configure managed IP addresses
std::vector<InetAddress> newManagedIps;
for(unsigned int i=0;i<nwc->assignedAddressCount;++i) {
const InetAddress *ii = reinterpret_cast<const InetAddress *>(&(nwc->assignedAddresses[i]));
switch(ii->ipScope()) {
case InetAddress::IP_SCOPE_NONE:
case InetAddress::IP_SCOPE_MULTICAST:
case InetAddress::IP_SCOPE_LOOPBACK:
case InetAddress::IP_SCOPE_LINK_LOCAL:
break; // ignore these -- they shouldn't appear here
case InetAddress::IP_SCOPE_GLOBAL:
if (!n.allowGlobal)
continue; // skip global IP ranges if we haven't given this network permission to assign them
// else fall through for PSEUDOPRIVATE, SHARED, PRIVATE
default:
newManagedIps.push_back(*ii);
break;
}
}
std::sort(newManagedIps.begin(),newManagedIps.end());
newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end());
for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
if (!n.tap->addIp(*ip))
fprintf(stderr,"ERROR: unable to add ip address %s"ZT_EOL_S, ip->toString().c_str());
}
}
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
if (!n.tap->removeIp(*ip))
fprintf(stderr,"ERROR: unable to remove ip address %s"ZT_EOL_S, ip->toString().c_str());
}
}
n.managedIps.swap(newManagedIps);
}
{ // configure managed routes
const std::string tapdev(n.tap->deviceName());
for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
bool haveRoute = false;
for(unsigned int i=0;i<nwc->routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(nwc->routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(nwc->routes[i].via));
if (mr->target() == *target) {
if ((via->ss_family == target->ss_family)&&(mr->via() == *via)) {
haveRoute = true;
break;
} else if (tapdev == mr->device()) {
haveRoute = true;
break;
}
}
}
if (haveRoute) {
++mr;
} else {
n.managedRoutes.erase(mr++); // also removes route via RAII behavior
}
}
for(unsigned int i=0;i<nwc->routeCount;++i) {
const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(nwc->routes[i].target));
const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(nwc->routes[i].via));
bool haveRoute = false;
// We don't need to bother applying local routes to local managed IPs since these are implied by setting the IP
for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) {
haveRoute = true;
break;
}
}
if (haveRoute)
continue;
for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) {
if (mr->target() == *target) {
if ((via->ss_family == target->ss_family)&&(mr->via() == *via)) {
haveRoute = true;
break;
} else if (tapdev == mr->device()) {
haveRoute = true;
break;
}
}
}
if (haveRoute)
continue;
n.managedRoutes.push_back(ManagedRoute());
if ((target->isDefaultRoute())&&(n.allowDefault)) {
if (!n.managedRoutes.back().set(*target,*via,tapdev.c_str()))
n.managedRoutes.pop_back();
} else {
switch(target->ipScope()) {
case InetAddress::IP_SCOPE_NONE:
case InetAddress::IP_SCOPE_MULTICAST:
case InetAddress::IP_SCOPE_LOOPBACK:
case InetAddress::IP_SCOPE_LINK_LOCAL:
break;
case InetAddress::IP_SCOPE_GLOBAL:
if (!n.allowGlobal)
continue; // skip global IP ranges if we haven't given this network permission to assign them
// else fall through for PSEUDOPRIVATE, SHARED, PRIVATE
default:
if (!n.managedRoutes.back().set(*target,*via,tapdev.c_str()))
n.managedRoutes.pop_back();
break;
}
}
}
}
}
syncManagedStuff(n,true,true);
} else {
_nets.erase(nwid);
return -999; // tap init failed