From: Vignesh PS <vignesh.purushotham.srini...@ericsson.com>

Add support to ip_frag library to perform IPv6 reassembly
when extension headers are present before the fragment
extension in the packet.

Signed-off-by: Vignesh PS <vignesh.purushotham.srini...@ericsson.com>
---
 .mailmap                          |   1 +
 app/test/test_reassembly_perf.c   | 163 +++++++++++++++++++-----------
 lib/ip_frag/ip_frag_common.h      |   4 +
 lib/ip_frag/ip_reassembly.h       |   2 +
 lib/ip_frag/rte_ipv6_reassembly.c |  75 ++++++++++++--
 5 files changed, 179 insertions(+), 66 deletions(-)

diff --git a/.mailmap b/.mailmap
index ed4ea17c4c..1bd3885424 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1555,6 +1555,7 @@ Viacheslav Ovsiienko <viachesl...@nvidia.com> 
<viachesl...@mellanox.com>
 Victor Kaplansky <vict...@redhat.com>
 Victor Raj <victor....@intel.com>
 Vidya Sagar Velumuri <vvelum...@marvell.com>
+Vignesh PS <vignesh.purushotham.srini...@ericsson.com> 
<vig.vigneshps1...@gmail.com>
 Vignesh Sridhar <vignesh.srid...@intel.com>
 Vijayakumar Muthuvel Manickam <mmvi...@gmail.com>
 Vijaya Mohan Guvva <vijay1...@gmail.com>
diff --git a/app/test/test_reassembly_perf.c b/app/test/test_reassembly_perf.c
index 3912179022..6090182943 100644
--- a/app/test/test_reassembly_perf.c
+++ b/app/test/test_reassembly_perf.c
@@ -47,6 +47,16 @@ static struct rte_mbuf *mbufs[MAX_FLOWS][MAX_FRAGMENTS];
 static uint8_t frag_per_flow[MAX_FLOWS];
 static uint32_t flow_cnt;
 
+struct ipv6_extension_routing {
+       uint8_t next_header;
+       uint8_t length;
+       uint8_t type;
+       uint8_t segments;
+       uint32_t data;
+};
+
+#define IPV6_ROUTING_HDR_SIZE sizeof(struct ipv6_extension_routing)
+
 #define FILL_MODE_LINEAR      0
 #define FILL_MODE_RANDOM      1
 #define FILL_MODE_INTERLEAVED 2
@@ -108,15 +118,15 @@ static void
 reassembly_print_banner(const char *proto_str)
 {
        printf("+=============================================================="
-              "============================================+\n");
-       printf("| %-32s| %-3s : %-58d|\n", proto_str, "Flow Count", MAX_FLOWS);
+              
"===========================================================+\n");
+       printf("| %-32s| %-3s : %-73d|\n", proto_str, "Flow Count", MAX_FLOWS);
        printf("+================+================+=============+=============+"
-              "========================+===================+\n");
-       printf("%-17s%-17s%-14s%-14s%-25s%-20s\n", "| Fragment Order",
-              "| Fragments/Flow", "| Outstanding", "| Cycles/Flow",
+              
"========================+==================================+\n");
+       printf("%-17s%-17s%-15s%-14s%-14s%-25s%-20s\n", "| Fragment Order",
+              "| Fragments/Flow", "| Extensions", "| Outstanding", "| 
Cycles/Flow",
               "| Cycles/Fragment insert", "| Cycles/Reassembly |");
        printf("+================+================+=============+=============+"
-              "========================+===================+\n");
+              
"========================+==================================+\n");
 }
 
 static void
@@ -272,9 +282,10 @@ ipv4_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
 
 static void
 ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t nb_frags, uint32_t flow_id,
