diff options
author | Sridhar Samudrala <sri@us.ibm.com> | 2009-07-09 04:09:47 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-07-12 17:29:21 -0400 |
commit | d7ca4cc01fd154f2da30ae6dae160fa5800af758 (patch) | |
tree | 8e772bbb2320f4b109e20e9e588345bd1a51fb12 /net | |
parent | 30ffee8480c13fbcf8ab6c28e31f79dfff683117 (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/af_inet.c | 12 | ||||
-rw-r--r-- | net/ipv4/udp.c | 61 |
2 files changed, 72 insertions, 1 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 566ea6c4321d..197d024b2536 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -1187,6 +1187,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) | |||
1187 | int proto; | 1187 | int proto; |
1188 | int ihl; | 1188 | int ihl; |
1189 | int id; | 1189 | int id; |
1190 | unsigned int offset = 0; | ||
1190 | 1191 | ||
1191 | if (!(features & NETIF_F_V4_CSUM)) | 1192 | if (!(features & NETIF_F_V4_CSUM)) |
1192 | features &= ~NETIF_F_SG; | 1193 | features &= ~NETIF_F_SG; |
@@ -1229,7 +1230,14 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) | |||
1229 | skb = segs; | 1230 | skb = segs; |
1230 | do { | 1231 | do { |
1231 | iph = ip_hdr(skb); | 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 | iph->tot_len = htons(skb->len - skb->mac_len); | 1241 | iph->tot_len = htons(skb->len - skb->mac_len); |
1234 | iph->check = 0; | 1242 | iph->check = 0; |
1235 | iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); | 1243 | iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); |
@@ -1425,6 +1433,8 @@ static struct net_protocol tcp_protocol = { | |||
1425 | static struct net_protocol udp_protocol = { | 1433 | static struct net_protocol udp_protocol = { |
1426 | .handler = udp_rcv, | 1434 | .handler = udp_rcv, |
1427 | .err_handler = udp_err, | 1435 | .err_handler = udp_err, |
1436 | .gso_send_check = udp4_ufo_send_check, | ||
1437 | .gso_segment = udp4_ufo_fragment, | ||
1428 | .no_policy = 1, | 1438 | .no_policy = 1, |
1429 | .netns_ok = 1, | 1439 | .netns_ok = 1, |
1430 | }; | 1440 | }; |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 80e3812837ad..7bc2d082a49e 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -1816,6 +1816,67 @@ void __init udp_init(void) | |||
1816 | sysctl_udp_wmem_min = SK_MEM_QUANTUM; | 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 | EXPORT_SYMBOL(udp_disconnect); | 1880 | EXPORT_SYMBOL(udp_disconnect); |
1820 | EXPORT_SYMBOL(udp_ioctl); | 1881 | EXPORT_SYMBOL(udp_ioctl); |
1821 | EXPORT_SYMBOL(udp_prot); | 1882 | EXPORT_SYMBOL(udp_prot); |