Commit d7ca4cc01fd154f2da30ae6dae160fa5800af758
Committed by
David S. Miller
1 parent
30ffee8480
Exists in
master
and in
7 other branches
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
include/net/udp.h
net/ipv4/af_inet.c
... | ... | @@ -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 | }; |
net/ipv4/udp.c
... | ... | @@ -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); |