static verdict handle_icmp6(struct xlation *state, struct pkt_metadata const *meta) { union { struct icmp6hdr icmp; struct frag_hdr frag; } buffer; union { struct icmp6hdr *icmp; struct frag_hdr *frag; } ptr; verdict result; ptr.icmp = skb_hdr_ptr(state->in.skb, meta->l4_offset, buffer.icmp); if (!ptr.icmp) return truncated(state, "ICMPv6 header"); if (has_inner_pkt6(ptr.icmp->icmp6_type)) { result = validate_inner6(state, meta); if (result != VERDICT_CONTINUE) return result; } if (xlat_is_siit() && meta->has_frag_hdr && is_icmp6_info(ptr.icmp->icmp6_type)) { ptr.frag = skb_hdr_ptr(state->in.skb, meta->frag_offset, buffer.frag); if (!ptr.frag) return truncated(state, "fragment header"); if (is_fragmented_ipv6(ptr.frag)) { log_debug("Packet is a fragmented ping; its checksum cannot be translated."); return drop(state, JSTAT_FRAGMENTED_PING); } } return VERDICT_CONTINUE; }
/** * 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; }