Merge branch 'master' of git://tinc-vpn.org/tinc into 1.1
[tinc] / src / route.c
index 0b2d22e..32be4de 100644 (file)
 
 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;
+bool broadcast = true;
 mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
 bool pcap = false;
 
@@ -439,11 +441,11 @@ static void route_ipv4(node_t *source, vpn_packet_t *packet) {
        if(!checklength(source, packet, ether_size + ip_size))
                return;
 
-       if(((packet->data[30] & 0xf0) == 0xe0) || (
+       if(broadcast && (((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);
@@ -731,7 +733,7 @@ static void route_ipv6(node_t *source, vpn_packet_t *packet) {
                return;
        }
 
-       if(packet->data[38] == 255)
+       if(broadcast && packet->data[38] == 255)
                broadcast_packet(source, packet);
        else
                route_ipv6_unicast(source, packet);
@@ -821,7 +823,8 @@ static void route_mac(node_t *source, vpn_packet_t *packet) {
        subnet = lookup_subnet_mac(NULL, &dest);
 
        if(!subnet) {
-               broadcast_packet(source, packet);
+               if(broadcast)
+                       broadcast_packet(source, packet);
                return;
        }
 
@@ -876,6 +879,50 @@ static void send_pcap(vpn_packet_t *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(pcap)
                send_pcap(packet);
@@ -888,6 +935,10 @@ void route(node_t *source, vpn_packet_t *packet) {
        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:
                        {