[ovs-dev] [patch v9 11/11] ipf: Add fragmentation status reporting.

Darrell Ball dlu998 at gmail.com
Mon Nov 19 19:09:30 UTC 2018


A new command "ovs-appctl dpctl/ipf-get-status" is added
for userspace datapath conntrack fragmentation support.
The command shows the configuration status, fragment counters and
ipf lists state.

Signed-off-by: Darrell Ball <dlu998 at gmail.com>
---
 NEWS                             |   2 +
 lib/ct-dpif.c                    |  34 +++++++-
 lib/ct-dpif.h                    |   9 +-
 lib/dpctl.c                      |  98 +++++++++++++++++++++-
 lib/dpctl.man                    |   7 ++
 lib/dpif-netdev.c                |  38 ++++++++-
 lib/dpif-netlink.c               |   6 +-
 lib/dpif-provider.h              |  41 ++++++++-
 lib/ipf.c                        | 114 +++++++++++++++++++++++++
 lib/ipf.h                        |  24 ++++++
 tests/system-kmod-macros.at      |  44 +++++++++-
 tests/system-traffic.at          |  41 ++++++---
 tests/system-userspace-macros.at | 175 ++++++++++++++++++++++++++++++++++++---
 13 files changed, 601 insertions(+), 32 deletions(-)

diff --git a/NEWS b/NEWS
index a64e0a5..3f2b72f 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,8 @@ Post-v2.10.0
        datapath conntrack fragmentation support.
      * New "ovs-appctl dpctl/ipf-set-max-nfrags" command for userspace datapath
        conntrack fragmentation support.
+     * New "ovs-appctl dpctl/ipf-get-status" command for userspace datapath
+       conntrack fragmentation support.
    - DPDK:
      * Add option for simple round-robin based Rxq to PMD assignment.
        It can be set with pmd-rxq-assign.
diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index aa34feb..b2c9b43 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Nicira, Inc.
+ * Copyright (c) 2015, 2018 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -218,6 +218,38 @@ ct_dpif_ipf_set_max_nfrags(struct dpif *dpif, uint32_t max_frags)
             : EOPNOTSUPP);
 }
 
