Check for IPv6 header files.
[tinc] / src / route.c
index fa99a7e..b6ec0dc 100644 (file)
@@ -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.45 2002/09/09 21:25:07 guus Exp $
+    $Id: route.c,v 1.1.2.53 2003/07/06 17:49:49 guus Exp $
 */
 
 #include "config.h"
 #ifdef HAVE_NETINET_IN_SYSTM_H
 #include <netinet/in_systm.h>
 #endif
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#ifdef HAVE_NETINET_IP6_H
 #include <netinet/ip6.h>
 #include <netinet/icmp6.h>
+#endif
 #include <netinet/if_ether.h>
 #include <utils.h>
 #include <xalloc.h>
 
 #include "system.h"
 
+/* Missing definitions */
+
 #ifndef ETHER_ADDR_LEN
 #define ETHER_ADDR_LEN 6
 #endif
 
+#ifndef ICMP_DEST_UNREACH
+#define ICMP_DEST_UNREACH 3
+#endif
+
+#ifndef ICMP_NET_UNKNOWN
+#define ICMP_NET_UNKNOWN 6
+#endif
+
+#ifndef ICMP_NET_UNREACH
+#define ICMP_NET_UNREACH 0
+#endif
+
 int routing_mode = RMODE_ROUTER;
 int priorityinheritance = 0;
 int macexpire = 600;
-subnet_t mymac;
+int overwrite_mac = 0;
+mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
+
+/* RFC 1071 */
 
-void learn_mac(mac_t * address)
+uint16_t inet_checksum(void *data, int len, uint16_t prevsum)
+{
+       uint16_t *p = data;
+       uint32_t checksum = prevsum ^ 0xFFFF;
+
+       while(len >= 2) {
+               checksum += *p++;
+               len -= 2;
+       }
+       
+       if(len)
+               checksum += *(unsigned char *)p;
+
+       while(checksum >> 16)
+               checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+       return ~checksum;
+}
+
+int ratelimit(void) {
+       static time_t lasttime = 0;
+       
+       if(lasttime == now)
+               return 1;
+
+       lasttime = now;
+       return 0;
+}
+       
+void learn_mac(mac_t *address)
 {
        subnet_t *subnet;
        avl_node_t *node;
@@ -131,7 +181,7 @@ void age_mac(void)
        }
 }
 
-node_t *route_mac(vpn_packet_t * packet)
+node_t *route_mac(vpn_packet_t *packet)
 {
        subnet_t *subnet;
 
@@ -139,11 +189,11 @@ node_t *route_mac(vpn_packet_t * packet)
 
        /* Learn source address */
 
-       learn_mac((mac_t *) (&packet->data[6]));
+       learn_mac((mac_t *)(&packet->data[6]));
 
        /* Lookup destination address */
 
-       subnet = lookup_subnet_mac((mac_t *) (&packet->data[0]));
+       subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
 
        if(subnet)
                return subnet->owner;
@@ -151,7 +201,68 @@ node_t *route_mac(vpn_packet_t * packet)
                return NULL;
 }
 
