+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 || !subnet->owner) {
+ 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;
+
+ uint16_t type = packet->data[12] << 8 | packet->data[13];
+
+ if(priorityinheritance && type == ETH_P_IP && packet->len >= ether_size + ip_size)
+ packet->priority = packet->data[15];
+
+ // 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);
+ length_t ethlen = 14;
+
+ if(type == ETH_P_8021Q) {
+ type = packet->data[16] << 8 | packet->data[17];
+ ethlen += 4;
+ }
+
+ if(type == ETH_P_IP && packet->len > 576 + ethlen) {
+ if(packet->data[6 + ethlen] & 0x40) {
+ packet->len = via->mtu;
+ route_ipv4_unreachable(source, packet, ethlen, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED);
+ } else {
+ fragment_ipv4_packet(via, packet, ethlen);
+ }
+ return;
+ } else if(type == ETH_P_IPV6 && packet->len > 1280 + ethlen) {
+ packet->len = via->mtu;
+ route_ipv6_unreachable(source, packet, ethlen, 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 list_each(connection_t, c, connection_list) {
+ 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];
+ length_t ethlen = ether_size;
+
+ if(type == ETH_P_8021Q) {
+ type = packet->data[16] << 8 | packet->data[17];
+ ethlen += 4;
+ }
+
+ switch (type) {
+ case ETH_P_IP:
+ if(!checklength(source, packet, ethlen + ip_size))
+ return false;
+
+ if(packet->data[ethlen + 8] < 1) {
+ if(packet->data[ethlen + 11] != IPPROTO_ICMP || packet->data[ethlen + 32] != ICMP_TIME_EXCEEDED)
+ route_ipv4_unreachable(source, packet, ethlen, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL);
+ return false;
+ }
+
+ uint16_t old = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
+ packet->data[ethlen + 8]--;
+ uint16_t new = packet->data[ethlen + 8] << 8 | packet->data[ethlen + 9];
+
+ uint32_t checksum = packet->data[ethlen + 10] << 8 | packet->data[ethlen + 11];
+ checksum += old + (~new & 0xFFFF);
+ while(checksum >> 16)
+ checksum = (checksum & 0xFFFF) + (checksum >> 16);
+ packet->data[ethlen + 10] = checksum >> 8;
+ packet->data[ethlen + 11] = checksum & 0xff;
+
+ return true;
+
+ case ETH_P_IPV6:
+ if(!checklength(source, packet, ethlen + ip6_size))
+ return false;
+
+ if(packet->data[ethlen + 7] < 1) {
+ if(packet->data[ethlen + 6] != IPPROTO_ICMPV6 || packet->data[ethlen + 40] != ICMP6_TIME_EXCEEDED)
+ route_ipv6_unreachable(source, packet, ethlen, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
+ return false;
+ }
+
+ packet->data[ethlen + 7]--;
+
+ 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;
+ }