-                   uint8_t fill_mode)
+                   uint8_t fill_mode, uint8_t num_exts)
 {
        struct ipv6_extension_fragment *frag_hdr;
+       struct ipv6_extension_routing *routing_hdr;
        struct rte_ether_hdr *eth_hdr;
        struct rte_ipv6_hdr *ip_hdr;
        struct rte_udp_hdr *udp_hdr;
@@ -303,15 +314,18 @@ ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t 
nb_frags, uint32_t flow_id,
                eth_hdr = rte_pktmbuf_mtod(frag, struct rte_ether_hdr *);
                ip_hdr = rte_pktmbuf_mtod_offset(frag, struct rte_ipv6_hdr *,
                                                 sizeof(struct rte_ether_hdr));
+
                udp_hdr = rte_pktmbuf_mtod_offset(
                        frag, struct rte_udp_hdr *,
                        sizeof(struct rte_ether_hdr) +
                                sizeof(struct rte_ipv6_hdr) +
+                               num_exts * IPV6_ROUTING_HDR_SIZE +
                                RTE_IPV6_FRAG_HDR_SIZE);
                frag_hdr = rte_pktmbuf_mtod_offset(
                        frag, struct ipv6_extension_fragment *,
                        sizeof(struct rte_ether_hdr) +
-                               sizeof(struct rte_ipv6_hdr));
+                               sizeof(struct rte_ipv6_hdr) +
+                               num_exts * IPV6_ROUTING_HDR_SIZE);
 
                rte_ether_unformat_addr("02:00:00:00:00:01",
                                        &eth_hdr->dst_addr);
@@ -334,11 +348,15 @@ ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t 
nb_frags, uint32_t flow_id,
                 * Initialize IP header.
                 */
                pkt_len = (uint16_t)(pkt_len + sizeof(struct rte_ipv6_hdr) +
-                                    RTE_IPV6_FRAG_HDR_SIZE);
+                       num_exts * IPV6_ROUTING_HDR_SIZE +
+                               RTE_IPV6_FRAG_HDR_SIZE);
                ip_hdr->vtc_flow = rte_cpu_to_be_32(IP6_VERSION << 28);
                ip_hdr->payload_len =
                        rte_cpu_to_be_16(pkt_len - sizeof(struct rte_ipv6_hdr));
-               ip_hdr->proto = IPPROTO_FRAGMENT;
+               if (num_exts > 0)
+                       ip_hdr->proto = IPPROTO_ROUTING;
+               else
+                       ip_hdr->proto = IPPROTO_FRAGMENT;
                ip_hdr->hop_limits = IP_DEFTTL;
                memcpy(ip_hdr->src_addr, ip6_addr, sizeof(ip_hdr->src_addr));
                memcpy(ip_hdr->dst_addr, ip6_addr, sizeof(ip_hdr->dst_addr));
@@ -352,6 +370,24 @@ ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t 
nb_frags, uint32_t flow_id,
                ip_hdr->dst_addr[8] = (flow_id >> 8) & 0xff;
                ip_hdr->dst_addr[9] = flow_id & 0xff;
 
+               for (uint8_t exts = 0; exts < num_exts; exts++) {
+                       routing_hdr = rte_pktmbuf_mtod_offset(
+                               frag, struct ipv6_extension_routing *,
+                                       sizeof(struct rte_ether_hdr) +
+                                       sizeof(struct rte_ipv6_hdr) +
+                                       exts * IPV6_ROUTING_HDR_SIZE);
+
+                       routing_hdr->length = 0;    /* zero because extension 
is bare, no data */
+                       routing_hdr->type = 4;
+                       routing_hdr->segments = num_exts - exts - 1;
+                       routing_hdr->data = 0;
+
+                       if (exts == num_exts - 1)
+                               routing_hdr->next_header = IPPROTO_FRAGMENT;
+                       else
+                               routing_hdr->next_header = IPPROTO_ROUTING;
+               }
+
                frag_hdr->next_header = IPPROTO_UDP;
                frag_hdr->reserved = 0;
                frag_hdr->frag_data = rte_cpu_to_be_16(frag_offset);
@@ -361,7 +397,9 @@ ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t 
nb_frags, uint32_t flow_id,
                frag->pkt_len = frag->data_len;
                frag->l2_len = sizeof(struct rte_ether_hdr);
                frag->l3_len =
-                       sizeof(struct rte_ipv6_hdr) + RTE_IPV6_FRAG_HDR_SIZE;
+                       sizeof(struct rte_ipv6_hdr) +
+                               num_exts * IPV6_ROUTING_HDR_SIZE +
+                               RTE_IPV6_FRAG_HDR_SIZE;
        }
 
        if (fill_mode == FILL_MODE_RANDOM)
