[ovs-dev] [PATCH v2 14/15] netdev: Add support for GRE segmentation

Pravin B Shelar pshelar at ovn.org
Fri Apr 22 01:54:29 UTC 2016


This patch adds support to segment large GRE packet. This does
means that there are two sets of headers for given packet. To
get offset of inner packet outer_hlen member is added to dp-packet.

Signed-off-by: Pravin B Shelar <pshelar at ovn.org>
---
 lib/dp-packet-lso.c     | 74 +++++++++++++++++++++++++++++++++++++++++++------
 lib/dp-packet-lso.h     |  2 ++
 lib/dp-packet.h         |  1 +
 lib/netdev-native-tnl.c | 12 ++++++--
 lib/netdev-vport.c      |  5 +++-
 5 files changed, 82 insertions(+), 12 deletions(-)

diff --git a/lib/dp-packet-lso.c b/lib/dp-packet-lso.c
index 14a5ed8..bdcc987 100644
--- a/lib/dp-packet-lso.c
+++ b/lib/dp-packet-lso.c
@@ -67,6 +67,9 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 #define TCP_CSUM_OFFSET offsetof(struct tcp_header, tcp_csum)
 
 static struct dp_packet *
+segment_eth_packet(struct dp_packet *orig, int offset);
+
+static struct dp_packet *
 segment_packet__(struct dp_packet *orig, int header_len)
 {
     struct dp_packet *seg_list = NULL, *prev = NULL;
@@ -74,12 +77,21 @@ segment_packet__(struct dp_packet *orig, int header_len)
     int offset = header_len;
     int size = dp_packet_size(orig);
     struct dp_packet *seg;
+    unsigned char *src;
 
+    src = (unsigned char *) dp_packet_data(orig) - orig->lso.outer_hlen;
     if (!mss) {
-        seg_list = dp_packet_clone(orig);
-        memset(&seg_list->lso, 0, sizeof seg_list->lso);
-        PACKET_LSO_CTX(seg_list)->next = NULL;
-        return seg_list;
+        seg = dp_packet_clone_with_headroom(orig, orig->lso.outer_hlen);
+
+        if (orig->lso.outer_hlen) {
+            unsigned char *dst;
+
+            dst = (unsigned char *) dp_packet_data(seg) - orig->lso.outer_hlen;
+            memcpy(dst, src, orig->lso.outer_hlen);
+        }
+        memset(&seg->lso, 0, sizeof seg->lso);
+        PACKET_LSO_CTX(seg)->next = NULL;
+        return seg;
     }
     while (offset < size) {
         int current_seg_size;
@@ -87,8 +99,10 @@ segment_packet__(struct dp_packet *orig, int header_len)
 
         current_seg_size = size < (offset + mss) ? (size - offset) : mss;
         seg = dp_packet_new(0);
-        dp_packet_put(seg, dp_packet_data(orig), header_len);
-
+        dp_packet_put(seg, src, header_len + orig->lso.outer_hlen);
+        if (orig->lso.outer_hlen) {
+            dp_packet_reset_packet(seg, orig->lso.outer_hlen);
+        }
         data = (unsigned char *)dp_packet_data(orig) + offset;
         dp_packet_put(seg, data, current_seg_size);
         offset += mss;
@@ -179,10 +193,54 @@ segment_tcp_packet(struct dp_packet *orig)
     return seg_list;
 }
 
+static void
+restore_outer_headers(struct dp_packet *p, int hlen, uint8_t l2_pad_size,
+                      uint16_t l2_5_ofs, uint16_t l3_ofs, uint16_t l4_ofs)
+{
+    dp_packet_reset_packet(p, -hlen);
+    p->l2_pad_size = l2_pad_size;
+    p->l2_5_ofs = l2_5_ofs;
+    p->l3_ofs = l3_ofs;
+    p->l4_ofs = l4_ofs;
+}
+
+static struct dp_packet *
+segment_gre_packet(struct dp_packet *orig)
+{
+    struct dp_packet *seg_list, *seg;
+    const struct gre_base_hdr *greh;
+    uint8_t l2_pad_size = orig->l2_pad_size;
+    uint16_t l2_5_ofs = orig->l2_5_ofs;
+    uint16_t l3_ofs = orig->l3_ofs;
+    uint16_t l4_ofs = orig->l4_ofs;
+
+    seg_list = segment_eth_packet(orig, orig->lso.outer_hlen);
+    restore_outer_headers(orig, orig->lso.outer_hlen, l2_pad_size, l2_5_ofs,
+                          l3_ofs, l4_ofs);
+
+    FOR_EACH_LSO_SEG(seg_list, seg) {
+        restore_outer_headers(seg, orig->lso.outer_hlen, l2_pad_size, l2_5_ofs,
+                              l3_ofs, l4_ofs);
+
+        greh = dp_packet_l4(seg);
+
+        if (greh->flags & htons(GRE_CSUM)) {
+            ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
+            int gre_size = dp_packet_size(seg) - seg->l4_ofs;
+
+            *csum_opt = csum(greh, gre_size);
+        }
+    }
+    return seg_list;
+}
+
 static struct dp_packet *
 segment_l4_packet(struct dp_packet *orig)
 {
-    if (orig->lso.type & (DPBUF_LSO_TCPv4 | DPBUF_LSO_TCPv6)) {
+    if (orig->lso.type & DPBUF_LSO_GRE) {
+        orig->lso.type &= ~DPBUF_LSO_GRE;
+        return segment_gre_packet(orig);
+    } else if (orig->lso.type & (DPBUF_LSO_TCPv4 | DPBUF_LSO_TCPv6)) {
         return segment_tcp_packet(orig);
     } else if (orig->lso.type & (DPBUF_LSO_UDPv4 | DPBUF_LSO_UDPv6)) {
         return segment_udp_packet(orig);
@@ -200,7 +258,7 @@ segment_ipv4_packet(struct dp_packet *orig)
     int ip_offset = 0;
     bool inc_ip_id = false;
 
-    if (orig->lso.type & DPBUF_LSO_TCPv4) {
+    if (orig->lso.type & (DPBUF_LSO_TCPv4 | DPBUF_LSO_GRE)) {
         inc_ip_id = true;
         ip_id = ntohs(orig_iph->ip_id);
     }
diff --git a/lib/dp-packet-lso.h b/lib/dp-packet-lso.h
index 09815e8..fdf93a6 100644
--- a/lib/dp-packet-lso.h
+++ b/lib/dp-packet-lso.h
@@ -31,9 +31,11 @@
 #define DPBUF_LSO_TCPv6 (1 << 1)
 #define DPBUF_LSO_UDPv4 (1 << 2)
 #define DPBUF_LSO_UDPv6 (1 << 3)
+#define DPBUF_LSO_GRE   (1 << 4)
 
 struct dp_packet_lso_ctx {
     struct dp_packet *next;       /* Used to list lso segments. */
+
 };
 
 BUILD_ASSERT_DECL(DP_PACKET_CONTEXT_SIZE >= sizeof(struct dp_packet_lso_ctx));
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index e247712..914b37f 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -66,6 +66,7 @@ struct dp_packet {
     };
     struct {
         uint16_t mss;
+        uint16_t outer_hlen;
         uint8_t  type;
     } lso;
 };
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 9c2dc7e..60cb81f 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -34,6 +34,7 @@
 #include "dirs.h"
 #include "dpif.h"
 #include "dp-packet.h"
+#include "dp-packet-lso.h"
 #include "entropy.h"
 #include "flow.h"
 #include "hash.h"
@@ -373,9 +374,14 @@ netdev_gre_push_header(struct dp_packet *packet,
 
     greh = push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
 
-    if (greh->flags & htons(GRE_CSUM)) {
-        ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
-        *csum_opt = csum(greh, ip_tot_size);
+    if (packet->lso.type) {
+        packet->lso.type |= DPBUF_LSO_GRE;
+        packet->lso.outer_hlen = data->header_len;
+    } else {
+        if (greh->flags & htons(GRE_CSUM)) {
+            ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
+            *csum_opt = csum(greh, ip_tot_size);
+        }
     }
 }
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index 1d2638b..5530818 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -207,7 +207,10 @@ netdev_vport_construct(struct netdev *netdev_)
     eth_addr_random(&dev->etheraddr);
 
     /* Add a default destination port for tunnel ports if none specified. */
-    if (!strcmp(type, "geneve")) {
+    if (!strcmp(type, "gre")) {
+        netdev_->supported_lso_types = DPBUF_LSO_TCPv4 | DPBUF_LSO_TCPv6 |
+                                       DPBUF_LSO_UDPv4 | DPBUF_LSO_UDPv6;
+    } else if (!strcmp(type, "geneve")) {
         dev->tnl_cfg.dst_port = htons(GENEVE_DST_PORT);
     } else if (!strcmp(type, "vxlan")) {
         dev->tnl_cfg.dst_port = htons(VXLAN_DST_PORT);
-- 
2.5.5




More information about the dev mailing list