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; }
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; }
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; }