@@ -369,7 +407,7 @@ ipv6_frag_fill_data(struct rte_mbuf **mbuf, uint8_t 
nb_frags, uint32_t flow_id,
 }
 
 static int
-ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag)
+ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t max_frag, uint8_t num_exts)
 {
        uint8_t nb_frag;
        int i;
@@ -379,7 +417,7 @@ ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t 
max_frag)
                if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
                    0)
                        return TEST_FAILED;
-               ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+               ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode, num_exts);
                frag_per_flow[i] = nb_frag;
        }
        flow_cnt = i;
@@ -388,7 +426,7 @@ ipv6_rand_frag_pkt_setup(uint8_t fill_mode, uint8_t 
max_frag)
 }
 
 static int
-ipv6_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
+ipv6_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag, uint8_t num_exts)
 {
        int i;
 
@@ -396,7 +434,7 @@ ipv6_frag_pkt_setup(uint8_t fill_mode, uint8_t nb_frag)
                if (rte_mempool_get_bulk(pkt_pool, (void **)mbufs[i], nb_frag) <
                    0)
                        return TEST_FAILED;
-               ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode);
+               ipv6_frag_fill_data(mbufs[i], nb_frag, i, fill_mode, num_exts);
                frag_per_flow[i] = nb_frag;
        }
        flow_cnt = i;
@@ -414,7 +452,7 @@ frag_pkt_teardown(void)
 }
 
 static void
-reassembly_print_stats(int8_t nb_frags, uint8_t fill_order,
+reassembly_print_stats(int8_t nb_frags, uint8_t fill_order, uint8_t num_exts,
                       uint32_t outstanding, uint64_t cyc_per_flow,
                       uint64_t cyc_per_frag_insert,
                       uint64_t cyc_per_reassembly)
@@ -440,12 +478,12 @@ reassembly_print_stats(int8_t nb_frags, uint8_t 
fill_order,
                break;
        }
 
-       printf("| %-14s | %-14s | %-11d | %-11" PRIu64 " | %-22" PRIu64
+       printf("| %-14s | %-14s | %-12d | %-11d | %-11" PRIu64 " | %-22" PRIu64
               " | %-17" PRIu64 " |\n",
-              order_str, frag_str, outstanding, cyc_per_flow,
+              order_str, frag_str, num_exts, outstanding, cyc_per_flow,
               cyc_per_frag_insert, cyc_per_reassembly);
        printf("+================+================+=============+=============+"
-              "========================+===================+\n");
+              
"========================+==================================+\n");
 }
 
 static void
@@ -501,7 +539,7 @@ ipv4_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
                mbufs[i][0] = buf_out;
        }
 
-       reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
+       reassembly_print_stats(nb_frags, fill_order, 0, 0, total_cyc / flow_cnt,
                               total_empty_cyc / frag_processed,
                               total_reassembled_cyc / flow_cnt);
 
@@ -576,7 +614,7 @@ ipv4_outstanding_reassembly_perf(int8_t nb_frags, uint8_t 
fill_order,
                mbufs[i][0] = buf_out;
        }
 
-       reassembly_print_stats(nb_frags, fill_order, outstanding,
+       reassembly_print_stats(nb_frags, fill_order, 0, outstanding,
                               total_cyc / flow_cnt,
                               total_empty_cyc / frag_processed,
                               total_reassembled_cyc / flow_cnt);
@@ -642,7 +680,7 @@ ipv4_reassembly_interleaved_flows_perf(uint8_t nb_frags)
                }
        }
 
-       reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
+       reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0, 0,
                               total_cyc / flow_cnt,
                               total_empty_cyc / frag_processed,
                               total_reassembled_cyc / flow_cnt);
@@ -651,7 +689,7 @@ ipv4_reassembly_interleaved_flows_perf(uint8_t nb_frags)
 }
 
 static int
-ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
+ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order, uint8_t num_exts)
 {
        struct rte_ip_frag_death_row death_row;
        uint64_t total_reassembled_cyc = 0;
@@ -673,8 +711,8 @@ ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
                        struct ipv6_extension_fragment *frag_hdr =
                                rte_pktmbuf_mtod_offset(
                                        buf, struct ipv6_extension_fragment *,
-                                       buf->l2_len +
-                                               sizeof(struct rte_ipv6_hdr));
+                                       buf->l2_len + sizeof(struct 
rte_ipv6_hdr) +
+                                               num_exts * 
IPV6_ROUTING_HDR_SIZE);
 
                        tstamp = rte_rdtsc_precise();
                        buf_out = rte_ipv6_frag_reassemble_packet(
@@ -699,7 +737,7 @@ ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
                mbufs[i][0] = buf_out;
        }
 
-       reassembly_print_stats(nb_frags, fill_order, 0, total_cyc / flow_cnt,
+       reassembly_print_stats(nb_frags, fill_order, num_exts, 0, total_cyc / 
flow_cnt,
                               total_empty_cyc / frag_processed,
                               total_reassembled_cyc / flow_cnt);
 
@@ -708,7 +746,7 @@ ipv6_reassembly_perf(int8_t nb_frags, uint8_t fill_order)
 
 static int
 ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t fill_order,
-                                uint32_t outstanding)
+                                uint32_t outstanding, uint8_t num_exts)
 {
        struct rte_ip_frag_death_row death_row;
        uint64_t total_reassembled_cyc = 0;
@@ -731,8 +769,8 @@ ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t 
fill_order,
                        struct ipv6_extension_fragment *frag_hdr =
                                rte_pktmbuf_mtod_offset(
                                        buf, struct ipv6_extension_fragment *,
-                                       buf->l2_len +
-                                               sizeof(struct rte_ipv6_hdr));
+                                       buf->l2_len + sizeof(struct 
rte_ipv6_hdr) +
+                                               num_exts * 
IPV6_ROUTING_HDR_SIZE);
 
                        tstamp = rte_rdtsc_precise();
                        buf_out = rte_ipv6_frag_reassemble_packet(
@@ -761,8 +799,8 @@ ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t 
fill_order,
                        struct ipv6_extension_fragment *frag_hdr =
                                rte_pktmbuf_mtod_offset(
                                        buf, struct ipv6_extension_fragment *,
-                                       buf->l2_len +
-                                               sizeof(struct rte_ipv6_hdr));
+                                       buf->l2_len + sizeof(struct 
rte_ipv6_hdr) +
+                                               num_exts * 
IPV6_ROUTING_HDR_SIZE);
 
                        tstamp = rte_rdtsc_precise();
                        buf_out = rte_ipv6_frag_reassemble_packet(
@@ -787,7 +825,7 @@ ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t 
fill_order,
                mbufs[i][0] = buf_out;
        }
 
-       reassembly_print_stats(nb_frags, fill_order, outstanding,
+       reassembly_print_stats(nb_frags, fill_order, num_exts, outstanding,
                               total_cyc / flow_cnt,
                               total_empty_cyc / frag_processed,
                               total_reassembled_cyc / flow_cnt);
@@ -796,7 +834,7 @@ ipv6_outstanding_reassembly_perf(int8_t nb_frags, uint8_t 
fill_order,
 }
 
 static int
-ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags)
+ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags, uint8_t num_exts)
 {
        struct rte_ip_frag_death_row death_row;
        uint64_t total_reassembled_cyc = 0;
@@ -830,8 +868,8 @@ ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags)
                        struct ipv6_extension_fragment *frag_hdr =
                                rte_pktmbuf_mtod_offset(
                                        buf, struct ipv6_extension_fragment *,
-                                       buf->l2_len +
-                                               sizeof(struct rte_ipv6_hdr));
+                                       buf->l2_len + sizeof(struct 
rte_ipv6_hdr) +
+                                               num_exts * 
IPV6_ROUTING_HDR_SIZE);
 
                        tstamp = rte_rdtsc_precise();
                        buf_out[reassembled] = rte_ipv6_frag_reassemble_packet(
@@ -859,7 +897,7 @@ ipv6_reassembly_interleaved_flows_perf(int8_t nb_frags)
                }
        }
 
