X-Git-Url: https://git.librecmc.org/?p=oweals%2Ftinc.git;a=blobdiff_plain;f=src%2Froute.c;h=8f238e2cca36ad356206b338490cdbe3b5512ba4;hp=6d391cdf4a232fab65f7108ced4c84655b1e9007;hb=aebc97a77f37ec63fbd36721f9b284c975e54270;hpb=5a1406adefd8b51981af0da5ac0ebec830eb43b4 diff --git a/src/route.c b/src/route.c index 6d391cd..8f238e2 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.70 2003/12/12 19:52:25 guus Exp $ + $Id: route.c,v 1.1.2.75 2003/12/24 10:48:15 guus Exp $ */ #include "system.h" @@ -53,7 +53,6 @@ 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}}; @@ -70,7 +69,7 @@ static const size_t opt_size = sizeof(struct nd_opt_hdr); /* RFC 1071 */ -static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) +static __inline__ uint16_t inet_checksum(void *data, int len, uint16_t prevsum) { uint16_t *p = data; uint32_t checksum = prevsum ^ 0xFFFF; @@ -89,7 +88,7 @@ static uint16_t inet_checksum(void *data, int len, uint16_t prevsum) return ~checksum; } -static bool ratelimit(int frequency) { +static __inline__ bool ratelimit(int frequency) { static time_t lasttime = 0; static int count = 0; @@ -104,7 +103,7 @@ static bool ratelimit(int frequency) { return false; } -static bool checklength(node_t *source, vpn_packet_t *packet, length_t length) { +static __inline__ 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; @@ -112,7 +111,7 @@ static bool checklength(node_t *source, vpn_packet_t *packet, length_t length) { return true; } -static void learn_mac(mac_t *address) +static __inline__ void learn_mac(mac_t *address) { subnet_t *subnet; avl_node_t *node; @@ -177,7 +176,7 @@ void age_subnets(void) } } -static void route_mac(node_t *source, vpn_packet_t *packet) +static __inline__ void route_mac(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; @@ -207,7 +206,7 @@ static void route_mac(node_t *source, vpn_packet_t *packet) /* RFC 792 */ -static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t code) +static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) { struct ip ip = {0}; struct icmp icmp = {0}; @@ -232,6 +231,9 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t 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; @@ -257,7 +259,7 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t /* Fill in ICMP header */ - icmp.icmp_type = ICMP_DEST_UNREACH; + icmp.icmp_type = type; icmp.icmp_code = code; icmp.icmp_cksum = 0; @@ -270,13 +272,66 @@ static void route_ipv4_unreachable(node_t *source, vpn_packet_t *packet, uint8_t memcpy(packet->data + ether_size + ip_size, &icmp, icmp_size); packet->len = ether_size + ip_size + icmp_size + oldlen; - + send_packet(source, packet); } -static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) +/* RFC 791 */ + +static __inline__ void fragment_ipv4_packet(node_t *dest, vpn_packet_t *packet) { + struct ip ip; + vpn_packet_t fragment; + int len, maxlen, todo; + uint8_t *offset; + uint16_t ip_off, origf; + + cp(); + + 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) { + ifdebug(TRAFFIC) logger(LOG_WARNING, _("Length of packet (%d) doesn't match length in IPv4 header (%d)"), packet->len, ether_size + ip_size + todo); + return; + } + + ifdebug(TRAFFIC) logger(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 __inline__ void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; + node_t *via; cp(); @@ -290,7 +345,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) packet->data[32], packet->data[33]); - route_ipv4_unreachable(source, packet, ICMP_NET_UNKNOWN); + route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNKNOWN); return; } @@ -300,34 +355,41 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) } if(!subnet->owner->status.reachable) - route_ipv4_unreachable(source, packet, ICMP_NET_UNREACH); + route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNREACH); if(priorityinheritance) packet->priority = packet->data[15]; + via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via; + + if(packet->len > via->mtu && via != myself) { + ifdebug(TRAFFIC) logger(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 = via->mtu; + route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED); + } else { + fragment_ipv4_packet(via, packet); + } + + return; + } + send_packet(subnet->owner, packet); } -static void route_ipv4(node_t *source, vpn_packet_t *packet) +static __inline__ 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(node_t *source, vpn_packet_t *packet, uint8_t code) +static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) { struct ip6_hdr ip6; struct icmp6_hdr icmp6 = {0}; @@ -355,6 +417,9 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t 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; @@ -374,7 +439,7 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t /* Fill in ICMP header */ - icmp6.icmp6_type = ICMP6_DST_UNREACH; + icmp6.icmp6_type = type; icmp6.icmp6_code = code; icmp6.icmp6_cksum = 0; @@ -401,9 +466,10 @@ static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t send_packet(source, packet); } -static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) +static __inline__ void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) { subnet_t *subnet; + node_t *via; cp(); @@ -421,7 +487,7 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) ntohs(*(uint16_t *) &packet->data[50]), ntohs(*(uint16_t *) &packet->data[52])); - route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH_ADDR); + route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR); return; } @@ -431,37 +497,19 @@ static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) } if(!subnet->owner->status.reachable) - route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH_NOROUTE); - - send_packet(subnet->owner, packet); -} + route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE); -#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); + via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via; + + if(packet->len > via->mtu && via != myself) { + ifdebug(TRAFFIC) logger(LOG_INFO, _("Packet for %s (%s) length %d larger than MTU %d"), subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu); + packet->len = via->mtu; + route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0); + return; } + + send_packet(subnet->owner, packet); } -#endif /* RFC 2461 */ @@ -591,101 +639,7 @@ static void route_neighborsol(node_t *source, vpn_packet_t *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) +static __inline__ void route_ipv6(node_t *source, vpn_packet_t *packet) { cp(); @@ -697,20 +651,6 @@ static void route_ipv6(node_t *source, vpn_packet_t *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); }