3 Copyright (C) 2000-2002 Ivo Timmermans <itimmermans@bigfoot.com>,
4 2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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.
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.
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.
20 $Id: route.c,v 1.1.2.23 2002/02/18 16:25:19 guus Exp $
25 #if defined(HAVE_FREEBSD) || defined(HAVE_OPENBSD)
26 #include <sys/param.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #if defined(HAVE_SOLARIS) || defined(HAVE_OPENBSD)
32 #define ETHER_ADDR_LEN 6
34 #include <net/ethernet.h>
36 #include <netinet/if_ether.h>
45 #include "connection.h"
53 int routing_mode = RMODE_ROUTER;
56 void learn_mac(mac_t *address)
62 subnet = lookup_subnet_mac(address);
64 /* If we don't know this MAC address yet, store it */
66 if(!subnet || subnet->owner!=myself)
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]);
72 subnet = new_subnet();
73 subnet->type = SUBNET_MAC;
74 memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
75 subnet_add(myself, subnet);
77 /* And tell all other tinc daemons it's our MAC */
79 for(node = connection_tree->head; node; node = node->next)
81 c = (connection_t *)node->data;
83 send_add_subnet(c, subnet);
88 node_t *route_mac(vpn_packet_t *packet)
92 /* Learn source address */
94 learn_mac((mac_t *)(&packet->data[6]));
96 /* Lookup destination address */
98 subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
101 return subnet->owner;
106 node_t *route_ipv4(vpn_packet_t *packet)
110 subnet = lookup_subnet_ipv4((ipv4_t *)&packet->data[30]);
114 if(debug_lvl >= DEBUG_TRAFFIC)
116 syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
117 packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
123 return subnet->owner;
126 node_t *route_ipv6(vpn_packet_t *packet)
130 subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
134 if(debug_lvl >= DEBUG_TRAFFIC)
136 syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
137 ntohs(*(short unsigned int *)&packet->data[38]),
138 ntohs(*(short unsigned int *)&packet->data[40]),
139 ntohs(*(short unsigned int *)&packet->data[42]),
140 ntohs(*(short unsigned int *)&packet->data[44]),
141 ntohs(*(short unsigned int *)&packet->data[46]),
142 ntohs(*(short unsigned int *)&packet->data[48]),
143 ntohs(*(short unsigned int *)&packet->data[50]),
144 ntohs(*(short unsigned int *)&packet->data[52]));
150 return subnet->owner;
153 void route_arp(vpn_packet_t *packet)
155 struct ether_arp *arp;
157 unsigned char ipbuf[4];
159 /* First, snatch the source address from the ARP packet */
161 memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
163 /* This routine generates replies to ARP requests.
164 You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
165 Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
168 arp = (struct ether_arp *)(packet->data + 14);
170 /* Check if this is a valid ARP request */
172 if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
173 ntohs(arp->arp_pro) != ETHERTYPE_IP ||
174 (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
175 (int) (arp->arp_pln) != 4 ||
176 ntohs(arp->arp_op) != ARPOP_REQUEST )
178 if(debug_lvl > DEBUG_TRAFFIC)
180 syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
185 /* Check if the IP address exists on the VPN */
187 subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
191 if(debug_lvl >= DEBUG_TRAFFIC)
193 syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
194 arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
200 /* Check if it is for our own subnet */
202 if(subnet->owner == myself)
203 return; /* silently ignore */
205 memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
206 packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
208 memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
209 memcpy(arp->arp_tpa, arp->arp_spa, 4); /* swap destination and source protocol address */
210 memcpy(arp->arp_spa, ipbuf, 4); /* ... */
212 memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
213 memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* add fake source hard addr */
214 arp->arp_op = htons(ARPOP_REPLY);
216 write_packet(packet);
220 void route_outgoing(vpn_packet_t *packet)
222 unsigned short int type;
225 /* FIXME: multicast? */
230 type = ntohs(*((unsigned short*)(&packet->data[12])));
234 n = route_ipv4(packet);
237 n = route_ipv6(packet);
243 if(debug_lvl >= DEBUG_TRAFFIC)
245 syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
250 send_packet(n, packet);
254 n = route_mac(packet);
256 send_packet(n, packet);
258 broadcast_packet(myself, packet);
262 broadcast_packet(myself, packet);
267 void route_incoming(node_t *source, vpn_packet_t *packet)
272 memcpy(packet->data, mymac.net.mac.address.x, 6); /* Override destination address to make the kernel accept it */
273 write_packet(packet);
279 subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
283 if(subnet->owner == myself)
284 write_packet(packet);
286 send_packet(subnet->owner, packet);
290 broadcast_packet(source, packet);
291 write_packet(packet);
296 broadcast_packet(source, packet); /* Spread it on */
297 write_packet(packet);