0e16565bbde40a9f115385647dd6a57cbe2e1d05
[oweals/tinc.git] / 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.22 2002/02/10 21:57:54 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/if_ether.h>
37 #include <utils.h>
38 #include <xalloc.h>
39 #include <syslog.h>
40 #include <string.h>
41
42 #include <avl_tree.h>
43
44 #include "net.h"
45 #include "connection.h"
46 #include "subnet.h"
47 #include "route.h"
48 #include "protocol.h"
49 #include "device.h"
50
51 #include "system.h"
52
53 int routing_mode = RMODE_ROUTER;
54 subnet_t mymac;
55
56 void learn_mac(mac_t *address)
57 {
58   subnet_t *subnet;
59   avl_node_t *node;
60   connection_t *c;
61 cp
62   subnet = lookup_subnet_mac(address);
63
64   /* If we don't know this MAC address yet, store it */
65   
66   if(!subnet || subnet->owner!=myself)
67     {
68       if(debug_lvl >= DEBUG_TRAFFIC)
69         syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
70                address->x[0], address->x[1], address->x[2], address->x[3],  address->x[4], address->x[5]);
71                
72       subnet = new_subnet();
73       subnet->type = SUBNET_MAC;
74       memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
75       subnet_add(myself, subnet);
76
77       /* And tell all other tinc daemons it's our MAC */
78       
79       for(node = connection_tree->head; node; node = node->next)
80         {
81           c = (connection_t *)node->data;
82           if(c->status.active)
83             send_add_subnet(c, subnet);
84         }
85     }
86 }
87
88 node_t *route_mac(vpn_packet_t *packet)
89 {
90   subnet_t *subnet;
91 cp
92   /* Learn source address */
93
94   learn_mac((mac_t *)(&packet->data[6]));
95   
96   /* Lookup destination address */
97     
98   subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
99
100   if(subnet)
101     return subnet->owner;
102   else
103     return NULL;
104 }
105
106 node_t *route_ipv4(vpn_packet_t *packet)
107 {
108   ipv4_t dest;
109   subnet_t *subnet;
110 cp
111 #ifdef HAVE_SOLARIS
112   /* The other form gives bus errors on a SparcStation 20. */
113   dest = ((packet->data[30] * 0x100 + packet->data[31]) * 0x100 + packet->data[32]) * 0x100 + packet->data[33];
114 #else
115   dest = ntohl(*((unsigned long*)(&packet->data[30])));
116 #endif
117 cp  
118   subnet = lookup_subnet_ipv4(&dest);
119 cp
120   if(!subnet)
121     {
122       if(debug_lvl >= DEBUG_TRAFFIC)
123         {
124           syslog(LOG_WARNING, _("Cannot route packet: unknown destination address %d.%d.%d.%d"),
125                  packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
126         }
127
128       return NULL;
129     }
130 cp
131   return subnet->owner;  
132 }
133
134 node_t *route_ipv6(vpn_packet_t *packet)
135 {
136   subnet_t *subnet;
137 cp
138   subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
139 cp
140   if(!subnet)
141     {
142       if(debug_lvl >= DEBUG_TRAFFIC)
143         {
144           syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
145             ntohs(*(short unsigned int *)&packet->data[38]),
146             ntohs(*(short unsigned int *)&packet->data[40]),
147             ntohs(*(short unsigned int *)&packet->data[42]),
148             ntohs(*(short unsigned int *)&packet->data[44]),
149             ntohs(*(short unsigned int *)&packet->data[46]),
150             ntohs(*(short unsigned int *)&packet->data[48]),
151             ntohs(*(short unsigned int *)&packet->data[50]),
152             ntohs(*(short unsigned int *)&packet->data[52]));
153         }
154
155       return NULL;
156     }
157 cp
158   return subnet->owner;  
159 }
160
161 void route_arp(vpn_packet_t *packet)
162 {
163   struct ether_arp *arp;
164   subnet_t *subnet;
165   unsigned char ipbuf[4];
166   ipv4_t dest;
167 cp
168   /* First, snatch the source address from the ARP packet */
169
170   memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
171
172   /* This routine generates replies to ARP requests.
173      You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
174      Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
175    */
176
177   arp = (struct ether_arp *)(packet->data + 14);
178
179   /* Check if this is a valid ARP request */
180
181   if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
182      ntohs(arp->arp_pro) != ETHERTYPE_IP ||
183      (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
184      (int) (arp->arp_pln) != 4 ||
185      ntohs(arp->arp_op) != ARPOP_REQUEST )
186     {
187       if(debug_lvl > DEBUG_TRAFFIC)
188         {
189           syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
190         } 
191       return;
192     }
193
194   /* Check if the IP address exists on the VPN */
195
196   dest = ntohl(*((unsigned long*)(arp->arp_tpa)));
197   subnet = lookup_subnet_ipv4(&dest);
198
199   if(!subnet)
200     {
201       if(debug_lvl >= DEBUG_TRAFFIC)
202         {
203           syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
204                  arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
205         }
206
207       return;
208     }
209
210   /* Check if it is for our own subnet */
211   
212   if(subnet->owner == myself)
213     return;     /* silently ignore */
214
215   memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* copy destination address */
216   packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF;                           /* mangle source address so it looks like it's not from us */
217
218   memcpy(ipbuf, arp->arp_tpa, 4);                                       /* save protocol addr */
219   memcpy(arp->arp_tpa, arp->arp_spa, 4);                                /* swap destination and source protocol address */
220   memcpy(arp->arp_spa, ipbuf, 4);                                       /* ... */
221
222   memcpy(arp->arp_tha, arp->arp_sha, 10);                               /* set target hard/proto addr */
223   memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* add fake source hard addr */
224   arp->arp_op = htons(ARPOP_REPLY);
225   
226   write_packet(packet);
227 cp
228 }
229
230 void route_outgoing(vpn_packet_t *packet)
231 {
232   unsigned short int type;
233   node_t *n;
234 cp
235   /* FIXME: multicast? */
236
237   switch(routing_mode)
238     {
239       case RMODE_ROUTER:
240         type = ntohs(*((unsigned short*)(&packet->data[12])));
241         switch(type)
242           {
243             case 0x0800:
244               n = route_ipv4(packet);
245               break;
246             case 0x86DD:
247               n = route_ipv6(packet);
248               break;
249             case 0x0806:
250               route_arp(packet);
251               return;
252             default:
253               if(debug_lvl >= DEBUG_TRAFFIC)
254                 {
255                   syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
256                 }
257               return;
258            }
259          if(n)
260            send_packet(n, packet);
261          break;
262         
263       case RMODE_SWITCH:
264         n = route_mac(packet);
265         if(n)
266           send_packet(n, packet);
267         else
268           broadcast_packet(myself, packet);
269         break;
270         
271       case RMODE_HUB:
272         broadcast_packet(myself, packet);
273         break;
274     }
275 }
276
277 void route_incoming(node_t *source, vpn_packet_t *packet)
278 {
279   switch(routing_mode)
280     {
281       case RMODE_ROUTER:
282         memcpy(packet->data, mymac.net.mac.address.x, 6);       /* Override destination address to make the kernel accept it */
283         write_packet(packet);
284         break;
285       case RMODE_SWITCH:
286         {
287           subnet_t *subnet;
288
289           subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
290
291           if(subnet)
292             {
293               if(subnet->owner == myself)
294                 write_packet(packet);
295               else
296                 send_packet(subnet->owner, packet);
297             }
298           else
299             {
300               broadcast_packet(source, packet);
301               write_packet(packet);
302             }
303           }
304         break;
305       case RMODE_HUB:
306         broadcast_packet(source, packet);                       /* Spread it on */
307         write_packet(packet);
308         break;
309     }
310 }