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