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