-node_t *route_ipv4(vpn_packet_t * packet)
+/* RFC 792 */
+
+void route_ipv4_unreachable(vpn_packet_t *packet, uint8_t code)
+{
+       struct ip *hdr;
+       struct icmp *icmp;
+       
+       struct in_addr ip_src;
+       struct in_addr ip_dst;
+       uint32_t oldlen;
+
+       if(ratelimit())
+               return;
+       
+       cp();
+
+       hdr = (struct ip *)(packet->data + 14);
+       icmp = (struct icmp *)(packet->data + 14 + 20);
+
+       /* Remember original source and destination */
+               
+       memcpy(&ip_src, &hdr->ip_src, 4);
+       memcpy(&ip_dst, &hdr->ip_dst, 4);
+       oldlen = packet->len - 14;
+       
+       if(oldlen >= IP_MSS - sizeof(*hdr) - sizeof(*icmp))
+               oldlen = IP_MSS - sizeof(*hdr) - sizeof(*icmp);
+       
+       /* Copy first part of original contents to ICMP message */
+       
+       memmove(&icmp->icmp_ip, hdr, oldlen);
+
+       /* Fill in IPv4 header */
+       
+       hdr->ip_v = 4;
+       hdr->ip_hl = sizeof(*hdr) / 4;
+       hdr->ip_tos = 0;
+       hdr->ip_len = htons(20 + 8 + oldlen);
+       hdr->ip_id = 0;
+       hdr->ip_off = 0;
+       hdr->ip_ttl = 255;
+       hdr->ip_p = IPPROTO_ICMP;
+       hdr->ip_sum = 0;
+       memcpy(&hdr->ip_src, &ip_dst, 4);
+       memcpy(&hdr->ip_dst, &ip_src, 4);
+
+       hdr->ip_sum = inet_checksum(hdr, 20, ~0);
+       
+       /* Fill in ICMP header */
+       
+       icmp->icmp_type = ICMP_DEST_UNREACH;
+       icmp->icmp_code = code;
+       icmp->icmp_cksum = 0;
+       
+       icmp->icmp_cksum = inet_checksum(icmp, 8 + oldlen, ~0);
+       
+       packet->len = 14 + 20 + 8 + oldlen;
+       
+       write_packet(packet);
+}
+
+node_t *route_ipv4(vpn_packet_t *packet)
 {
        subnet_t *subnet;
 
@@ -169,13 +280,89 @@ node_t *route_ipv4(vpn_packet_t * packet)
                                   packet->data[33]);
                }
 
+               route_ipv4_unreachable(packet, ICMP_NET_UNKNOWN);
                return NULL;
        }
+       
+       if(!subnet->owner->status.reachable)
+               route_ipv4_unreachable(packet, ICMP_NET_UNREACH);
 
        return subnet->owner;
 }
 
-node_t *route_ipv6(vpn_packet_t * packet)
+#ifdef HAVE_NETINET_IP6_H
+
+/* RFC 2463 */
+
+void route_ipv6_unreachable(vpn_packet_t *packet, uint8_t code)
+{
+       struct ip6_hdr *hdr;
+       struct icmp6_hdr *icmp;
+       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;
+
+       if(ratelimit())
+               return;
+       
+       cp();
+
+       hdr = (struct ip6_hdr *)(packet->data + 14);
+       icmp = (struct icmp6_hdr *)(packet->data + 14 + sizeof(*hdr));
+
+       /* Remember original source and destination */
+               
+       memcpy(&pseudo.ip6_src, &hdr->ip6_dst, 16);
+       memcpy(&pseudo.ip6_dst, &hdr->ip6_src, 16);
+       pseudo.length = ntohs(hdr->ip6_plen) + sizeof(*hdr);
+       
+       if(pseudo.length >= IP_MSS - sizeof(*hdr) - sizeof(*icmp))
+               pseudo.length = IP_MSS - sizeof(*hdr) - sizeof(*icmp);
+       
+       /* Copy first part of original contents to ICMP message */
+       
+       memmove(((char *)icmp) + sizeof(*icmp), hdr, pseudo.length);
+
+       /* Fill in IPv6 header */
+       
+       hdr->ip6_flow = htonl(0x60000000UL);
+       hdr->ip6_plen = htons(sizeof(*icmp) + pseudo.length);
+       hdr->ip6_nxt = IPPROTO_ICMPV6;
+       hdr->ip6_hlim = 255;
+       memcpy(&hdr->ip6_dst, &pseudo.ip6_dst, 16);
+       memcpy(&hdr->ip6_src, &pseudo.ip6_src, 16);
+
+       /* Fill in ICMP header */
+       
+       icmp->icmp6_type = ICMP6_DST_UNREACH;
+       icmp->icmp6_code = code;
+       icmp->icmp6_cksum = 0;
+
+       /* Create pseudo header */
+               
+       pseudo.length = htonl(sizeof(*icmp) + pseudo.length);
+       pseudo.next = htonl(IPPROTO_ICMPV6);
+
+       /* Generate checksum */
+       
+       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(icmp, ntohl(pseudo.length), checksum);
+
+       icmp->icmp6_cksum = checksum;
+       
+       packet->len = 14 + sizeof(*hdr) + ntohl(pseudo.length);
+       
+       write_packet(packet);
+}
+
+#endif
+
+node_t *route_ipv6(vpn_packet_t *packet)
 {
        subnet_t *subnet;
 
@@ -195,27 +382,26 @@ node_t *route_ipv6(vpn_packet_t * packet)
                                   ntohs(*(uint16_t *) & packet->data[50]),
                                   ntohs(*(uint16_t *) & packet->data[52]));
                }
+#ifdef HAVE_NETINET_IP6_H
+               route_ipv6_unreachable(packet, ICMP6_DST_UNREACH_ADDR);
+#endif
 
                return NULL;
        }
 
+#ifdef HAVE_NETINET_IP6_H
+       if(!subnet->owner->status.reachable)
+               route_ipv6_unreachable(packet, ICMP6_DST_UNREACH_NOROUTE);
+#endif
+       
        return subnet->owner;
 }
 
-uint16_t inet_checksum(uint16_t * data, int len, uint16_t prevsum)
-{
-       uint32_t checksum = prevsum ^ 0xFFFF;
-
-       while(len--)
-               checksum += ntohs(*data++);
+#ifdef HAVE_NETINET_IP6_H
 
-       while(checksum >> 16)
-               checksum = (checksum & 0xFFFF) + (checksum >> 16);
-
-       return checksum ^ 0xFFFF;
-}
+/* RFC 2461 */
 
-void route_neighborsol(vpn_packet_t * packet)
+void route_neighborsol(vpn_packet_t *packet)
 {
        struct ip6_hdr *hdr;
        struct nd_neighbor_solicit *ns;
@@ -227,18 +413,19 @@ void route_neighborsol(vpn_packet_t * packet)
                struct in6_addr ip6_src;        /* source address */
                struct in6_addr ip6_dst;        /* destination address */
                uint32_t length;
-               uint8_t junk[4];
+               uint32_t next;
        } pseudo;
 
        cp();
 
