[ovs-dev] [PATCH 12/13] Upstream UDP: Add generic Tunnel segmentation.
Pravin B Shelar
pshelar at nicira.com
Wed Nov 7 01:00:45 UTC 2012
Signed-off-by: Pravin B Shelar <pshelar at nicira.com>
---
include/linux/skbuff.h | 3 ++
net/ipv4/af_inet.c | 6 ++-
net/ipv4/udp.c | 106 ++++++++++++++++++++++++++++++++++++------------
net/ipv6/af_inet6.c | 1 +
net/ipv6/udp.c | 7 +++-
5 files changed, 96 insertions(+), 27 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 585aca7..a23df86 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -309,6 +309,8 @@ enum {
SKB_GSO_FCOE = 1 << 5,
SKB_GSO_GRE = 1 << 6,
+
+ SKB_GSO_UDP_TUNNEL = 1 << 7,
};
#if BITS_PER_LONG > 32
@@ -422,6 +424,7 @@ struct sk_buff {
__u16 csum_offset;
};
};
+ __u16 tunnel_hlen;
__u32 priority;
kmemcheck_bitfield_begin(flags1);
__u8 local_df:1,
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 55cecba..b0ea634 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1294,6 +1294,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
int ihl;
int id;
unsigned int offset = 0;
+ bool udp_tunnel;
if (!(features & NETIF_F_V4_CSUM))
features &= ~NETIF_F_SG;
@@ -1304,6 +1305,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_GRE |
+ SKB_GSO_UDP_TUNNEL |
0)))
goto out;
@@ -1318,6 +1320,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
if (unlikely(!pskb_may_pull(skb, ihl)))
goto out;
+ udp_tunnel = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL);
+
__skb_pull(skb, ihl);
skb_reset_transport_header(skb);
iph = ip_hdr(skb);
@@ -1337,7 +1341,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
skb = segs;
do {
iph = ip_hdr(skb);
- if (proto == IPPROTO_UDP) {
+ if (!udp_tunnel && proto == IPPROTO_UDP) {
iph->id = htons(id);
iph->frag_off = htons(offset >> 3);
if (skb->next != NULL)
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index c256008..1ce522a 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2246,31 +2246,79 @@ void __init udp_init(void)
int udp4_ufo_send_check(struct sk_buff *skb)
{
- const struct iphdr *iph;
- struct udphdr *uh;
-
- if (!pskb_may_pull(skb, sizeof(*uh)))
+ if (!pskb_may_pull(skb, sizeof(struct udphdr)))
return -EINVAL;
- iph = ip_hdr(skb);
- uh = udp_hdr(skb);
+ if (!skb->tunnel_hlen) {
+ const struct iphdr *iph;
+ struct udphdr *uh;
+
+ 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;
+ 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;
}
+static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ int mac_len = skb->mac_len;
+ unsigned char *mac = skb_mac_header(skb);
+ int hlen = sizeof(struct udphdr) + skb->tunnel_hlen;
+ int doffset;
+ int network_hlen = skb_network_header_len(skb);
+
+ skb->tunnel_hlen = 0;
+ if (unlikely(!pskb_may_pull(skb, hlen)))
+ goto out;
+
+ __skb_pull(skb, hlen);
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, skb->mac_len);
+ doffset = skb_mac_header(skb) - mac;
+
+ /* segment inner packet. */
+ segs = skb_gso_segment(skb, 0);
+ if (!segs || IS_ERR(segs))
+ goto out;
+
+ skb = segs;
+ do {
+ unsigned char *smac;
+ struct udphdr *uh;
+
+ skb_push(skb, doffset);
+
+ skb_reset_mac_header(skb);
+ smac = skb_mac_header(skb);
+ skb->mac_len = mac_len;
+
+ /* Copy entire outer header from original skb. */
+ memcpy(smac, mac, doffset);
+
+ skb_set_network_header(skb, mac_len);
+ skb->ip_summed = CHECKSUM_NONE;
+ skb_set_transport_header(skb, skb_network_offset(skb) + network_hlen);
+ uh = udp_hdr(skb);
+ uh->len = htons(skb->len - (doffset - hlen));
+ uh->check = 0;
+ } while ((skb = skb->next));
+out:
+ return segs;
+}
+
struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
netdev_features_t 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;
@@ -2280,6 +2328,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
int type = skb_shinfo(skb)->gso_type;
if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
+ SKB_GSO_UDP_TUNNEL |
SKB_GSO_GRE) ||
!(type & (SKB_GSO_UDP))))
goto out;
@@ -2290,20 +2339,27 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
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_checksum_start_offset(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);
+ if (skb->tunnel_hlen && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
+ segs = skb_udp_tunnel_segment(skb, features);
+ else {
+ int offset;
+ __wsum csum;
+
+ /* 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_checksum_start_offset(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;
+
+ segs = skb_segment(skb, features);
+ }
out:
return segs;
}
-
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 820fc1f..5fe4def 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -781,6 +781,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
SKB_GSO_TCP_ECN |
SKB_GSO_TCPV6 |
SKB_GSO_GRE |
+ SKB_GSO_UDP_TUNNEL |
0)))
goto out;
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index e247a2b..cdbbe68 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1348,6 +1348,9 @@ static int udp6_ufo_send_check(struct sk_buff *skb)
const struct ipv6hdr *ipv6h;
struct udphdr *uh;
+ if (skb->tunnel_hlen)
+ return -EINVAL;
+
if (!pskb_may_pull(skb, sizeof(*uh)))
return -EINVAL;
@@ -1383,7 +1386,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
/* 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 |
+ if (unlikely(type & ~(SKB_GSO_UDP |
+ SKB_GSO_DODGY |
+ SKB_GSO_UDP_TUNNEL |
SKB_GSO_GRE) ||
!(type & (SKB_GSO_UDP))))
goto out;
--
1.7.10
More information about the dev
mailing list