-       reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, 0,
+       reassembly_print_stats(nb_frags, FILL_MODE_INTERLEAVED, num_exts, 0,
                               total_cyc / flow_cnt,
                               total_empty_cyc / frag_processed,
                               total_reassembled_cyc / flow_cnt);
@@ -894,25 +932,27 @@ ipv4_reassembly_test(int8_t nb_frags, uint8_t fill_order, 
uint32_t outstanding)
 }
 
 static int
-ipv6_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding)
+ipv6_reassembly_test(int8_t nb_frags, uint8_t fill_order, uint32_t outstanding,
+                       uint8_t num_exts)
 {
        int rc;
 
        if (nb_frags > 0)
-               rc = ipv6_frag_pkt_setup(fill_order, nb_frags);
+               rc = ipv6_frag_pkt_setup(fill_order, nb_frags, num_exts);
        else
-               rc = ipv6_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS);
+               rc = ipv6_rand_frag_pkt_setup(fill_order, MAX_FRAGMENTS, 
num_exts);
+
 
        if (rc)
                return rc;
 
        if (outstanding)
                rc = ipv6_outstanding_reassembly_perf(nb_frags, fill_order,
-                                                     outstanding);
+                                                     outstanding, num_exts);
        else if (fill_order == FILL_MODE_INTERLEAVED)
-               rc = ipv6_reassembly_interleaved_flows_perf(nb_frags);
+               rc = ipv6_reassembly_interleaved_flows_perf(nb_frags, num_exts);
        else
-               rc = ipv6_reassembly_perf(nb_frags, fill_order);
+               rc = ipv6_reassembly_perf(nb_frags, fill_order, num_exts);
 
        frag_pkt_teardown();
 
@@ -925,7 +965,8 @@ test_reassembly_perf(void)
        int8_t nb_fragments[] = {2, 3, MAX_FRAGMENTS, -1 /* Random */};
        uint8_t order_type[] = {FILL_MODE_LINEAR, FILL_MODE_RANDOM};
        uint32_t outstanding[] = {100, 500, 1000, 2000, 3000};
-       uint32_t i, j;
+       uint8_t num_exts[] = {0, 4, 8};
+       uint32_t i, j, k;
        int rc;
 
        rc = reassembly_test_setup();
@@ -967,32 +1008,40 @@ test_reassembly_perf(void)
        /* Test variable fragment count and ordering. */
        for (i = 0; i < RTE_DIM(nb_fragments); i++) {
                for (j = 0; j < RTE_DIM(order_type); j++) {
-                       rc = ipv6_reassembly_test(nb_fragments[i],
-                                                 order_type[j], 0);
-                       if (rc)
-                               return rc;
+                       for (k = 0; k < RTE_DIM(num_exts); k++) {
+                               rc = ipv6_reassembly_test(nb_fragments[i],
+                                                         order_type[j], 0, 
num_exts[k]);
+                               if (rc)
+                                       return rc;
+                       }
                }
        }
 
        /* Test outstanding fragments in the table. */
        for (i = 0; i < RTE_DIM(outstanding); i++) {
-               rc = ipv6_reassembly_test(2, 0, outstanding[i]);
-               if (rc)
-                       return rc;
+               for (k = 0; k < RTE_DIM(num_exts); k++) {
+                       rc = ipv6_reassembly_test(2, 0, outstanding[i], 
num_exts[k]);
+                       if (rc)
+                               return rc;
+               }
        }
 
        for (i = 0; i < RTE_DIM(outstanding); i++) {
-               rc = ipv6_reassembly_test(MAX_FRAGMENTS, 0, outstanding[i]);
-               if (rc)
-                       return rc;
+               for (k = 0; k < RTE_DIM(num_exts); k++) {
+                       rc = ipv6_reassembly_test(MAX_FRAGMENTS, 0, 
outstanding[i], num_exts[k]);
+                       if (rc)
+                               return rc;
+               }
        }
 
        /* Test interleaved flow reassembly perf */
        for (i = 0; i < RTE_DIM(nb_fragments); i++) {
-               rc = ipv6_reassembly_test(nb_fragments[i],
-                                         FILL_MODE_INTERLEAVED, 0);
-               if (rc)
-                       return rc;
+               for (k = 0; k < RTE_DIM(num_exts); k++) {
+                       rc = ipv6_reassembly_test(nb_fragments[i],
+                                                 FILL_MODE_INTERLEAVED, 0, 
num_exts[k]);
+                       if (rc)
+                               return rc;
+               }
        }
        reassembly_test_teardown();
 
