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.35 2002/03/15 15:40:40 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/ip6.h>
37 #include <netinet/icmp6.h>
38 #include <netinet/if_ether.h>
47 #include "connection.h"
56 #define s6_addr16 __u6_addr.__u6_addr16
59 int routing_mode = RMODE_ROUTER;
60 int priorityinheritance = 0;
64 void learn_mac(mac_t *address)
70 subnet = lookup_subnet_mac(address);
72 /* If we don't know this MAC address yet, store it */
74 if(!subnet || subnet->owner!=myself)
76 if(debug_lvl >= DEBUG_TRAFFIC)
77 syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
78 address->x[0], address->x[1], address->x[2], address->x[3], address->x[4], address->x[5]);
80 subnet = new_subnet();
81 subnet->type = SUBNET_MAC;
82 memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
83 subnet_add(myself, subnet);
85 /* And tell all other tinc daemons it's our MAC */
87 for(node = connection_tree->head; node; node = node->next)
89 c = (connection_t *)node->data;
91 send_add_subnet(c, subnet);
95 subnet->net.mac.lastseen = now;
102 avl_node_t *node, *next, *node2;
104 for(node = myself->subnet_tree->head; node; node = next)
107 s = (subnet_t *)node->data;
108 if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now)
110 if(debug_lvl >= DEBUG_TRAFFIC)
111 syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
112 s->net.mac.address.x[0], s->net.mac.address.x[1], s->net.mac.address.x[2], s->net.mac.address.x[3], s->net.mac.address.x[4], s->net.mac.address.x[5]);
113 for(node2 = connection_tree->head; node2; node2 = node2->next)
115 c = (connection_t *)node2->data;
117 send_del_subnet(c, s);
119 subnet_del(myself, s);
125 node_t *route_mac(vpn_packet_t *packet)
129 /* Learn source address */
131 learn_mac((mac_t *)(&packet->data[6]));
133 /* Lookup destination address */
135 subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
138 return subnet->owner;
143 node_t *route_ipv4(vpn_packet_t *packet)
147 if(priorityinheritance)
148 packet->priority = packet->data[15];
150 subnet = lookup_subnet_ipv4((ipv4_t *)&packet->data[30]);
154 if(debug_lvl >= DEBUG_TRAFFIC)
156 syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
157 packet->data[30], packet->data[31], packet->data[32], packet->data[33]);
163 return subnet->owner;
166 node_t *route_ipv6(vpn_packet_t *packet)
170 subnet = lookup_subnet_ipv6((ipv6_t *)&packet->data[38]);
174 if(debug_lvl >= DEBUG_TRAFFIC)
176 syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
177 ntohs(*(short unsigned int *)&packet->data[38]),
178 ntohs(*(short unsigned int *)&packet->data[40]),
179 ntohs(*(short unsigned int *)&packet->data[42]),
180 ntohs(*(short unsigned int *)&packet->data[44]),
181 ntohs(*(short unsigned int *)&packet->data[46]),
182 ntohs(*(short unsigned int *)&packet->data[48]),
183 ntohs(*(short unsigned int *)&packet->data[50]),
184 ntohs(*(short unsigned int *)&packet->data[52]));
190 return subnet->owner;
193 unsigned short int inet_checksum(unsigned short int *data, int len, unsigned short int prevsum)
195 unsigned long int checksum = prevsum ^ 0xFFFF;
198 checksum += ntohs(*data++);
200 while(checksum >> 16)
201 checksum = (checksum & 0xFFFF) + (checksum >> 16);
203 return checksum ^ 0xFFFF;
206 void route_neighborsol(vpn_packet_t *packet)
209 struct nd_neighbor_solicit *ns;
210 struct nd_opt_hdr *opt;
212 short unsigned int checksum;
215 struct in6_addr ip6_src; /* source address */
216 struct in6_addr ip6_dst; /* destination address */
222 hdr = (struct ip6_hdr *)(packet->data + 14);
223 ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
224 opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
226 /* First, snatch the source address from the neighbor solicitation packet */
228 memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
230 /* Check if this is a valid neighbor solicitation request */
232 if(ns->nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
233 opt->nd_opt_type != ND_OPT_SOURCE_LINKADDR)
235 if(debug_lvl > DEBUG_TRAFFIC)
237 syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
242 /* Create pseudo header */
244 memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
245 memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
246 pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
247 pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
248 pseudo.junk[3] = IPPROTO_ICMPV6;
250 /* Generate checksum */
252 checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
253 checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
257 if(debug_lvl >= DEBUG_TRAFFIC)
258 syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
262 /* Check if the IPv6 address exists on the VPN */
264 subnet = lookup_subnet_ipv6((ipv6_t *)&ns->nd_ns_target);
268 if(debug_lvl >= DEBUG_TRAFFIC)
270 syslog(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
271 ntohs(ns->nd_ns_target.s6_addr16[0]), ntohs(ns->nd_ns_target.s6_addr16[1]), ntohs(ns->nd_ns_target.s6_addr16[2]), ntohs(ns->nd_ns_target.s6_addr16[3]),
272 ntohs(ns->nd_ns_target.s6_addr16[4]), ntohs(ns->nd_ns_target.s6_addr16[5]), ntohs(ns->nd_ns_target.s6_addr16[6]), ntohs(ns->nd_ns_target.s6_addr16[7]));
278 /* Check if it is for our own subnet */
280 if(subnet->owner == myself)
281 return; /* silently ignore */
283 /* Create neighbor advertation reply */
285 memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
286 packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
288 memcpy(&hdr->ip6_dst, &hdr->ip6_src, 16); /* swap destination and source protocol address */
289 memcpy(&hdr->ip6_src, &ns->nd_ns_target, 16); /* ... */
291 memcpy((char *)opt + sizeof(*opt), packet->data + ETHER_ADDR_LEN, 6); /* add fake source hard addr */
293 ns->nd_ns_hdr.icmp6_cksum = 0;
294 ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_ADVERT;
295 ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[0] = 0x40; /* Set solicited flag */
296 ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[1] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[2] = ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[3] = 0;
297 opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
299 /* Create pseudo header */
301 memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
302 memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
303 pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
304 pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
305 pseudo.junk[3] = IPPROTO_ICMPV6;
307 /* Generate checksum */
309 checksum = inet_checksum((unsigned short int *)&pseudo, sizeof(pseudo)/2, ~0);
310 checksum = inet_checksum((unsigned short int *)ns, sizeof(*ns)/2 + 4, checksum);
312 ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
314 write_packet(packet);
318 void route_arp(vpn_packet_t *packet)
320 struct ether_arp *arp;
322 unsigned char ipbuf[4];
324 /* First, snatch the source address from the ARP packet */
326 memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
328 /* This routine generates replies to ARP requests.
329 You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
330 Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
333 arp = (struct ether_arp *)(packet->data + 14);
335 /* Check if this is a valid ARP request */
337 if(ntohs(arp->arp_hrd) != ARPHRD_ETHER ||
338 ntohs(arp->arp_pro) != ETHERTYPE_IP ||
339 (int) (arp->arp_hln) != ETHER_ADDR_LEN ||
340 (int) (arp->arp_pln) != 4 ||
341 ntohs(arp->arp_op) != ARPOP_REQUEST )
343 if(debug_lvl > DEBUG_TRAFFIC)
345 syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
350 /* Check if the IPv4 address exists on the VPN */
352 subnet = lookup_subnet_ipv4((ipv4_t *)arp->arp_tpa);
356 if(debug_lvl >= DEBUG_TRAFFIC)
358 syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
359 arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2], arp->arp_tpa[3]);
365 /* Check if it is for our own subnet */
367 if(subnet->owner == myself)
368 return; /* silently ignore */
370 memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
371 packet->data[ETHER_ADDR_LEN*2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
373 memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
374 memcpy(arp->arp_tpa, arp->arp_spa, 4); /* swap destination and source protocol address */
375 memcpy(arp->arp_spa, ipbuf, 4); /* ... */
377 memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
378 memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* add fake source hard addr */
379 arp->arp_op = htons(ARPOP_REPLY);
381 write_packet(packet);
385 void route_outgoing(vpn_packet_t *packet)
387 unsigned short int type;
390 /* FIXME: multicast? */
395 type = ntohs(*((unsigned short*)(&packet->data[12])));
399 n = route_ipv4(packet);
402 if(packet->data[20] == IPPROTO_ICMPV6 && packet->data[54] = ND_NEIGHBOR_SOLICIT)
404 route_neighborsol(packet);
407 n = route_ipv6(packet);
413 if(debug_lvl >= DEBUG_TRAFFIC)
415 syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
420 send_packet(n, packet);
424 n = route_mac(packet);
426 send_packet(n, packet);
428 broadcast_packet(myself, packet);
432 broadcast_packet(myself, packet);
437 void route_incoming(node_t *source, vpn_packet_t *packet)
444 unsigned short int type;
446 type = ntohs(*((unsigned short*)(&packet->data[12])));
450 n = route_ipv4(packet);
453 n = route_ipv6(packet);
464 memcpy(packet->data, mymac.net.mac.address.x, 6);
465 write_packet(packet);
468 send_packet(n, packet);
476 subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
480 if(subnet->owner == myself)
481 write_packet(packet);
483 send_packet(subnet->owner, packet);
487 broadcast_packet(source, packet);
488 write_packet(packet);
493 broadcast_packet(source, packet); /* Spread it on */
494 write_packet(packet);