aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSridhar Samudrala <sri@us.ibm.com>2009-07-09 04:09:47 -0400
committerDavid S. Miller <davem@davemloft.net>2009-07-12 17:29:21 -0400
commitd7ca4cc01fd154f2da30ae6dae160fa5800af758 (patch)
tree8e772bbb2320f4b109e20e9e588345bd1a51fb12
parent30ffee8480c13fbcf8ab6c28e31f79dfff683117 (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>
-rw-r--r--include/net/udp.h3
-rw-r--r--net/ipv4/af_inet.c12
-rw-r--r--net/ipv4/udp.c61
3 files changed, 75 insertions, 1 deletions
diff --git a/include/net/udp.h b/include/net/udp.h
index 90e6ce56be65..5fb029f817a3 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -207,4 +207,7 @@ extern void udp4_proc_exit(void);
207#endif 207#endif
208 208
209extern void udp_init(void); 209extern void udp_init(void);
210
211extern int udp4_ufo_send_check(struct sk_buff *skb);
212extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features);
210#endif /* _UDP_H */ 213#endif /* _UDP_H */
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 = {
1425static struct net_protocol udp_protocol = { 1433static 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
1819int 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
1838struct 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);
1876out:
1877 return segs;
1878}
1879
1819EXPORT_SYMBOL(udp_disconnect); 1880EXPORT_SYMBOL(udp_disconnect);
1820EXPORT_SYMBOL(udp_ioctl); 1881EXPORT_SYMBOL(udp_ioctl);
1821EXPORT_SYMBOL(udp_prot); 1882EXPORT_SYMBOL(udp_prot);