diff --git a/lib/ip_frag/ip_frag_common.h b/lib/ip_frag/ip_frag_common.h
index 51fc9d47fb..67f5f01c5e 100644
--- a/lib/ip_frag/ip_frag_common.h
+++ b/lib/ip_frag/ip_frag_common.h
@@ -34,6 +34,8 @@ extern int ipfrag_logtype;
 #define IPV4_KEYLEN 1
 #define IPV6_KEYLEN 4
 
+#define MAX_NUM_IPV6_EXTS 8
+
 /* helper macros */
 #define        IP_FRAG_MBUF2DR(dr, mb) ((dr)->row[(dr)->cnt++] = (mb))
 
@@ -169,6 +171,8 @@ ip_frag_reset(struct ip_frag_pkt *fp, uint64_t tms)
        fp->total_size = UINT32_MAX;
        fp->frag_size = 0;
        fp->last_idx = IP_MIN_FRAG_NUM;
+       fp->exts_len = 0;
+       fp->next_proto = NULL;
        fp->frags[IP_LAST_FRAG_IDX] = zero_frag;
        fp->frags[IP_FIRST_FRAG_IDX] = zero_frag;
 }
diff --git a/lib/ip_frag/ip_reassembly.h b/lib/ip_frag/ip_reassembly.h
index 54afed5417..689a36ed9a 100644
--- a/lib/ip_frag/ip_reassembly.h
+++ b/lib/ip_frag/ip_reassembly.h
@@ -51,9 +51,11 @@ struct __rte_cache_aligned ip_frag_pkt {
        RTE_TAILQ_ENTRY(ip_frag_pkt) lru;      /* LRU list */
        struct ip_frag_key key;                /* fragmentation key */
        uint64_t start;                        /* creation timestamp */
+       uint8_t *next_proto;                   /* pointer of the next_proto 
field */
        uint32_t total_size;                   /* expected reassembled size */
        uint32_t frag_size;                    /* size of fragments received */
        uint32_t last_idx;                     /* index of next entry to fill */
+       uint32_t exts_len;                     /* length of extension hdrs for 
first fragment */
        struct ip_frag frags[IP_MAX_FRAG_NUM]; /* fragments */
 };
 
diff --git a/lib/ip_frag/rte_ipv6_reassembly.c 
b/lib/ip_frag/rte_ipv6_reassembly.c
index 88863a98d1..fa41bd8e37 100644
--- a/lib/ip_frag/rte_ipv6_reassembly.c
+++ b/lib/ip_frag/rte_ipv6_reassembly.c
@@ -91,19 +91,19 @@ ipv6_frag_reassemble(struct ip_frag_pkt *fp)
        /* update ipv6 header for the reassembled datagram */
        ip_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv6_hdr *, m->l2_len);
 
+       payload_len += fp->exts_len;
        ip_hdr->payload_len = rte_cpu_to_be_16(payload_len);
 
        /*
         * remove fragmentation header. note that per RFC2460, we need to update
         * the last non-fragmentable header with the "next header" field to 
contain
-        * type of the first fragmentable header, but we currently don't support
-        * other headers, so we assume there are no other headers and thus 
update
-        * the main IPv6 header instead.
+        * type of the first fragmentable header.
         */
-       move_len = m->l2_len + m->l3_len - sizeof(*frag_hdr);
-       frag_hdr = (struct rte_ipv6_fragment_ext *) (ip_hdr + 1);
-       ip_hdr->proto = frag_hdr->next_header;
+       frag_hdr = (struct rte_ipv6_fragment_ext *)
+               ((uint8_t *) (ip_hdr + 1) + fp->exts_len);
+       *fp->next_proto = frag_hdr->next_header;
 
