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