-       hdr = (struct ip6_hdr *) (packet->data + 14);
-       ns = (struct nd_neighbor_solicit *) (packet->data + 14 + sizeof(*hdr));
-       opt = (struct nd_opt_hdr *) (packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
+       hdr = (struct ip6_hdr *)(packet->data + 14);
+       ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
+       opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
 
        /* First, snatch the source address from the neighbor solicitation packet */
 
-       memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
+       if(overwrite_mac)
+               memcpy(mymac.x, packet->data + 6, 6);
 
        /* Check if this is a valid neighbor solicitation request */
 
@@ -255,13 +442,12 @@ void route_neighborsol(vpn_packet_t * packet)
        memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
        memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
        pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
-       pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
-       pseudo.junk[3] = IPPROTO_ICMPV6;
+       pseudo.next = htonl(IPPROTO_ICMPV6);
 
        /* Generate checksum */
 
-       checksum = inet_checksum((uint16_t *) & pseudo, sizeof(pseudo) / 2, ~0);
-       checksum = inet_checksum((uint16_t *) ns, sizeof(*ns) / 2 + 4, checksum);
+       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(ns, sizeof(*ns) + 8, checksum);
 
        if(checksum) {
                if(debug_lvl >= DEBUG_TRAFFIC)
@@ -317,20 +503,23 @@ void route_neighborsol(vpn_packet_t * packet)
        memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
        memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
        pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
-       pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
-       pseudo.junk[3] = IPPROTO_ICMPV6;
+       pseudo.next = htonl(IPPROTO_ICMPV6);
 
        /* Generate checksum */
 
-       checksum = inet_checksum((uint16_t *) & pseudo, sizeof(pseudo) / 2, ~0);
-       checksum = inet_checksum((uint16_t *) ns, sizeof(*ns) / 2 + 4, checksum);
+       checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
+       checksum = inet_checksum(ns, sizeof(*ns) + 8, checksum);
 
-       ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
+       ns->nd_ns_hdr.icmp6_cksum = checksum;
 
        write_packet(packet);
 }
 
-void route_arp(vpn_packet_t * packet)
+#endif
+
+/* RFC 826 */
+
+void route_arp(vpn_packet_t *packet)
 {
        struct ether_arp *arp;
        subnet_t *subnet;
@@ -340,14 +529,15 @@ void route_arp(vpn_packet_t * packet)
 
        /* First, snatch the source address from the ARP packet */
 
-       memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
+       if(overwrite_mac)
+               memcpy(mymac.x, packet->data + 6, 6);
 
        /* This routine generates replies to ARP requests.
           You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
           Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
         */
 
-       arp = (struct ether_arp *) (packet->data + 14);
+       arp = (struct ether_arp *)(packet->data + 14);
 
        /* Check if this is a valid ARP request */
 
@@ -392,7 +582,7 @@ void route_arp(vpn_packet_t * packet)
        write_packet(packet);
 }
 
-void route_outgoing(vpn_packet_t * packet)
+void route_outgoing(vpn_packet_t *packet)
 {
        uint16_t type;
        node_t *n = NULL;
@@ -403,17 +593,19 @@ void route_outgoing(vpn_packet_t * packet)
 
        switch (routing_mode) {
                case RMODE_ROUTER:
-                       type = ntohs(*((uint16_t *) (&packet->data[12])));
+                       type = ntohs(*((uint16_t *)(&packet->data[12])));
                        switch (type) {
                                case 0x0800:
                                        n = route_ipv4(packet);
                                        break;
 
                                case 0x86DD:
+#ifdef HAVE_NETINET_IP6_H
                                        if(packet->data[20] == IPPROTO_ICMPV6 && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
                                                route_neighborsol(packet);
                                                return;
                                        }
+#endif
                                        n = route_ipv6(packet);
                                        break;
 
@@ -444,7 +636,7 @@ void route_outgoing(vpn_packet_t * packet)
        }
 }
 
-void route_incoming(node_t * source, vpn_packet_t * packet)
+void route_incoming(node_t *source, vpn_packet_t *packet)
 {
        switch (routing_mode) {
                case RMODE_ROUTER:
@@ -452,7 +644,7 @@ void route_incoming(node_t * source, vpn_packet_t * packet)
                                node_t *n = NULL;
                                uint16_t type;
 
-                               type = ntohs(*((uint16_t *) (&packet->data[12])));
+                               type = ntohs(*((uint16_t *)(&packet->data[12])));
                                switch (type) {
                                        case 0x0800:
                                                n = route_ipv4(packet);
@@ -469,7 +661,8 @@ void route_incoming(node_t * source, vpn_packet_t * packet)
 
                                if(n) {
                                        if(n == myself) {
-                                               memcpy(packet->data, mymac.net.mac.address.x, 6);
+                                               if(overwrite_mac)
+                                                       memcpy(packet->data, mymac.x, 6);
                                                write_packet(packet);
                                        } else
                                                send_packet(n, packet);
@@ -481,7 +674,7 @@ void route_incoming(node_t * source, vpn_packet_t * packet)
                        {
                                subnet_t *subnet;
 
-                               subnet = lookup_subnet_mac((mac_t *) (&packet->data[0]));
+                               subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
 
                                if(subnet) {
                                        if(subnet->owner == myself)