Commit d7ca4cc01fd154f2da30ae6dae160fa5800af758

Authored by Sridhar Samudrala
Committed by David S. Miller
1 parent 30ffee8480

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: Sridhar Samudrala <sri@us.ibm.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 3 changed files with 75 additions and 1 deletions Side-by-side Diff

... ... @@ -207,5 +207,8 @@
207 207 #endif
208 208  
209 209 extern void udp_init(void);
  210 +
  211 +extern int udp4_ufo_send_check(struct sk_buff *skb);
  212 +extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features);
210 213 #endif /* _UDP_H */
... ... @@ -1187,6 +1187,7 @@
1187 1187 int proto;
1188 1188 int ihl;
1189 1189 int id;
  1190 + unsigned int offset = 0;
1190 1191  
1191 1192 if (!(features & NETIF_F_V4_CSUM))
1192 1193 features &= ~NETIF_F_SG;
... ... @@ -1229,7 +1230,14 @@
1229 1230 skb = segs;
1230 1231 do {
1231 1232 iph = ip_hdr(skb);
1232   - iph->id = htons(id++);
  1233 + if (proto == IPPROTO_UDP) {
  1234 + iph->id = htons(id);
  1235 + iph->frag_off = htons(offset >> 3);
  1236 + if (skb->next != NULL)
  1237 + iph->frag_off |= htons(IP_MF);
  1238 + offset += (skb->len - skb->mac_len - iph->ihl * 4);
  1239 + } else
  1240 + iph->id = htons(id++);
1233 1241 iph->tot_len = htons(skb->len - skb->mac_len);
1234 1242 iph->check = 0;
1235 1243 iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
... ... @@ -1425,6 +1433,8 @@
1425 1433 static struct net_protocol udp_protocol = {
1426 1434 .handler = udp_rcv,
1427 1435 .err_handler = udp_err,
  1436 + .gso_send_check = udp4_ufo_send_check,
  1437 + .gso_segment = udp4_ufo_fragment,
1428 1438 .no_policy = 1,
1429 1439 .netns_ok = 1,
1430 1440 };
... ... @@ -1816,6 +1816,67 @@
1816 1816 sysctl_udp_wmem_min = SK_MEM_QUANTUM;
1817 1817 }
1818 1818  
  1819 +int udp4_ufo_send_check(struct sk_buff *skb)
  1820 +{
  1821 + const struct iphdr *iph;
  1822 + struct udphdr *uh;
  1823 +
  1824 + if (!pskb_may_pull(skb, sizeof(*uh)))
  1825 + return -EINVAL;
  1826 +
  1827 + iph = ip_hdr(skb);
  1828 + uh = udp_hdr(skb);
  1829 +
  1830 + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
  1831 + IPPROTO_UDP, 0);
  1832 + skb->csum_start = skb_transport_header(skb) - skb->head;
  1833 + skb->csum_offset = offsetof(struct udphdr, check);
  1834 + skb->ip_summed = CHECKSUM_PARTIAL;
  1835 + return 0;
  1836 +}
  1837 +
  1838 +struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features)
  1839 +{
  1840 + struct sk_buff *segs = ERR_PTR(-EINVAL);
  1841 + unsigned int mss;
  1842 + int offset;
  1843 + __wsum csum;
  1844 +
  1845 + mss = skb_shinfo(skb)->gso_size;
  1846 + if (unlikely(skb->len <= mss))
  1847 + goto out;
  1848 +
  1849 + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
  1850 + /* Packet is from an untrusted source, reset gso_segs. */
  1851 + int type = skb_shinfo(skb)->gso_type;
  1852 +
  1853 + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
  1854 + !(type & (SKB_GSO_UDP))))
  1855 + goto out;
  1856 +
  1857 + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
  1858 +
  1859 + segs = NULL;
  1860 + goto out;
  1861 + }
  1862 +
  1863 + /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
  1864 + * do checksum of UDP packets sent as multiple IP fragments.
  1865 + */
  1866 + offset = skb->csum_start - skb_headroom(skb);
  1867 + csum = skb_checksum(skb, offset, skb->len- offset, 0);
  1868 + offset += skb->csum_offset;
  1869 + *(__sum16 *)(skb->data + offset) = csum_fold(csum);
  1870 + skb->ip_summed = CHECKSUM_NONE;
  1871 +
  1872 + /* Fragment the skb. IP headers of the fragments are updated in
  1873 + * inet_gso_segment()
  1874 + */
  1875 + segs = skb_segment(skb, features);
  1876 +out:
  1877 + return segs;
  1878 +}
  1879 +
1819 1880 EXPORT_SYMBOL(udp_disconnect);
1820 1881 EXPORT_SYMBOL(udp_ioctl);
1821 1882 EXPORT_SYMBOL(udp_prot);