- 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) {
- logger(DEBUG_TRAFFIC, 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)) {
- logger(DEBUG_TRAFFIC, 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) {
- logger(DEBUG_TRAFFIC, 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) {
- logger(DEBUG_TRAFFIC, 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(broadcast_mode && 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) {
- logger(DEBUG_TRAFFIC, 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) {
- logger(DEBUG_TRAFFIC, 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) {
- logger(DEBUG_TRAFFIC, 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) {
- logger(DEBUG_TRAFFIC, 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) {
- logger(DEBUG_TRAFFIC, 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);
-}
-
-static void send_pcap(vpn_packet_t *packet) {
- pcap = false;
- for(splay_node_t *node = connection_tree->head; node; node = node->next) {
- connection_t *c = node->data;
- if(!c->status.pcap)
- continue;
-
- pcap = true;
- int len = packet->len;
- if(c->outmaclength && c->outmaclength < len)
- len = c->outmaclength;
-
- if(send_request(c, "%d %d %d", CONTROL, REQ_PCAP, len))
- send_meta(c, (char *)packet->data, len);
- }
-}
-
-static bool do_decrement_ttl(node_t *source, vpn_packet_t *packet) {
- uint16_t type = packet->data[12] << 8 | packet->data[13];
-
- switch (type) {
- case ETH_P_IP:
- if(!checklength(source, packet, 14 + 32))
- return false;
-
- if(packet->data[22] < 1) {
- if(packet->data[25] != IPPROTO_ICMP || packet->data[46] != ICMP_TIME_EXCEEDED)
- route_ipv4_unreachable(source, packet, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
- return false;
- }
-
- uint16_t old = packet->data[22] << 8 | packet->data[23];
- packet->data[22]--;
- uint16_t new = packet->data[22] << 8 | packet->data[23];
-
- uint32_t checksum = packet->data[24] << 8 | packet->data[25];
- checksum += old + (~new & 0xFFFF);
- while(checksum >> 16)
- checksum = (checksum & 0xFFFF) + (checksum >> 16);
- packet->data[24] = checksum >> 8;
- packet->data[25] = checksum & 0xff;
-
- return true;
-
- case ETH_P_IPV6:
- if(!checklength(source, packet, 14 + 40))
- return false;
-
- if(packet->data[21] < 1) {
- if(packet->data[20] != IPPROTO_ICMPV6 || packet->data[54] != ICMP6_TIME_EXCEEDED)
- route_ipv6_unreachable(source, packet, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
- return false;
- }
-
- packet->data[21]--;
-
- return true;
-
- default:
- return true;
- }
-}
-
-void route(node_t *source, vpn_packet_t *packet) {
- if(pcap)
- send_pcap(packet);
-
- if(forwarding_mode == FMODE_KERNEL && source != myself) {
- send_packet(myself, packet);
- return;
- }
-
- if(!checklength(source, packet, ether_size))
- return;
-
- if(decrement_ttl && source != myself)
- if(!do_decrement_ttl(source, packet))
- 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:
- logger(DEBUG_TRAFFIC, 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;
- }