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