From 5a1406adefd8b51981af0da5ac0ebec830eb43b4 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Fri, 12 Dec 2003 19:52:25 +0000 Subject: [PATCH] Code beautification, start of multicast support. --- src/conf.c | 19 +- src/net.c | 6 +- src/net_packet.c | 15 +- src/net_socket.c | 5 +- src/protocol_subnet.c | 56 ++--- src/route.c | 490 ++++++++++++++++++++++++++---------------- src/route.h | 7 +- src/subnet.c | 37 ++-- src/subnet.h | 10 +- 9 files changed, 366 insertions(+), 279 deletions(-) diff --git a/src/conf.c b/src/conf.c index 57bee094..d0a2d2df 100644 --- a/src/conf.c +++ b/src/conf.c @@ -19,7 +19,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: conf.c,v 1.9.4.76 2003/08/28 21:05:10 guus Exp $ + $Id: conf.c,v 1.9.4.77 2003/12/12 19:52:24 guus Exp $ */ #include "system.h" @@ -214,16 +214,14 @@ bool get_config_address(const config_t *cfg, struct addrinfo **result) bool get_config_subnet(const config_t *cfg, subnet_t ** result) { - subnet_t *subnet; + subnet_t subnet = {0}; cp(); if(!cfg) return false; - subnet = str2net(cfg->value); - - if(!subnet) { + if(!str2net(&subnet, cfg->value)) { logger(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"), cfg->variable, cfg->file, cfg->line); return false; @@ -231,17 +229,16 @@ bool get_config_subnet(const config_t *cfg, subnet_t ** result) /* Teach newbies what subnets are... */ - if(((subnet->type == SUBNET_IPV4) - && !maskcheck(&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t))) - || ((subnet->type == SUBNET_IPV6) - && !maskcheck(&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t)))) { + if(((subnet.type == SUBNET_IPV4) + && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t))) + || ((subnet.type == SUBNET_IPV6) + && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) { logger(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"), cfg->variable, cfg->file, cfg->line); - free(subnet); return false; } - *result = subnet; + *(*result = new_subnet()) = subnet; return true; } diff --git a/src/net.c b/src/net.c index e0b5e6f4..16449768 100644 --- a/src/net.c +++ b/src/net.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: net.c,v 1.35.4.201 2003/11/17 15:30:17 guus Exp $ + $Id: net.c,v 1.35.4.202 2003/12/12 19:52:24 guus Exp $ */ #include "system.h" @@ -270,7 +270,7 @@ static void check_network_activity(fd_set * f) if(FD_ISSET(device_fd, f)) { if(read_packet(&packet)) - route_outgoing(&packet); + route(myself, &packet); } for(node = connection_tree->head; node; node = node->next) { @@ -367,7 +367,7 @@ int main_loop(void) last_ping_check = now; if(routing_mode == RMODE_SWITCH) - age_mac(); + age_subnets(); age_past_requests(); diff --git a/src/net_packet.c b/src/net_packet.c index d64b6bf9..af34d059 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: net_packet.c,v 1.1.2.43 2003/10/11 12:16:12 guus Exp $ + $Id: net_packet.c,v 1.1.2.44 2003/12/12 19:52:25 guus Exp $ */ #include "system.h" @@ -104,7 +104,7 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), packet->len, n->name, n->hostname); - route_incoming(n, packet); + route(n, packet); } static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) @@ -242,8 +242,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *inpkt) /* Since packet is on the stack of handle_tap_input(), we have to make a copy of it first. */ - copy = xmalloc(sizeof(vpn_packet_t)); - memcpy(copy, inpkt, sizeof(vpn_packet_t)); + *(copy = xmalloc(sizeof(*copy))) = *inpkt; list_insert_tail(n->queue, copy); @@ -344,14 +343,14 @@ void send_packet(const node_t *n, vpn_packet_t *packet) cp(); - ifdebug(TRAFFIC) logger(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"), - packet->len, n->name, n->hostname); - if(n == myself) { - ifdebug(TRAFFIC) logger(LOG_NOTICE, _("Packet is looping back to us!")); + write_packet(packet); return; } + ifdebug(TRAFFIC) logger(LOG_ERR, _("Sending packet of %d bytes to %s (%s)"), + packet->len, n->name, n->hostname); + if(!n->status.reachable) { ifdebug(TRAFFIC) logger(LOG_INFO, _("Node %s (%s) is not reachable"), n->name, n->hostname); diff --git a/src/net_socket.c b/src/net_socket.c index 10f2ca01..4e4a0080 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: net_socket.c,v 1.1.2.34 2003/10/06 14:41:45 guus Exp $ + $Id: net_socket.c,v 1.1.2.35 2003/12/12 19:52:25 guus Exp $ */ #include "system.h" @@ -255,8 +255,7 @@ begin: goto begin; } - memcpy(&c->address, c->outgoing->aip->ai_addr, - c->outgoing->aip->ai_addrlen); + memcpy(&c->address, c->outgoing->aip->ai_addr, c->outgoing->aip->ai_addrlen); c->outgoing->aip = c->outgoing->aip->ai_next; if(c->hostname) diff --git a/src/protocol_subnet.c b/src/protocol_subnet.c index e0297b99..76cdd49a 100644 --- a/src/protocol_subnet.c +++ b/src/protocol_subnet.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: protocol_subnet.c,v 1.1.4.17 2003/11/17 15:30:18 guus Exp $ + $Id: protocol_subnet.c,v 1.1.4.18 2003/12/12 19:52:25 guus Exp $ */ #include "system.h" @@ -35,17 +35,14 @@ bool send_add_subnet(connection_t *c, const subnet_t *subnet) { - bool x; - char *netstr; + char netstr[MAXNETSTR]; cp(); - x = send_request(c, "%d %lx %s %s", ADD_SUBNET, random(), - subnet->owner->name, netstr = net2str(subnet)); - - free(netstr); + if(!net2str(netstr, sizeof netstr, subnet)) + return false; - return x; + return send_request(c, "%d %lx %s %s", ADD_SUBNET, random(), subnet->owner->name, netstr); } bool add_subnet_h(connection_t *c) @@ -53,7 +50,7 @@ bool add_subnet_h(connection_t *c) char subnetstr[MAX_STRING_SIZE]; char name[MAX_STRING_SIZE]; node_t *owner; - subnet_t *s; + subnet_t s = {0}, *new; cp(); @@ -73,9 +70,7 @@ bool add_subnet_h(connection_t *c) /* Check if subnet string is valid */ - s = str2net(subnetstr); - - if(!s) { + if(!str2net(&s, subnetstr)) { logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name, c->hostname, _("invalid subnet string")); return false; @@ -99,18 +94,16 @@ bool add_subnet_h(connection_t *c) /* Check if we already know this subnet */ - if(lookup_subnet(owner, s)) { - free_subnet(s); + 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) { ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for ourself"), "ADD_SUBNET", c->name, c->hostname); - s->owner = myself; - send_del_subnet(c, s); + s.owner = myself; + send_del_subnet(c, &s); return true; } @@ -124,7 +117,7 @@ bool add_subnet_h(connection_t *c) if(!get_config_subnet(cfg, &allowed)) return false; - if(!subnet_compare(s, allowed)) + if(!subnet_compare(&s, allowed)) break; free_subnet(allowed); @@ -138,7 +131,8 @@ bool add_subnet_h(connection_t *c) /* If everything is correct, add the subnet to the list of the owner */ - subnet_add(owner, s); + *(new = new_subnet()) = s; + subnet_add(owner, new); /* Tell the rest */ @@ -150,18 +144,14 @@ bool add_subnet_h(connection_t *c) bool send_del_subnet(connection_t *c, const subnet_t *s) { - bool x; - char *netstr; + char netstr[MAXNETSTR]; cp(); - netstr = net2str(s); - - x = send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr); - - free(netstr); + if(!net2str(netstr, sizeof netstr, s)) + return false; - return x; + return send_request(c, "%d %lx %s %s", DEL_SUBNET, random(), s->owner->name, netstr); } bool del_subnet_h(connection_t *c) @@ -169,7 +159,7 @@ bool del_subnet_h(connection_t *c) char subnetstr[MAX_STRING_SIZE]; char name[MAX_STRING_SIZE]; node_t *owner; - subnet_t *s, *find; + subnet_t s = {0}, *find; cp(); @@ -202,9 +192,7 @@ bool del_subnet_h(connection_t *c) /* Check if subnet string is valid */ - s = str2net(subnetstr); - - if(!s) { + if(!str2net(&s, subnetstr)) { logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name, c->hostname, _("invalid subnet string")); return false; @@ -215,11 +203,9 @@ bool del_subnet_h(connection_t *c) /* If everything is correct, delete the subnet from the list of the owner */ - s->owner = owner; - - find = lookup_subnet(owner, s); + s.owner = owner; - free_subnet(s); + find = lookup_subnet(owner, &s); if(!find) { ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which does not appear in his subnet tree"), diff --git a/src/route.c b/src/route.c index b586157d..6d391cdf 100644 --- a/src/route.c +++ b/src/route.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: route.c,v 1.1.2.69 2003/12/08 12:00:40 guus Exp $ + $Id: route.c,v 1.1.2.70 2003/12/12 19:52:25 guus Exp $ */ #include "system.h" @@ -40,7 +40,6 @@ #include "avl_tree.h" #include "connection.h" -#include "device.h" #include "ethernet.h" #include "ipv4.h" #include "ipv6.h" @@ -54,6 +53,7 @@ rmode_t routing_mode = RMODE_ROUTER; bool priorityinheritance = false; int macexpire = 600; +int multicastexpire = 375; bool overwrite_mac = false; mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}}; @@ -81,7 +81,7 @@ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) } if(len) - checksum += *(unsigned char *)p; + checksum += *(uint8_t *)p; while(checksum >> 16) checksum = (checksum & 0xFFFF) + (checksum >> 16); @@ -103,6 +103,14 @@ static bool ratelimit(int frequency) { return false; } + +static bool checklength(node_t *source, vpn_packet_t *packet, length_t length) { + if(packet->len < length) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Got too short packet from %s (%s)"), source->name, source->hostname); + return false; + } else + return true; +} static void learn_mac(mac_t *address) { @@ -116,14 +124,15 @@ static void learn_mac(mac_t *address) /* If we don't know this MAC address yet, store it */ - if(!subnet || subnet->owner != myself) { + if(!subnet) { ifdebug(TRAFFIC) logger(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; - memcpy(&subnet->net.mac.address, address, sizeof(mac_t)); + subnet->expires = now + macexpire; + subnet->net.mac.address = *address; subnet_add(myself, subnet); /* And tell all other tinc daemons it's our MAC */ @@ -135,10 +144,11 @@ static void learn_mac(mac_t *address) } } - subnet->net.mac.lastseen = now; + if(subnet->expires) + subnet->expires = now + macexpire; } -void age_mac(void) +void age_subnets(void) { subnet_t *s; connection_t *c; @@ -149,11 +159,12 @@ void age_mac(void) for(node = myself->subnet_tree->head; node; node = next) { next = node->next; s = node->data; - if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now) { - ifdebug(TRAFFIC) logger(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"), - s->net.mac.address.x[0], s->net.mac.address.x[1], - s->net.mac.address.x[2], s->net.mac.address.x[3], - s->net.mac.address.x[4], s->net.mac.address.x[5]); + if(s->expires && s->expires < now) { + ifdebug(TRAFFIC) { + char netstr[MAXNETSTR]; + if(net2str(netstr, sizeof netstr, s)) + logger(LOG_INFO, _("Subnet %s expired"), netstr); + } for(node2 = connection_tree->head; node2; node2 = node2->next) { c = node2->data; @@ -166,7 +177,7 @@ void age_mac(void) } } -static node_t *route_mac(vpn_packet_t *packet) +static void route_mac(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; @@ -174,24 +185,32 @@ static node_t *route_mac(vpn_packet_t *packet) /* Learn source address */ - learn_mac((mac_t *)(&packet->data[6])); + if(source == myself) + learn_mac((mac_t *)(&packet->data[6])); /* Lookup destination address */ subnet = lookup_subnet_mac((mac_t *)(&packet->data[0])); - if(subnet) - return subnet->owner; - else - return NULL; + if(!subnet) { + broadcast_packet(source, packet); + return; + } + + if(subnet->owner == source) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Packet looping back to %s (%s)!"), source->name, source->hostname); + return; + } + + send_packet(subnet->owner, packet); } /* RFC 792 */ -static void route_ipv4_unreachable(vpn_packet_t *packet, uint8_t code) +static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t code) { - struct ip ip; - struct icmp icmp; + struct ip ip = {0}; + struct icmp icmp = {0}; struct in_addr ip_src; struct in_addr ip_dst; @@ -205,15 +224,14 @@ static void route_ipv4_unreachable(vpn_packet_t *packet, uint8_t code) /* Copy headers from packet into properly aligned structs on the stack */ memcpy(&ip, packet->data + ether_size, ip_size); - memcpy(&icmp, packet->data + ether_size + ip_size, icmp_size); /* Remember original source and destination */ - - memcpy(&ip_src, &ip.ip_src, sizeof(ip_src)); - memcpy(&ip_dst, &ip.ip_dst, sizeof(ip_dst)); + + ip_src = ip.ip_src; + ip_dst = ip.ip_dst; oldlen = packet->len - ether_size; - + if(oldlen >= IP_MSS - ip_size - icmp_size) oldlen = IP_MSS - ip_size - icmp_size; @@ -232,8 +250,8 @@ static void route_ipv4_unreachable(vpn_packet_t *packet, uint8_t code) ip.ip_ttl = 255; ip.ip_p = IPPROTO_ICMP; ip.ip_sum = 0; - memcpy(&ip.ip_src, &ip_dst, sizeof(ip_src)); - memcpy(&ip.ip_dst, &ip_src, sizeof(ip_dst)); + ip.ip_src = ip_dst; + ip.ip_dst = ip_src; ip.ip_sum = inet_checksum(&ip, ip_size, ~0); @@ -253,41 +271,66 @@ static void route_ipv4_unreachable(vpn_packet_t *packet, uint8_t code) packet->len = ether_size + ip_size + icmp_size + oldlen; - write_packet(packet); + send_packet(source, packet); } -static node_t *route_ipv4(vpn_packet_t *packet) +static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; cp(); - if(priorityinheritance) - packet->priority = packet->data[15]; - subnet = lookup_subnet_ipv4((ipv4_t *) &packet->data[30]); if(!subnet) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"), - packet->data[30], packet->data[31], packet->data[32], - packet->data[33]); - - route_ipv4_unreachable(packet, ICMP_NET_UNKNOWN); - return NULL; + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet from %s (%s): unknown IPv4 destination address %d.%d.%d.%d"), + source->name, source->hostname, + packet->data[30], + packet->data[31], + packet->data[32], + packet->data[33]); + + route_ipv4_unreachable(source, packet, ICMP_NET_UNKNOWN); + return; } + if(subnet->owner == source) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Packet looping back to %s (%s)!"), source->name, source->hostname); + return; + } + if(!subnet->owner->status.reachable) - route_ipv4_unreachable(packet, ICMP_NET_UNREACH); + route_ipv4_unreachable(source, packet, ICMP_NET_UNREACH); + + if(priorityinheritance) + packet->priority = packet->data[15]; - return subnet->owner; + send_packet(subnet->owner, packet); +} + +static void route_ipv4(node_t *source, vpn_packet_t *packet) +{ + cp(); + + if(!checklength(source, packet, ether_size + ip_size)) + return; + +#if 0 + if(packet->data[30] & 0xf0 == 0xe0) { + route_ipv4_multicast(source, packet); + return; + } +#endif + + route_ipv4_unicast(source, packet); } /* RFC 2463 */ -static void route_ipv6_unreachable(vpn_packet_t *packet, uint8_t code) +static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t code) { struct ip6_hdr ip6; - struct icmp6_hdr icmp6; + struct icmp6_hdr icmp6 = {0}; uint16_t checksum; struct { @@ -305,14 +348,13 @@ static void route_ipv6_unreachable(vpn_packet_t *packet, uint8_t code) /* Copy headers from packet to structs on the stack */ memcpy(&ip6, packet->data + ether_size, ip6_size); - memcpy(&icmp6, packet->data + ether_size + ip6_size, icmp6_size); /* Remember original source and destination */ - - memcpy(&pseudo.ip6_src, &ip6.ip6_dst, sizeof(ip6.ip6_src)); - memcpy(&pseudo.ip6_dst, &ip6.ip6_src, sizeof(ip6.ip6_dst)); + + pseudo.ip6_src = ip6.ip6_dst; + pseudo.ip6_dst = ip6.ip6_src; - pseudo.length = ntohs(ip6.ip6_plen) + ip6_size; + pseudo.length = packet->len - ether_size; if(pseudo.length >= IP_MSS - ip6_size - icmp6_size) pseudo.length = IP_MSS - ip6_size - icmp6_size; @@ -327,8 +369,8 @@ static void route_ipv6_unreachable(vpn_packet_t *packet, uint8_t code) ip6.ip6_plen = htons(icmp6_size + pseudo.length); ip6.ip6_nxt = IPPROTO_ICMPV6; ip6.ip6_hlim = 255; - memcpy(&ip6.ip6_src, &pseudo.ip6_src, sizeof(ip6.ip6_src)); - memcpy(&ip6.ip6_dst, &pseudo.ip6_dst, sizeof(ip6.ip6_dst)); + ip6.ip6_src = pseudo.ip6_src; + ip6.ip6_dst = pseudo.ip6_dst; /* Fill in ICMP header */ @@ -356,10 +398,10 @@ static void route_ipv6_unreachable(vpn_packet_t *packet, uint8_t code) packet->len = ether_size + ip6_size + ntohl(pseudo.length); - write_packet(packet); + send_packet(source, packet); } -static node_t *route_ipv6(vpn_packet_t *packet) +static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; @@ -368,29 +410,62 @@ static node_t *route_ipv6(vpn_packet_t *packet) subnet = lookup_subnet_ipv6((ipv6_t *) &packet->data[38]); if(!subnet) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"), - ntohs(*(uint16_t *) &packet->data[38]), - ntohs(*(uint16_t *) &packet->data[40]), - ntohs(*(uint16_t *) &packet->data[42]), - ntohs(*(uint16_t *) &packet->data[44]), - ntohs(*(uint16_t *) &packet->data[46]), - ntohs(*(uint16_t *) &packet->data[48]), - ntohs(*(uint16_t *) &packet->data[50]), - ntohs(*(uint16_t *) &packet->data[52])); - route_ipv6_unreachable(packet, ICMP6_DST_UNREACH_ADDR); - - return NULL; + ifdebug(TRAFFIC) logger(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(*(uint16_t *) &packet->data[38]), + ntohs(*(uint16_t *) &packet->data[40]), + ntohs(*(uint16_t *) &packet->data[42]), + ntohs(*(uint16_t *) &packet->data[44]), + ntohs(*(uint16_t *) &packet->data[46]), + ntohs(*(uint16_t *) &packet->data[48]), + ntohs(*(uint16_t *) &packet->data[50]), + ntohs(*(uint16_t *) &packet->data[52])); + + route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH_ADDR); + return; + } + + if(subnet->owner == source) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Packet looping back to %s (%s)!"), source->name, source->hostname); + return; } if(!subnet->owner->status.reachable) - route_ipv6_unreachable(packet, ICMP6_DST_UNREACH_NOROUTE); + route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH_NOROUTE); - return subnet->owner; + send_packet(subnet->owner, packet); +} + +#ifdef ENABLE_MULTICAST +static void route_ipv6_multicast(node_t *source, vpn_packet_t *packet) +{ + avl_node_t *node; + subnet_t *subnet, search = {0}; + + cp(); + + search.type = SUBNET_IPV6; + search.net.ipv6.address = *(ipv6_t *)(packet->data + ether_size + ip6_size + icmp6_size); + search.net.ipv6.prefixlength = 128; + search.owner = NULL; + + ifdebug(TRAFFIC) logger(LOG_INFO, _("Multicasting packet of %d bytes from %s (%s)"), packet->len, source->name, source->hostname); + + for(node = avl_search_closest_smaller_node(myself->subnet_tree, &search); node; node = node->next) { + subnet = node->data; + + if(subnet->type != SUBNET_IPV6 || memcmp(&subnet->net.ipv6.address, packet->data + ether_size + ip6_size + icmp6_size, sizeof(ipv6_t))) + break; + + if(subnet->owner != source) + send_packet(subnet->owner, packet); + } } +#endif /* RFC 2461 */ -static void route_neighborsol(vpn_packet_t *packet) +static void route_neighborsol(node_t *source, vpn_packet_t *packet) { struct ip6_hdr ip6; struct nd_neighbor_solicit ns; @@ -407,6 +482,14 @@ static void route_neighborsol(vpn_packet_t *packet) cp(); + if(!checklength(source, packet, ether_size + ip6_size + ns_size + opt_size + ETH_ALEN)) + return; + + if(source != myself) { + ifdebug(TRAFFIC) logger(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); @@ -428,8 +511,8 @@ static void route_neighborsol(vpn_packet_t *packet) /* Create pseudo header */ - memcpy(&pseudo.ip6_src, &ip6.ip6_src, sizeof(ip6.ip6_src)); - memcpy(&pseudo.ip6_dst, &ip6.ip6_dst, sizeof(ip6.ip6_dst)); + pseudo.ip6_src = ip6.ip6_src; + pseudo.ip6_dst = ip6.ip6_dst; pseudo.length = htonl(ns_size + opt_size + ETH_ALEN); pseudo.next = htonl(IPPROTO_ICMPV6); @@ -473,8 +556,8 @@ static void route_neighborsol(vpn_packet_t *packet) 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(&ip6.ip6_dst, &ip6.ip6_src, sizeof(ip6.ip6_dst)); /* ... */ - memcpy(&ip6.ip6_src, &ns.nd_ns_target, sizeof(ip6.ip6_src)); /* swap destination and source protocol address */ + ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */ + ip6.ip6_src = ns.nd_ns_target; memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */ @@ -485,8 +568,8 @@ static void route_neighborsol(vpn_packet_t *packet) /* Create pseudo header */ - memcpy(&pseudo.ip6_src, &ip6.ip6_src, sizeof(ip6.ip6_src)); - memcpy(&pseudo.ip6_dst, &ip6.ip6_dst, sizeof(ip6.ip6_dst)); + pseudo.ip6_src = ip6.ip6_src; + pseudo.ip6_dst = ip6.ip6_dst; pseudo.length = htonl(ns_size + opt_size + ETH_ALEN); pseudo.next = htonl(IPPROTO_ICMPV6); @@ -505,12 +588,135 @@ static void route_neighborsol(vpn_packet_t *packet) memcpy(packet->data + ether_size + ip6_size, &ns, ns_size); memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size); - write_packet(packet); + send_packet(source, packet); +} + +/* RFC 2710 */ + +#ifdef ENABLE_MULTICAST +static void route_membershipreport(node_t *source, vpn_packet_t *packet) +{ + struct ip6_hdr ip6; + struct icmp6_hdr icmp6; + subnet_t *subnet, search = {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; + + cp(); + + if(!checklength(source, packet, ether_size + ip6_size + icmp6_size + sizeof(ipv6_t))) + return; + + if(source != myself) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Got membership report 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(&icmp6, packet->data + ether_size + ip6_size + 8, icmp6_size); + + /* Create pseudo header */ + + pseudo.ip6_src = ip6.ip6_src; + pseudo.ip6_dst = ip6.ip6_dst; + pseudo.length = htonl(icmp6_size + sizeof(ipv6_t)); + 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 + 8 + icmp6_size, sizeof(ipv6_t), checksum); + + if(checksum) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: checksum error for membership report")); + return; + } + + /* Check if the IPv6 address exists on the VPN */ + + search.type = SUBNET_IPV6; + search.net.ipv6.address = *(ipv6_t *)(packet->data + ether_size + ip6_size + 8 + icmp6_size); + search.net.ipv6.prefixlength = 128; + search.owner = myself; + + subnet = avl_search(myself->subnet_tree, &search); + + if(!subnet) { + avl_node_t *node; + connection_t *c; + + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Learned new IPv6 multicast address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"), + ntohs(*(uint16_t *) &packet->data[70]), + ntohs(*(uint16_t *) &packet->data[72]), + ntohs(*(uint16_t *) &packet->data[74]), + ntohs(*(uint16_t *) &packet->data[76]), + ntohs(*(uint16_t *) &packet->data[78]), + ntohs(*(uint16_t *) &packet->data[80]), + ntohs(*(uint16_t *) &packet->data[82]), + ntohs(*(uint16_t *) &packet->data[84])); + + subnet = new_subnet(); + subnet->type = SUBNET_IPV6; + subnet->net.ipv6.address = *(ipv6_t *)(packet->data + ether_size + ip6_size + 8 + icmp6_size); + subnet->net.ipv6.prefixlength = 128; + subnet->expires = now + multicastexpire; + subnet_add(myself, subnet); + + /* And tell all other tinc daemons it's ours */ + + for(node = connection_tree->head; node; node = node->next) { + c = node->data; + if(c->status.active) + send_add_subnet(c, subnet); + } + } + + if(subnet->expires) + subnet->expires = now + multicastexpire; +} +#endif + +static void route_ipv6(node_t *source, vpn_packet_t *packet) +{ + cp(); + + 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; + } + +#ifdef ENABLE_MULTICAST + if(packet->data[20] == IPPROTO_HOPOPTS && checklength(source, packet, ether_size + ip6_size + 8) + && packet->data[54] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + 8 + icmp6_size) + && packet->data[62] == ICMP6_MEMBERSHIP_REPORT) { + route_membershipreport(source, packet); + return; + } + + if(packet->data[38] == 0xff && packet->data[39] & 0x0c) { + route_ipv6_multicast(source, packet); + return; + } +#endif + + route_ipv6_unicast(source, packet); } /* RFC 826 */ -static void route_arp(vpn_packet_t *packet) +static void route_arp(node_t *source, vpn_packet_t *packet) { struct ether_arp arp; subnet_t *subnet; @@ -518,6 +724,14 @@ static void route_arp(vpn_packet_t *packet) cp(); + if(!checklength(source, packet, ether_size + arp_size)) + return; + + if(source != myself) { + ifdebug(TRAFFIC) logger(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) @@ -566,150 +780,48 @@ static void route_arp(vpn_packet_t *packet) memcpy(packet->data + ether_size, &arp, arp_size); - write_packet(packet); + send_packet(source, packet); } -void route_outgoing(vpn_packet_t *packet) +void route(node_t *source, vpn_packet_t *packet) { - uint16_t type; - node_t *n = NULL; - cp(); - if(packet->len < ether_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); - return; - } - - /* FIXME: multicast? */ - - switch (routing_mode) { - case RMODE_ROUTER: - type = ntohs(*((uint16_t *)(&packet->data[12]))); - switch (type) { - case ETH_P_IP: - if(packet->len < ether_size + ip_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); - return; - } - - n = route_ipv4(packet); - break; - - case ETH_P_IPV6: - if(packet->len < ether_size + ip6_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); - return; - } - - if(packet->data[20] == IPPROTO_ICMPV6 && packet->len >= ether_size + ip6_size + ns_size && packet->data[54] == ND_NEIGHBOR_SOLICIT) { - route_neighborsol(packet); - return; - } - n = route_ipv6(packet); - break; - - case ETH_P_ARP: - if(packet->len < ether_size + arp_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); - return; - } - - route_arp(packet); - return; - - default: - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type); - return; - } - if(n) - send_packet(n, packet); - break; - - case RMODE_SWITCH: - n = route_mac(packet); - if(n) - send_packet(n, packet); - else - broadcast_packet(myself, packet); - break; - - case RMODE_HUB: - broadcast_packet(myself, packet); - break; - } -} - -void route_incoming(node_t *source, vpn_packet_t *packet) -{ - if(packet->len < ether_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); + if(!checklength(source, packet, ether_size)) return; - } switch (routing_mode) { case RMODE_ROUTER: { - node_t *n = NULL; uint16_t type; type = ntohs(*((uint16_t *)(&packet->data[12]))); switch (type) { - case ETH_P_IP: - if(packet->len < ether_size + ip_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); - return; - } + case ETH_P_ARP: + route_arp(source, packet); + break; - n = route_ipv4(packet); + case ETH_P_IP: + route_ipv4(source, packet); break; case ETH_P_IPV6: - if(packet->len < ether_size + ip6_size) { - ifdebug(TRAFFIC) logger(LOG_WARNING, _("Read too short packet")); - return; - } - - n = route_ipv6(packet); + route_ipv6(source, packet); break; default: - n = myself; + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Cannot route packet from %s (%s): unknown type %hx"), source->name, source->hostname, type); break; } - - if(n) { - if(n == myself) { - if(overwrite_mac) - memcpy(packet->data, mymac.x, ETH_ALEN); - write_packet(packet); - } else - send_packet(n, packet); - } } break; case RMODE_SWITCH: - { - subnet_t *subnet; - - subnet = lookup_subnet_mac((mac_t *)(&packet->data[0])); - - if(subnet) { - if(subnet->owner == myself) - write_packet(packet); - else - send_packet(subnet->owner, packet); - } else { - broadcast_packet(source, packet); - write_packet(packet); - } - } + route_mac(source, packet); break; case RMODE_HUB: - broadcast_packet(source, packet); /* Spread it on */ - write_packet(packet); + broadcast_packet(source, packet); break; } } diff --git a/src/route.h b/src/route.h index 6d1c034d..a26411aa 100644 --- a/src/route.h +++ b/src/route.h @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: route.h,v 1.1.2.13 2003/07/22 20:55:20 guus Exp $ + $Id: route.h,v 1.1.2.14 2003/12/12 19:52:25 guus Exp $ */ #ifndef __TINC_ROUTE_H__ @@ -39,8 +39,7 @@ extern int macexpire; extern mac_t mymac; -extern void age_mac(void); -extern void route_incoming(struct node_t *, struct vpn_packet_t *); -extern void route_outgoing(struct vpn_packet_t *); +extern void age_subnets(void); +extern void route(struct node_t *, struct vpn_packet_t *); #endif /* __TINC_ROUTE_H__ */ diff --git a/src/subnet.c b/src/subnet.c index ae8d029e..d5eca583 100644 --- a/src/subnet.c +++ b/src/subnet.c @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: subnet.c,v 1.1.2.51 2003/11/17 15:30:18 guus Exp $ + $Id: subnet.c,v 1.1.2.52 2003/12/12 19:52:25 guus Exp $ */ #include "system.h" @@ -177,16 +177,13 @@ void subnet_del(node_t *n, subnet_t *subnet) /* Ascii representation of subnets */ -subnet_t *str2net(const char *subnetstr) +bool str2net(subnet_t *subnet, const char *subnetstr) { int i, l; - subnet_t *subnet; uint16_t x[8]; cp(); - subnet = new_subnet(); - if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d", &x[0], &x[1], &x[2], &x[3], &l) == 5) { subnet->type = SUBNET_IPV4; @@ -195,7 +192,7 @@ subnet_t *str2net(const char *subnetstr) for(i = 0; i < 4; i++) subnet->net.ipv4.address.x[i] = x[i]; - return subnet; + return true; } if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", @@ -207,7 +204,7 @@ subnet_t *str2net(const char *subnetstr) for(i = 0; i < 8; i++) subnet->net.ipv6.address.x[i] = htons(x[i]); - return subnet; + return true; } if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) { @@ -217,7 +214,7 @@ subnet_t *str2net(const char *subnetstr) for(i = 0; i < 4; i++) subnet->net.ipv4.address.x[i] = x[i]; - return subnet; + return true; } if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", @@ -228,7 +225,7 @@ subnet_t *str2net(const char *subnetstr) for(i = 0; i < 8; i++) subnet->net.ipv6.address.x[i] = htons(x[i]); - return subnet; + return true; } if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx", @@ -238,23 +235,19 @@ subnet_t *str2net(const char *subnetstr) for(i = 0; i < 6; i++) subnet->net.mac.address.x[i] = x[i]; - return subnet; + return true; } - free(subnet); - - return NULL; + return false; } -char *net2str(const subnet_t *subnet) +bool net2str(char *netstr, int len, const subnet_t *subnet) { - char *netstr; - cp(); switch (subnet->type) { case SUBNET_MAC: - asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx", + snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx", subnet->net.mac.address.x[0], subnet->net.mac.address.x[1], subnet->net.mac.address.x[2], @@ -263,7 +256,7 @@ char *net2str(const subnet_t *subnet) break; case SUBNET_IPV4: - asprintf(&netstr, "%hu.%hu.%hu.%hu/%d", + snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d", subnet->net.ipv4.address.x[0], subnet->net.ipv4.address.x[1], subnet->net.ipv4.address.x[2], @@ -271,7 +264,7 @@ char *net2str(const subnet_t *subnet) break; case SUBNET_IPV6: - asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", + snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d", ntohs(subnet->net.ipv6.address.x[0]), ntohs(subnet->net.ipv6.address.x[1]), ntohs(subnet->net.ipv6.address.x[2]), @@ -394,7 +387,7 @@ subnet_t *lookup_subnet_ipv6(const ipv6_t *address) void dump_subnets(void) { - char *netstr; + char netstr[MAXNETSTR]; subnet_t *subnet; avl_node_t *node; @@ -404,9 +397,9 @@ void dump_subnets(void) for(node = subnet_tree->head; node; node = node->next) { subnet = node->data; - netstr = net2str(subnet); + if(!net2str(netstr, sizeof netstr, subnet)) + continue; logger(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name); - free(netstr); } logger(LOG_DEBUG, _("End of subnet list.")); diff --git a/src/subnet.h b/src/subnet.h index c055edae..d82bfa3e 100644 --- a/src/subnet.h +++ b/src/subnet.h @@ -17,7 +17,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: subnet.h,v 1.1.2.26 2003/11/17 15:30:18 guus Exp $ + $Id: subnet.h,v 1.1.2.27 2003/12/12 19:52:25 guus Exp $ */ #ifndef __TINC_SUBNET_H__ @@ -34,7 +34,6 @@ typedef enum subnet_type_t { typedef struct subnet_mac_t { mac_t address; - time_t lastseen; } subnet_mac_t; typedef struct subnet_ipv4_t { @@ -53,6 +52,7 @@ 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 */ /* And now for the actual subnet: */ @@ -63,6 +63,8 @@ typedef struct subnet_t { } net; } subnet_t; +#define MAXNETSTR 64 + 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 *); @@ -72,8 +74,8 @@ extern avl_tree_t *new_subnet_tree(void) __attribute__ ((__malloc__)); extern void free_subnet_tree(avl_tree_t *); extern void subnet_add(struct node_t *, subnet_t *); extern void subnet_del(struct node_t *, subnet_t *); -extern char *net2str(const subnet_t *); -extern subnet_t *str2net(const char *); +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 mac_t *); extern subnet_t *lookup_subnet_ipv4(const ipv4_t *); -- 2.20.1