- 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)
- return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
-
- if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
- return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
-
- via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
-
- if(directonly && subnet->owner != via)
- return route_ipv6_unreachable(source, packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
-
- 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;
- }
-
- clamp_mss(source, via, packet);
-
- 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);
-}
-
-/* RFC 826 */
-
-static void route_arp(node_t *source, vpn_packet_t *packet) {
- struct ether_arp arp;
- subnet_t *subnet;
- struct in_addr addr;
-
- if(!checklength(source, packet, ether_size + arp_size))
- return;
-
- if(source != myself) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Got ARP request from %s (%s) while in router mode!", source->name, source->hostname);
- return;
- }
-
- /* First, snatch the source address from the ARP packet */
-
- if(overwrite_mac)
- memcpy(mymac.x, packet->data + ETH_ALEN, ETH_ALEN);
-
- /* Copy headers from packet to structs on the stack */
-
- memcpy(&arp, packet->data + ether_size, arp_size);
-
- /* Check if this is a valid ARP request */
-
- if(ntohs(arp.arp_hrd) != ARPHRD_ETHER || ntohs(arp.arp_pro) != ETH_P_IP ||
- arp.arp_hln != ETH_ALEN || arp.arp_pln != sizeof(addr) || ntohs(arp.arp_op) != ARPOP_REQUEST) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: received unknown type ARP request");
- return;
- }
-
- /* Check if the IPv4 address exists on the VPN */
-
- subnet = lookup_subnet_ipv4((ipv4_t *) &arp.arp_tpa);
-
- if(!subnet) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet: ARP request for unknown address %d.%d.%d.%d",
- arp.arp_tpa[0], arp.arp_tpa[1], arp.arp_tpa[2],
- arp.arp_tpa[3]);
- return;
- }
-
- /* Check if it is for our own subnet */
-
- if(subnet->owner == myself)
- return; /* silently ignore */
-
- 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 */
-
- memcpy(&addr, arp.arp_tpa, sizeof(addr)); /* save protocol addr */
- memcpy(arp.arp_tpa, arp.arp_spa, sizeof(addr)); /* swap destination and source protocol address */
- memcpy(arp.arp_spa, &addr, sizeof(addr)); /* ... */
-
- memcpy(arp.arp_tha, arp.arp_sha, ETH_ALEN); /* set target hard/proto addr */
- memcpy(arp.arp_sha, packet->data + ETH_ALEN, ETH_ALEN); /* add fake source hard addr */
- arp.arp_op = htons(ARPOP_REPLY);
-
- /* Copy structs on stack back to packet */
-
- memcpy(packet->data + ether_size, &arp, arp_size);
-
- send_packet(source, packet);
-}
-
-static void route_mac(node_t *source, vpn_packet_t *packet) {
- subnet_t *subnet;
- mac_t dest;
-
- /* Learn source address */
-
- if(source == myself) {
- mac_t src;
- memcpy(&src, &packet->data[6], sizeof src);
- learn_mac(&src);
- }
-
- /* Lookup destination address */
-
- memcpy(&dest, &packet->data[0], sizeof dest);
- subnet = lookup_subnet_mac(NULL, &dest);
-
- if(!subnet) {
- broadcast_packet(source, packet);
- return;
- }
-
- if(subnet->owner == source) {
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Packet looping back to %s (%s)!", source->name, source->hostname);
- return;
- }
-
- if(forwarding_mode == FMODE_OFF && source != myself && subnet->owner != myself)
- return;
-
- // Handle packets larger than PMTU
-
- node_t *via = (subnet->owner->via == myself) ? subnet->owner->nexthop : subnet->owner->via;
-
- if(directonly && subnet->owner != via)
- return;
-
- if(via && packet->len > via->mtu && 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);
- uint16_t type = packet->data[12] << 8 | packet->data[13];
- if(type == ETH_P_IP && packet->len > 590) {
- if(packet->data[20] & 0x40) {
- packet->len = via->mtu;
- route_ipv4_unreachable(source, packet, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
- } else {
- fragment_ipv4_packet(via, packet);
- }
- return;
- } else if(type == ETH_P_IPV6 && packet->len > 1294) {
- packet->len = via->mtu;
- route_ipv6_unreachable(source, packet, ICMP6_PACKET_TOO_BIG, 0);
- return;
- }
- }
-
- clamp_mss(source, via, packet);
-
- send_packet(subnet->owner, packet);
-}
-
-void route(node_t *source, vpn_packet_t *packet) {
- if(forwarding_mode == FMODE_KERNEL && source != myself) {
- send_packet(myself, packet);
- return;
- }
-
- if(!checklength(source, packet, ether_size))
- return;
-
- switch (routing_mode) {
- case RMODE_ROUTER:
- {
- uint16_t type = packet->data[12] << 8 | packet->data[13];
-
- switch (type) {
- case ETH_P_ARP:
- route_arp(source, packet);
- break;
-
- case ETH_P_IP:
- route_ipv4(source, packet);
- break;
-
- case ETH_P_IPV6:
- route_ipv6(source, packet);
- break;
-
- default:
- ifdebug(TRAFFIC) logger(LOG_WARNING, "Cannot route packet from %s (%s): unknown type %hx", source->name, source->hostname, type);
- break;
- }
- }
- break;
-
- case RMODE_SWITCH:
- route_mac(source, packet);
- break;
-
- case RMODE_HUB:
- broadcast_packet(source, packet);
- break;
- }