X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=blobdiff_plain;f=src%2Froute.c;h=3d8d54af143397f7632695b7bdf461eff137a971;hp=829773180c87ab6d549e26b3d3e1577ed1c2499d;hb=a9bbb3357a89e27185312fbce0ee134eda4eda90;hpb=2a538ed34332b3392f866d56accd9efecc9467ed diff --git a/src/route.c b/src/route.c index 82977318..3d8d54af 100644 --- a/src/route.c +++ b/src/route.c @@ -1,7 +1,7 @@ /* route.c -- routing Copyright (C) 2000-2005 Ivo Timmermans, - 2000-2009 Guus Sliepen + 2000-2010 Guus Sliepen 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 @@ -33,6 +33,8 @@ #include "utils.h" rmode_t routing_mode = RMODE_ROUTER; +fmode_t forwarding_mode = FMODE_INTERNAL; +bool directonly = false; bool priorityinheritance = false; int macexpire = 600; bool overwrite_mac = false; @@ -48,7 +50,7 @@ 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); -#define max(a, b) ((a) > (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) /* RFC 1071 */ @@ -93,49 +95,74 @@ static bool checklength(node_t *source, vpn_packet_t *packet, length_t length) { return true; } -void clamp_mss(node_t *source, node_t *via, vpn_packet_t *packet) { - int i; - int len = ((packet->data[46] >> 4) - 5) * 4; +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 = 0; + uint16_t type = packet->data[12] << 8 | packet->data[13]; + + if(type == ETH_P_IP && packet->data[23] == 6) + start = 14 + (packet->data[14] & 0xf) * 4; + else if(type == ETH_P_IPV6 && packet->data[20] == 6) + start = 14 + 40; + + if(!start || 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[54 + i] == 0) + if(packet->data[start + 20 + i] == 0) break; - if(packet->data[54 + i] == 1) { + if(packet->data[start + 20 + i] == 1) { i++; continue; } - if(i > len - 2 || i > len - packet->data[55 + i]) + if(i > len - 2 || i > len - packet->data[start + 21 + i]) break; - if(packet->data[54 + i] != 2) { - if(packet->data[55 + i] < 2) + if(packet->data[start + 20 + i] != 2) { + if(packet->data[start + 21 + i] < 2) break; - i += packet->data[55 + i]; + i += packet->data[start + 21 + i]; continue; } - if(packet->data[55] != 4) + if(packet->data[start + 21] != 4) break; - uint16_t oldmss = packet->data[56 + i] << 8 | packet->data[57 + i]; - uint16_t newmss = via->mtu - 54; - uint16_t csum = packet->data[50] << 8 | packet->data[51]; + /* 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; ifdebug(TRAFFIC) logger(LOG_INFO, "Clamping MSS of packet from %s to %s to %d", source->name, via->name, newmss); - packet->data[56 + i] = newmss >> 8; - packet->data[57 + i] = newmss & 0xff; + /* 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[50] = csum >> 8; - packet->data[51] = csum & 0xff; + packet->data[start + 16] = csum >> 8; + packet->data[start + 17] = csum & 0xff; break; } } @@ -167,6 +194,7 @@ static void learn_mac(mac_t *address) { 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 */ @@ -202,6 +230,7 @@ void age_subnets(void) { send_del_subnet(c, s); } + subnet_update(myself, s, false); subnet_del(myself, s); } } @@ -356,17 +385,23 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) { } if(!subnet->owner->status.reachable) - route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNREACH); + return route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_UNREACH); + + if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself) + return route_ipv4_unreachable(source, packet, 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 && packet->len > max(via->mtu, 590) && via != myself) { + if(directonly && subnet->owner != via) + return route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_NET_ANO); + + if(via && packet->len > MAX(via->mtu, 590) && 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 = max(via->mtu, 590); + packet->len = MAX(via->mtu, 590); route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED); } else { fragment_ipv4_packet(via, packet); @@ -375,8 +410,7 @@ static void route_ipv4_unicast(node_t *source, vpn_packet_t *packet) { return; } - if(via && via != myself && packet->data[23] == 6 && packet->len >= 58 && (packet->data[46] >> 4) > 5) - clamp_mss(source, via, packet); + clamp_mss(source, via, packet); send_packet(subnet->owner, packet); } @@ -505,17 +539,25 @@ 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, ICMP6_DST_UNREACH_NOROUTE); + return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE); + + if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself) + return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN); via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via; - if(via && packet->len > max(via->mtu, 1294) && via != myself) { + if(directonly && subnet->owner != via) + return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN); + + if(via && packet->len > MAX(via->mtu, 1294) && 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 = max(via->mtu, 1294); + packet->len = MAX(via->mtu, 1294); route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0); return; } + clamp_mss(source, via, packet); + send_packet(subnet->owner, packet); } @@ -768,9 +810,15 @@ static void route_mac(node_t *source, vpn_packet_t *packet) { return; } + if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself) + return; + // 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) { 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); @@ -790,13 +838,17 @@ static void route_mac(node_t *source, vpn_packet_t *packet) { } } - if(via && via != myself && (packet->data[12] << 8 | packet->data[13]) == ETH_P_IP && packet->len >= 58 && packet->data[23] == 6 && (packet->data[46] >> 4) > 5) - clamp_mss(source, via, packet); + clamp_mss(source, via, packet); send_packet(subnet->owner, packet); } void route(node_t *source, vpn_packet_t *packet) { + if(forwarding_mode == FMODE_KERNEL && source != myself) { + send_packet(myself, packet); + return; + } + if(!checklength(source, packet, ether_size)) return;