/*
route.c -- routing
Copyright (C) 2000-2005 Ivo Timmermans,
- 2000-2010 Guus Sliepen <guus@tinc-vpn.org>
+ 2000-2012 Guus Sliepen <guus@tinc-vpn.org>
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
rmode_t routing_mode = RMODE_ROUTER;
fmode_t forwarding_mode = FMODE_INTERNAL;
+bmode_t broadcast_mode = BMODE_MST;
+bool decrement_ttl = false;
bool directonly = false;
bool priorityinheritance = false;
int macexpire = 600;
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))
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
/* RFC 1071 */
static int count = 0;
if(lasttime == now) {
- if(++count > frequency)
+ if(count >= frequency)
return true;
} else {
lasttime = now;
count = 0;
}
+ count++;
return false;
}
for(node = myself->subnet_tree->head; node; node = next) {
next = node->next;
s = node->data;
- if(s->expires && s->expires < now) {
+ if(s->expires && s->expires <= now) {
ifdebug(TRAFFIC) {
char netstr[MAXNETSTR];
if(net2str(netstr, sizeof netstr, s))
packet->priority = packet->data[15];
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
+
+ if(via == source) {
+ ifdebug(TRAFFIC) logger(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, ICMP_DEST_UNREACH, ICMP_NET_ANO);
- if(via && packet->len > max(via->mtu, 590) && via != myself) {
+ 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);
if(!checklength(source, packet, ether_size + ip_size))
return;
- if(((packet->data[30] & 0xf0) == 0xe0) || (
+ if(broadcast_mode && (((packet->data[30] & 0xf0) == 0xe0) || (
packet->data[30] == 255 &&
packet->data[31] == 255 &&
packet->data[32] == 255 &&
- packet->data[33] == 255))
+ packet->data[33] == 255)))
broadcast_packet(source, packet);
else
route_ipv4_unicast(source, packet);
via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
+ if(via == source) {
+ ifdebug(TRAFFIC) logger(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, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
- if(via && packet->len > max(via->mtu, 1294) && via != myself) {
+ 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;
}
return;
}
- if(packet->data[38] == 255)
+ if(broadcast_mode && packet->data[38] == 255)
broadcast_packet(source, packet);
else
route_ipv6_unicast(source, packet);
send_packet(subnet->owner, packet);
}
+static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
+ uint16_t type = packet->data[12] << 8 | packet->data[13];
+
+ switch (type) {
+ case ETH_P_IP:
+ if(!checklength(source, packet, 14 + 32))
+ return false;
+
+ if(packet->data[22] < 1) {
+ if(packet->data[25] != IPPROTO_ICMP || packet->data[46] != ICMP_TIME_EXCEEDED)
+ route_ipv4_unreachable(source, packet, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
+ return false;
+ }
+
+ uint16_t old = packet->data[22] << 8 | packet->data[23];
+ packet->data[22]--;
+ uint16_t new = packet->data[22] << 8 | packet->data[23];
+
+ uint32_t checksum = packet->data[24] << 8 | packet->data[25];
+ checksum += old + (~new & 0xFFFF);
+ while(checksum >> 16)
+ checksum = (checksum & 0xFFFF) + (checksum >> 16);
+ packet->data[24] = checksum >> 8;
+ packet->data[25] = checksum & 0xff;
+
+ return true;
+
+ case ETH_P_IPV6:
+ if(!checklength(source, packet, 14 + 40))
+ return false;
+
+ if(packet->data[21] < 1) {
+ if(packet->data[20] != IPPROTO_ICMPV6 || packet->data[54] != ICMP6_TIME_EXCEEDED)
+ route_ipv6_unreachable(source, packet, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
+ return false;
+ }
+
+ packet->data[21]--;
+
+ return true;
+
+ default:
+ return true;
+ }
+}
+
void route(node_t *source, vpn_packet_t *packet) {
- if(forwarding_mode == FMODE_KERNEL) {
+ 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;
+
switch (routing_mode) {
case RMODE_ROUTER:
{