Provide all missing IPv6 definitions in lib/ipv6.h.
[tinc] / src / route.c
index fa99a7e..89271ba 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.56 2003/07/07 11:11:33 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 <syslog.h>
 #include <string.h>
 #ifdef HAVE_INTTYPES_H
 #include <inttypes.h>
 #include "route.h"
 #include "protocol.h"
 #include "device.h"
+#include "logger.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;
+static mac_t mymac = {{0xFE, 0xFD, 0, 0, 0, 0}};
+
+/* RFC 1071 */
 
-void learn_mac(mac_t * address)
+static 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;
+}
+
+static int ratelimit(void) {
+       static time_t lasttime = 0;
+       
+       if(lasttime == now)
+               return 1;
+
+       lasttime = now;
+       return 0;
+}
+       
+static void learn_mac(mac_t *address)
 {
        subnet_t *subnet;
        avl_node_t *node;
@@ -80,8 +130,7 @@ void learn_mac(mac_t * address)
        /* If we don't know this MAC address yet, store it */
 
        if(!subnet || subnet->owner != myself) {
-               if(debug_lvl >= DEBUG_TRAFFIC)
-                       syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
+               logger(DEBUG_TRAFFIC, LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
                                   address->x[0], address->x[1], address->x[2], address->x[3],
                                   address->x[4], address->x[5]);
 
@@ -114,8 +163,7 @@ void age_mac(void)
                next = node->next;
                s = (subnet_t *) node->data;
                if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now) {
-                       if(debug_lvl >= DEBUG_TRAFFIC)
-                               syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
+                       logger(DEBUG_TRAFFIC, LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
                                           s->net.mac.address.x[0], s->net.mac.address.x[1],
                                           s->net.mac.address.x[2], s->net.mac.address.x[3],
                                           s->net.mac.address.x[4], s->net.mac.address.x[5]);
@@ -131,7 +179,7 @@ void age_mac(void)
        }
 }
 
-node_t *route_mac(vpn_packet_t * packet)
+static node_t *route_mac(vpn_packet_t *packet)
 {
        subnet_t *subnet;
 
@@ -139,11 +187,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 +199,68 @@ node_t *route_mac(vpn_packet_t * packet)
                return NULL;
 }
 
-node_t *route_ipv4(vpn_packet_t * packet)
+/* RFC 792 */
+
+static 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);
+}
+
+static node_t *route_ipv4(vpn_packet_t *packet)
 {
        subnet_t *subnet;
 
@@ -163,19 +272,89 @@ node_t *route_ipv4(vpn_packet_t * packet)
        subnet = lookup_subnet_ipv4((ipv4_t *) & packet->data[30]);
 
        if(!subnet) {
-               if(debug_lvl >= DEBUG_TRAFFIC) {
-                       syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
                                   packet->data[30], packet->data[31], packet->data[32],
                                   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)
+/* RFC 2463 */
+
+static 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);
+}
+
+static node_t *route_ipv6(vpn_packet_t *packet)
 {
        subnet_t *subnet;
 
@@ -184,8 +363,7 @@ node_t *route_ipv6(vpn_packet_t * packet)
        subnet = lookup_subnet_ipv6((ipv6_t *) & packet->data[38]);
 
        if(!subnet) {
-               if(debug_lvl >= DEBUG_TRAFFIC) {
-                       syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
                                   ntohs(*(uint16_t *) & packet->data[38]),
                                   ntohs(*(uint16_t *) & packet->data[40]),
                                   ntohs(*(uint16_t *) & packet->data[42]),
@@ -194,28 +372,20 @@ node_t *route_ipv6(vpn_packet_t * packet)
                                   ntohs(*(uint16_t *) & packet->data[48]),
                                   ntohs(*(uint16_t *) & packet->data[50]),
                                   ntohs(*(uint16_t *) & packet->data[52]));
-               }
+               route_ipv6_unreachable(packet, ICMP6_DST_UNREACH_ADDR);
 
                return NULL;
        }
 
+       if(!subnet->owner->status.reachable)
+               route_ipv6_unreachable(packet, ICMP6_DST_UNREACH_NOROUTE);
+       
        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++);
-
-       while(checksum >> 16)
-               checksum = (checksum & 0xFFFF) + (checksum >> 16);
-
-       return checksum ^ 0xFFFF;
-}
+/* RFC 2461 */
 
-void route_neighborsol(vpn_packet_t * packet)
+static void route_neighborsol(vpn_packet_t *packet)
 {
        struct ip6_hdr *hdr;
        struct nd_neighbor_solicit *ns;
@@ -227,26 +397,25 @@ 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 */
 
        if(ns->nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
           opt->nd_opt_type != ND_OPT_SOURCE_LINKADDR) {
-               if(debug_lvl > DEBUG_TRAFFIC) {
-                       syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
-               }
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
                return;
        }
 
@@ -255,17 +424,15 @@ 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)
-                       syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
                return;
        }
 
@@ -274,8 +441,7 @@ void route_neighborsol(vpn_packet_t * packet)
        subnet = lookup_subnet_ipv6((ipv6_t *) & ns->nd_ns_target);
 
        if(!subnet) {
-               if(debug_lvl >= DEBUG_TRAFFIC) {
-                       syslog(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
                                   ntohs(((uint16_t *) & ns->nd_ns_target)[0]),
                                   ntohs(((uint16_t *) & ns->nd_ns_target)[1]),
                                   ntohs(((uint16_t *) & ns->nd_ns_target)[2]),
@@ -284,7 +450,6 @@ void route_neighborsol(vpn_packet_t * packet)
                                   ntohs(((uint16_t *) & ns->nd_ns_target)[5]),
                                   ntohs(((uint16_t *) & ns->nd_ns_target)[6]),
                                   ntohs(((uint16_t *) & ns->nd_ns_target)[7]));
-               }
 
                return;
        }
@@ -317,20 +482,21 @@ 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)
+/* RFC 826 */
+
+static void route_arp(vpn_packet_t *packet)
 {
        struct ether_arp *arp;
        subnet_t *subnet;
@@ -340,22 +506,21 @@ 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 */
 
        if(ntohs(arp->arp_hrd) != ARPHRD_ETHER || ntohs(arp->arp_pro) != ETHERTYPE_IP ||
           arp->arp_hln != ETHER_ADDR_LEN || arp->arp_pln != 4 || ntohs(arp->arp_op) != ARPOP_REQUEST) {
-               if(debug_lvl > DEBUG_TRAFFIC) {
-                       syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
-               }
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
                return;
        }
 
@@ -364,12 +529,9 @@ void route_arp(vpn_packet_t * packet)
        subnet = lookup_subnet_ipv4((ipv4_t *) arp->arp_tpa);
 
        if(!subnet) {
-               if(debug_lvl >= DEBUG_TRAFFIC) {
-                       syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
+               logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
                                   arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2],
                                   arp->arp_tpa[3]);
-               }
-
                return;
        }
 
@@ -392,7 +554,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,7 +565,7 @@ 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);
@@ -422,8 +584,7 @@ void route_outgoing(vpn_packet_t * packet)
                                        return;
 
                                default:
-                                       if(debug_lvl >= DEBUG_TRAFFIC)
-                                               syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
+                                       logger(DEBUG_TRAFFIC, LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
                                        return;
                        }
                        if(n)
@@ -444,7 +605,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 +613,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 +630,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 +643,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)