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