Moving files, first attempt at gcrypt compatibility, more interface
[tinc] / src / pokey / 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 2002/04/28 12:46:26 zarq 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 <string.h>
42
43 #include <avl_tree.h>
44
45 #include "net.h"
46 #include "interface.h"
47 #include "connection.h"
48 #include "subnet.h"
49 #include "route.h"
50 #include "protocol.h"
51 #include "device.h"
52 #include "logging.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 cp
311 }
312
313 void route_arp(vpn_packet_t *packet)
314 {
315   struct ether_arp *arp;
316   subnet_t *subnet;
317   unsigned char ipbuf[4];
318 cp
319   /* First, snatch the source address from the ARP packet */
320
321   memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
322
323   /* This routine generates replies to ARP requests.
324      You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
325      Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
326    */
327
328   arp = (struct ether_arp *)(packet->data + 14);
329
330   /* Check if this is a valid ARP request */
331
332   if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
333      ntohs(arp->arp_pro) != ETHERTYPE_IP ||
334      (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
335      (int) (arp->arp_pln) != 4 ||
336      ntohs(arp->arp_op) != ARPOP_REQUEST )
337     {
338       if(debug_lvl > DEBUG_TRAFFIC)
339         {
340           syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
341         } 
342       return;
343     }
344
345   /* Check if the IPv4 address exists on the VPN */
346
347   subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
348
349   if(!subnet)
350     {
351       if(debug_lvl >= DEBUG_TRAFFIC)
352         {
353           syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
354                  arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
355         }
356
357       return;
358     }
359
360   /* Check if it is for our own subnet */
361   
362   if(subnet->owner == myself)
363     return;     /* silently ignore */
364
365   memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* copy destination address */
366   packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF;                           /* mangle source address so it looks like it's not from us */
367
368   memcpy(ipbuf, arp->arp_tpa, 4);                                       /* save protocol addr */
369   memcpy(arp->arp_tpa, arp->arp_spa, 4);                                /* swap destination and source protocol address */
370   memcpy(arp->arp_spa, ipbuf, 4);                                       /* ... */
371
372   memcpy(arp->arp_tha, arp->arp_sha, 10);                               /* set target hard/proto addr */
373   memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* add fake source hard addr */
374   arp->arp_op = htons(ARPOP_REPLY);
375 cp
376 }
377