+int ct_dpif_ipf_get_status(struct dpif *dpif,
+                           struct dpif_ipf_status *dpif_ipf_status)
+{
+    return (dpif->dpif_class->ipf_get_status
+            ? dpif->dpif_class->ipf_get_status(dpif, dpif_ipf_status)
+            : EOPNOTSUPP);
+}
+
+int
+ct_dpif_ipf_dump_start(struct dpif *dpif, struct ipf_dump_ctx **dump_ctx)
+{
+    return (dpif->dpif_class->ipf_dump_start
+           ? dpif->dpif_class->ipf_dump_start(dpif, dump_ctx)
+           : EOPNOTSUPP);
+}
+
+int
+ct_dpif_ipf_dump_next(struct dpif *dpif, void *dump_ctx,  char **dump)
+{
+    return (dpif->dpif_class->ipf_dump_next
+            ? dpif->dpif_class->ipf_dump_next(dpif, dump_ctx, dump)
+            : EOPNOTSUPP);
+}
+
+int
+ct_dpif_ipf_dump_done(struct dpif *dpif, void *dump_ctx)
+{
+    return (dpif->dpif_class->ipf_dump_done
+            ? dpif->dpif_class->ipf_dump_done(dpif, dump_ctx)
+            : EOPNOTSUPP);
+}
+
 void
 ct_dpif_entry_uninit(struct ct_dpif_entry *entry)
 {
diff --git a/lib/ct-dpif.h b/lib/ct-dpif.h
index d104e8a..0151cfe 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Nicira, Inc.
+ * Copyright (c) 2015, 2018 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -186,6 +186,8 @@ enum {
 };
 
 struct dpif;
+struct dpif_ipf_status;
+struct ipf_dump_ctx;
 
 struct ct_dpif_dump_state {
     struct dpif *dpif;
@@ -215,6 +217,11 @@ int ct_dpif_del_limits(struct dpif *dpif, const struct ovs_list *);
 int ct_dpif_ipf_set_enabled(struct dpif *, bool v6, bool enable);
 int ct_dpif_ipf_set_min_frag(struct dpif *, bool v6, uint32_t min_frag);
 int ct_dpif_ipf_set_max_nfrags(struct dpif *, uint32_t max_frags);
+int ct_dpif_ipf_get_status(struct dpif *dpif,
+                           struct dpif_ipf_status *dpif_ipf_status);
+int ct_dpif_ipf_dump_start(struct dpif *dpif, struct ipf_dump_ctx **);
+int ct_dpif_ipf_dump_next(struct dpif *dpif, void *, char **);
+int ct_dpif_ipf_dump_done(struct dpif *dpif, void *);
 void ct_dpif_entry_uninit(struct ct_dpif_entry *);
 void ct_dpif_format_entry(const struct ct_dpif_entry *, struct ds *,
                           bool verbose, bool print_stats);
diff --git a/lib/dpctl.c b/lib/dpctl.c
index e020234..f5a09b7 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2017 Nicira, Inc.
+ * Copyright (c) 2008-2018 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
 #include "dirs.h"
 #include "dpctl.h"
 #include "dpif.h"
+#include "dpif-provider.h"
 #include "openvswitch/dynamic-string.h"
 #include "flow.h"
 #include "openvswitch/match.h"
@@ -2028,6 +2029,99 @@ dpctl_ipf_set_max_nfrags(int argc, const char *argv[],
     return error;
 }
 
+static void
+dpctl_dump_ipf(struct dpif *dpif, struct dpctl_params *dpctl_p)
+{
+    struct ipf_dump_ctx *dump_ctx;
+    char *dump;
+
+    int error = ct_dpif_ipf_dump_start(dpif, &dump_ctx);
+    if (error) {
+        dpctl_error(dpctl_p, error, "starting ipf list dump");
+        /* Nothing to clean up, just return. */
+        return;
+    }
+
+    dpctl_print(dpctl_p, "\n        Fragment Lists:\n\n");
+    while (!(error = ct_dpif_ipf_dump_next(dpif, dump_ctx, &dump))) {
+        dpctl_print(dpctl_p, "%s\n", dump);
+        free(dump);
+    }
+
+    if (error && error != EOF) {
+        dpctl_error(dpctl_p, error, "dumping ipf lists failed");
+    }
+
+    ct_dpif_ipf_dump_done(dpif, dump_ctx);
+}
+
+static int
+dpctl_ct_ipf_get_status(int argc, const char *argv[],
+                        struct dpctl_params *dpctl_p)
+{
+    struct dpif *dpif;
+    int error = opt_dpif_open(argc, argv, dpctl_p, 2, &dpif);
+
+    if (!error) {
+        struct dpif_ipf_status dpif_ipf_status;
+        error = ct_dpif_ipf_get_status(dpif, &dpif_ipf_status);
+
+        if (!error) {
+            dpctl_print(dpctl_p, "        Fragmentation Module Status\n");
+            dpctl_print(dpctl_p, "        ---------------------------\n");
+            dpctl_print(dpctl_p, "        v4 enabled: %u\n",
+                        dpif_ipf_status.v4.enabled);
+            dpctl_print(dpctl_p, "        v6 enabled: %u\n",
+                        dpif_ipf_status.v6.enabled);
+            dpctl_print(dpctl_p, "        max num frags (v4/v6): %u\n",
+                        dpif_ipf_status.nfrag_max);
+            dpctl_print(dpctl_p, "        num frag: %u\n",
+                        dpif_ipf_status.nfrag);
+            dpctl_print(dpctl_p, "        min v4 frag size: %u\n",
+                        dpif_ipf_status.v4.min_frag_size);
+            dpctl_print(dpctl_p, "        v4 frags accepted: %"PRIu64"\n",
+                        dpif_ipf_status.v4.nfrag_accepted);
+            dpctl_print(dpctl_p, "        v4 frags completed: %"PRIu64"\n",
+                        dpif_ipf_status.v4.nfrag_completed_sent);
+            dpctl_print(dpctl_p, "        v4 frags expired: %"PRIu64"\n",
+                        dpif_ipf_status.v4.nfrag_expired_sent);
+            dpctl_print(dpctl_p, "        v4 frags too small: %"PRIu64"\n",
+                        dpif_ipf_status.v4.nfrag_too_small);
+            dpctl_print(dpctl_p, "        v4 frags overlapped: %"PRIu64"\n",
+                        dpif_ipf_status.v4.nfrag_overlap);
+            dpctl_print(dpctl_p, "        v4 frags purged: %"PRIu64"\n",
+                        dpif_ipf_status.v4.nfrag_purged);
+
+            dpctl_print(dpctl_p, "        min v6 frag size: %u\n",
+                        dpif_ipf_status.v6.min_frag_size);
+            dpctl_print(dpctl_p, "        v6 frags accepted: %"PRIu64"\n",
+                        dpif_ipf_status.v6.nfrag_accepted);
+            dpctl_print(dpctl_p, "        v6 frags completed: %"PRIu64"\n",
+                        dpif_ipf_status.v6.nfrag_completed_sent);
+            dpctl_print(dpctl_p, "        v6 frags expired: %"PRIu64"\n",
+                        dpif_ipf_status.v6.nfrag_expired_sent);
+            dpctl_print(dpctl_p, "        v6 frags too small: %"PRIu64"\n",
+                        dpif_ipf_status.v6.nfrag_too_small);
+            dpctl_print(dpctl_p, "        v6 frags overlapped: %"PRIu64"\n",
+                        dpif_ipf_status.v6.nfrag_overlap);
+            dpctl_print(dpctl_p, "        v6 frags purged: %"PRIu64"\n",
+                        dpif_ipf_status.v6.nfrag_purged);
+        } else {
+            dpctl_error(dpctl_p, error,
+                        "ipf status could not be retrieved");
+            return error;
+        }
+
+        if (dpctl_p->verbosity) {
+            dpctl_dump_ipf(dpif, dpctl_p);
+        }
+
+        dpif_close(dpif);
+    }
+
+    return error;
+}
+
 /* Undocumented commands for unit testing. */
 
 static int
@@ -2339,6 +2433,8 @@ static const struct dpctl_command all_commands[] = {
        dpctl_ipf_set_min_frag, DP_RW },
     { "ipf-set-max-nfrags", "[dp] maxfrags", 1, 2,
        dpctl_ipf_set_max_nfrags, DP_RW },
+    { "ipf-get-status", "[dp]", 0, 1, dpctl_ct_ipf_get_status,
+       DP_RO },
     { "help", "", 0, INT_MAX, dpctl_help, DP_RO },
     { "list-commands", "", 0, INT_MAX, dpctl_list_commands, DP_RO },
 
diff --git a/lib/dpctl.man b/lib/dpctl.man
index 43eb45f..16c6477 100644
--- a/lib/dpctl.man
+++ b/lib/dpctl.man
@@ -249,6 +249,13 @@ after 15 seconds.  Memory pool sizing should be set accordingly when
 fragmentation is enabled.  Only supported for userspace datapath.
 .
 .TP
+.DO "[\fB\-m\fR | \fB\-\-more\fR]" "\*(DX\fBipf\-get\-status\fR [\fIdp\fR]"
+Gets the configuration settings and fragment counters associated with the
+fragmentation handling of the userspace datapath connection tracker.
+With \fB\-m\fR or \fB\-\-more\fR, also dumps the IP fragment lists.
+Only supported for userspace datapath.
+.
+.TP
 .DO "[\fB\-m\fR | \fB\-\-more\fR] [\fB\-s\fR | \fB\-\-statistics\fR]" "\*(DX\fBdump\-conntrack\fR" "[\fIdp\fR] [\fBzone=\fIzone\fR]"
 Prints to the console all the connection entries in the tracker used by
 \fIdp\fR.  If \fBzone=\fIzone\fR is specified, only shows the connections
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 221ce18..6e043d8 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Nicira, Inc.
+ * Copyright (c) 2009-2014, 2016-2018 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -6944,6 +6944,38 @@ dpif_netdev_ipf_set_max_nfrags(struct dpif *dpif OVS_UNUSED,
     return ipf_set_max_nfrags(max_frags);
 }
 
+/* Adjust this function if 'dpif_ipf_status' and 'ipf_status' were to
+ * diverge. */
+static int
+dpif_netdev_ipf_get_status(struct dpif *dpif OVS_UNUSED,
+                           struct dpif_ipf_status *dpif_ipf_status)
+{
+    ipf_get_status((struct ipf_status *) dpif_ipf_status);
+    return 0;
+}
+
+static int
+dpif_netdev_ipf_dump_start(struct dpif *dpif OVS_UNUSED,
+                           struct ipf_dump_ctx **ipf_dump_ctx)
+{
+    return ipf_dump_start(ipf_dump_ctx);
+}
+
+static int
+dpif_netdev_ipf_dump_next(struct dpif *dpif OVS_UNUSED,
+                          void *ipf_dump_ctx, char **dump)
+{
+    return ipf_dump_next(ipf_dump_ctx, dump);
+}
+
+static int
+dpif_netdev_ipf_dump_done(struct dpif *dpif OVS_UNUSED,
+                          void *ipf_dump_ctx)
+{
+    return ipf_dump_done(ipf_dump_ctx);
+
+}
+
 const struct dpif_class dpif_netdev_class = {
     "netdev",
     dpif_netdev_init,
@@ -6998,6 +7030,10 @@ const struct dpif_class dpif_netdev_class = {
     dpif_netdev_ipf_set_enabled,
     dpif_netdev_ipf_set_min_frag,
     dpif_netdev_ipf_set_max_nfrags,
+    dpif_netdev_ipf_get_status,
+    dpif_netdev_ipf_dump_start,
+    dpif_netdev_ipf_dump_next,
+    dpif_netdev_ipf_dump_done,
     dpif_netdev_meter_get_features,
     dpif_netdev_meter_set,
     dpif_netdev_meter_get,
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index b08eea1..4edf4ab 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2017 Nicira, Inc.
+ * Copyright (c) 2008-2018 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -3432,6 +3432,10 @@ const struct dpif_class dpif_netlink_class = {
     NULL,                       /* ipf_set_enabled */
     NULL,                       /* ipf_set_min_frag */
     NULL,                       /* ipf_set_max_nfrags */
+    NULL,                       /* ipf_get_status */
+    NULL,                       /* ipf_dump_start */
+    NULL,                       /* ipf_dump_next */
+    NULL,                       /* ipf_dump_done */
     dpif_netlink_meter_get_features,
     dpif_netlink_meter_set,
     dpif_netlink_meter_get,
diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
index 7cc7804..b2a4dff 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009-2014, 2018 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -42,6 +42,9 @@ struct dpif {
     long long int current_ms;
 };
 
+struct dpif_ipf_status;
+struct ipf_dump_ctx;
+
 void dpif_init(struct dpif *, const struct dpif_class *, const char *name,
                uint8_t netflow_engine_type, uint8_t netflow_engine_id);
 void dpif_uninit(struct dpif *dpif, bool close);
@@ -78,6 +81,27 @@ struct ct_dpif_dump_state;
 struct ct_dpif_entry;
 struct ct_dpif_tuple;
 
+/* 'dpif_ipf_proto_status' and 'dpif_ipf_status' are presently in
+ * sync with 'ipf_proto_status' and 'ipf_status', but more
+ * generally represent a superset of present and future support. */
+struct dpif_ipf_proto_status {
+   uint64_t nfrag_accepted;
+   uint64_t nfrag_completed_sent;
+   uint64_t nfrag_expired_sent;
+   uint64_t nfrag_too_small;
+   uint64_t nfrag_overlap;
+   uint64_t nfrag_purged;
+   unsigned int min_frag_size;
+   bool enabled;
+};
+
+struct dpif_ipf_status {
+   struct dpif_ipf_proto_status v4;
+   struct dpif_ipf_proto_status v6;
+   unsigned int nfrag;
+   unsigned int nfrag_max;
+};
+
 /* Datapath interface class structure, to be defined by each implementation of
  * a datapath interface.
  *
@@ -480,6 +504,21 @@ struct dpif_class {
     /* Set maximum number of fragments tracked. */
     int (*ipf_set_max_nfrags)(struct dpif *, uint32_t max_nfrags);
 
+    /* Get fragmentation configuration status and counters. */
+    int (*ipf_get_status)(struct dpif *,
+                          struct dpif_ipf_status *dpif_ipf_status);
+
+    /* The following 3 apis find and print ipf lists by creating a string
+     * representation of the state of an ipf list, to which 'dump' is pointed
+     * to.  'ipf_dump_start()' allocates memory for 'ipf_dump_ctx'.
+     * 'ipf_dump_next()' finds the next ipf list and copies it's
+     * characteristics to a string, which is freed by the caller.
+     * 'ipf_dump_done()' frees the 'ipf_dump_ctx' that was allocated in
+     * 'ipf_dump_start'. */
+    int (*ipf_dump_start)(struct dpif *, struct ipf_dump_ctx **ipf_dump_ctx);
+    int (*ipf_dump_next)(struct dpif *, void *ipf_dump_ctx, char **dump);
+    int (*ipf_dump_done)(struct dpif *, void *ipf_dump_ctx);
+
     /* Meters */
 
     /* Queries 'dpif' for supported meter features.
diff --git a/lib/ipf.c b/lib/ipf.c
index b3f5045..631e7bc 100644
--- a/lib/ipf.c
+++ b/lib/ipf.c
@@ -54,6 +54,10 @@ enum ipf_list_state {
     IPF_LIST_STATE_NUM,
 };
 
+static char *ipf_state_name[IPF_LIST_STATE_NUM] =
+    {"unused", "reassemble fail", "other frag", "first frag", "last frag",
+     "first/last frag", "complete"};
+
 enum ipf_list_type {
     IPF_FRAG_COMPLETED_LIST,
     IPF_FRAG_EXPIRY_LIST,
@@ -1401,3 +1405,113 @@ ipf_set_max_nfrags(uint32_t value)
     atomic_store_relaxed(&nfrag_max, value);
     return 0;
 }
+
+int
+ipf_get_status(struct ipf_status *ipf_status)
+{
+    ipf_status->nfrag = atomic_count_get(&nfrag);
+    atomic_read_relaxed(&nfrag_max, &ipf_status->nfrag_max);
+
+    atomic_read_relaxed(&ifp_v4_enabled, &ipf_status->v4.enabled);
+    atomic_read_relaxed(&min_v4_frag_size, &ipf_status->v4.min_frag_size);
+    atomic_read_relaxed(&n4frag_accepted, &ipf_status->v4.nfrag_accepted);
+    atomic_read_relaxed(&n4frag_completed_sent,
+                        &ipf_status->v4.nfrag_completed_sent);
+    atomic_read_relaxed(&n4frag_expired_sent,
+                        &ipf_status->v4.nfrag_expired_sent);
+    atomic_read_relaxed(&n4frag_too_small, &ipf_status->v4.nfrag_too_small);
+    atomic_read_relaxed(&n4frag_overlap, &ipf_status->v4.nfrag_overlap);
+    atomic_read_relaxed(&n4frag_purged, &ipf_status->v4.nfrag_purged);
+
+    atomic_read_relaxed(&ifp_v6_enabled, &ipf_status->v6.enabled);
+    atomic_read_relaxed(&min_v6_frag_size, &ipf_status->v6.min_frag_size);
+    atomic_read_relaxed(&n6frag_accepted, &ipf_status->v6.nfrag_accepted);
+    atomic_read_relaxed(&n6frag_completed_sent,
+                        &ipf_status->v6.nfrag_completed_sent);
+    atomic_read_relaxed(&n6frag_expired_sent,
+                        &ipf_status->v6.nfrag_expired_sent);
+    atomic_read_relaxed(&n6frag_too_small, &ipf_status->v6.nfrag_too_small);
+    atomic_read_relaxed(&n6frag_overlap, &ipf_status->v6.nfrag_overlap);
+    atomic_read_relaxed(&n4frag_overlap, &ipf_status->v4.nfrag_overlap);
+    atomic_read_relaxed(&n6frag_purged, &ipf_status->v6.nfrag_purged);
+    return 0;
+}
+
+struct ipf_dump_ctx {
+    struct hmap_position bucket_pos;
+};
+
+/* Allocates an 'ipf_dump_ctx' to keep track of an hmap position. The
+ * caller must call ipf_dump_done() when dumping is finished. */
+int
+ipf_dump_start(struct ipf_dump_ctx **ipf_dump_ctx)
+{
+    *ipf_dump_ctx = xzalloc(sizeof **ipf_dump_ctx);
+    return 0;
+}
+
+/* Creates a string representation of the state of an 'ipf_list' and puts
+ * it in  'ds'. */
+static void
+ipf_dump_create(const struct ipf_list *ipf_list, struct ds *ds)
+{
+
+    ds_put_cstr(ds, "(");
+    if (ipf_list->key.dl_type == htons(ETH_TYPE_IP)) {
+        ds_put_format(ds, "src="IP_FMT",dst="IP_FMT",",
+                      IP_ARGS(ipf_list->key.src_addr.ipv4_aligned),
+                      IP_ARGS(ipf_list->key.dst_addr.ipv4_aligned));
+    } else {
+        ds_put_cstr(ds, "src=");
+        ipv6_format_addr(&ipf_list->key.src_addr.ipv6_aligned, ds);
+        ds_put_cstr(ds, ",dst=");
+        ipv6_format_addr(&ipf_list->key.dst_addr.ipv6_aligned, ds);
+        ds_put_cstr(ds, ",");
+    }
+
+    ds_put_format(ds, "recirc_id=%u,ip_id=%u,dl_type=0x%x,zone=%u,nw_proto=%u",
+                  ipf_list->key.recirc_id, ntohl(ipf_list->key.ip_id),
+                  ntohs(ipf_list->key.dl_type), ipf_list->key.zone,
+                  ipf_list->key.nw_proto);
+
+    ds_put_format(ds, ",num_fragments=%u,state=%s",
+                  ipf_list->last_inuse_idx + 1,
+                  ipf_state_name[ipf_list->state]);
+
+    ds_put_cstr(ds, ")");
+}
+
+/* Finds the next ipf list starting from 'ipf_dump_ctx->bucket_pos' and uses
+ * ipf_dump_create() to create a string representation of the state of an
+ * ipf list, to which 'dump' is pointed to.  Returns EOF when there are no
+ * more ipf lists. */
+int
+ipf_dump_next(struct ipf_dump_ctx *ipf_dump_ctx, char **dump)
+{
+    ipf_lock_lock(&ipf_lock);
+
+    struct hmap_node *node = hmap_at_position(&frag_lists,
+                                              &ipf_dump_ctx->bucket_pos);
+    if (!node) {
+        ipf_lock_unlock(&ipf_lock);
+        return EOF;
+    } else {
+        struct ipf_list *ipf_list_;
+        INIT_CONTAINER(ipf_list_, node, node);
+        struct ipf_list ipf_list = *ipf_list_;
+        ipf_lock_unlock(&ipf_lock);
+        struct ds ds = DS_EMPTY_INITIALIZER;
+        ipf_dump_create(&ipf_list, &ds);
+        *dump = xstrdup(ds.string);
+        ds_destroy(&ds);
+        return 0;
+    }
+}
+
+/* Frees 'ipf_dump_ctx' allocated by ipf_dump_start(). */
+int
+ipf_dump_done(struct ipf_dump_ctx *ipf_dump_ctx)
+{
+    free(ipf_dump_ctx);
+    return 0;
+}
diff --git a/lib/ipf.h b/lib/ipf.h
index 6cf4d1e..fd0a19c 100644
--- a/lib/ipf.h
+++ b/lib/ipf.h
@@ -20,6 +20,24 @@
 #include "dp-packet.h"
 #include "openvswitch/types.h"
 
+struct ipf_proto_status {
+   uint64_t nfrag_accepted;
+   uint64_t nfrag_completed_sent;
+   uint64_t nfrag_expired_sent;
+   uint64_t nfrag_too_small;
+   uint64_t nfrag_overlap;
+   uint64_t nfrag_purged;
+   unsigned int min_frag_size;
+   bool enabled;
+};
+
+struct ipf_status {
+   struct ipf_proto_status v4;
+   struct ipf_proto_status v6;
+   unsigned int nfrag;
+   unsigned int nfrag_max;
+};
+
 void ipf_preprocess_conntrack(struct dp_packet_batch *pb, long long now,
                               ovs_be16 dl_type, uint16_t zone,
                               uint32_t hash_basis);
@@ -32,5 +50,11 @@ void ipf_destroy(void);
 int ipf_set_enabled(bool v6, bool enable);
 int ipf_set_min_frag(bool v6, uint32_t value);
 int ipf_set_max_nfrags(uint32_t value);
+int ipf_get_status(struct ipf_status *ipf_status);
+
+struct ipf_dump_ctx;
+int ipf_dump_start(struct ipf_dump_ctx **ipf_dump_ctx);
+int ipf_dump_next(struct ipf_dump_ctx *ipf_dump_ctx, char **dump);
+int ipf_dump_done(struct ipf_dump_ctx *ipf_dump_ctx);
 
 #endif /* ipf.h */
diff --git a/tests/system-kmod-macros.at b/tests/system-kmod-macros.at
index 3fbead8..1057e34 100644
--- a/tests/system-kmod-macros.at
+++ b/tests/system-kmod-macros.at
@@ -85,10 +85,6 @@ m4_define([CHECK_CONNTRACK_ALG])
 # needed.
 m4_define([CHECK_CONNTRACK_LOCAL_STACK])
 
-# CHECK_CONNTRACK_SMALL_FRAG()
-#
-m4_define([CHECK_CONNTRACK_SMALL_FRAG])
-
 # CHECK_CONNTRACK_FRAG_OVERLAP()
 #
 # The kernel does not support overlapping fragments checking.
@@ -138,6 +134,46 @@ m4_define([CHECK_CT_DPIF_GET_NCONNS],
     AT_SKIP_IF([:])
 ])
 
+# DPCTL_SET_MIN_FRAG_SIZE()
+#
+# The kernel does not support this command.
+m4_define([DPCTL_SET_MIN_FRAG_SIZE],
+[
+
+])
+
+# DPCTL_MODIFY_FRAGMENTATION()
+#
+# The kernel does not support this command.
+m4_define([DPCTL_MODIFY_FRAGMENTATION],
+[
+
+])
+
+# DPCTL_CHECK_FRAGMENTATION_PASS()
+#
+# The kernel does not support this command.
+m4_define([DPCTL_CHECK_FRAGMENTATION_PASS],
+[
+
+])
+
+# DPCTL_CHECK_V6_FRAGMENTATION_PASS()
+#
+# The kernel does not support this command.
+m4_define([DPCTL_CHECK_V6_FRAGMENTATION_PASS],
+[
+
+])
+
+# DPCTL_CHECK_FRAGMENTATION_FAIL()
+#
+# The kernel does not support this command.
+m4_define([DPCTL_CHECK_FRAGMENTATION_FAIL],
+[
+
+])
+
 # OVS_CHECK_KERNEL([minversion], [minsublevel], [maxversion], [maxsublevel])
 #
 # Check if kernel version falls between minversion.minsublevel and
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index c4f6e47..7229aab 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -2365,6 +2365,9 @@ priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1
 
 AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
 
+dnl Modify userspace conntrack fragmentation handling.
+DPCTL_MODIFY_FRAGMENTATION()
+
 dnl Ipv4 fragmentation connectivity check.
 NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
@@ -2375,6 +2378,9 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
 ])
 
+dnl Check userspace conntrack fragmentation counters.
+DPCTL_CHECK_FRAGMENTATION_PASS()
+
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
@@ -2400,11 +2406,17 @@ priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1
 
 AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
 
+dnl Modify userspace conntrack fragmentation handling.
+DPCTL_MODIFY_FRAGMENTATION()
+
 dnl Ipv4 fragmentation connectivity check.
 NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 1 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl
 7 packets transmitted, 0 received, 100% packet loss, time 0ms
 ])
 
