tinc/src/subnet.c
Guus Sliepen 57991e2642 Use PATHEXT when checking for the presence of scripts on Windows.
It seems like a lot of overhead to call access() for every possible extension
defined in PATHEXT, but apparently this is what Windows does itself too. At
least this avoids calling system() when the script one is looking for does not
exist at all.

Since the tinc utility also needs to call scripts, execute_script() is now
split off into its own source file.
2013-08-23 21:23:46 +02:00

282 lines
6.3 KiB
C

/*
subnet.c -- handle subnet lookups and lists
Copyright (C) 2000-2013 Guus Sliepen <guus@tinc-vpn.org>,
2000-2005 Ivo Timmermans
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "system.h"
#include "splay_tree.h"
#include "control_common.h"
#include "device.h"
#include "hash.h"
#include "logger.h"
#include "names.h"
#include "net.h"
#include "netutl.h"
#include "node.h"
#include "script.h"
#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
/* lists type of subnet */
splay_tree_t *subnet_tree;
/* Subnet lookup cache */
hash_t *ipv4_cache;
hash_t *ipv6_cache;
hash_t *mac_cache;
void subnet_cache_flush(void) {
hash_clear(ipv4_cache);
hash_clear(ipv6_cache);
hash_clear(mac_cache);
}
/* Initialising trees */
void init_subnets(void) {
subnet_tree = splay_alloc_tree((splay_compare_t) subnet_compare, (splay_action_t) free_subnet);
ipv4_cache = hash_alloc(0x100, sizeof(ipv4_t));
ipv6_cache = hash_alloc(0x100, sizeof(ipv6_t));
mac_cache = hash_alloc(0x100, sizeof(mac_t));
}
void exit_subnets(void) {
splay_delete_tree(subnet_tree);
hash_free(ipv4_cache);
hash_free(ipv6_cache);
hash_free(mac_cache);
}
splay_tree_t *new_subnet_tree(void) {
return splay_alloc_tree((splay_compare_t) subnet_compare, NULL);
}
void free_subnet_tree(splay_tree_t *subnet_tree) {
splay_delete_tree(subnet_tree);
}
/* Allocating and freeing space for subnets */
subnet_t *new_subnet(void) {
return xzalloc(sizeof(subnet_t));
}
void free_subnet(subnet_t *subnet) {
free(subnet);
}
/* Adding and removing subnets */
void subnet_add(node_t *n, subnet_t *subnet) {
subnet->owner = n;
splay_insert(subnet_tree, subnet);
splay_insert(n->subnet_tree, subnet);
subnet_cache_flush();
}
void subnet_del(node_t *n, subnet_t *subnet) {
splay_delete(n->subnet_tree, subnet);
splay_delete(subnet_tree, subnet);
subnet_cache_flush();
}
/* Subnet lookup routines */
subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) {
return splay_search(owner->subnet_tree, subnet);
}
subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
subnet_t *r = NULL;
// Check if this address is cached
if((r = hash_search(mac_cache, address)))
return r;
// Search all subnets for a matching one
for splay_each(subnet_t, p, owner ? owner->subnet_tree : subnet_tree) {
if(!p || p->type != SUBNET_MAC)
continue;
if(!memcmp(address, &p->net.mac.address, sizeof *address)) {
r = p;
if(p->owner->status.reachable)
break;
}
}
// Cache the result
if(r)
hash_insert(mac_cache, address, r);
return r;
}
subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
subnet_t *r = NULL;
// Check if this address is cached
if((r = hash_search(ipv4_cache, address)))
return r;
// Search all subnets for a matching one
for splay_each(subnet_t, p, subnet_tree) {
if(!p || p->type != SUBNET_IPV4)
continue;
if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
r = p;
if(p->owner->status.reachable)
break;
}
}
// Cache the result
if(r)
hash_insert(ipv4_cache, address, r);
return r;
}
subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
subnet_t *r = NULL;
// Check if this address is cached
if((r = hash_search(ipv6_cache, address)))
return r;
// Search all subnets for a matching one
for splay_each(subnet_t, p, subnet_tree) {
if(!p || p->type != SUBNET_IPV6)
continue;
if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
r = p;
if(p->owner->status.reachable)
break;
}
}
// Cache the result
if(r)
hash_insert(ipv6_cache, address, r);
return r;
}
void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
char netstr[MAXNETSTR];
char *name, *address, *port;
char empty[] = "";
// Prepare environment variables to be passed to the script
char *envp[10] = {NULL};
xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
xasprintf(&envp[1], "DEVICE=%s", device ? : "");
xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
xasprintf(&envp[3], "NODE=%s", owner->name);
if(owner != myself) {
sockaddr2str(&owner->address, &address, &port);
// 4 and 5 are reserved for SUBNET and WEIGHT
xasprintf(&envp[6], "REMOTEADDRESS=%s", address);
xasprintf(&envp[7], "REMOTEPORT=%s", port);
free(port);
free(address);
}
xasprintf(&envp[8], "NAME=%s", myself->name);
name = up ? "subnet-up" : "subnet-down";
if(!subnet) {
for splay_each(subnet_t, subnet, owner->subnet_tree) {
if(!net2str(netstr, sizeof netstr, subnet))
continue;
// Strip the weight from the subnet, and put it in its own environment variable
char *weight = strchr(netstr, '#');
if(weight)
*weight++ = 0;
else
weight = empty;
// Prepare the SUBNET and WEIGHT variables
if(envp[4])
free(envp[4]);
if(envp[5])
free(envp[5]);
xasprintf(&envp[4], "SUBNET=%s", netstr);
xasprintf(&envp[5], "WEIGHT=%s", weight);
execute_script(name, envp);
}
} else {
if(net2str(netstr, sizeof netstr, subnet)) {
// Strip the weight from the subnet, and put it in its own environment variable
char *weight = strchr(netstr, '#');
if(weight)
*weight++ = 0;
else
weight = empty;
// Prepare the SUBNET and WEIGHT variables
xasprintf(&envp[4], "SUBNET=%s", netstr);
xasprintf(&envp[5], "WEIGHT=%s", weight);
execute_script(name, envp);
}
}
for(int i = 0; envp[i] && i < 9; i++)
free(envp[i]);
}
bool dump_subnets(connection_t *c) {
for splay_each(subnet_t, subnet, subnet_tree) {
char netstr[MAXNETSTR];
if(!net2str(netstr, sizeof netstr, subnet))
continue;
send_request(c, "%d %d %s %s",
CONTROL, REQ_DUMP_SUBNETS,
netstr, subnet->owner->name);
}
return send_request(c, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
}