+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);
+}
+
+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) {
+ 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) {
+ 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(forwarding_mode == FMODE_KERNEL && source != myself) {
+ send_packet(myself, packet);
+ return;
+ }