+dnl Check userspace conntrack fragmentation counters.
+DPCTL_CHECK_FRAGMENTATION_FAIL()
+
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
@@ -2430,6 +2442,9 @@ priority=100,in_port=2,ct_state=+trk+est-new,icmp,action=1
 
 AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
 
+dnl Modify userspace conntrack fragmentation handling.
+DPCTL_MODIFY_FRAGMENTATION()
+
 dnl Ipv4 fragmentation connectivity check.
 NS_CHECK_EXEC([at_ns0], [ping -s 1600 -q -c 3 -i 0.3 -w 2 10.2.2.2 | FORMAT_PING], [0], [dnl
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
@@ -2440,6 +2455,9 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.2.2.2 | FORMAT_PING
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
 ])
 
+dnl Check userspace conntrack fragmentation counters.
+DPCTL_CHECK_FRAGMENTATION_PASS()
+
 OVS_TRAFFIC_VSWITCHD_STOP
 AT_CLEANUP
 
@@ -2498,6 +2516,8 @@ AT_CLEANUP
 AT_SETUP([conntrack - IPv4 fragmentation incomplete reassembled packet])
 CHECK_CONNTRACK()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
+
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2519,8 +2539,8 @@ AT_CLEANUP
 dnl Uses same first fragment as above 'incomplete reassembled packet' test.
 AT_SETUP([conntrack - IPv4 fragmentation with fragments specified])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2543,8 +2563,8 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv4 fragmentation out of order])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2567,9 +2587,9 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv4 fragmentation overlapping fragments by 1 octet])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 CHECK_CONNTRACK_FRAG_OVERLAP()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2591,9 +2611,9 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv4 fragmentation overlapping fragments by 1 octet out of order])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 CHECK_CONNTRACK_FRAG_OVERLAP()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2790,6 +2810,7 @@ AT_CLEANUP
 AT_SETUP([conntrack - IPv6 fragmentation incomplete reassembled packet])
 CHECK_CONNTRACK()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2810,8 +2831,8 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv6 fragmentation with fragments specified])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2834,8 +2855,8 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv6 fragmentation out of order])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2858,9 +2879,9 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 CHECK_CONNTRACK_FRAG_IPV6_MULT_EXTEN()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2884,9 +2905,9 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers + out of order])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 CHECK_CONNTRACK_FRAG_IPV6_MULT_EXTEN()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2910,9 +2931,9 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers 2])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 CHECK_CONNTRACK_FRAG_IPV6_MULT_EXTEN()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
