3 Copyright (C) 2000-2002 Ivo Timmermans <ivo@o2w.nl>,
4 2000-2002 Guus Sliepen <guus@sliepen.eu.org>
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.46 2002/09/09 22:33:16 guus Exp $
25 #ifdef HAVE_SYS_PARAM_H
26 #include <sys/param.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
33 #ifdef HAVE_NET_ETHERNET_H
34 #include <net/ethernet.h>
36 #ifdef HAVE_NETINET_IN_SYSTM_H
37 #include <netinet/in_systm.h>
39 #include <netinet/ip6.h>
40 #include <netinet/icmp6.h>
41 #include <netinet/if_ether.h>
46 #ifdef HAVE_INTTYPES_H
53 #include "connection.h"
61 #ifndef ETHER_ADDR_LEN
62 #define ETHER_ADDR_LEN 6
65 int routing_mode = RMODE_ROUTER;
66 int priorityinheritance = 0;
70 void learn_mac(mac_t *address)
78 subnet = lookup_subnet_mac(address);
80 /* If we don't know this MAC address yet, store it */
82 if(!subnet || subnet->owner != myself) {
83 if(debug_lvl >= DEBUG_TRAFFIC)
84 syslog(LOG_INFO, _("Learned new MAC address %hx:%hx:%hx:%hx:%hx:%hx"),
85 address->x[0], address->x[1], address->x[2], address->x[3],
86 address->x[4], address->x[5]);
88 subnet = new_subnet();
89 subnet->type = SUBNET_MAC;
90 memcpy(&subnet->net.mac.address, address, sizeof(mac_t));
91 subnet_add(myself, subnet);
93 /* And tell all other tinc daemons it's our MAC */
95 for(node = connection_tree->head; node; node = node->next) {
96 c = (connection_t *) node->data;
98 send_add_subnet(c, subnet);
102 subnet->net.mac.lastseen = now;
109 avl_node_t *node, *next, *node2;
113 for(node = myself->subnet_tree->head; node; node = next) {
115 s = (subnet_t *) node->data;
116 if(s->type == SUBNET_MAC && s->net.mac.lastseen && s->net.mac.lastseen + macexpire < now) {
117 if(debug_lvl >= DEBUG_TRAFFIC)
118 syslog(LOG_INFO, _("MAC address %hx:%hx:%hx:%hx:%hx:%hx expired"),
119 s->net.mac.address.x[0], s->net.mac.address.x[1],
120 s->net.mac.address.x[2], s->net.mac.address.x[3],
121 s->net.mac.address.x[4], s->net.mac.address.x[5]);
123 for(node2 = connection_tree->head; node2; node2 = node2->next) {
124 c = (connection_t *) node2->data;
126 send_del_subnet(c, s);
129 subnet_del(myself, s);
134 node_t *route_mac(vpn_packet_t *packet)
140 /* Learn source address */
142 learn_mac((mac_t *)(&packet->data[6]));
144 /* Lookup destination address */
146 subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
149 return subnet->owner;
154 node_t *route_ipv4(vpn_packet_t *packet)
160 if(priorityinheritance)
161 packet->priority = packet->data[15];
163 subnet = lookup_subnet_ipv4((ipv4_t *) & packet->data[30]);
166 if(debug_lvl >= DEBUG_TRAFFIC) {
167 syslog(LOG_WARNING, _("Cannot route packet: unknown IPv4 destination address %d.%d.%d.%d"),
168 packet->data[30], packet->data[31], packet->data[32],
175 return subnet->owner;
178 node_t *route_ipv6(vpn_packet_t *packet)
184 subnet = lookup_subnet_ipv6((ipv6_t *) & packet->data[38]);
187 if(debug_lvl >= DEBUG_TRAFFIC) {
188 syslog(LOG_WARNING, _("Cannot route packet: unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
189 ntohs(*(uint16_t *) & packet->data[38]),
190 ntohs(*(uint16_t *) & packet->data[40]),
191 ntohs(*(uint16_t *) & packet->data[42]),
192 ntohs(*(uint16_t *) & packet->data[44]),
193 ntohs(*(uint16_t *) & packet->data[46]),
194 ntohs(*(uint16_t *) & packet->data[48]),
195 ntohs(*(uint16_t *) & packet->data[50]),
196 ntohs(*(uint16_t *) & packet->data[52]));
202 return subnet->owner;
205 uint16_t inet_checksum(uint16_t *data, int len, uint16_t prevsum)
207 uint32_t checksum = prevsum ^ 0xFFFF;
210 checksum += ntohs(*data++);
212 while(checksum >> 16)
213 checksum = (checksum & 0xFFFF) + (checksum >> 16);
215 return checksum ^ 0xFFFF;
218 void route_neighborsol(vpn_packet_t *packet)
221 struct nd_neighbor_solicit *ns;
222 struct nd_opt_hdr *opt;
227 struct in6_addr ip6_src; /* source address */
228 struct in6_addr ip6_dst; /* destination address */
235 hdr = (struct ip6_hdr *)(packet->data + 14);
236 ns = (struct nd_neighbor_solicit *)(packet->data + 14 + sizeof(*hdr));
237 opt = (struct nd_opt_hdr *)(packet->data + 14 + sizeof(*hdr) + sizeof(*ns));
239 /* First, snatch the source address from the neighbor solicitation packet */
241 memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
243 /* Check if this is a valid neighbor solicitation request */
245 if(ns->nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
246 opt->nd_opt_type != ND_OPT_SOURCE_LINKADDR) {
247 if(debug_lvl > DEBUG_TRAFFIC) {
248 syslog(LOG_WARNING, _("Cannot route packet: received unknown type neighbor solicitation request"));
253 /* Create pseudo header */
255 memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
256 memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
257 pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
258 pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
259 pseudo.junk[3] = IPPROTO_ICMPV6;
261 /* Generate checksum */
263 checksum = inet_checksum((uint16_t *) & pseudo, sizeof(pseudo) / 2, ~0);
264 checksum = inet_checksum((uint16_t *) ns, sizeof(*ns) / 2 + 4, checksum);
267 if(debug_lvl >= DEBUG_TRAFFIC)
268 syslog(LOG_WARNING, _("Cannot route packet: checksum error for neighbor solicitation request"));
272 /* Check if the IPv6 address exists on the VPN */
274 subnet = lookup_subnet_ipv6((ipv6_t *) & ns->nd_ns_target);
277 if(debug_lvl >= DEBUG_TRAFFIC) {
278 syslog(LOG_WARNING, _("Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx"),
279 ntohs(((uint16_t *) & ns->nd_ns_target)[0]),
280 ntohs(((uint16_t *) & ns->nd_ns_target)[1]),
281 ntohs(((uint16_t *) & ns->nd_ns_target)[2]),
282 ntohs(((uint16_t *) & ns->nd_ns_target)[3]),
283 ntohs(((uint16_t *) & ns->nd_ns_target)[4]),
284 ntohs(((uint16_t *) & ns->nd_ns_target)[5]),
285 ntohs(((uint16_t *) & ns->nd_ns_target)[6]),
286 ntohs(((uint16_t *) & ns->nd_ns_target)[7]));
292 /* Check if it is for our own subnet */
294 if(subnet->owner == myself)
295 return; /* silently ignore */
297 /* Create neighbor advertation reply */
299 memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
300 packet->data[ETHER_ADDR_LEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
302 memcpy(&hdr->ip6_dst, &hdr->ip6_src, 16); /* swap destination and source protocol address */
303 memcpy(&hdr->ip6_src, &ns->nd_ns_target, 16); /* ... */
305 memcpy((char *) opt + sizeof(*opt), packet->data + ETHER_ADDR_LEN, 6); /* add fake source hard addr */
307 ns->nd_ns_hdr.icmp6_cksum = 0;
308 ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_ADVERT;
309 ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[0] = 0x40; /* Set solicited flag */
310 ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[1] =
311 ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[2] =
312 ns->nd_ns_hdr.icmp6_dataun.icmp6_un_data8[3] = 0;
313 opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
315 /* Create pseudo header */
317 memcpy(&pseudo.ip6_src, &hdr->ip6_src, 16);
318 memcpy(&pseudo.ip6_dst, &hdr->ip6_dst, 16);
319 pseudo.length = htonl(sizeof(*ns) + sizeof(*opt) + 6);
320 pseudo.junk[0] = pseudo.junk[1] = pseudo.junk[2] = 0;
321 pseudo.junk[3] = IPPROTO_ICMPV6;
323 /* Generate checksum */
325 checksum = inet_checksum((uint16_t *) & pseudo, sizeof(pseudo) / 2, ~0);
326 checksum = inet_checksum((uint16_t *) ns, sizeof(*ns) / 2 + 4, checksum);
328 ns->nd_ns_hdr.icmp6_cksum = htons(checksum);
330 write_packet(packet);
333 void route_arp(vpn_packet_t *packet)
335 struct ether_arp *arp;
341 /* First, snatch the source address from the ARP packet */
343 memcpy(mymac.net.mac.address.x, packet->data + 6, 6);
345 /* This routine generates replies to ARP requests.
346 You don't need to set NOARP flag on the interface anymore (which is broken on FreeBSD).
347 Most of the code here is taken from choparp.c by Takamichi Tateoka (tree@mma.club.uec.ac.jp)
350 arp = (struct ether_arp *)(packet->data + 14);
352 /* Check if this is a valid ARP request */
354 if(ntohs(arp->arp_hrd) != ARPHRD_ETHER || ntohs(arp->arp_pro) != ETHERTYPE_IP ||
355 arp->arp_hln != ETHER_ADDR_LEN || arp->arp_pln != 4 || ntohs(arp->arp_op) != ARPOP_REQUEST) {
356 if(debug_lvl > DEBUG_TRAFFIC) {
357 syslog(LOG_WARNING, _("Cannot route packet: received unknown type ARP request"));
362 /* Check if the IPv4 address exists on the VPN */
364 subnet = lookup_subnet_ipv4((ipv4_t *) arp->arp_tpa);
367 if(debug_lvl >= DEBUG_TRAFFIC) {
368 syslog(LOG_WARNING, _("Cannot route packet: ARP request for unknown address %d.%d.%d.%d"),
369 arp->arp_tpa[0], arp->arp_tpa[1], arp->arp_tpa[2],
376 /* Check if it is for our own subnet */
378 if(subnet->owner == myself)
379 return; /* silently ignore */
381 memcpy(packet->data, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* copy destination address */
382 packet->data[ETHER_ADDR_LEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
384 memcpy(ipbuf, arp->arp_tpa, 4); /* save protocol addr */
385 memcpy(arp->arp_tpa, arp->arp_spa, 4); /* swap destination and source protocol address */
386 memcpy(arp->arp_spa, ipbuf, 4); /* ... */
388 memcpy(arp->arp_tha, arp->arp_sha, 10); /* set target hard/proto addr */
389 memcpy(arp->arp_sha, packet->data + ETHER_ADDR_LEN, ETHER_ADDR_LEN); /* add fake source hard addr */
390 arp->arp_op = htons(ARPOP_REPLY);
392 write_packet(packet);
395 void route_outgoing(vpn_packet_t *packet)
402 /* FIXME: multicast? */
404 switch (routing_mode) {
406 type = ntohs(*((uint16_t *)(&packet->data[12])));
409 n = route_ipv4(packet);
413 if(packet->data[20] == IPPROTO_ICMPV6 && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
414 route_neighborsol(packet);
417 n = route_ipv6(packet);
425 if(debug_lvl >= DEBUG_TRAFFIC)
426 syslog(LOG_WARNING, _("Cannot route packet: unknown type %hx"), type);
430 send_packet(n, packet);
434 n = route_mac(packet);
436 send_packet(n, packet);
438 broadcast_packet(myself, packet);
442 broadcast_packet(myself, packet);
447 void route_incoming(node_t *source, vpn_packet_t *packet)
449 switch (routing_mode) {
455 type = ntohs(*((uint16_t *)(&packet->data[12])));
458 n = route_ipv4(packet);
462 n = route_ipv6(packet);
472 memcpy(packet->data, mymac.net.mac.address.x, 6);
473 write_packet(packet);
475 send_packet(n, packet);
484 subnet = lookup_subnet_mac((mac_t *)(&packet->data[0]));
487 if(subnet->owner == myself)
488 write_packet(packet);
490 send_packet(subnet->owner, packet);
492 broadcast_packet(source, packet);
493 write_packet(packet);
499 broadcast_packet(source, packet); /* Spread it on */
500 write_packet(packet);