protocol_edge.c \
protocol_key.c \
protocol_misc.c \
- protocol_subnet.c \
route.c route.h \
rsa.h \
rsagen.h \
script.c script.h \
splay_tree.c splay_tree.h \
sptps.c sptps.h \
- subnet.c subnet.h \
- subnet_parse.c \
system.h \
tincd.c \
utils.c utils.h \
return false;
}
-bool get_config_subnet(const config_t *cfg, subnet_t ** result) {
- subnet_t subnet = {NULL};
-
- if(!cfg)
- return false;
-
- if(!str2net(&subnet, cfg->value)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
- cfg->variable, cfg->file, cfg->line);
- return false;
- }
-
- /* Teach newbies what subnets are... */
-
- if(((subnet.type == SUBNET_IPV4)
- && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address))
- || ((subnet.type == SUBNET_IPV6)
- && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
- cfg->variable, cfg->file, cfg->line);
- return false;
- }
-
- *(*result = new_subnet()) = subnet;
-
- return true;
-}
-
/*
Read exactly one line and strip the trailing newline if any.
*/
int line;
} config_t;
-#include "subnet.h"
-
extern splay_tree_t *config_tree;
extern int pinginterval;
extern bool get_config_int(const config_t *, int *);
extern bool get_config_string(const config_t *, char **);
extern bool get_config_address(const config_t *, struct addrinfo **);
-extern bool get_config_subnet(const config_t *, struct subnet_t **);
extern config_t *parse_config_line(char *, const char *, int);
extern bool read_config_file(splay_tree_t *, const char *);
#include "list.h"
#include "logger.h"
#include "rsa.h"
-#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
case REQ_DUMP_EDGES:
return dump_edges(c);
- case REQ_DUMP_SUBNETS:
- return dump_subnets(c);
-
case REQ_DUMP_CONNECTIONS:
return dump_connections(c);
#include "node.h"
#include "protocol.h"
#include "script.h"
-#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
#include "graph.h"
for(int i = 0; i < 7; i++)
free(envp[i]);
- subnet_update(n, NULL, n->status.reachable);
-
if(!n->status.reachable) {
update_node_udp(n, NULL);
memset(&n->status, 0, sizeof n->status);
}
void graph(void) {
- subnet_cache_flush();
sssp_bfs();
check_reachability();
mst_kruskal();
*/
bool setup_meshlink_network(void) {
init_connections();
- init_subnets();
init_nodes();
init_edges();
init_requests();
if(!init_control())
return false;
- /* Run subnet-up scripts for our own subnets */
-
- subnet_update(myself, NULL, true);
-
return true;
}
#include "net.h"
#include "netutl.h"
#include "protocol.h"
-#include "subnet.h"
#include "xalloc.h"
int contradicting_add_edge = 0;
static timeout_t pingtimer;
static timeout_t periodictimer;
-/* Purge edges and subnets of unreachable nodes. Use carefully. */
+/* Purge edges of unreachable nodes. Use carefully. */
+// TODO: remove
void purge(void) {
logger(DEBUG_PROTOCOL, LOG_DEBUG, "Purging unreachable nodes");
- /* Remove all edges and subnets owned by unreachable nodes. */
+ /* Remove all edges owned by unreachable nodes. */
for splay_each(node_t, n, node_tree) {
if(!n->status.reachable) {
logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Purging node %s (%s)", n->name, n->hostname);
- for splay_each(subnet_t, s, n->subnet_tree) {
- send_del_subnet(everyone, s);
- if(!strictsubnets)
- subnet_del(n, s);
- }
-
for splay_each(edge_t, e, n->edge_tree) {
if(!tunnelserver)
send_del_edge(everyone, e);
for splay_each(edge_t, e, edge_weight_tree)
if(e->to == n)
return;
-
- if(!autoconnect && (!strictsubnets || !n->subnet_tree->head))
- /* in strictsubnets mode do not delete nodes with subnets */
- node_del(n);
}
}
}
setup_myself_reloadable();
- /* If StrictSubnet is set, expire deleted Subnets and read new ones in */
-
- if(strictsubnets) {
- for splay_each(subnet_t, subnet, subnet_tree)
- subnet->expires = 1;
-
- load_all_subnets();
-
- for splay_each(subnet_t, subnet, subnet_tree) {
- if(subnet->expires == 1) {
- send_del_subnet(everyone, subnet);
- if(subnet->owner->status.reachable)
- subnet_update(subnet->owner, subnet, false);
- subnet_del(subnet->owner, subnet);
- } else if(subnet->expires == -1) {
- subnet->expires = 0;
- } else {
- send_add_subnet(everyone, subnet);
- if(subnet->owner->status.reachable)
- subnet_update(subnet->owner, subnet, true);
- }
- }
- } else { /* Only read our own subnets back in */
- for splay_each(subnet_t, subnet, myself->subnet_tree)
- if(!subnet->expires)
- subnet->expires = 1;
-
- config_t *cfg = lookup_config(config_tree, "Subnet");
-
- while(cfg) {
- subnet_t *subnet, *s2;
-
- if(!get_config_subnet(cfg, &subnet))
- continue;
-
- if((s2 = lookup_subnet(myself, subnet))) {
- if(s2->expires == 1)
- s2->expires = 0;
-
- free_subnet(subnet);
- } else {
- subnet_add(myself, subnet);
- send_add_subnet(everyone, subnet);
- subnet_update(myself, subnet, true);
- }
-
- cfg = lookup_config_next(config_tree, cfg);
- }
-
- for splay_each(subnet_t, subnet, myself->subnet_tree) {
- if(subnet->expires == 1) {
- send_del_subnet(everyone, subnet);
- subnet_update(myself, subnet, false);
- subnet_del(myself, subnet);
- }
- }
- }
-
/* Try to make outgoing connections */
try_outgoing_connections();
#include "route.h"
#include "rsa.h"
#include "script.h"
-#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
send_key_changed();
}
-/*
- Read Subnets from all host config files
-*/
-void load_all_subnets(void) {
- DIR *dir;
- struct dirent *ent;
- char *dname;
-
- xasprintf(&dname, "%s" SLASH "hosts", confbase);
- dir = opendir(dname);
- if(!dir) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
- free(dname);
- return;
- }
-
- while((ent = readdir(dir))) {
- if(!check_id(ent->d_name))
- continue;
-
- node_t *n = lookup_node(ent->d_name);
- #ifdef _DIRENT_HAVE_D_TYPE
- //if(ent->d_type != DT_REG)
- // continue;
- #endif
-
- splay_tree_t *config_tree;
- init_configuration(&config_tree);
- read_config_options(config_tree, ent->d_name);
- read_host_config(config_tree, ent->d_name);
-
- if(!n) {
- n = new_node();
- n->name = xstrdup(ent->d_name);
- node_add(n);
- }
-
- for(config_t *cfg = lookup_config(config_tree, "Subnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
- subnet_t *s, *s2;
-
- if(!get_config_subnet(cfg, &s))
- continue;
-
- if((s2 = lookup_subnet(n, s))) {
- s2->expires = -1;
- } else {
- subnet_add(n, s);
- }
- }
-
- exit_configuration(&config_tree);
- }
-
- closedir(dir);
-}
-
void load_all_nodes(void) {
DIR *dir;
struct dirent *ent;
sockaddr2str(&sa, NULL, &myport);
}
- /* Read in all the subnets specified in the host configuration file */
-
- for(config_t *cfg = lookup_config(config_tree, "Subnet"); cfg; cfg = lookup_config_next(config_tree, cfg)) {
- subnet_t *subnet;
-
- if(!get_config_subnet(cfg, &subnet))
- return false;
-
- subnet_add(myself, subnet);
- }
-
/* Check some options */
if(!setup_myself_reloadable())
return false;
- get_config_bool(lookup_config(config_tree, "StrictSubnets"), &strictsubnets);
get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
- strictsubnets |= tunnelserver;
if(get_config_int(lookup_config(config_tree, "MaxConnectionBurst"), &max_connection_burst)) {
if(max_connection_burst <= 0) {
graph();
- if(strictsubnets)
- load_all_subnets();
- else if(autoconnect)
+ if(autoconnect)
load_all_nodes();
/* Open sockets */
*/
bool setup_network(void) {
init_connections();
- init_subnets();
init_nodes();
init_edges();
init_requests();
for(int i = 0; i < 4; i++)
free(envp[i]);
- /* Run subnet-up scripts for our own subnets */
-
- subnet_update(myself, NULL, true);
-
return true;
}
list_delete_list(outgoing_list);
if(myself && myself->connection) {
- subnet_update(myself, NULL, false);
terminate_connection(myself->connection, false);
free_connection(myself->connection);
}
exit_requests();
exit_edges();
- exit_subnets();
exit_nodes();
exit_connections();
node_t *n = xzalloc(sizeof *n);
if(replaywin) n->late = xzalloc(replaywin);
- n->subnet_tree = new_subnet_tree();
n->edge_tree = new_edge_tree();
n->mtu = MTU;
n->maxmtu = MTU;
}
void free_node(node_t *n) {
- if(n->subnet_tree)
- free_subnet_tree(n->subnet_tree);
-
if(n->edge_tree)
free_edge_tree(n->edge_tree);
}
void node_del(node_t *n) {
- for splay_each(subnet_t, s, n->subnet_tree)
- subnet_del(n, s);
-
for splay_each(edge_t, e, n->edge_tree)
edge_del(e);
#include "connection.h"
#include "digest.h"
#include "event.h"
-#include "subnet.h"
typedef struct node_status_t {
unsigned int unused_active:1; /* 1 if active (not used for nodes) */
struct edge_t *prevedge; /* nearest node from him to us */
struct node_t *via; /* next hop for UDP packets */
- splay_tree_t *subnet_tree; /* Pointer to a tree of subnets belonging to this node */
-
splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */
struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */
#include "net.h"
#include "node.h"
#include "process.h"
-#include "subnet.h"
#include "utils.h"
#include "xalloc.h"
#include "xalloc.h"
bool tunnelserver = false;
-bool strictsubnets = false;
bool experimental = true;
/* Jumptable for the request handlers */
id_h, metakey_h, challenge_h, chal_reply_h, ack_h,
status_h, error_h, termreq_h,
ping_h, pong_h,
- add_subnet_h, del_subnet_h,
+ NULL, NULL, //add_subnet_h, del_subnet_h,
add_edge_h, del_edge_h,
key_changed_h, req_key_h, ans_key_h, tcppacket_h, control_h,
};
} past_request_t;
extern bool tunnelserver;
-extern bool strictsubnets;
extern bool experimental;
extern ecdsa_t *invitation_key;
#include "edge.h"
#include "net.h"
#include "node.h"
-#include "subnet.h"
/* Basic functions */
extern bool send_termreq(struct connection_t *);
extern bool send_ping(struct connection_t *);
extern bool send_pong(struct connection_t *);
-extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
-extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_t *);
extern bool send_del_edge(struct connection_t *, const struct edge_t *);
extern void send_key_changed(void);
extern bool termreq_h(struct connection_t *, const char *);
extern bool ping_h(struct connection_t *, const char *);
extern bool pong_h(struct connection_t *, const char *);
-extern bool add_subnet_h(struct connection_t *, const char *);
-extern bool del_subnet_h(struct connection_t *, const char *);
extern bool add_edge_h(struct connection_t *, const char *);
extern bool del_edge_h(struct connection_t *, const char *);
extern bool key_changed_h(struct connection_t *, const char *);
static void send_everything(connection_t *c) {
/* Send all known subnets and edges */
+ // TODO: remove this
if(disablebuggypeers) {
static struct {
vpn_packet_t pkt;
send_tcppacket(c, &zeropkt.pkt);
}
- if(tunnelserver) {
- for splay_each(subnet_t, s, myself->subnet_tree)
- send_add_subnet(c, s);
-
- return;
- }
-
for splay_each(node_t, n, node_tree) {
- for splay_each(subnet_t, s, n->subnet_tree)
- send_add_subnet(c, s);
-
for splay_each(edge_t, e, n->edge_tree)
send_add_edge(c, e);
}
+++ /dev/null
-/*
- protocol_subnet.c -- handle the meta-protocol, subnets
- Copyright (C) 1999-2005 Ivo Timmermans,
- 2000-2012 Guus Sliepen <guus@tinc-vpn.org>
- 2009 Michael Tokarev <mjt@tls.msk.ru>
-
- 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 "conf.h"
-#include "connection.h"
-#include "logger.h"
-#include "net.h"
-#include "netutl.h"
-#include "node.h"
-#include "protocol.h"
-#include "subnet.h"
-#include "utils.h"
-#include "xalloc.h"
-
-bool send_add_subnet(connection_t *c, const subnet_t *subnet) {
- char netstr[MAXNETSTR];
-
- if(!net2str(netstr, sizeof netstr, subnet))
- return false;
-
- return send_request(c, "%d %x %s %s", ADD_SUBNET, rand(), subnet->owner->name, netstr);
-}
-
-bool add_subnet_h(connection_t *c, const char *request) {
- char subnetstr[MAX_STRING_SIZE];
- char name[MAX_STRING_SIZE];
- node_t *owner;
- subnet_t s = {NULL}, *new, *old;
-
- if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "ADD_SUBNET", c->name,
- c->hostname);
- return false;
- }
-
- /* Check if owner name is valid */
-
- if(!check_id(name)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
- c->hostname, "invalid name");
- return false;
- }
-
- /* Check if subnet string is valid */
-
- if(!str2net(&s, subnetstr)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "ADD_SUBNET", c->name,
- c->hostname, "invalid subnet string");
- return false;
- }
-
- if(seen_request(request))
- return true;
-
- /* Check if the owner of the new subnet is in the connection list */
-
- owner = lookup_node(name);
-
- if(tunnelserver && owner != myself && owner != c->node) {
- /* in case of tunnelserver, ignore indirect subnet registrations */
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
- "ADD_SUBNET", c->name, c->hostname, subnetstr);
- return true;
- }
-
- if(!owner) {
- owner = new_node();
- owner->name = xstrdup(name);
- node_add(owner);
- }
-
- /* Check if we already know this subnet */
-
- if(lookup_subnet(owner, &s))
- return true;
-
- /* If we don't know this subnet, but we are the owner, retaliate with a DEL_SUBNET */
-
- if(owner == myself) {
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself",
- "ADD_SUBNET", c->name, c->hostname);
- s.owner = myself;
- send_del_subnet(c, &s);
- return true;
- }
-
- /* In tunnel server mode, we should already know all allowed subnets */
-
- if(tunnelserver) {
- logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
- "ADD_SUBNET", c->name, c->hostname, subnetstr);
- return true;
- }
-
- /* Ignore if strictsubnets is true, but forward it to others */
-
- if(strictsubnets) {
- logger(DEBUG_ALWAYS, LOG_WARNING, "Ignoring unauthorized %s from %s (%s): %s",
- "ADD_SUBNET", c->name, c->hostname, subnetstr);
- forward_request(c, request);
- return true;
- }
-
- /* If everything is correct, add the subnet to the list of the owner */
-
- *(new = new_subnet()) = s;
- subnet_add(owner, new);
-
- if(owner->status.reachable)
- subnet_update(owner, new, true);
-
- /* Tell the rest */
-
- if(!tunnelserver)
- forward_request(c, request);
-
- /* Fast handoff of roaming MAC addresses */
-
- if(s.type == SUBNET_MAC && owner != myself && (old = lookup_subnet(myself, &s)) && old->expires)
- old->expires = 1;
-
- return true;
-}
-
-bool send_del_subnet(connection_t *c, const subnet_t *s) {
- char netstr[MAXNETSTR];
-
- if(!net2str(netstr, sizeof netstr, s))
- return false;
-
- return send_request(c, "%d %x %s %s", DEL_SUBNET, rand(), s->owner->name, netstr);
-}
-
-bool del_subnet_h(connection_t *c, const char *request) {
- char subnetstr[MAX_STRING_SIZE];
- char name[MAX_STRING_SIZE];
- node_t *owner;
- subnet_t s = {NULL}, *find;
-
- if(sscanf(request, "%*d %*x " MAX_STRING " " MAX_STRING, name, subnetstr) != 2) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s)", "DEL_SUBNET", c->name,
- c->hostname);
- return false;
- }
-
- /* Check if owner name is valid */
-
- if(!check_id(name)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
- c->hostname, "invalid name");
- return false;
- }
-
- /* Check if subnet string is valid */
-
- if(!str2net(&s, subnetstr)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Got bad %s from %s (%s): %s", "DEL_SUBNET", c->name,
- c->hostname, "invalid subnet string");
- return false;
- }
-
- if(seen_request(request))
- return true;
-
- /* Check if the owner of the subnet being deleted is in the connection list */
-
- owner = lookup_node(name);
-
- if(tunnelserver && owner != myself && owner != c->node) {
- /* in case of tunnelserver, ignore indirect subnet deletion */
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Ignoring indirect %s from %s (%s) for %s",
- "DEL_SUBNET", c->name, c->hostname, subnetstr);
- return true;
- }
-
- if(!owner) {
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for %s which is not in our node tree",
- "DEL_SUBNET", c->name, c->hostname, name);
- return true;
- }
-
- /* If everything is correct, delete the subnet from the list of the owner */
-
- s.owner = owner;
-
- find = lookup_subnet(owner, &s);
-
- if(!find) {
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for %s which does not appear in his subnet tree",
- "DEL_SUBNET", c->name, c->hostname, name);
- if(strictsubnets)
- forward_request(c, request);
- return true;
- }
-
- /* If we are the owner of this subnet, retaliate with an ADD_SUBNET */
-
- if(owner == myself) {
- logger(DEBUG_PROTOCOL, LOG_WARNING, "Got %s from %s (%s) for ourself",
- "DEL_SUBNET", c->name, c->hostname);
- send_add_subnet(c, find);
- return true;
- }
-
- if(tunnelserver)
- return true;
-
- /* Tell the rest */
-
- if(!tunnelserver)
- forward_request(c, request);
- if(strictsubnets)
- return true;
-
- /* Finally, delete it. */
-
- if(owner->status.reachable)
- subnet_update(owner, find, false);
-
- subnet_del(owner, find);
-
- return true;
-}
#include "system.h"
-#include "connection.h"
-#include "control_common.h"
-#include "ethernet.h"
-#include "ipv4.h"
-#include "ipv6.h"
#include "logger.h"
-#include "meta.h"
#include "net.h"
-#include "protocol.h"
#include "route.h"
-#include "subnet.h"
#include "utils.h"
rmode_t routing_mode = RMODE_ROUTER;
mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
bool pcap = false;
-/* Sizes of various headers */
-
-static const size_t ether_size = sizeof(struct ether_header);
-static const size_t arp_size = sizeof(struct ether_arp);
-static const size_t ip_size = sizeof(struct ip);
-static const size_t icmp_size = sizeof(struct icmp) - sizeof(struct ip);
-static const size_t ip6_size = sizeof(struct ip6_hdr);
-static const size_t icmp6_size = sizeof(struct icmp6_hdr);
-static const size_t ns_size = sizeof(struct nd_neighbor_solicit);
-static const size_t opt_size = sizeof(struct nd_opt_hdr);
-
-#ifndef MAX
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-#endif
-
-static timeout_t age_subnets_timeout;
-
-/* RFC 1071 */
-
-static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) {
- uint16_t *p = data;
- uint32_t checksum = prevsum ^ 0xFFFF;
-
- while(len >= 2) {
- checksum += *p++;
- len -= 2;
- }
-
- if(len)
- checksum += *(uint8_t *)p;
-
- while(checksum >> 16)
- checksum = (checksum & 0xFFFF) + (checksum >> 16);
-
- return ~checksum;
-}
-
static bool ratelimit(int frequency) {
static time_t lasttime = 0;
static int count = 0;
return true;
}
-static void clamp_mss(const node_t *source, const node_t *via, vpn_packet_t *packet) {
- if(!source || !via || !(via->options & OPTION_CLAMP_MSS))
- return;
-
- uint16_t mtu = source->mtu;
- if(via != myself && via->mtu < mtu)
- mtu = via->mtu;
-
- /* Find TCP header */
- int start = ether_size;
- uint16_t type = packet->data[12] << 8 | packet->data[13];
-
- if(type == ETH_P_8021Q) {
- start += 4;
- type = packet->data[16] << 8 | packet->data[17];
- }
-
- if(type == ETH_P_IP && packet->data[start + 9] == 6)
- start += (packet->data[start] & 0xf) * 4;
- else if(type == ETH_P_IPV6 && packet->data[start + 6] == 6)
- start += 40;
- else
- return;
-
- if(packet->len <= start + 20)
- return;
-
- /* Use data offset field to calculate length of options field */
- int len = ((packet->data[start + 12] >> 4) - 5) * 4;
-
- if(packet->len < start + 20 + len)
- return;
-
- /* Search for MSS option header */
- for(int i = 0; i < len;) {
- if(packet->data[start + 20 + i] == 0)
- break;
-
- if(packet->data[start + 20 + i] == 1) {
- i++;
- continue;
- }
-
- if(i > len - 2 || i > len - packet->data[start + 21 + i])
- break;
-
- if(packet->data[start + 20 + i] != 2) {
- if(packet->data[start + 21 + i] < 2)
- break;
- i += packet->data[start + 21 + i];
- continue;
- }
-
- if(packet->data[start + 21] != 4)
- break;
-
- /* Found it */
- uint16_t oldmss = packet->data[start + 22 + i] << 8 | packet->data[start + 23 + i];
- uint16_t newmss = mtu - start - 20;
- uint16_t csum = packet->data[start + 16] << 8 | packet->data[start + 17];
-
- if(oldmss <= newmss)
- break;
-
- logger(DEBUG_TRAFFIC, LOG_INFO, "Clamping MSS of packet from %s to %s to %d", source->name, via->name, newmss);
-
- /* Update the MSS value and the checksum */
- packet->data[start + 22 + i] = newmss >> 8;
- packet->data[start + 23 + i] = newmss & 0xff;
- csum ^= 0xffff;
- csum -= oldmss;
- csum += newmss;
- csum ^= 0xffff;
- packet->data[start + 16] = csum >> 8;
- packet->data[start + 17] = csum & 0xff;
- break;
- }
-}
-
-static void swap_mac_addresses(vpn_packet_t *packet) {
- mac_t tmp;
- memcpy(&tmp, &packet->data[0], sizeof tmp);
- memcpy(&packet->data[0], &packet->data[6], sizeof tmp);
- memcpy(&packet->data[6], &tmp, sizeof tmp);
-}
-
-static void age_subnets(void *data) {
- bool left = false;
-
- for splay_each(subnet_t, s, myself->subnet_tree) {
- if(s->expires && s->expires < now.tv_sec) {
- if(debug_level >= DEBUG_TRAFFIC) {
- char netstr[MAXNETSTR];
- if(net2str(netstr, sizeof netstr, s))
- logger(DEBUG_TRAFFIC, LOG_INFO, "Subnet %s expired", netstr);
- }
-
- for list_each(connection_t, c, connection_list)
- if(c->status.active)
- send_del_subnet(c, s);
-
- subnet_del(myself, s);
- } else {
- if(s->expires)
- left = true;
- }
- }
-
- if(left)
- timeout_set(&age_subnets_timeout, &(struct timeval){10, rand() % 100000});
-}
-
-static void learn_mac(mac_t *address) {
- subnet_t *subnet = lookup_subnet_mac(myself, address);
-
- /* If we don't know this MAC address yet, store it */
-
- if(!subnet) {
- logger(DEBUG_TRAFFIC, LOG_INFO, "Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx",
- address->x[0], address->x[1], address->x[2], address->x[3],
- address->x[4], address->x[5]);
-
- subnet = new_subnet();
- subnet->type = SUBNET_MAC;
- subnet->expires = now.tv_sec + macexpire;
- subnet->net.mac.address = *address;
- subnet->weight = 10;
- subnet_add(myself, subnet);
- subnet_update(myself, subnet, true);
-
- /* And tell all other tinc daemons it's our MAC */
-
- for list_each(connection_t, c, connection_list)
- if(c->status.active)
- send_add_subnet(c, subnet);
-
- timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000});
- } else {
- if(subnet->expires)
- subnet->expires = now.tv_sec + macexpire;
- }
-}
-
-/* RFC 792 */
-
-static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
- struct ip ip = {0};
- struct icmp icmp = {0};
-
- struct in_addr ip_src;
- struct in_addr ip_dst;
- uint32_t oldlen;
-
- if(ratelimit(3))
- return;
-
- /* Swap Ethernet source and destination addresses */
-
- swap_mac_addresses(packet);
-
- /* Copy headers from packet into properly aligned structs on the stack */
-
- memcpy(&ip, packet->data + ether_size, ip_size);
-
- /* Remember original source and destination */
-
- ip_src = ip.ip_src;
- ip_dst = ip.ip_dst;
-
- oldlen = packet->len - ether_size;
-
- if(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
- icmp.icmp_nextmtu = htons(packet->len - ether_size);
-
- if(oldlen >= IP_MSS - ip_size - icmp_size)
- oldlen = IP_MSS - ip_size - icmp_size;
-
- /* Copy first part of original contents to ICMP message */
-
- memmove(packet->data + ether_size + ip_size + icmp_size, packet->data + ether_size, oldlen);
-
- /* Fill in IPv4 header */
-
- ip.ip_v = 4;
- ip.ip_hl = ip_size / 4;
- ip.ip_tos = 0;
- ip.ip_len = htons(ip_size + icmp_size + oldlen);
- ip.ip_id = 0;
- ip.ip_off = 0;
- ip.ip_ttl = 255;
- ip.ip_p = IPPROTO_ICMP;
- ip.ip_sum = 0;
- ip.ip_src = ip_dst;
- ip.ip_dst = ip_src;
-
- ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
-
- /* Fill in ICMP header */
-
- icmp.icmp_type = type;
- icmp.icmp_code = code;
- icmp.icmp_cksum = 0;
-
- icmp.icmp_cksum = inet_checksum(&icmp, icmp_size, ~0);
- icmp.icmp_cksum = inet_checksum(packet->data + ether_size + ip_size + icmp_size, oldlen, icmp.icmp_cksum);
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &ip, ip_size);
- memcpy(packet->data + ether_size + ip_size, &icmp, icmp_size);
-
- packet->len = ether_size + ip_size + icmp_size + oldlen;
-
- send_packet(source, packet);
-}
-
-/* RFC 791 */
-
-static void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet, length_t ether_size) {
- struct ip ip;
- vpn_packet_t fragment;
- int len, maxlen, todo;
- uint8_t *offset;
- uint16_t ip_off, origf;
-
- memcpy(&ip, packet->data + ether_size, ip_size);
- fragment.priority = packet->priority;
-
- if(ip.ip_hl != ip_size / 4)
- return;
-
- todo = ntohs(ip.ip_len) - ip_size;
-
- if(ether_size + ip_size + todo != packet->len) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Length of packet (%d) doesn't match length in IPv4 header (%d)", packet->len, (int)(ether_size + ip_size + todo));
- return;
- }
-
- logger(DEBUG_TRAFFIC, LOG_INFO, "Fragmenting packet of %d bytes to %s (%s)", packet->len, dest->name, dest->hostname);
-
- offset = packet->data + ether_size + ip_size;
- maxlen = (dest->mtu - ether_size - ip_size) & ~0x7;
- ip_off = ntohs(ip.ip_off);
- origf = ip_off & ~IP_OFFMASK;
- ip_off &= IP_OFFMASK;
-
- while(todo) {
- len = todo > maxlen ? maxlen : todo;
- memcpy(fragment.data + ether_size + ip_size, offset, len);
- todo -= len;
- offset += len;
-
- ip.ip_len = htons(ip_size + len);
- ip.ip_off = htons(ip_off | origf | (todo ? IP_MF : 0));
- ip.ip_sum = 0;
- ip.ip_sum = inet_checksum(&ip, ip_size, ~0);
- memcpy(fragment.data, packet->data, ether_size);
- memcpy(fragment.data + ether_size, &ip, ip_size);
- fragment.len = ether_size + ip_size + len;
-
- send_packet(dest, &fragment);
-
- ip_off += len / 8;
- }
-}
-
-static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) {
- subnet_t *subnet;
- node_t *via;
- ipv4_t dest;
-
- memcpy(&dest, &packet->data[30], sizeof dest);
- subnet = lookup_subnet_ipv4(&dest);
-
- if(!subnet) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv4 destination address %d.%d.%d.%d",
- source->name, source->hostname,
- dest.x[0],
- dest.x[1],
- dest.x[2],
- dest.x[3]);
-
- route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN);
- return;
- }
-
- if(subnet->owner == source) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(!subnet->owner->status.reachable)
- return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_UNREACH);
-
- if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
- return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
-
- if(priorityinheritance)
- packet->priority = packet->data[15];
-
- via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
-
- if(via == source) {
- logger(DEBUG_TRAFFIC, LOG_ERR, "Routing loop for packet from %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(directonly && subnet->owner != via)
- return route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_NET_ANO);
-
- if(via && packet->len > MAX(via->mtu, 590) && via != myself) {
- logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
- if(packet->data[20] & 0x40) {
- packet->len = MAX(via->mtu, 590);
- route_ipv4_unreachable(source, packet, ether_size, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
- } else {
- fragment_ipv4_packet(via, packet, ether_size);
- }
-
- return;
- }
-
- clamp_mss(source, via, packet);
-
- send_packet(subnet->owner, packet);
-}
-
-static void route_ipv4(node_t *source, vpn_packet_t *packet) {
- if(!checklength(source, packet, ether_size + ip_size))
- return;
-
- if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
- packet->data[30] == 255 &&
- packet->data[31] == 255 &&
- packet->data[32] == 255 &&
- packet->data[33] == 255)))
- broadcast_packet(source, packet);
- else
- route_ipv4_unicast(source, packet);
-}
-
-/* RFC 2463 */
-
-static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, length_t ether_size, uint8_t type, uint8_t code) {
- struct ip6_hdr ip6;
- struct icmp6_hdr icmp6 = {0};
- uint16_t checksum;
-
- struct {
- struct in6_addr ip6_src; /* source address */
- struct in6_addr ip6_dst; /* destination address */
- uint32_t length;
- uint32_t next;
- } pseudo;
-
- if(ratelimit(3))
- return;
-
- /* Swap Ethernet source and destination addresses */
-
- swap_mac_addresses(packet);
-
- /* Copy headers from packet to structs on the stack */
-
- memcpy(&ip6, packet->data + ether_size, ip6_size);
-
- /* Remember original source and destination */
-
- pseudo.ip6_src = ip6.ip6_dst;
- pseudo.ip6_dst = ip6.ip6_src;
-
- pseudo.length = packet->len - ether_size;
-
- if(type == ICMP6_PACKET_TOO_BIG)
- icmp6.icmp6_mtu = htonl(pseudo.length);
-
- if(pseudo.length >= IP_MSS - ip6_size - icmp6_size)
- pseudo.length = IP_MSS - ip6_size - icmp6_size;
-
- /* Copy first part of original contents to ICMP message */
-
- memmove(packet->data + ether_size + ip6_size + icmp6_size, packet->data + ether_size, pseudo.length);
-
- /* Fill in IPv6 header */
-
- ip6.ip6_flow = htonl(0x60000000UL);
- ip6.ip6_plen = htons(icmp6_size + pseudo.length);
- ip6.ip6_nxt = IPPROTO_ICMPV6;
- ip6.ip6_hlim = 255;
- ip6.ip6_src = pseudo.ip6_src;
- ip6.ip6_dst = pseudo.ip6_dst;
-
- /* Fill in ICMP header */
-
- icmp6.icmp6_type = type;
- icmp6.icmp6_code = code;
- icmp6.icmp6_cksum = 0;
-
- /* Create pseudo header */
-
- pseudo.length = htonl(icmp6_size + pseudo.length);
- pseudo.next = htonl(IPPROTO_ICMPV6);
-
- /* Generate checksum */
-
- checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
- checksum = inet_checksum(&icmp6, icmp6_size, checksum);
- checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
-
- icmp6.icmp6_cksum = checksum;
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &ip6, ip6_size);
- memcpy(packet->data + ether_size + ip6_size, &icmp6, icmp6_size);
-
- packet->len = ether_size + ip6_size + ntohl(pseudo.length);
-
- send_packet(source, packet);
-}
-
-static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
- subnet_t *subnet;
- node_t *via;
- ipv6_t dest;
-
- memcpy(&dest, &packet->data[38], sizeof dest);
- subnet = lookup_subnet_ipv6(&dest);
-
- if(!subnet) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
- source->name, source->hostname,
- ntohs(dest.x[0]),
- ntohs(dest.x[1]),
- ntohs(dest.x[2]),
- ntohs(dest.x[3]),
- ntohs(dest.x[4]),
- ntohs(dest.x[5]),
- ntohs(dest.x[6]),
- ntohs(dest.x[7]));
-
- route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
- return;
- }
-
- if(subnet->owner == source) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(!subnet->owner->status.reachable)
- return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
-
- if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
- return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
-
- via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
-
- if(via == source) {
- logger(DEBUG_TRAFFIC, LOG_ERR, "Routing loop for packet from %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(directonly && subnet->owner != via)
- return route_ipv6_unreachable(source, packet, ether_size, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
-
- if(via && packet->len > MAX(via->mtu, 1294) && via != myself) {
- logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
- packet->len = MAX(via->mtu, 1294);
- route_ipv6_unreachable(source, packet, ether_size, ICMP6_PACKET_TOO_BIG, 0);
- return;
- }
-
- clamp_mss(source, via, packet);
-
- send_packet(subnet->owner, packet);
-}
-
-/* RFC 2461 */
-
-static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
- struct ip6_hdr ip6;
- struct nd_neighbor_solicit ns;
- struct nd_opt_hdr opt;
- subnet_t *subnet;
- uint16_t checksum;
- bool has_opt;
-
- struct {
- struct in6_addr ip6_src;
- struct in6_addr ip6_dst;
- uint32_t length;
- uint32_t next;
- } pseudo;
-
- if(!checklength(source, packet, ether_size + ip6_size + ns_size))
- return;
-
- has_opt = packet->len >= ether_size + ip6_size + ns_size + opt_size + ETH_ALEN;
-
- if(source != myself) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Got neighbor solicitation request from %s (%s) while in router mode!", source->name, source->hostname);
- return;
- }
-
- /* Copy headers from packet to structs on the stack */
-
- memcpy(&ip6, packet->data + ether_size, ip6_size);
- memcpy(&ns, packet->data + ether_size + ip6_size, ns_size);
- if(has_opt)
- memcpy(&opt, packet->data + ether_size + ip6_size + ns_size, opt_size);
-
- /* First, snatch the source address from the neighbor solicitation packet */
-
- if(overwrite_mac)
- memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
-
- /* Check if this is a valid neighbor solicitation request */
-
- if(ns.nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
- (has_opt && opt.nd_opt_type != ND_OPT_SOURCE_LINKADDR)) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: received unknown type neighbor solicitation request");
- return;
- }
-
- /* Create pseudo header */
-
- pseudo.ip6_src = ip6.ip6_src;
- pseudo.ip6_dst = ip6.ip6_dst;
- if(has_opt)
- pseudo.length = htonl(ns_size + opt_size + ETH_ALEN);
- else
- pseudo.length = htonl(ns_size);
- pseudo.next = htonl(IPPROTO_ICMPV6);
-
- /* Generate checksum */
-
- checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
- checksum = inet_checksum(&ns, ns_size, checksum);
- if(has_opt) {
- checksum = inet_checksum(&opt, opt_size, checksum);
- checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
- }
-
- if(checksum) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: checksum error for neighbor solicitation request");
- return;
- }
-
- /* Check if the IPv6 address exists on the VPN */
-
- subnet = lookup_subnet_ipv6((ipv6_t *) &ns.nd_ns_target);
-
- if(!subnet) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
- ntohs(((uint16_t *) &ns.nd_ns_target)[0]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[1]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[2]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[3]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[4]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[5]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[6]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[7]));
-
- return;
- }
-
- /* Check if it is for our own subnet */
-
- if(subnet->owner == myself)
- return; /* silently ignore */
-
- /* Create neighbor advertation reply */
-
- memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
- packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
-
- ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */
- ip6.ip6_src = ns.nd_ns_target;
-
- if(has_opt)
- memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
-
- ns.nd_ns_cksum = 0;
- ns.nd_ns_type = ND_NEIGHBOR_ADVERT;
- ns.nd_ns_reserved = htonl(0x40000000UL); /* Set solicited flag */
- opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
-
- /* Create pseudo header */
-
- pseudo.ip6_src = ip6.ip6_src;
- pseudo.ip6_dst = ip6.ip6_dst;
- if(has_opt)
- pseudo.length = htonl(ns_size + opt_size + ETH_ALEN);
- else
- pseudo.length = htonl(ns_size);
- pseudo.next = htonl(IPPROTO_ICMPV6);
-
- /* Generate checksum */
-
- checksum = inet_checksum(&pseudo, sizeof pseudo, ~0);
- checksum = inet_checksum(&ns, ns_size, checksum);
- if(has_opt) {
- checksum = inet_checksum(&opt, opt_size, checksum);
- checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
- }
-
- ns.nd_ns_hdr.icmp6_cksum = checksum;
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &ip6, ip6_size);
- memcpy(packet->data + ether_size + ip6_size, &ns, ns_size);
- if(has_opt)
- memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size);
-
- send_packet(source, packet);
-}
-
-static void route_ipv6(node_t *source, vpn_packet_t *packet) {
- if(!checklength(source, packet, ether_size + ip6_size))
- return;
-
- if(packet->data[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
- route_neighborsol(source, packet);
- return;
- }
-
- if(broadcast_mode && packet->data[38] == 255)
- broadcast_packet(source, packet);
- else
- route_ipv6_unicast(source, packet);
-}
-
-/* RFC 826 */
-
-static void route_arp(node_t *source, vpn_packet_t *packet) {
- struct ether_arp arp;
- subnet_t *subnet;
- struct in_addr addr;
-
- if(!checklength(source, packet, ether_size + arp_size))
- return;
-
- if(source != myself) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Got ARP request from %s (%s) while in router mode!", source->name, source->hostname);
- return;
- }
-
- /* First, snatch the source address from the ARP packet */
-
- if(overwrite_mac)
- memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
-
- /* Copy headers from packet to structs on the stack */
-
- memcpy(&arp, packet->data + ether_size, arp_size);
-
- /* Check if this is a valid ARP request */
-
- if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP ||
- arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof addr || ntohs(arp.arp_op) != ARPOP_REQUEST) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: received unknown type ARP request");
- return;
- }
-
- /* Check if the IPv4 address exists on the VPN */
-
- subnet = lookup_subnet_ipv4((ipv4_t *) &arp.arp_tpa);
-
- if(!subnet) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet: ARP request for unknown address %d.%d.%d.%d",
- arp.arp_tpa[0], arp.arp_tpa[1], arp.arp_tpa[2],
- arp.arp_tpa[3]);
- return;
- }
-
- /* Check if it is for our own subnet */
-
- if(subnet->owner == myself)
- return; /* silently ignore */
-
- memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
- packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
-
- memcpy(&addr, arp.arp_tpa, sizeof addr); /* save protocol addr */
- memcpy(arp.arp_tpa, arp.arp_spa, sizeof addr); /* swap destination and source protocol address */
- memcpy(arp.arp_spa, &addr, sizeof addr); /* ... */
-
- memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */
- memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
- arp.arp_op = htons(ARPOP_REPLY);
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &arp, arp_size);
-
- send_packet(source, packet);
-}
-
-static void route_mac(node_t *source, vpn_packet_t *packet) {
- subnet_t *subnet;
- mac_t dest;
-
- /* Learn source address */
-
- if(source == myself) {
- mac_t src;
- memcpy(&src, &packet->data[6], sizeof src);
- learn_mac(&src);
- }
-
- /* Lookup destination address */
-
- memcpy(&dest, &packet->data[0], sizeof dest);
- subnet = lookup_subnet_mac(NULL, &dest);
-
- if(!subnet) {
- broadcast_packet(source, packet);
- return;
- }
-
- if(subnet->owner == source) {
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
- return;
-
- uint16_t type = packet->data[12] << 8 | packet->data[13];
-
- if(priorityinheritance && type == ETH_P_IP && packet->len >= ether_size + ip_size)
- packet->priority = packet->data[15];
-
- // Handle packets larger than PMTU
-
- node_t *via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
-
- if(directonly && subnet->owner != via)
- return;
-
- if(via && packet->len > via->mtu && via != myself) {
- logger(DEBUG_TRAFFIC, LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
- length_t ethlen = 14;
-
- if(type == ETH_P_8021Q) {
- type = packet->data[16] << 8 | packet->data[17];
- ethlen += 4;
- }
-
- if(type == ETH_P_IP && packet->len > 576 + ethlen) {
- if(packet->data[6 + ethlen] & 0x40) {
- packet->len = via->mtu;
- route_ipv4_unreachable(source, packet, ethlen, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
- } else {
- fragment_ipv4_packet(via, packet, ethlen);
- }
- return;
- } else if(type == ETH_P_IPV6 && packet->len > 1280 + ethlen) {
- packet->len = via->mtu;
- route_ipv6_unreachable(source, packet, ethlen, ICMP6_PACKET_TOO_BIG, 0);
- return;
- }
- }
-
- clamp_mss(source, via, packet);
-
- send_packet(subnet->owner, packet);
-}
-
-static void send_pcap(vpn_packet_t *packet) {
- pcap = false;
-
- for list_each(connection_t, c, connection_list) {
- if(!c->status.pcap)
- continue;
-
- pcap = true;
- int len = packet->len;
- if(c->outmaclength && c->outmaclength < len)
- len = c->outmaclength;
-
- if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, len))
- send_meta(c, (char *)packet->data, len);
- }
-}
-
-static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
- uint16_t type = packet->data[12] << 8 | packet->data[13];
- length_t ethlen = ether_size;
-
- if(type == ETH_P_8021Q) {
- type = packet->data[16] << 8 | packet->data[17];
- ethlen += 4;
- }
-
- switch (type) {
- case ETH_P_IP:
- if(!checklength(source, packet, ethlen + ip_size))
- return false;
-
- if(packet->data[ethlen + 8] < 1) {
- if(packet->data[ethlen + 11] != IPPROTO_ICMP || packet->data[ethlen + 32] != ICMP_TIME_EXCEEDED)
- route_ipv4_unreachable(source, packet, ethlen, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
- return false;
- }
-
- uint16_t old = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
- packet->data[ethlen + 8]--;
- uint16_t new = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
-
- uint32_t checksum = packet->data[ethlen + 10] << 8 | packet->data[ethlen + 11];
- checksum += old + (~new & 0xFFFF);
- while(checksum >> 16)
- checksum = (checksum & 0xFFFF) + (checksum >> 16);
- packet->data[ethlen + 10] = checksum >> 8;
- packet->data[ethlen + 11] = checksum & 0xff;
-
- return true;
-
- case ETH_P_IPV6:
- if(!checklength(source, packet, ethlen + ip6_size))
- return false;
-
- if(packet->data[ethlen + 7] < 1) {
- if(packet->data[ethlen + 6] != IPPROTO_ICMPV6 || packet->data[ethlen + 40] != ICMP6_TIME_EXCEEDED)
- route_ipv6_unreachable(source, packet, ethlen, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
- return false;
- }
-
- packet->data[ethlen + 7]--;
-
- return true;
-
- default:
- return true;
- }
-}
-
void route(node_t *source, vpn_packet_t *packet) {
- if(pcap)
- send_pcap(packet);
-
- if(forwarding_mode == FMODE_KERNEL && source != myself) {
- send_packet(myself, packet);
- return;
- }
-
- if(!checklength(source, packet, ether_size))
- return;
-
- if(decrement_ttl && source != myself)
- if(!do_decrement_ttl(source, packet))
- return;
-
- uint16_t type = packet->data[12] << 8 | packet->data[13];
-
- switch (routing_mode) {
- case RMODE_ROUTER:
- switch (type) {
- case ETH_P_ARP:
- route_arp(source, packet);
- break;
-
- case ETH_P_IP:
- route_ipv4(source, packet);
- break;
-
- case ETH_P_IPV6:
- route_ipv6(source, packet);
- break;
-
- default:
- logger(DEBUG_TRAFFIC, LOG_WARNING, "Cannot route packet from %s (%s): unknown type %hx", source->name, source->hostname, type);
- break;
- }
- break;
-
- case RMODE_SWITCH:
- route_mac(source, packet);
- break;
-
- case RMODE_HUB:
- broadcast_packet(source, packet);
- break;
- }
+ // TODO: route on name or key
}
#include "conf.h"
#include "logger.h"
#include "names.h"
+#include "net.h"
#include "script.h"
#include "xalloc.h"
+++ /dev/null
-/*
- 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 "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[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);
-}
+++ /dev/null
-/*
- subnet.h -- header for subnet.c
- Copyright (C) 2000-2012 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.
-*/
-
-#ifndef __TINC_SUBNET_H__
-#define __TINC_SUBNET_H__
-
-#include "net.h"
-
-typedef enum subnet_type_t {
- SUBNET_MAC = 0,
- SUBNET_IPV4,
- SUBNET_IPV6,
- SUBNET_TYPES /* Guardian */
-} subnet_type_t;
-
-typedef struct subnet_mac_t {
- mac_t address;
-} subnet_mac_t;
-
-typedef struct subnet_ipv4_t {
- ipv4_t address;
- int prefixlength;
-} subnet_ipv4_t;
-
-typedef struct subnet_ipv6_t {
- ipv6_t address;
- int prefixlength;
-} subnet_ipv6_t;
-
-#include "node.h"
-
-typedef struct subnet_t {
- struct node_t *owner; /* the owner of this subnet */
-
- subnet_type_t type; /* subnet type (IPv4? IPv6? MAC? something even weirder?) */
- time_t expires; /* expiry time */
- int weight; /* weight (higher value is higher priority) */
-
- /* And now for the actual subnet: */
-
- union net {
- subnet_mac_t mac;
- subnet_ipv4_t ipv4;
- subnet_ipv6_t ipv6;
- } net;
-} subnet_t;
-
-#define MAXNETSTR 64
-
-extern splay_tree_t *subnet_tree;
-
-extern int subnet_compare(const struct subnet_t *, const struct subnet_t *);
-extern subnet_t *new_subnet(void) __attribute__ ((__malloc__));
-extern void free_subnet(subnet_t *);
-extern void init_subnets(void);
-extern void exit_subnets(void);
-extern splay_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__));
-extern void free_subnet_tree(splay_tree_t *);
-extern void subnet_add(struct node_t *, subnet_t *);
-extern void subnet_del(struct node_t *, subnet_t *);
-extern void subnet_update(struct node_t *, subnet_t *, bool);
-extern int maskcmp(const void *, const void *, int);
-extern void maskcpy(void *, const void *, int, int);
-extern void mask(void *, int, int);
-extern bool maskcheck(const void *, int, int);
-extern bool net2str(char *, int, const subnet_t *);
-extern bool str2net(subnet_t *, const char *);
-extern subnet_t *lookup_subnet(const struct node_t *, const subnet_t *);
-extern subnet_t *lookup_subnet_mac(const struct node_t *, const mac_t *);
-extern subnet_t *lookup_subnet_ipv4(const ipv4_t *);
-extern subnet_t *lookup_subnet_ipv6(const ipv6_t *);
-extern bool dump_subnets(struct connection_t *);
-extern void subnet_cache_flush(void);
-
-#endif /* __TINC_SUBNET_H__ */
+++ /dev/null
-/*
- subnet_parse.c -- handle subnet parsing
- Copyright (C) 2000-2012 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 "logger.h"
-#include "net.h"
-#include "netutl.h"
-#include "subnet.h"
-#include "utils.h"
-#include "xalloc.h"
-
-/* Subnet mask handling */
-
-int maskcmp(const void *va, const void *vb, int masklen) {
- int i, m, result;
- const char *a = va;
- const char *b = vb;
-
- for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
- result = a[i] - b[i];
- if(result)
- return result;
- }
-
- if(m)
- return (a[i] & (0x100 - (1 << (8 - m)))) -
- (b[i] & (0x100 - (1 << (8 - m))));
-
- return 0;
-}
-
-void mask(void *va, int masklen, int len) {
- int i;
- char *a = va;
-
- i = masklen / 8;
- masklen %= 8;
-
- if(masklen)
- a[i++] &= (0x100 - (1 << (8 - masklen)));
-
- for(; i < len; i++)
- a[i] = 0;
-}
-
-void maskcpy(void *va, const void *vb, int masklen, int len) {
- int i, m;
- char *a = va;
- const char *b = vb;
-
- for(m = masklen, i = 0; m >= 8; m -= 8, i++)
- a[i] = b[i];
-
- if(m) {
- a[i] = b[i] & (0x100 - (1 << (8 - m)));
- i++;
- }
-
- for(; i < len; i++)
- a[i] = 0;
-}
-
-bool maskcheck(const void *va, int masklen, int len) {
- int i;
- const char *a = va;
-
- i = masklen / 8;
- masklen %= 8;
-
- if(masklen && a[i++] & (0xff >> masklen))
- return false;
-
- for(; i < len; i++)
- if(a[i] != 0)
- return false;
-
- return true;
-}
-
-/* Subnet comparison */
-
-static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
- int result;
-
- result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof a->net.mac.address);
-
- if(result)
- return result;
-
- result = a->weight - b->weight;
-
- if(result || !a->owner || !b->owner)
- return result;
-
- return strcmp(a->owner->name, b->owner->name);
-}
-
-static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) {
- int result;
-
- result = b->net.ipv4.prefixlength - a->net.ipv4.prefixlength;
-
- if(result)
- return result;
-
- result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
-
- if(result)
- return result;
-
- result = a->weight - b->weight;
-
- if(result || !a->owner || !b->owner)
- return result;
-
- return strcmp(a->owner->name, b->owner->name);
-}
-
-static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) {
- int result;
-
- result = b->net.ipv6.prefixlength - a->net.ipv6.prefixlength;
-
- if(result)
- return result;
-
- result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
-
- if(result)
- return result;
-
- result = a->weight - b->weight;
-
- if(result || !a->owner || !b->owner)
- return result;
-
- return strcmp(a->owner->name, b->owner->name);
-}
-
-int subnet_compare(const subnet_t *a, const subnet_t *b) {
- int result;
-
- result = a->type - b->type;
-
- if(result)
- return result;
-
- switch (a->type) {
- case SUBNET_MAC:
- return subnet_compare_mac(a, b);
- case SUBNET_IPV4:
- return subnet_compare_ipv4(a, b);
- case SUBNET_IPV6:
- return subnet_compare_ipv6(a, b);
- default:
- logger(DEBUG_ALWAYS, LOG_ERR, "subnet_compare() was called with unknown subnet type %d, exitting!", a->type);
- exit(1);
- }
-
- return 0;
-}
-
-/* Ascii representation of subnets */
-
-bool str2net(subnet_t *subnet, const char *subnetstr) {
- int i, l;
- uint16_t x[8];
- int weight = 10;
-
- if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d#%d",
- &x[0], &x[1], &x[2], &x[3], &l, &weight) >= 5) {
- if(l < 0 || l > 32)
- return false;
-
- subnet->type = SUBNET_IPV4;
- subnet->net.ipv4.prefixlength = l;
- subnet->weight = weight;
-
- for(int i = 0; i < 4; i++) {
- if(x[i] > 255)
- return false;
- subnet->net.ipv4.address.x[i] = x[i];
- }
-
- return true;
- }
-
- if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
- &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
- &l, &weight) >= 9) {
- if(l < 0 || l > 128)
- return false;
-
- subnet->type = SUBNET_IPV6;
- subnet->net.ipv6.prefixlength = l;
- subnet->weight = weight;
-
- for(i = 0; i < 8; i++)
- subnet->net.ipv6.address.x[i] = htons(x[i]);
-
- return true;
- }
-
- if(sscanf(subnetstr, "%hu.%hu.%hu.%hu#%d", &x[0], &x[1], &x[2], &x[3], &weight) >= 4) {
- subnet->type = SUBNET_IPV4;
- subnet->net.ipv4.prefixlength = 32;
- subnet->weight = weight;
-
- for(i = 0; i < 4; i++) {
- if(x[i] > 255)
- return false;
- subnet->net.ipv4.address.x[i] = x[i];
- }
-
- return true;
- }
-
- if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx#%d",
- &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &weight) >= 8) {
- subnet->type = SUBNET_IPV6;
- subnet->net.ipv6.prefixlength = 128;
- subnet->weight = weight;
-
- for(i = 0; i < 8; i++)
- subnet->net.ipv6.address.x[i] = htons(x[i]);
-
- return true;
- }
-
- if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
- &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &weight) >= 6) {
- subnet->type = SUBNET_MAC;
- subnet->weight = weight;
-
- for(i = 0; i < 6; i++)
- subnet->net.mac.address.x[i] = x[i];
-
- return true;
- }
-
- // IPv6 short form
- if(strstr(subnetstr, "::")) {
- const char *p;
- char *q;
- int colons = 0;
-
- // Count number of colons
- for(p = subnetstr; *p; p++)
- if(*p == ':')
- colons++;
-
- if(colons > 7)
- return false;
-
- // Scan numbers before the double colon
- p = subnetstr;
- for(i = 0; i < colons; i++) {
- if(*p == ':')
- break;
- x[i] = strtoul(p, &q, 0x10);
- if(!q || p == q || *q != ':')
- return false;
- p = ++q;
- }
-
- p++;
- colons -= i;
- if(!i) {
- p++;
- colons--;
- }
-
- if(!*p || *p == '/' || *p == '#')
- colons--;
-
- // Fill in the blanks
- for(; i < 8 - colons; i++)
- x[i] = 0;
-
- // Scan the remaining numbers
- for(; i < 8; i++) {
- x[i] = strtoul(p, &q, 0x10);
- if(!q || p == q)
- return false;
- if(i == 7) {
- p = q;
- break;
- }
- if(*q != ':')
- return false;
- p = ++q;
- }
-
- l = 128;
- if(*p == '/')
- sscanf(p, "/%d#%d", &l, &weight);
- else if(*p == '#')
- sscanf(p, "#%d", &weight);
-
- if(l < 0 || l > 128)
- return false;
-
- subnet->type = SUBNET_IPV6;
- subnet->net.ipv6.prefixlength = l;
- subnet->weight = weight;
-
- for(i = 0; i < 8; i++)
- subnet->net.ipv6.address.x[i] = htons(x[i]);
-
- return true;
- }
-
- return false;
-}
-
-bool net2str(char *netstr, int len, const subnet_t *subnet) {
- if(!netstr || !subnet) {
- logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with netstr=%p, subnet=%p!", netstr, subnet);
- return false;
- }
-
- switch (subnet->type) {
- case SUBNET_MAC:
- snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
- subnet->net.mac.address.x[0],
- subnet->net.mac.address.x[1],
- subnet->net.mac.address.x[2],
- subnet->net.mac.address.x[3],
- subnet->net.mac.address.x[4],
- subnet->net.mac.address.x[5],
- subnet->weight);
- break;
-
- case SUBNET_IPV4:
- snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%d",
- subnet->net.ipv4.address.x[0],
- subnet->net.ipv4.address.x[1],
- subnet->net.ipv4.address.x[2],
- subnet->net.ipv4.address.x[3],
- subnet->net.ipv4.prefixlength,
- subnet->weight);
- break;
-
- case SUBNET_IPV6:
- snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
- ntohs(subnet->net.ipv6.address.x[0]),
- ntohs(subnet->net.ipv6.address.x[1]),
- ntohs(subnet->net.ipv6.address.x[2]),
- ntohs(subnet->net.ipv6.address.x[3]),
- ntohs(subnet->net.ipv6.address.x[4]),
- ntohs(subnet->net.ipv6.address.x[5]),
- ntohs(subnet->net.ipv6.address.x[6]),
- ntohs(subnet->net.ipv6.address.x[7]),
- subnet->net.ipv6.prefixlength,
- subnet->weight);
- break;
-
- default:
- logger(DEBUG_ALWAYS, LOG_ERR, "net2str() was called with unknown subnet type %d, exiting!", subnet->type);
- exit(1);
- }
-
- return true;
-}