@@ -2936,9 +2957,9 @@ AT_CLEANUP
 
 AT_SETUP([conntrack - IPv6 fragmentation, multiple extension headers 2 + out of order])
 CHECK_CONNTRACK()
-CHECK_CONNTRACK_SMALL_FRAG()
 CHECK_CONNTRACK_FRAG_IPV6_MULT_EXTEN()
 OVS_TRAFFIC_VSWITCHD_START()
+DPCTL_SET_MIN_FRAG_SIZE()
 
 ADD_NAMESPACES(at_ns0, at_ns1)
 
diff --git a/tests/system-userspace-macros.at b/tests/system-userspace-macros.at
index 219c046..4ea55ea 100644
--- a/tests/system-userspace-macros.at
+++ b/tests/system-userspace-macros.at
@@ -84,20 +84,10 @@ m4_define([CHECK_CONNTRACK_LOCAL_STACK],
     AT_SKIP_IF([:])
 ])
 
-# CHECK_CONNTRACK_SMALL_FRAG()
-#
-m4_define([CHECK_CONNTRACK_SMALL_FRAG],
-[
-    AT_SKIP_IF([:])
-])
-
 # CHECK_CONNTRACK_FRAG_OVERLAP()
 #
-# The userspace datapath does not support fragments yet.
-m4_define([CHECK_CONNTRACK_FRAG_OVERLAP],
-[
-    AT_SKIP_IF([:])
-])
+# The userspace datapath supports fragment overlap check.
+m4_define([CHECK_CONNTRACK_FRAG_OVERLAP])
 
 # CHECK_CONNTRACK_FRAG_IPV6_MULT_EXTEN
 #