+       move_len = m->l2_len + m->l3_len - sizeof(*frag_hdr);
        ip_frag_memmove(rte_pktmbuf_mtod_offset(m, char *, sizeof(*frag_hdr)),
                        rte_pktmbuf_mtod(m, char*), move_len);
 
@@ -112,6 +112,42 @@ ipv6_frag_reassemble(struct ip_frag_pkt *fp)
        return m;
 }
 
+/*
+ * Function to crawl through the extension header stack.
+ * This function breaks as soon a the fragment header is
+ * found and returns the total length the traversed exts
+ * and the last extension before the fragment header
+ */
+static inline int
+ip_frag_get_last_exthdr(struct rte_ipv6_hdr *ip_hdr, uint8_t **last_ext)
+{
+       uint32_t total_len = 0;
+       uint8_t num_exts = 0;
+       size_t ext_len = 0;
+       *last_ext = (uint8_t *)(ip_hdr + 1);
+       int next_proto = ip_hdr->proto;
+
+       if (next_proto == IPPROTO_FRAGMENT)
+               return total_len;
+
+       while (next_proto != IPPROTO_FRAGMENT && num_exts < MAX_NUM_IPV6_EXTS) {
+
+               next_proto = rte_ipv6_get_next_ext(*last_ext, next_proto, 
&ext_len);
+               if (next_proto < 0)
+                       break;
+
+               total_len += ext_len;
+
+               if (next_proto == IPPROTO_FRAGMENT)
+                       return total_len;
+
+               *last_ext += ext_len;
+               num_exts++;
+       }
+
+       return -1;
+}
+
 /*
  * Process new mbuf with fragment of IPV6 datagram.
  * Incoming mbuf should have its l2_len/l3_len fields setup correctly.
@@ -139,6 +175,8 @@ rte_ipv6_frag_reassemble_packet(struct rte_ip_frag_tbl *tbl,
 {
        struct ip_frag_pkt *fp;
        struct ip_frag_key key;
+       uint8_t *last_ipv6_ext;
+       int exts_len;
        uint16_t ip_ofs;
        int32_t ip_len;
        int32_t trim;
@@ -154,10 +192,15 @@ rte_ipv6_frag_reassemble_packet(struct rte_ip_frag_tbl 
*tbl,
        /*
         * as per RFC2460, payload length contains all extension headers
         * as well.
-        * since we don't support anything but frag headers,
-        * this is what we remove from the payload len.
+        * so we remove the extension len from the payload len.
         */
-       ip_len = rte_be_to_cpu_16(ip_hdr->payload_len) - sizeof(*frag_hdr);
+       exts_len = ip_frag_get_last_exthdr(ip_hdr, &last_ipv6_ext);
+       if (exts_len < 0) {
+               IP_FRAG_MBUF2DR(dr, mb);
+               return NULL;
+       }
+
+       ip_len = rte_be_to_cpu_16(ip_hdr->payload_len) - exts_len - 
sizeof(*frag_hdr);
        trim = mb->pkt_len - (ip_len + mb->l3_len + mb->l2_len);
 
        IP_FRAG_LOG(DEBUG, "%s:%d:\n"
@@ -197,10 +240,24 @@ rte_ipv6_frag_reassemble_packet(struct rte_ip_frag_tbl 
*tbl,
                fp, IPv6_KEY_BYTES(fp->key.src_dst), fp->key.id, fp->start,
                fp->total_size, fp->frag_size, fp->last_idx);
 
+       /* store extension stack info, only for first fragment */
+       if (ip_ofs == 0) {
+               /*
+                * fp->next_proto points to either the IP's next header
+                * or th next header of the extension before the fragment
+                * extension
+                */
+               fp->next_proto = (uint8_t *)&ip_hdr->proto;
+               if (exts_len > 0) {
+                       fp->exts_len = exts_len;
+                       fp->next_proto = last_ipv6_ext;
+               }
+       }
 
        /* process the fragmented packet. */
        mb = ip_frag_process(fp, dr, mb, ip_ofs, ip_len,
                        MORE_FRAGS(frag_hdr->frag_data));
+
        ip_frag_inuse(tbl, fp);
 
        IP_FRAG_LOG(DEBUG, "%s:%d:\n"
-- 
2.34.1

Reply via email to