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