예제 #1
0
파일: packet.c 프로젝트: NICMx/Jool
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;
}
예제 #2
0
/**
 * 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;
}