From: Pravin Shelar <[email protected]>
Signed-off-by: Pravin B Shelar <[email protected]>
---
include/linux/skbuff.h | 3 +
net/core/skbuff.c | 1 +
net/ipv4/af_inet.c | 6 ++-
net/ipv4/udp.c | 106 ++++++++++++++++++++++++++++++++++++-----------
net/ipv6/ip6_offload.c | 1 +
net/ipv6/udp_offload.c | 9 +++-
6 files changed, 98 insertions(+), 28 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/core/skbuff.c b/net/core/skbuff.c
index 880722e..c21283e 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -697,6 +697,7 @@ static void __copy_skb_header(struct sk_buff *new, const
struct sk_buff *old)
new->ip_summed = old->ip_summed;
skb_copy_queue_mapping(new, old);
new->priority = old->priority;
+ new->tunnel_hlen = old->tunnel_hlen;
#if IS_ENABLED(CONFIG_IP_VS)
new->ipvs_property = old->ipvs_property;
#endif
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 9a3d5d2..f5f1c92 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1297,6 +1297,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;
@@ -1307,6 +1308,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;
@@ -1321,6 +1323,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);
@@ -1340,7 +1344,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/ip6_offload.c b/net/ipv6/ip6_offload.c
index 8234c1d..e02a65a 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_GRE |
+ SKB_GSO_UDP_TUNNEL |
SKB_GSO_TCPV6 |
0)))
goto out;
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index cf05cf0..8530d57 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -21,6 +21,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;
@@ -56,8 +59,10 @@ 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 |
- SKB_GSO_GRE) ||
+ if (unlikely(type & ~(SKB_GSO_UDP |
+ SKB_GSO_DODGY |
+ SKB_GSO_UDP_TUNNEL |
+ SKB_GSO_GRE) ||
!(type & (SKB_GSO_UDP))))
goto out;
--
1.7.1
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev