[ovs-dev,v7,9/9] ipf: Add fragmentation status reporting.

Message ID 1531180095-74941-10-git-send-email-dlu998@gmail.com
State New
Headers show
Series
  • Userspace datapath: Add fragmentation support.
Related show

Commit Message

Darrell Ball July 9, 2018, 11:48 p.m.
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@gmail.com>
---
 NEWS                             |   2 +
 lib/ct-dpif.c                    |  45 +++++++++++++++
 lib/ct-dpif.h                    |   9 +++
 lib/dpctl.c                      | 107 ++++++++++++++++++++++++++++++++++
 lib/dpctl.man                    |   6 ++
 lib/dpif-netdev.c                |  58 +++++++++++++++++++
 lib/dpif-netlink.c               |   4 ++
 lib/dpif-provider.h              |  16 +++++
 lib/ipf.c                        | 107 ++++++++++++++++++++++++++++++++++
 lib/ipf.h                        |  10 ++++
 tests/system-kmod-macros.at      |  32 ++++++++++
 tests/system-traffic.at          |  40 +++++++++----
 tests/system-userspace-macros.at | 122 +++++++++++++++++++++++++++++++++++++++
 13 files changed, 548 insertions(+), 10 deletions(-)

Patch

diff --git a/NEWS b/NEWS
index 2b22a84..af8f9a8 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,8 @@  Post-v2.9.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.
    - ovs-vsctl: New commands "add-bond-iface" and "del-bond-iface".
    - OpenFlow:
      * OFPT_ROLE_STATUS is now available in OpenFlow 1.3.
diff --git a/lib/ct-dpif.c b/lib/ct-dpif.c
index ee23a4d..a59bc1e 100644
--- a/lib/ct-dpif.c
+++ b/lib/ct-dpif.c
@@ -188,6 +188,51 @@  ct_dpif_ipf_set_max_nfrags(struct dpif *dpif, uint32_t max_frags)
             : EOPNOTSUPP);
 }
 
+int ct_dpif_ipf_get_status(struct dpif *dpif, bool *ipf_v4_enabled,
+    unsigned int *min_v4_frag_size, unsigned int *nfrag_max,
+    unsigned int *nfrag, unsigned int *n4frag_accepted,
+    unsigned int *n4frag_completed_sent,
+    unsigned int *n4frag_expired_sent, unsigned int *n4frag_too_small,
+    unsigned int *n4frag_overlap, bool *ipf_v6_enabled,
+    unsigned int *min_v6_frag_size, unsigned int *n6frag_accepted,
+    unsigned int *n6frag_completed_sent,
+    unsigned int *n6frag_expired_sent, unsigned int *n6frag_too_small,
+    unsigned int *n6frag_overlap)
+{
+    return (dpif->dpif_class->ipf_get_status
+            ? dpif->dpif_class->ipf_get_status(dpif, ipf_v4_enabled,
+            min_v4_frag_size, nfrag_max, nfrag, n4frag_accepted,
+            n4frag_completed_sent, n4frag_expired_sent, n4frag_too_small,
+            n4frag_overlap, ipf_v6_enabled, min_v6_frag_size, n6frag_accepted,
+            n6frag_completed_sent, n6frag_expired_sent, n6frag_too_small,
+            n6frag_overlap)
+            : 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 f886ab9..2ff7e26 100644
--- a/lib/ct-dpif.h
+++ b/lib/ct-dpif.h
@@ -204,6 +204,15 @@  int ct_dpif_get_nconns(struct dpif *dpif, uint32_t *nconns);
 int ct_dpif_ipf_set_enabled(struct dpif *, bool v6, bool enable);
 int ct_dpif_ipf_set_min_frag(struct dpif *, bool, uint32_t);
 int ct_dpif_ipf_set_max_nfrags(struct dpif *, uint32_t);
+int ct_dpif_ipf_get_status(struct dpif *dpif, bool *, unsigned int *,
+                           unsigned int *, unsigned int *, unsigned int *,
+                           unsigned int *, unsigned int *, unsigned int *,
+                           unsigned int *, bool *, unsigned int *,
+                           unsigned int *, unsigned int *, unsigned int *,
+                           unsigned int *, unsigned int *);
+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 ab0f60b..2b2a74a 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1792,6 +1792,111 @@  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, 3, &dpif);
+    if (!error) {
+        bool ipf_v4_enabled;
+        unsigned int min_v4_frag_size;
+        unsigned int nfrag_max;
+        unsigned int nfrag;
+        unsigned int n4frag_accepted;
+        unsigned int n4frag_completed_sent;
+        unsigned int n4frag_expired_sent;
+        unsigned int n4frag_too_small;
+        unsigned int n4frag_overlap;
+        unsigned int min_v6_frag_size;
+        bool ipf_v6_enabled;
+        unsigned int n6frag_accepted;
+        unsigned int n6frag_completed_sent;
+        unsigned int n6frag_expired_sent;
+        unsigned int n6frag_too_small;
+        unsigned int n6frag_overlap;
+        error = ct_dpif_ipf_get_status(dpif, &ipf_v4_enabled,
+            &min_v4_frag_size, &nfrag_max, &nfrag, &n4frag_accepted,
+            &n4frag_completed_sent, &n4frag_expired_sent, &n4frag_too_small,
+            &n4frag_overlap, &ipf_v6_enabled, &min_v6_frag_size,
+            &n6frag_accepted, &n6frag_completed_sent, &n6frag_expired_sent,
+            &n6frag_too_small, &n6frag_overlap);
+
+        if (!error) {
+            dpctl_print(dpctl_p, "        Fragmentation Module Status\n");
+            dpctl_print(dpctl_p, "        ---------------------------\n");
+            dpctl_print(dpctl_p, "        v4 enabled: %u\n", ipf_v4_enabled);
+            dpctl_print(dpctl_p, "        v6 enabled: %u\n", ipf_v6_enabled);
+            dpctl_print(dpctl_p, "        max num frags (v4/v6): %u\n",
+                        nfrag_max);
+            dpctl_print(dpctl_p, "        num frag: %u\n", nfrag);
+            dpctl_print(dpctl_p, "        min v4 frag size: %u\n",
+                        min_v4_frag_size);
+            dpctl_print(dpctl_p, "        v4 frags accepted: %u\n",
+                        n4frag_accepted);
+            dpctl_print(dpctl_p, "        v4 frags completed: %u\n",
+                        n4frag_completed_sent);
+            dpctl_print(dpctl_p, "        v4 frags expired: %u\n",
+                        n4frag_expired_sent);
+            dpctl_print(dpctl_p, "        v4 frags too small: %u\n",
+                        n4frag_too_small);
+            dpctl_print(dpctl_p, "        v4 frags overlapped: %u\n",
+                        n4frag_overlap);
+            dpctl_print(dpctl_p, "        min v6 frag size: %u\n",
+                        min_v6_frag_size);
+            dpctl_print(dpctl_p, "        v6 frags accepted: %u\n",
+                        n6frag_accepted);
+            dpctl_print(dpctl_p, "        v6 frags completed: %u\n",
+                        n6frag_completed_sent);
+            dpctl_print(dpctl_p, "        v6 frags expired: %u\n",
+                        n6frag_expired_sent);
+            dpctl_print(dpctl_p, "        v6 frags too small: %u\n",
+                        n6frag_too_small);
+            dpctl_print(dpctl_p, "        v6 frags overlapped: %u\n",
+                        n6frag_overlap);
+        } else {
+            dpctl_error(dpctl_p, error,
+                        "ipf status could not be retrieved");
+            return error;
+        }
+
+        if (argc > 0 && (!strncmp(argv[argc - 1], "-m", 2)
+            || !strncmp(argv[argc - 1], "--more", 6))) {
+            dpctl_dump_ipf(dpif, dpctl_p);
+        }
+
+        dpif_close(dpif);
+    }
+
+    return error;
+}
+
 /* Undocumented commands for unit testing. */
 
 static int
@@ -2099,6 +2204,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] [-m | --more]", 0, 2, 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 c6c4a87..fb66b58 100644
--- a/lib/dpctl.man
+++ b/lib/dpctl.man
@@ -304,3 +304,9 @@  connection tracker.  The default value is 1000 and the clamped maximum
 is 5000.  Note that packet buffers can be held by the fragmentation
 module while fragments are incomplete, but will timeout after 15 seconds.
 Memory pool sizing should be set accordingly when fragmentation is enabled.
+.
+.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 ipf lists.
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 76bc1d9..db551ea 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -6553,6 +6553,60 @@  dpif_netdev_ipf_set_max_nfrags(struct dpif *dpif OVS_UNUSED,
     return ipf_set_max_nfrags(max_frags);
 }
 
+static int
+dpif_netdev_ipf_get_status(struct dpif *dpif OVS_UNUSED,
+    bool *ipf_v4_enabled, unsigned int *min_v4_frag_size,
+    unsigned int *nfrag_max, unsigned int *nfrag,
+    unsigned int *n4frag_accepted, unsigned int *n4frag_completed_sent,
+    unsigned int *n4frag_expired_sent, unsigned int *n4frag_too_small,
+    unsigned int *n4frag_overlap, bool *ipf_v6_enabled,
+    unsigned int *min_v6_frag_size, unsigned int *n6frag_accepted,
+    unsigned int *n6frag_completed_sent, unsigned int *n6frag_expired_sent,
+    unsigned int *n6frag_too_small, unsigned int *n6frag_overlap)
+{
+    struct ipf_status ipf_status;
+    ipf_get_status(&ipf_status);
+    *ipf_v4_enabled = ipf_status.ifp_v4_enabled;
+    *min_v4_frag_size = ipf_status.min_v4_frag_size;
+    *nfrag_max = ipf_status.nfrag_max;
+    *nfrag = ipf_status.nfrag;
+    *n4frag_accepted = ipf_status.n4frag_accepted;
+    *n4frag_completed_sent = ipf_status.n4frag_completed_sent;
+    *n4frag_expired_sent = ipf_status.n4frag_expired_sent;
+    *n4frag_too_small = ipf_status.n4frag_too_small;
+    *n4frag_overlap = ipf_status.n4frag_overlap;
+    *ipf_v6_enabled = ipf_status.ifp_v6_enabled;
+    *min_v6_frag_size = ipf_status.min_v6_frag_size;
+    *n6frag_accepted = ipf_status.n6frag_accepted;
+    *n6frag_completed_sent = ipf_status.n6frag_completed_sent;
+    *n6frag_expired_sent = ipf_status.n6frag_expired_sent;
+    *n6frag_too_small = ipf_status.n6frag_too_small;
+    *n6frag_overlap = ipf_status.n6frag_overlap;
+    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,
@@ -6604,6 +6658,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 80c54f5..42ac01d 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -3009,6 +3009,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 214f570..2e0d596 100644
--- a/lib/dpif-provider.h
+++ b/lib/dpif-provider.h
@@ -451,6 +451,22 @@  struct dpif_class {
     int (*ipf_set_min_frag)(struct dpif *, bool v6, uint32_t min_frag);
     /* 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 *, bool *ipf_v4_enabled,
+        unsigned int *min_v4_frag_size,
+        unsigned int *nfrag_max, unsigned int *nfrag,
+        unsigned int *n4frag_accepted, unsigned int *n4frag_completed_sent,
+        unsigned int *n4frag_expired_sent, unsigned int *n4frag_too_small,
+        unsigned int *n4frag_overlap, bool *ipf_v6_enabled,
+        unsigned int *min_v6_frag_size, unsigned int *n6frag_accepted,
+        unsigned int *n6frag_completed_sent,
+        unsigned int *n6frag_expired_sent, unsigned int *n6frag_too_small,
+        unsigned int *n6frag_overlap);
+    int (*ipf_dump_start)(struct dpif *, struct ipf_dump_ctx **ipf_dump_ctx);
+    /* Finds the next ipf list and creates a string representation of the
+     * state of an ipf list, to which 'dump' is pointed to. */
+    int (*ipf_dump_next)(struct dpif *, void *, 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 ef3236a..9beaaa1 100644
--- a/lib/ipf.c
+++ b/lib/ipf.c
@@ -52,6 +52,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,
@@ -1304,3 +1308,106 @@  ipf_set_max_nfrags(uint32_t value)
     atomic_store_relaxed(&nfrag_max, value);
     return 0;
 }
+
+int
+ipf_get_status(struct ipf_status *ipf_status)
+{
+    atomic_read_relaxed(&ifp_v4_enabled, &ipf_status->ifp_v4_enabled);
+    atomic_read_relaxed(&min_v4_frag_size, &ipf_status->min_v4_frag_size);
+    atomic_read_relaxed(&nfrag_max, &ipf_status->nfrag_max);
+    ipf_status->nfrag = atomic_count_get(&nfrag);
+    ipf_status->n4frag_accepted = atomic_count_get(&n4frag_accepted);
+    ipf_status->n4frag_completed_sent =
+        atomic_count_get(&n4frag_completed_sent);
+    ipf_status->n4frag_expired_sent =
+        atomic_count_get(&n4frag_expired_sent);
+    ipf_status->n4frag_too_small = atomic_count_get(&n4frag_too_small);
+    ipf_status->n4frag_overlap = atomic_count_get(&n4frag_overlap);
+    atomic_read_relaxed(&ifp_v6_enabled, &ipf_status->ifp_v6_enabled);
+    atomic_read_relaxed(&min_v6_frag_size, &ipf_status->min_v6_frag_size);
+    ipf_status->n6frag_accepted = atomic_count_get(&n6frag_accepted);
+    ipf_status->n6frag_completed_sent =
+        atomic_count_get(&n6frag_completed_sent);
+    ipf_status->n6frag_expired_sent =
+        atomic_count_get(&n6frag_expired_sent);
+    ipf_status->n6frag_too_small = atomic_count_get(&n6frag_too_small);
+    ipf_status->n6frag_overlap = atomic_count_get(&n6frag_overlap);
+    return 0;
+}
+
+struct ipf_dump_ctx {
+    struct hmap_position bucket_pos;
+};
+
+/* Allocates an 'ipf_dump_ctx' to keep track of an hmap position. */
+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. */
+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 an 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 4289e5e..36a182a 100644
--- a/lib/ipf.h
+++ b/lib/ipf.h
@@ -63,4 +63,14 @@  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 3ea5b87..736a643 100644
--- a/tests/system-kmod-macros.at
+++ b/tests/system-kmod-macros.at
@@ -130,3 +130,35 @@  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_FRAGMENTATION_FAIL()
+#
+# The kernel does not support this command.
+m4_define([DPCTL_CHECK_FRAGMENTATION_FAIL],
+[
+
+])
diff --git a/tests/system-traffic.at b/tests/system-traffic.at
index e17d39c..d00e7a2 100644
--- a/tests/system-traffic.at
+++ b/tests/system-traffic.at
@@ -1923,6 +1923,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
@@ -1933,6 +1936,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
 
@@ -1958,11 +1964,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
 
@@ -1988,6 +2000,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
@@ -1998,6 +2013,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
 
@@ -2056,6 +2074,7 @@  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)
 
@@ -2077,8 +2096,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)
 
@@ -2101,8 +2120,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)
 
@@ -2125,9 +2144,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)
 
@@ -2149,9 +2168,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)
 
@@ -2348,6 +2367,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)
 
@@ -2368,8 +2388,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)
 
@@ -2392,8 +2412,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)
 
@@ -2416,9 +2436,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)
 
@@ -2442,9 +2462,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)
 
@@ -2468,9 +2488,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)
 
@@ -2494,9 +2514,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 40b7567..fcae3cc 100644
--- a/tests/system-userspace-macros.at
+++ b/tests/system-userspace-macros.at
@@ -119,3 +119,125 @@  m4_define([CHECK_CT_DPIF_SET_GET_MAXCONNS])
 # Perform requirements checks for running ovs-dpctl ct-get-nconns. The
 # 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
+        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
+])
+])
+
+# 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
+        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
+
+        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
+        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
+
+        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)
+])
+])