Exemplo n.º 1
0
static int whine_if_too_big(struct packet *in, struct packet *out)
{
	unsigned int len;
	unsigned int mtu;

	if (pkt_l3_proto(in) == L3PROTO_IPV4 && !is_dont_fragment_set(pkt_ip4_hdr(in)))
		return 0;

	len = pkt_len(out);
	mtu = get_nexthop_mtu(out);
	if (len > mtu) {
		/*
		 * We don't have to worry about ICMP errors causing this because the translate code already
		 * truncates them.
		 */
		log_debug("Packet is too big (len: %u, mtu: %u).", len, mtu);

		switch (pkt_l3_proto(out)) {
		case L3PROTO_IPV6:
			mtu -= 20;
			break;
		case L3PROTO_IPV4:
			mtu += 20;
			break;
		}
		icmp64_send(out, ICMPERR_FRAG_NEEDED, mtu);

		return -EINVAL;
	}

	return 0;
}
Exemplo n.º 2
0
static bool test_function_is_dont_fragment_set(void)
{
	struct iphdr hdr;
	bool success = true;

	hdr.frag_off = cpu_to_be16(0x0000);
	success &= assert_equals_u16(0, is_dont_fragment_set(&hdr), "All zeroes");

	hdr.frag_off = cpu_to_be16(0x4000);
	success &= assert_equals_u16(1, is_dont_fragment_set(&hdr), "All zeroes except DF");

	hdr.frag_off = cpu_to_be16(0xFFFF);
	success &= assert_equals_u16(1, is_dont_fragment_set(&hdr), "All ones");

	hdr.frag_off = cpu_to_be16(0xBFFF);
	success &= assert_equals_u16(0, is_dont_fragment_set(&hdr), "All ones except DF");

	return success;
}
Exemplo n.º 3
0
static bool ipv4_validate_packet_len(struct sk_buff *skb_in, struct sk_buff *skb_out)
{
	struct iphdr *ip4_hdr = ip_hdr(skb_out);

	if (skb_out->len <= skb_out->dev->mtu)
		return true;

	if (ip4_hdr->protocol == IPPROTO_ICMP) {
		struct icmphdr *icmp4_hdr = icmp_hdr(skb_out);
		if (is_icmp4_error(icmp4_hdr->type)) {
			int new_packet_len = skb_out->dev->mtu;

			skb_trim(skb_out, new_packet_len);

			ip4_hdr->tot_len = cpu_to_be16(new_packet_len);
			ip4_hdr->check = 0;
			ip4_hdr->check = ip_fast_csum(ip4_hdr, ip4_hdr->ihl);

			icmp4_hdr->checksum = 0;
			icmp4_hdr->checksum = ip_compute_csum(icmp4_hdr, new_packet_len - 4 * ip4_hdr->ihl);

			return true;
		}
	}

	if (is_dont_fragment_set(ip4_hdr)) {
		unsigned int ipv6_mtu = skb_in->dev->mtu;
		unsigned int ipv4_mtu = skb_out->dev->mtu;

		log_debug("Packet is too large for the outgoing MTU and the DF flag is set. Dropping...");
		icmpv6_send(skb_in, ICMPV6_PKT_TOOBIG, 0, cpu_to_be32(min_uint(ipv4_mtu, ipv6_mtu - 20)));
		return false;
	}

	/* The kernel will fragment it. */
	return true;
}