@@ -132,6 +122,167 @@ m4_define([CHECK_CT_DPIF_SET_GET_MAXCONNS])
 # userspace datapath does support this feature.
 m4_define([CHECK_CT_DPIF_GET_NCONNS])
 
+# DPCTL_SET_MIN_FRAG_SIZE()
+#
+# The userspace datapath supports this command.
+m4_define([DPCTL_SET_MIN_FRAG_SIZE],
+[
+AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v4 400], [], [dnl
+setting minimum fragment size successful
+])
+AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v6 400], [], [dnl
+setting minimum fragment size successful
+])
+])
+
+# DPCTL_MODIFY_FRAGMENTATION()
+#
+# The userspace datapath supports this command.
+m4_define([DPCTL_MODIFY_FRAGMENTATION],
+[
+AT_CHECK([ovs-appctl dpctl/ipf-set-min-frag v4 1000], [], [dnl
+setting minimum fragment size successful
+])
+AT_CHECK([ovs-appctl dpctl/ipf-set-max-nfrags 500], [], [dnl
+setting maximum fragments successful
+])
+AT_CHECK([ovs-appctl dpctl/ipf-get-status], [], [dnl
+        Fragmentation Module Status
+        ---------------------------
+        v4 enabled: 1
+        v6 enabled: 1
+        max num frags (v4/v6): 500
+        num frag: 0
+        min v4 frag size: 1000
+        v4 frags accepted: 0
+        v4 frags completed: 0
+        v4 frags expired: 0
+        v4 frags too small: 0
+        v4 frags overlapped: 0
+        v4 frags purged: 0
+        min v6 frag size: 1280
+        v6 frags accepted: 0
+        v6 frags completed: 0
+        v6 frags expired: 0
+        v6 frags too small: 0
+        v6 frags overlapped: 0
+        v6 frags purged: 0
+])
+])
+
+# DPCTL_CHECK_FRAGMENTATION_PASS()
+#
+# Used to check fragmentation counters for some fragmentation tests using
+# the userspace datapath.
+m4_define([DPCTL_CHECK_FRAGMENTATION_PASS],
+[
+AT_CHECK([ovs-appctl dpctl/ipf-get-status --more], [], [dnl
+        Fragmentation Module Status
+        ---------------------------
+        v4 enabled: 1
+        v6 enabled: 1
+        max num frags (v4/v6): 500
+        num frag: 0
+        min v4 frag size: 1000
+        v4 frags accepted: 30
+        v4 frags completed: 30
+        v4 frags expired: 0
+        v4 frags too small: 0
+        v4 frags overlapped: 0
+        v4 frags purged: 0
+        min v6 frag size: 1280
+        v6 frags accepted: 0
+        v6 frags completed: 0
+        v6 frags expired: 0
+        v6 frags too small: 0
+        v6 frags overlapped: 0
+        v6 frags purged: 0
+
+        Fragment Lists:
+
+])
+])
+
+# DPCTL_CHECK_V6_FRAGMENTATION_PASS()
+#
+# Used to check fragmentation counters for some fragmentation tests using
+# the userspace datapath.
+m4_define([DPCTL_CHECK_V6_FRAGMENTATION_PASS],
+[
+AT_CHECK([ovs-appctl dpctl/ipf-get-status --more], [], [dnl
+        Fragmentation Module Status
+        ---------------------------
+        v4 enabled: 1
+        v6 enabled: 1
+        max num frags (v4/v6): 1000
+        num frag: 0
+        min v4 frag size: 1200
+        v4 frags accepted: 0
+        v4 frags completed: 0
+        v4 frags expired: 0
+        v4 frags too small: 0
+        v4 frags overlapped: 0
+        v4 frags purged: 0
+        min v6 frag size: 1280
+        v6 frags accepted: 30
+        v6 frags completed: 30
+        v6 frags expired: 0
+        v6 frags too small: 0
+        v6 frags overlapped: 0
+        v6 frags purged: 0
+
+        Fragment Lists:
+
+])
+])
+
+# FORMAT_FRAG_LIST([])
+#
+# Strip content from the piped input which can differ from test to test; recirc_id
+# and ip_id fields in an ipf_list vary from test to test and hence are cleared.
+m4_define([FORMAT_FRAG_LIST],
+    [[sed -e 's/ip_id=[0-9]*/ip_id=<cleared>/g' -e 's/recirc_id=[0-9]*/recirc_id=<cleared>/g']])
+
+# DPCTL_CHECK_FRAGMENTATION_FAIL()
+#
+# Used to check fragmentation counters for some fragmentation tests using
+# the userspace datapath, when failure to transmit fragments is expected.
+m4_define([DPCTL_CHECK_FRAGMENTATION_FAIL],
+[
+AT_CHECK([ovs-appctl dpctl/ipf-get-status -m | FORMAT_FRAG_LIST()], [], [dnl
+        Fragmentation Module Status
+        ---------------------------
+        v4 enabled: 1
+        v6 enabled: 1
+        max num frags (v4/v6): 500
+        num frag: 7
+        min v4 frag size: 1000
+        v4 frags accepted: 7
+        v4 frags completed: 0
+        v4 frags expired: 0
+        v4 frags too small: 0
+        v4 frags overlapped: 0
+        v4 frags purged: 0
+        min v6 frag size: 1280
+        v6 frags accepted: 0
+        v6 frags completed: 0
+        v6 frags expired: 0
+        v6 frags too small: 0
+        v6 frags overlapped: 0
+        v6 frags purged: 0
+
+        Fragment Lists:
+
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+(src=10.1.1.1,dst=10.1.1.2,recirc_id=<cleared>,ip_id=<cleared>,dl_type=0x800,zone=9,nw_proto=1,num_fragments=1,state=first frag)
+])
+])
+
 # OVS_CHECK_KERNEL([minversion], [maxversion], [minsublevel], [maxsublevel])
 #
 # The userspace skips all tests that check kernel version.
-- 
1.9.1



More information about the dev mailing list