Rename connection_t *broadcast to everyone.
[tinc] / src / route.c
index 6c381cc..9e9f9d0 100644 (file)
@@ -33,6 +33,9 @@
 #include "utils.h"
 
 rmode_t routing_mode = RMODE_ROUTER;
+fmode_t forwarding_mode = FMODE_INTERNAL;
+bool decrement_ttl = true;
+bool directonly = false;
 bool priorityinheritance = false;
 int macexpire = 600;
 bool overwrite_mac = false;
@@ -48,7 +51,10 @@ 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))
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
 
 /* RFC 1071 */
 
@@ -215,7 +221,7 @@ void age_subnets(void) {
        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))
@@ -383,17 +389,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);
@@ -531,13 +543,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, 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;
        }
@@ -796,9 +814,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);
@@ -823,10 +847,63 @@ static void route_mac(node_t *source, vpn_packet_t *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) {
+                               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) {
+                               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 && 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:
                        {