- return;
- }
-
- send_packet(subnet->owner, packet);
-}
-
-static void route_ipv4(node_t *source, vpn_packet_t *packet) {
- if(!checklength(source, packet, ether_size + ip_size))
- return;
-
- if(((packet->data[30] & 0xf0) == 0xe0) || (
- packet->data[30] == 255 &&
- packet->data[31] == 255 &&
- packet->data[32] == 255 &&
- packet->data[33] == 255))
- broadcast_packet(source, packet);
- else
- route_ipv4_unicast(source, packet);
-}
-
-/* RFC 2463 */
-
-static void route_ipv6_unreachable(node_t *source, vpn_packet_t *packet, uint8_t type, uint8_t code) {
- struct ip6_hdr ip6;
- struct icmp6_hdr icmp6 = {0};
- uint16_t checksum;
-
- struct {
- struct in6_addr ip6_src; /* source address */
- struct in6_addr ip6_dst; /* destination address */
- uint32_t length;
- uint32_t next;
- } pseudo;
-
- if(ratelimit(3))
- return;
-
- /* Swap Ethernet source and destination addresses */
-
- swap_mac_addresses(packet);
-
- /* Copy headers from packet to structs on the stack */
-
- memcpy(&ip6, packet->data + ether_size, ip6_size);
-
- /* Remember original source and destination */
-
- pseudo.ip6_src = ip6.ip6_dst;
- pseudo.ip6_dst = ip6.ip6_src;
-
- pseudo.length = packet->len - ether_size;
-
- if(type == ICMP6_PACKET_TOO_BIG)
- icmp6.icmp6_mtu = htonl(pseudo.length);
-
- if(pseudo.length >= IP_MSS - ip6_size - icmp6_size)
- pseudo.length = IP_MSS - ip6_size - icmp6_size;
-
- /* Copy first part of original contents to ICMP message */
-
- memmove(packet->data + ether_size + ip6_size + icmp6_size, packet->data + ether_size, pseudo.length);
-
- /* Fill in IPv6 header */
-
- ip6.ip6_flow = htonl(0x60000000UL);
- ip6.ip6_plen = htons(icmp6_size + pseudo.length);
- ip6.ip6_nxt = IPPROTO_ICMPV6;
- ip6.ip6_hlim = 255;
- ip6.ip6_src = pseudo.ip6_src;
- ip6.ip6_dst = pseudo.ip6_dst;
-
- /* Fill in ICMP header */
-
- icmp6.icmp6_type = type;
- icmp6.icmp6_code = code;
- icmp6.icmp6_cksum = 0;
-
- /* Create pseudo header */
-
- pseudo.length = htonl(icmp6_size + pseudo.length);
- pseudo.next = htonl(IPPROTO_ICMPV6);
-
- /* Generate checksum */
-
- checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
- checksum = inet_checksum(&icmp6, icmp6_size, checksum);
- checksum = inet_checksum(packet->data + ether_size + ip6_size + icmp6_size, ntohl(pseudo.length) - icmp6_size, checksum);
-
- icmp6.icmp6_cksum = checksum;
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &ip6, ip6_size);
- memcpy(packet->data + ether_size + ip6_size, &icmp6, icmp6_size);
-
- packet->len = ether_size + ip6_size + ntohl(pseudo.length);
-
- send_packet(source, packet);
-}
-
-static void route_ipv6_unicast(node_t *source, vpn_packet_t *packet) {
- subnet_t *subnet;
- node_t *via;
- ipv6_t dest;
-
- memcpy(&dest, &packet->data[38], sizeof dest);
- subnet = lookup_subnet_ipv6(&dest);
-
- if(!subnet) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet from %s (%s): unknown IPv6 destination address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
- source->name, source->hostname,
- ntohs(dest.x[0]),
- ntohs(dest.x[1]),
- ntohs(dest.x[2]),
- ntohs(dest.x[3]),
- ntohs(dest.x[4]),
- ntohs(dest.x[5]),
- ntohs(dest.x[6]),
- ntohs(dest.x[7]));
-
- route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
- return;
- }
-
- if(subnet->owner == source) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(!subnet->owner->status.reachable)
- route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
-
- via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
-
- if(via && packet->len > max(via->mtu, 1294) && via != myself) {
- ifdebug(TRAFFIC) logger(LOG_INFO, "Packet for %s (%s) length %d larger than MTU %d", subnet->owner->name, subnet->owner->hostname, packet->len, via->mtu);
- packet->len = max(via->mtu, 1294);
- route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0);
- return;
- }
-
- send_packet(subnet->owner, packet);
-}
-
-/* RFC 2461 */
-
-static void route_neighborsol(node_t *source, vpn_packet_t *packet) {
- struct ip6_hdr ip6;
- struct nd_neighbor_solicit ns;
- struct nd_opt_hdr opt;
- subnet_t *subnet;
- uint16_t checksum;
- bool has_opt;
-
- struct {
- struct in6_addr ip6_src; /* source address */
- struct in6_addr ip6_dst; /* destination address */
- uint32_t length;
- uint32_t next;
- } pseudo;
-
- if(!checklength(source, packet, ether_size + ip6_size + ns_size))
- return;
-
- has_opt = packet->len >= ether_size + ip6_size + ns_size + opt_size + ETH_ALEN;
-
- if(source != myself) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Got neighbor solicitation request from %s (%s) while in router mode!", source->name, source->hostname);
- return;
- }
-
- /* Copy headers from packet to structs on the stack */
-
- memcpy(&ip6, packet->data + ether_size, ip6_size);
- memcpy(&ns, packet->data + ether_size + ip6_size, ns_size);
- if(has_opt)
- memcpy(&opt, packet->data + ether_size + ip6_size + ns_size, opt_size);
-
- /* First, snatch the source address from the neighbor solicitation packet */
-
- if(overwrite_mac)
- memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
-
- /* Check if this is a valid neighbor solicitation request */
-
- if(ns.nd_ns_hdr.icmp6_type != ND_NEIGHBOR_SOLICIT ||
- (has_opt && opt.nd_opt_type != ND_OPT_SOURCE_LINKADDR)) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: received unknown type neighbor solicitation request");
- return;
- }
-
- /* Create pseudo header */
-
- pseudo.ip6_src = ip6.ip6_src;
- pseudo.ip6_dst = ip6.ip6_dst;
- if(has_opt)
- pseudo.length = htonl(ns_size + opt_size + ETH_ALEN);
- else
- pseudo.length = htonl(ns_size);
- pseudo.next = htonl(IPPROTO_ICMPV6);
-
- /* Generate checksum */
-
- checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
- checksum = inet_checksum(&ns, ns_size, checksum);
- if(has_opt) {
- checksum = inet_checksum(&opt, opt_size, checksum);
- checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
- }
-
- if(checksum) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: checksum error for neighbor solicitation request");
- return;
- }
-
- /* Check if the IPv6 address exists on the VPN */
-
- subnet = lookup_subnet_ipv6((ipv6_t *) &ns.nd_ns_target);
-
- if(!subnet) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: neighbor solicitation request for unknown address %hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
- ntohs(((uint16_t *) &ns.nd_ns_target)[0]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[1]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[2]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[3]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[4]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[5]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[6]),
- ntohs(((uint16_t *) &ns.nd_ns_target)[7]));
-
- return;
- }
-
- /* Check if it is for our own subnet */
-
- if(subnet->owner == myself)
- return; /* silently ignore */
-
- /* Create neighbor advertation reply */
-
- memcpy(packet->data, packet->data + ETH_ALEN, ETH_ALEN); /* copy destination address */
- packet->data[ETH_ALEN * 2 - 1] ^= 0xFF; /* mangle source address so it looks like it's not from us */
-
- ip6.ip6_dst = ip6.ip6_src; /* swap destination and source protocoll address */
- ip6.ip6_src = ns.nd_ns_target;
-
- if(has_opt)
- memcpy(packet->data + ether_size + ip6_size + ns_size + opt_size, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
-
- ns.nd_ns_cksum = 0;
- ns.nd_ns_type = ND_NEIGHBOR_ADVERT;
- ns.nd_ns_reserved = htonl(0x40000000UL); /* Set solicited flag */
- opt.nd_opt_type = ND_OPT_TARGET_LINKADDR;
-
- /* Create pseudo header */
-
- pseudo.ip6_src = ip6.ip6_src;
- pseudo.ip6_dst = ip6.ip6_dst;
- if(has_opt)
- pseudo.length = htonl(ns_size + opt_size + ETH_ALEN);
- else
- pseudo.length = htonl(ns_size);
- pseudo.next = htonl(IPPROTO_ICMPV6);
-
- /* Generate checksum */
-
- checksum = inet_checksum(&pseudo, sizeof(pseudo), ~0);
- checksum = inet_checksum(&ns, ns_size, checksum);
- if(has_opt) {
- checksum = inet_checksum(&opt, opt_size, checksum);
- checksum = inet_checksum(packet->data + ether_size + ip6_size + ns_size + opt_size, ETH_ALEN, checksum);
- }
-
- ns.nd_ns_hdr.icmp6_cksum = checksum;
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &ip6, ip6_size);
- memcpy(packet->data + ether_size + ip6_size, &ns, ns_size);
- if(has_opt)
- memcpy(packet->data + ether_size + ip6_size + ns_size, &opt, opt_size);
-
- send_packet(source, packet);
-}
-
-static void route_ipv6(node_t *source, vpn_packet_t *packet) {
- if(!checklength(source, packet, ether_size + ip6_size))
- return;
-
- if(packet->data[20] == IPPROTO_ICMPV6 && checklength(source, packet, ether_size + ip6_size + icmp6_size) && packet->data[54] == ND_NEIGHBOR_SOLICIT) {
- route_neighborsol(source, packet);
- return;
- }
-
- if(packet->data[38] == 255)
- broadcast_packet(source, packet);
- else
- route_ipv6_unicast(source, packet);
-}