Typo.
[oweals/tinc.git] / src / route.c
1 /*
2     route.c -- routing
3     Copyright (C) 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
4                   2000-2002 Guus Sliepen <guus@sliepen.warande.net>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: route.c,v 1.1.2.36 2002/03/15 15:50:14 guus Exp $
21 */
22
23 #include "config.h"
24
25 #if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
26  #include <sys/param.h>
27 #endif
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #if defined(HAVE_SOLARIS) || defined(HAVE_OPENBSD)
31  #include <net/if.h>
32  #define ETHER_ADDR_LEN 6
33 #else
34  #include <net/ethernet.h>
35 #endif
36 #include <netinet/ip6.h>
37 #include <netinet/icmp6.h>
38 #include <netinet/if_ether.h>
39 #include <utils.h>
40 #include <xalloc.h>
41 #include <syslog.h>
42 #include <string.h>
43
44 #include <avl_tree.h>
45
46 #include "net.h"
47 #include "connection.h"
48 #include "subnet.h"
49 #include "route.h"
50 #include "protocol.h"
51 #include "device.h"
52
53 #include "system.h"
54
55 #ifndef s6_addr16
56 #define s6_addr16 __u6_addr.__u6_addr16
57 #endif
58
59 int routing_mode = RMODE_ROUTER;
60 int priorityinheritance = 0;
61 int macexpire = 600;
62 subnet_t mymac;
63
64 void learn_mac(mac_t *address)
65 {
66   subnet_t *subnet;
67   avl_node_t *node;
68   connection_t *c;
69 cp
70   subnet = lookup_subnet_mac(address);
71
72   /* If we don't know this MAC address yet, store it */
73   
74   if(!subnet || subnet->owner!=myself)
75     {
76       if(debug_lvl >= DEBUG_TRAFFIC)
77         syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
78                address->x[0], address->x[1], address->x[2], address->x[3],  address->x[4], address->x[5]);
79                
80       subnet = new_subnet();
81       subnet->type = SUBNET_MAC;
82       memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
83       subnet_add(myself, subnet);
84
85       /* And tell all other tinc daemons it's our MAC */
86       
87       for(node = connection_tree->head; node; node = node->next)
88         {
89           c = (connection_t *)node->data;
90           if(c->status.active)
91             send_add_subnet(c, subnet);
92         }
93     }
94
95   subnet->net.mac.lastseen = now;
96 }
97
98 void age_mac(void)
99 {
100   subnet_t *s;
101   connection_t *c;
102   avl_node_t *node, *next, *node2;
103 cp
104   for(node = myself->subnet_tree->head; node; node = next)
105     {
106       next = node->next;
107       s = (subnet_t *)node->data;
108       if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now)
109         {
110           if(debug_lvl >= DEBUG_TRAFFIC)
111             syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
112                    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]);
113           for(node2 = connection_tree->head; node2; node2 = node2->next)
114             {
115               c = (connection_t *)node2->data;
116               if(c->status.active)
117                 send_del_subnet(c, s);
118             }
119           subnet_del(myself, s);
120         }
121     }
122 cp
123 }
124
125 node_t *route_mac(vpn_packet_t *packet)
126 {
127   subnet_t *subnet;
128 cp
129   /* Learn source address */
130
131   learn_mac((mac_t *)(&packet->data[6]));
132   
133   /* Lookup destination address */
134     
135   subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
136
137   if(subnet)
138     return subnet->owner;
139   else
140     return NULL;
141 }
142
143 node_t *route_ipv4(vpn_packet_t *packet)
144 {
145   subnet_t *subnet;
146 cp
147   if(priorityinheritance)
148     packet->priority = packet->data[15];
149
150   subnet = lookup_subnet_ipv4((ipv4_t *)&packet->data[30]);
151 cp
152   if(!subnet)
153     {
154       if(debug_lvl >= DEBUG_TRAFFIC)
155         {
156           syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
157                  packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
158         }
159
160       return NULL;
161     }
162 cp
163   return subnet->owner;  
164 }
165
166 node_t *route_ipv6(vpn_packet_t *packet)
167 {
168   subnet_t *subnet;
169 cp
170   subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
171 cp
172   if(!subnet)
173     {
174       if(debug_lvl >= DEBUG_TRAFFIC)
175         {
176           syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
177             ntohs(*(short unsigned int *)&packet->data[38]),
178             ntohs(*(short unsigned int *)&packet->data[40]),
179             ntohs(*(short unsigned int *)&packet->data[42]),
180             ntohs(*(short unsigned int *)&packet->data[44]),
181             ntohs(*(short unsigned int *)&packet->data[46]),
182             ntohs(*(short unsigned int *)&packet->data[48]),
183             ntohs(*(short unsigned int *)&packet->data[50]),
184             ntohs(*(short unsigned int *)&packet->data[52]));
185         }
186
187       return NULL;
188     }
189 cp
190   return subnet->owner;  
191 }
192
193 unsigned short int inet_checksum(unsigned short int *data, int len, unsigned short int prevsum)
194 {
195   unsigned long int checksum = prevsum ^ 0xFFFF;
196
197   while(len--)
198     checksum += ntohs(*data++);
199
200   while(checksum >> 16)
201     checksum = (checksum & 0xFFFF) + (checksum >> 16);
202
203   return checksum ^ 0xFFFF;
204 }
205
206 void route_neighborsol(vpn_packet_t *packet)
207 {
208   struct ip6_hdr *hdr;
209   struct nd_neighbor_solicit *ns;
210   struct nd_opt_hdr *opt;
211   subnet_t *subnet;
212   short unsigned int checksum;
213   
214   struct {
215     struct in6_addr ip6_src;      /* source address */
216     struct in6_addr ip6_dst;      /* destination address */
217     uint32_t length;
218     uint8_t junk[4];
219   } pseudo;
220
221 cp
222   hdr = (struct ip6_hdr *)(packet->data + 14);
223   ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
224   opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
225
226   /* First, snatch the source address from the neighbor solicitation packet */
227
228   memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
229
230   /* Check if this is a valid neighbor solicitation request */
231   
232   if(ns->nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
233      opt->nd_opt_type != ND_OPT_SOURCE_LINKADDR)
234     {
235       if(debug_lvl > DEBUG_TRAFFIC)
236         {
237           syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
238         } 
239       return;
240     }
241
242   /* Create pseudo header */
243
244   memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
245   memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
246   pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
247   pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
248   pseudo.junk[3] = IPPROTO_ICMPV6;
249   
250   /* Generate checksum */
251   
252   checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
253   checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
254
255   if(checksum)
256     {
257       if(debug_lvl >= DEBUG_TRAFFIC)
258           syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
259       return;
260     }
261
262   /* Check if the IPv6 address exists on the VPN */
263
264   subnet = lookup_subnet_ipv6((ipv6_t *)&ns->nd_ns_target);
265
266   if(!subnet)
267     {
268       if(debug_lvl >= DEBUG_TRAFFIC)
269         {
270           syslog(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
271                  ntohs(ns->nd_ns_target.s6_addr16[0]), ntohs(ns->nd_ns_target.s6_addr16[1]), ntohs(ns->nd_ns_target.s6_addr16[2]), ntohs(ns->nd_ns_target.s6_addr16[3]),
272                  ntohs(ns->nd_ns_target.s6_addr16[4]), ntohs(ns->nd_ns_target.s6_addr16[5]), ntohs(ns->nd_ns_target.s6_addr16[6]), ntohs(ns->nd_ns_target.s6_addr16[7]));
273         }
274
275       return;
276     }
277
278   /* Check if it is for our own subnet */
279   
280   if(subnet->owner == myself)
281     return;     /* silently ignore */
282
283   /* Create neighbor advertation reply */
284
285   memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* copy destination address */
286   packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF;                           /* mangle source address so it looks like it's not from us */
287
288   memcpy(&hdr->ip6_dst, &hdr->ip6_src, 16);                             /* swap destination and source protocol address */
289   memcpy(&hdr->ip6_src, &ns->nd_ns_target, 16);                         /* ... */
290
291   memcpy((char *)opt + sizeof(*opt), packet->data + ETHER_ADDR_LEN, 6); /* add fake source hard addr */
292
293   ns->nd_ns_hdr.icmp6_cksum = 0;
294   ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_ADVERT;
295   ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[0] = 0x40;                  /* Set solicited flag */
296   ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[1] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[2] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[3] = 0;
297   opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
298
299   /* Create pseudo header */
300   
301   memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
302   memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
303   pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
304   pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
305   pseudo.junk[3] = IPPROTO_ICMPV6;
306   
307   /* Generate checksum */
308   
309   checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
310   checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
311
312   ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
313
314   write_packet(packet);
315 cp
316 }
317
318 void route_arp(vpn_packet_t *packet)
319 {
320   struct ether_arp *arp;
321   subnet_t *subnet;
322   unsigned char ipbuf[4];
323 cp
324   /* First, snatch the source address from the ARP packet */
325
326   memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
327
328   /* This routine generates replies to ARP requests.
329      You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
330      Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
331    */
332
333   arp = (struct ether_arp *)(packet->data + 14);
334
335   /* Check if this is a valid ARP request */
336
337   if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
338      ntohs(arp->arp_pro) != ETHERTYPE_IP ||
339      (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
340      (int) (arp->arp_pln) != 4 ||
341      ntohs(arp->arp_op) != ARPOP_REQUEST )
342     {
343       if(debug_lvl > DEBUG_TRAFFIC)
344         {
345           syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
346         } 
347       return;
348     }
349
350   /* Check if the IPv4 address exists on the VPN */
351
352   subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
353
354   if(!subnet)
355     {
356       if(debug_lvl >= DEBUG_TRAFFIC)
357         {
358           syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
359                  arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
360         }
361
362       return;
363     }
364
365   /* Check if it is for our own subnet */
366   
367   if(subnet->owner == myself)
368     return;     /* silently ignore */
369
370   memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* copy destination address */
371   packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF;                           /* mangle source address so it looks like it's not from us */
372
373   memcpy(ipbuf, arp->arp_tpa, 4);                                       /* save protocol addr */
374   memcpy(arp->arp_tpa, arp->arp_spa, 4);                                /* swap destination and source protocol address */
375   memcpy(arp->arp_spa, ipbuf, 4);                                       /* ... */
376
377   memcpy(arp->arp_tha, arp->arp_sha, 10);                               /* set target hard/proto addr */
378   memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* add fake source hard addr */
379   arp->arp_op = htons(ARPOP_REPLY);
380   
381   write_packet(packet);
382 cp
383 }
384
385 void route_outgoing(vpn_packet_t *packet)
386 {
387   unsigned short int type;
388   node_t *n = NULL;
389 cp
390   /* FIXME: multicast? */
391
392   switch(routing_mode)
393     {
394       case RMODE_ROUTER:
395         type = ntohs(*((unsigned short*)(&packet->data[12])));
396         switch(type)
397           {
398             case 0x0800:
399               n = route_ipv4(packet);
400               break;
401             case 0x86DD:
402               if(packet->data[20] == IPPROTO_ICMPV6 && packet->data[54] == ND_NEIGHBOR_SOLICIT)
403                 {
404                   route_neighborsol(packet);
405                   return;
406                 }
407               n = route_ipv6(packet);
408               break;
409             case 0x0806:
410               route_arp(packet);
411               return;
412             default:
413               if(debug_lvl >= DEBUG_TRAFFIC)
414                 {
415                   syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
416                 }
417               return;
418            }
419          if(n)
420            send_packet(n, packet);
421          break;
422         
423       case RMODE_SWITCH:
424         n = route_mac(packet);
425         if(n)
426           send_packet(n, packet);
427         else
428           broadcast_packet(myself, packet);
429         break;
430         
431       case RMODE_HUB:
432         broadcast_packet(myself, packet);
433         break;
434     }
435 }
436
437 void route_incoming(node_t *source, vpn_packet_t *packet)
438 {
439   switch(routing_mode)
440     {
441       case RMODE_ROUTER:
442         {
443           node_t *n = NULL;
444           unsigned short int type;
445
446           type = ntohs(*((unsigned short*)(&packet->data[12])));
447           switch(type)
448             {
449               case 0x0800:
450                 n = route_ipv4(packet);
451                 break;
452               case 0x86DD:
453                 n = route_ipv6(packet);
454                 break;
455               default:
456                 n = myself;
457                 break;
458              }
459
460           if(n)
461             {
462               if(n == myself)
463                 {
464                   memcpy(packet->data, mymac.net.mac.address.x, 6);
465                   write_packet(packet);
466                 }
467               else
468                 send_packet(n, packet);
469             }
470           }
471         break;
472       case RMODE_SWITCH:
473         {
474           subnet_t *subnet;
475
476           subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
477
478           if(subnet)
479             {
480               if(subnet->owner == myself)
481                 write_packet(packet);
482               else
483                 send_packet(subnet->owner, packet);
484             }
485           else
486             {
487               broadcast_packet(source, packet);
488               write_packet(packet);
489             }
490           }
491         break;
492       case RMODE_HUB:
493         broadcast_packet(source, packet);                       /* Spread it on */
494         write_packet(packet);
495         break;
496     }
497 }