bool validate_frag_hdr(struct frag_hdr *hdr, u16 frag_offset, u16 mf, __u8 nexthdr) { bool success = true; success &= assert_equals_u8(nexthdr, hdr->nexthdr, "Fraghdr-nexthdr"); success &= assert_equals_u8(0, hdr->reserved, "Fraghdr-nexthdr"); success &= assert_equals_u16(frag_offset, get_fragment_offset_ipv6(hdr), "Fraghdr-frag offset"); success &= assert_equals_u16(mf, is_more_fragments_set_ipv6(hdr), "Fraghdr-MF"); success &= assert_equals_u16(4321, be32_to_cpu(hdr->identification), "Fraghdr-ID"); return success; }
/** * Groups "skb_in" with the rest of its fragments. * If the rest of the fragments have not yet arrived, this will return VER_STOLEN and store skb_in. * If all of the fragments have arrived, this will return VER_CONTINUE and the zero-offset fragment * will be returned in "skb_out". The rest of the fragments can be accesed via skb_out's list * (skb_shinfo(skb_out)->frag_list). */ verdict fragdb_handle(struct packet *pkt) { /* The fragment collector skb belongs to. */ struct reassembly_buffer *buffer; struct frag_hdr *hdr_frag = pkt_frag_hdr(pkt); int error; if (!is_fragmented_ipv6(hdr_frag)) return VERDICT_CONTINUE; /* * Because the packet *is* fragmented, we know we're being compiled in kernel 3.12 or lower at * this point. * (Other defragmenters conceal the fragment header, effectively pretending there's no * fragmentation.) */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) WARN(true, "This code is supposed to be unreachable in kernels 3.13+! Please report."); return VERDICT_DROP; #endif log_debug("Adding fragment to database."); error = validate_skb(pkt->skb); if (error) return VERDICT_DROP; spin_lock_bh(&table_lock); buffer = add_pkt(pkt); if (!buffer) { spin_unlock_bh(&table_lock); return VERDICT_DROP; } /* * nf_defrag_ipv6 is supposed to sort the fragments, so this condition should be all we need * to figure out whether we have all the fragments. * Otherwise we'd need to keep track of holes. If you ever find yourself needing to add hole * logic, keep in mind that this module used to do that in Jool 3.2, so you might be able * to reuse it. */ if (is_more_fragments_set_ipv6(hdr_frag)) { spin_unlock_bh(&table_lock); return VERDICT_STOLEN; } *pkt = buffer->pkt; pkt->original_pkt = pkt; buffer->pkt.skb = NULL; /* Note, at this point, buffer->pkt is invalid. Do not use. */ buffer_destroy(buffer, pkt); spin_unlock_bh(&table_lock); if (!skb_make_writable(pkt->skb, pkt_l3hdr_len(pkt))) return VERDICT_DROP; /* Why this? Dunno, both defrags do it when they support frag_list. */ pkt_ip6_hdr(pkt)->payload_len = cpu_to_be16(pkt->skb->len - sizeof(struct ipv6hdr)); /* * The kernel's defrag also removes the fragment header. * That actually harms us, so we don't mirror it. Instead, we make the fragment atomic. * The rest of Jool must assume the packet might have a redundant fragment header. */ pkt_frag_hdr(pkt)->frag_off &= cpu_to_be16(~IP6_MF); log_debug("All the fragments are now available. Resuming translation..."); return VERDICT_CONTINUE; }