Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7ca4cc0 authored by Sridhar Samudrala's avatar Sridhar Samudrala Committed by David S. Miller
Browse files

udpv4: Handle large incoming UDP/IPv4 packets and support software UFO.



- validate and forward GSO UDP/IPv4 packets from untrusted sources.
- do software UFO if the outgoing device doesn't support UFO.

Signed-off-by: default avatarSridhar Samudrala <sri@us.ibm.com>
Acked-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 30ffee84
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -207,4 +207,7 @@ extern void udp4_proc_exit(void);
#endif

extern void udp_init(void);

extern int udp4_ufo_send_check(struct sk_buff *skb);
extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features);
#endif	/* _UDP_H */
+11 −1
Original line number Diff line number Diff line
@@ -1187,6 +1187,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
	int proto;
	int ihl;
	int id;
	unsigned int offset = 0;

	if (!(features & NETIF_F_V4_CSUM))
		features &= ~NETIF_F_SG;
@@ -1229,6 +1230,13 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
	skb = segs;
	do {
		iph = ip_hdr(skb);
		if (proto == IPPROTO_UDP) {
			iph->id = htons(id);
			iph->frag_off = htons(offset >> 3);
			if (skb->next != NULL)
				iph->frag_off |= htons(IP_MF);
			offset += (skb->len - skb->mac_len - iph->ihl * 4);
		} else
			iph->id = htons(id++);
		iph->tot_len = htons(skb->len - skb->mac_len);
		iph->check = 0;
@@ -1425,6 +1433,8 @@ static struct net_protocol tcp_protocol = {
static struct net_protocol udp_protocol = {
	.handler =	udp_rcv,
	.err_handler =	udp_err,
	.gso_send_check = udp4_ufo_send_check,
	.gso_segment = udp4_ufo_fragment,
	.no_policy =	1,
	.netns_ok =	1,
};
+61 −0
Original line number Diff line number Diff line
@@ -1816,6 +1816,67 @@ void __init udp_init(void)
	sysctl_udp_wmem_min = SK_MEM_QUANTUM;
}

int udp4_ufo_send_check(struct sk_buff *skb)
{
	const struct iphdr *iph;
	struct udphdr *uh;

	if (!pskb_may_pull(skb, sizeof(*uh)))
		return -EINVAL;

	iph = ip_hdr(skb);
	uh = udp_hdr(skb);

	uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
				       IPPROTO_UDP, 0);
	skb->csum_start = skb_transport_header(skb) - skb->head;
	skb->csum_offset = offsetof(struct udphdr, check);
	skb->ip_summed = CHECKSUM_PARTIAL;
	return 0;
}

struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features)
{
	struct sk_buff *segs = ERR_PTR(-EINVAL);
	unsigned int mss;
	int offset;
	__wsum csum;

	mss = skb_shinfo(skb)->gso_size;
	if (unlikely(skb->len <= mss))
		goto out;

	if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
		/* Packet is from an untrusted source, reset gso_segs. */
		int type = skb_shinfo(skb)->gso_type;

		if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
			     !(type & (SKB_GSO_UDP))))
			goto out;

		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);

		segs = NULL;
		goto out;
	}

	/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
	 * do checksum of UDP packets sent as multiple IP fragments.
	 */
	offset = skb->csum_start - skb_headroom(skb);
	csum = skb_checksum(skb, offset, skb->len- offset, 0);
	offset += skb->csum_offset;
	*(__sum16 *)(skb->data + offset) = csum_fold(csum);
	skb->ip_summed = CHECKSUM_NONE;

	/* Fragment the skb. IP headers of the fragments are updated in
	 * inet_gso_segment()
	 */
	segs = skb_segment(skb, features);
out:
	return segs;
}

EXPORT_SYMBOL(udp_disconnect);
EXPORT_SYMBOL(udp_ioctl);
EXPORT_SYMBOL(udp_prot);