Exemple #1
0
static verdict validate_inner6(struct xlation *state,
		struct pkt_metadata const *outer_meta)
{
	union {
		struct ipv6hdr ip6;
		struct frag_hdr frag;
		struct icmp6hdr icmp;
	} buffer;
	union {
		struct ipv6hdr *ip6;
		struct frag_hdr *frag;
		struct icmp6hdr *icmp;
	} ptr;

	struct pkt_metadata meta;
	verdict result;

	ptr.ip6 = skb_hdr_ptr(state->in.skb, outer_meta->payload_offset,
			buffer.ip6);
	if (!ptr.ip6)
		return truncated(state, "inner IPv6 header");
	if (unlikely(ptr.ip6->version != 6))
		return inhdr6(state, "Version is not 6.");

	result = summarize_skb6(state, outer_meta->payload_offset, &meta);
	if (result != VERDICT_CONTINUE)
		return result;

	if (meta.has_frag_hdr) {
		ptr.frag = skb_hdr_ptr(state->in.skb, meta.frag_offset,
				buffer.frag);
		if (!ptr.frag)
			return truncated(state, "inner fragment header");
		if (!is_first_frag6(ptr.frag))
			return inhdr6(state, "Inner packet is not a first fragment.");
	}

	if (meta.l4_proto == L4PROTO_ICMP) {
		ptr.icmp = skb_hdr_ptr(state->in.skb, meta.l4_offset,
				buffer.icmp);
		if (!ptr.icmp)
			return truncated(state, "inner ICMPv6 header");
		if (has_inner_pkt6(ptr.icmp->icmp6_type))
			return inhdr6(state, "Packet inside packet inside packet.");
	}

	if (!pskb_may_pull(state->in.skb, meta.payload_offset)) {
		log_debug("Could not 'pull' the headers out of the skb.");
		return truncated(state, "inner headers");
	}

	return VERDICT_CONTINUE;
}
Exemple #2
0
static struct reassembly_buffer *add_pkt(struct packet *pkt)
{
	struct reassembly_buffer *buffer;
	struct frag_hdr *hdr_frag = pkt_frag_hdr(pkt);
	unsigned int payload_len;

	/* Does it already exist? If so, add to and return existing buffer */
	buffer = fragdb_table_get(&table, pkt);
	if (buffer) {
		if (WARN(is_first_frag6(hdr_frag), "Non-first fragment's offset is zero." COMMON_MSG))
			return NULL;

		*buffer->next_slot = pkt->skb;
		buffer->next_slot = &pkt->skb->next;

		/* Why this? Dunno, both defrags do it when they support frag_list. */
		/*
		 * Note, since we're in the middle of its sort of initialization, pkt is illegal at this
		 * point. It looks like we should call pkt_payload_len_frag() instead of
		 * pkt_payload_len_pkt(), but that's not the case because it represents a subsequent
		 * fragment. Be careful with the calculation of this length.
		 */
		payload_len = pkt_payload_len_pkt(pkt);
		buffer->pkt.skb->len += payload_len;
		buffer->pkt.skb->data_len += payload_len;
		buffer->pkt.skb->truesize += pkt->skb->truesize;
		skb_pull(pkt->skb, pkt_hdrs_len(pkt));

		return buffer;
	}

	if (WARN(!is_first_frag6(hdr_frag), "First fragment's offset is nonzero." COMMON_MSG))
		return NULL;

	/*
	 * TODO (fine) Maybe pskb_expand_head() can be used here as fallback.
	 * I decided to leave this as is for the moment because it's such an
	 * obnoxious ridiculous corner case scenario and it will probably never
	 * cause any problems.
	 * Until somebody complains, I think I should probably work on more
	 * pressing stuff.
	 * I learned about pskb_expand_head() in the defrag modules.
	 */
	if (skb_cloned(pkt->skb)) {
		log_debug("Packet is cloned, so I can't edit its shared area. Canceling translation.");
		return NULL;
	}

	/* Create buffer, add the packet to it, index */
	buffer = kmem_cache_alloc(buffer_cache, GFP_ATOMIC);
	if (!buffer)
		return NULL;

