- tinc can now act as a switch or a hub too (as opposed to a router only)
[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.11 2001/06/05 16:09:55 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   /* This routine generates replies to ARP requests.
136      You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
137      Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
138    */
139
140   arp = (struct ether_arp *)(packet->data + 14);
141
142   /* Check if this is a valid ARP request */
143
144   if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
145      ntohs(arp->arp_pro) != ETHERTYPE_IP ||
146      (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
147      (int) (arp->arp_pln) != 4 ||
148      ntohs(arp->arp_op) != ARPOP_REQUEST )
149     {
150       if(debug_lvl > DEBUG_TRAFFIC)
151         {
152           syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
153         } 
154       return;
155     }
156
157   /* Check if the IP address exists on the VPN */
158
159   dest = ntohl(*((unsigned long*)(arp->arp_tpa)));
160   subnet = lookup_subnet_ipv4(&dest);
161
162   if(!subnet)
163     {
164       if(debug_lvl >= DEBUG_TRAFFIC)
165         {
166           syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
167                  arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
168         }
169
170       return;
171     }
172
173   /* Check if it is for our own subnet */
174   
175   if(subnet->owner == myself)
176     return;     /* silently ignore */
177
178   memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* copy destination address */
179   packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF;                           /* mangle source address so it looks like it's not from us */
180
181   memcpy(ipbuf, arp->arp_tpa, 4);                                       /* save protocol addr */
182   memcpy(arp->arp_tpa, arp->arp_spa, 4);                                /* swap destination and source protocol address */
183   memcpy(arp->arp_spa, ipbuf, 4);                                       /* ... */
184
185   memcpy(arp->arp_tha, arp->arp_sha, 10);                               /* set target hard/proto addr */
186   memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN);  /* add fake source hard addr */
187   arp->arp_op = htons(ARPOP_REPLY);
188   
189   accept_packet(packet);
190 cp
191 }
192
193 void route_outgoing(vpn_packet_t *packet)
194 {
195   unsigned short int type;
196   connection_t *cl;
197 cp
198   /* FIXME: multicast? */
199
200   switch(routing_mode)
201     {
202       case RMODE_ROUTER:
203         type = ntohs(*((unsigned short*)(&packet->data[12])));
204         switch(type)
205           {
206             case 0x0800:
207               cl = route_ipv4(packet);
208               break;
209             case 0x86DD:
210               cl = route_ipv6(packet);
211               break;
212             case 0x0806:
213               route_arp(packet);
214               return;
215             default:
216               if(debug_lvl >= DEBUG_TRAFFIC)
217                 {
218                   syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
219                 }
220               return;
221            }
222          if(cl)
223            send_packet(cl, packet);
224          break;
225         
226       case RMODE_SWITCH:
227         cl = route_mac(myself, packet);
228         if(cl)
229           send_packet(cl, packet);
230         else
231           broadcast_packet(myself, packet);
232         break;
233         
234       case RMODE_HUB:
235         broadcast_packet(myself, packet);
236         break;
237     }
238 }
239
240 void route_incoming(connection_t *source, vpn_packet_t *packet)
241 {
242   switch(routing_mode)
243     {
244       case RMODE_ROUTER:
245         memcpy(packet->data, mymac.net.mac.address.x, 6);       /* Override destination address to make the kernel accept it */
246         break;
247       case RMODE_SWITCH:
248         if((packet->data[0] & packet->data[1]) == 0xFF)         /* Broadcast? */
249           broadcast_packet(source, packet);                     /* If yes, spread it on */
250         else
251           learn_mac(source, (mac_t *)(&packet->data[6]));
252         break;
253       case RMODE_HUB:
254         broadcast_packet(source,packet);                        /* Spread it on */
255         break;
256     }
257   
258   accept_packet(packet);
259 }