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