	buffer->pkt = *pkt;
	buffer->pkt.original_pkt = &buffer->pkt;
	buffer->next_slot = &skb_shinfo(pkt->skb)->frag_list;
	buffer->dying_time = jiffies + config_get_ttl_frag();

	if (is_error(fragdb_table_put(&table, pkt, buffer))) {
		kmem_cache_free(buffer_cache, buffer);
		return NULL;
	}

	/* Schedule for automatic deletion */
	list_add(&buffer->list_hook, expire_list.prev);
	if (!timer_pending(&expire_timer)) {
		mod_timer(&expire_timer, buffer->dying_time);
		log_debug("The fragment cleaning timer will awake in %u msecs.",
				jiffies_to_msecs(expire_timer.expires - jiffies));
	}

	return buffer;
}
Exemple #3
0
/**
 * Walks through @skb's headers, collecting data and adding it to @meta.
 *
 * @hdr6_offset number of bytes between skb->data and the IPv6 header.
 *
 * BTW: You might want to read summarize_skb4() first, since it's a lot simpler.
 */
static verdict summarize_skb6(struct xlation *state,
		unsigned int hdr6_offset,
		struct pkt_metadata *meta)
{
	union {
		struct ipv6_opt_hdr opt;
		struct frag_hdr frag;
		struct tcphdr tcp;
	} buffer;
	union {
		struct ipv6_opt_hdr *opt;
		struct frag_hdr *frag;
		struct tcphdr *tcp;
		u8 *nexthdr;
	} ptr;

	struct sk_buff *skb = state->in.skb;
	u8 nexthdr;
	unsigned int offset;
	bool is_first = true;

	ptr.nexthdr = skb_hdr_ptr(skb,
			hdr6_offset + offsetof(struct ipv6hdr, nexthdr),
			nexthdr);
	if (!ptr.nexthdr)
		return truncated(state, "IPv6 header");
	nexthdr = *ptr.nexthdr;
	offset = hdr6_offset + sizeof(struct ipv6hdr);

	meta->has_frag_hdr = false;

	do {
		switch (nexthdr) {
		case NEXTHDR_TCP:
			meta->l4_proto = L4PROTO_TCP;
			meta->l4_offset = offset;
			meta->payload_offset = offset;

			if (is_first) {
				ptr.tcp = skb_hdr_ptr(skb, offset, buffer.tcp);
				if (!ptr.tcp)
					return truncated(state, "TCP header");
				meta->payload_offset += tcp_hdr_len(ptr.tcp);
			}

			return VERDICT_CONTINUE;

		case NEXTHDR_UDP:
			meta->l4_proto = L4PROTO_UDP;
			meta->l4_offset = offset;
			meta->payload_offset = is_first
					? (offset + sizeof(struct udphdr))
					: offset;
			return VERDICT_CONTINUE;

		case NEXTHDR_ICMP:
			meta->l4_proto = L4PROTO_ICMP;
			meta->l4_offset = offset;
			meta->payload_offset = is_first
					? (offset + sizeof(struct icmp6hdr))
					: offset;
			return VERDICT_CONTINUE;

		case NEXTHDR_FRAGMENT:
			ptr.frag = skb_hdr_ptr(skb, offset, buffer.frag);
			if (!ptr.frag)
				return truncated(state, "fragment header");

			meta->has_frag_hdr = true;
			meta->frag_offset = offset;
			is_first = is_first_frag6(ptr.frag);

			offset += sizeof(struct frag_hdr);
			nexthdr = ptr.frag->nexthdr;
			break;

		case NEXTHDR_HOP:
		case NEXTHDR_ROUTING:
		case NEXTHDR_DEST:
			ptr.opt = skb_hdr_ptr(skb, offset, buffer.opt);
			if (!ptr.opt)
				return truncated(state, "extension header");

			offset += ipv6_optlen(ptr.opt);
			nexthdr = ptr.opt->nexthdr;
			break;

		default:
			meta->l4_proto = L4PROTO_OTHER;
			meta->l4_offset = offset;
			meta->payload_offset = offset;
			return VERDICT_CONTINUE;
		}
	} while (true);

	return VERDICT_CONTINUE; /* whatever. */
}