[ovs-dev] [PATCH 5/6] vswitchd: Add new configuration table for per-flow packet sampling.

Romain Lenglet rlenglet at vmware.com
Tue Mar 26 01:33:06 UTC 2013


Add support for configuring multiple IPFIX collectors for per-flow
packet sampling.

Signed-off-by: Romain Lenglet <rlenglet at vmware.com>
---
 ofproto/ofproto-dpif-ipfix.c | 238 +++++++++++++++++++++++++++++++++++++++----
 ofproto/ofproto-dpif-ipfix.h |   6 +-
 ofproto/ofproto-dpif.c       |  10 +-
 ofproto/ofproto-provider.h   |   9 +-
 ofproto/ofproto.c            |   8 +-
 ofproto/ofproto.h            |  10 +-
 utilities/ovs-vsctl.8.in     |   7 +-
 utilities/ovs-vsctl.c        |  13 ++-
 vswitchd/bridge.c            |  80 ++++++++++++---
 vswitchd/vswitch.ovsschema   |  22 +++-
 vswitchd/vswitch.xml         |  23 +++++
 11 files changed, 377 insertions(+), 49 deletions(-)

diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 446f2c6..529c016 100644
--- a/ofproto/ofproto-dpif-ipfix.c
+++ b/ofproto/ofproto-dpif-ipfix.c
@@ -17,6 +17,7 @@
 #include <config.h>
 #include "ofproto-dpif-ipfix.h"
 #include "collectors.h"
+#include "hmap.h"
 #include "ofproto.h"
 #include "sset.h"
 #include "util.h"
@@ -29,14 +30,29 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 /* Cf. IETF RFC 5101 Section 10.3.4. */
 #define IPFIX_DEFAULT_COLLECTOR_PORT 4739
 
-struct dpif_ipfix_bridge_exporter {
+struct dpif_ipfix_exporter {
     struct collectors *collectors;
+};
+
+struct dpif_ipfix_bridge_exporter {
+    struct dpif_ipfix_exporter exporter;
     struct ofproto_ipfix_bridge_exporter_options *options;
     uint32_t probability;
 };
 
+struct dpif_ipfix_flow_exporter {
+    struct dpif_ipfix_exporter exporter;
+    struct ofproto_ipfix_flow_exporter_options *options;
+};
+
+struct dpif_ipfix_flow_exporter_map_node {
+    struct hmap_node node;
+    struct dpif_ipfix_flow_exporter exporter;
+};
+
 struct dpif_ipfix {
     struct dpif_ipfix_bridge_exporter bridge_exporter;
+    struct hmap flow_exporter_map;  /* dpif_ipfix_flow_exporter_map_nodes. */
 };
 
 static bool
@@ -70,27 +86,78 @@ ofproto_ipfix_bridge_exporter_options_destroy(
     }
 }
 
+static bool
+ofproto_ipfix_flow_exporter_options_equal(
+    const struct ofproto_ipfix_flow_exporter_options *a,
+    const struct ofproto_ipfix_flow_exporter_options *b)
+{
+    return (a->collector_set_id == b->collector_set_id
+            && sset_equals(&a->targets, &b->targets));
+}
+
+static struct ofproto_ipfix_flow_exporter_options *
+ofproto_ipfix_flow_exporter_options_clone(
+    const struct ofproto_ipfix_flow_exporter_options *old)
+{
+    struct ofproto_ipfix_flow_exporter_options *new =
+        xmemdup(old, sizeof *old);
+    sset_clone(&new->targets, &old->targets);
+    return new;
+}
+
 static void
-dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
+ofproto_ipfix_flow_exporter_options_destroy(
+    struct ofproto_ipfix_flow_exporter_options *options)
+{
+    if (options) {
+        sset_destroy(&options->targets);
+        free(options);
+    }
+}
+
+static void
+dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
 {
     collectors_destroy(exporter->collectors);
     exporter->collectors = NULL;
+}
+
+static int
+dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
+                                const struct sset *targets)
+{
+    collectors_destroy(exporter->collectors);
+    collectors_create(targets, IPFIX_DEFAULT_COLLECTOR_PORT,
+                      &exporter->collectors);
+    if (exporter->collectors == NULL) {
+        VLOG_WARN_RL(&rl, "no collectors could be initialized, "
+                     "IPFIX exporter disabled");
+        dpif_ipfix_exporter_clear(exporter);
+        return -1;
+    }
+    return 0;
+}
+
+static void
+dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
+{
+    dpif_ipfix_exporter_clear(&exporter->exporter);
     ofproto_ipfix_bridge_exporter_options_destroy(exporter->options);
     exporter->options = NULL;
     exporter->probability = 0;
 }
 
-static void
+static int
 dpif_ipfix_bridge_exporter_set_options(
     struct dpif_ipfix_bridge_exporter *exporter,
     const struct ofproto_ipfix_bridge_exporter_options *options)
 {
     bool options_changed;
 
-    if (sset_is_empty(&options->targets)) {
+    if (!options || sset_is_empty(&options->targets)) {
         /* No point in doing any work if there are no targets. */
         dpif_ipfix_bridge_exporter_clear(exporter);
-        return;
+        return 0;
     }
 
     options_changed = (
@@ -103,37 +170,160 @@ dpif_ipfix_bridge_exporter_set_options(
      * more of the configured collectors failed, so that we should
      * retry). */
     if (options_changed
-        || collectors_count(exporter->collectors)
+        || collectors_count(exporter->exporter.collectors)
             < sset_count(&options->targets)) {
-        collectors_destroy(exporter->collectors);
-        collectors_create(&options->targets, IPFIX_DEFAULT_COLLECTOR_PORT,
-                          &exporter->collectors);
-        if (exporter->collectors == NULL) {
-            VLOG_WARN_RL(&rl, "no collectors could be initialized, "
-                         "IPFIX exporter disabled");
-            dpif_ipfix_bridge_exporter_clear(exporter);
-            return;
+        if (dpif_ipfix_exporter_set_options(&exporter->exporter,
+                                            &options->targets)) {
+            return -1;
         }
     }
 
     /* Avoid reconfiguring if options didn't change. */
     if (!options_changed) {
-        return;
+        return 0;
     }
 
     ofproto_ipfix_bridge_exporter_options_destroy(exporter->options);
     exporter->options = ofproto_ipfix_bridge_exporter_options_clone(options);
     exporter->probability =
         MAX(1, UINT32_MAX / exporter->options->sampling_rate);
+
+    return 0;
+}
+
+/* Hash into 16 bits since that's the standard size for size_t. */
+#define COLLECTOR_SET_ID_HASH(ID) (((ID) & 0xffff) ^ ((ID) >> 16))
+
+static struct dpif_ipfix_flow_exporter_map_node*
+dpif_ipfix_find_flow_exporter_map_node(
+    const struct dpif_ipfix *di, const uint32_t collector_set_id)
+{
+    struct dpif_ipfix_flow_exporter_map_node *exporter_node;
+
+    HMAP_FOR_EACH_WITH_HASH (exporter_node, node,
+                             COLLECTOR_SET_ID_HASH(collector_set_id),
+                             &di->flow_exporter_map) {
+        if (exporter_node->exporter.options->collector_set_id
+            == collector_set_id) {
+            return exporter_node;
+        }
+    }
+
+    return NULL;
+}
+
+static void
+dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
+{
+    dpif_ipfix_exporter_clear(&exporter->exporter);
+    ofproto_ipfix_flow_exporter_options_destroy(exporter->options);
+    exporter->options = NULL;
+}
+
+static int
+dpif_ipfix_flow_exporter_set_options(
+    struct dpif_ipfix_flow_exporter *exporter,
+    const struct ofproto_ipfix_flow_exporter_options *options)
+{
+    bool options_changed;
+
+    if (sset_is_empty(&options->targets)) {
+        /* No point in doing any work if there are no targets. */
+        dpif_ipfix_flow_exporter_clear(exporter);
+        return 0;
+    }
+
+    options_changed = (
+        !exporter->options
+        || !ofproto_ipfix_flow_exporter_options_equal(
+            options, exporter->options));
+
+    /* Configure collectors if options have changed or if we're
+     * shortchanged in collectors (which indicates that opening one or
+     * more of the configured collectors failed, so that we should
+     * retry). */
+    if (options_changed
+        || collectors_count(exporter->exporter.collectors)
+            < sset_count(&options->targets)) {
+        if (dpif_ipfix_exporter_set_options(&exporter->exporter,
+                                            &options->targets)) {
+            return -1;
+        }
+    }
+
+    /* Avoid reconfiguring if options didn't change. */
+    if (!options_changed) {
+        return 0;
+    }
+
+    ofproto_ipfix_flow_exporter_options_destroy(exporter->options);
+    exporter->options = ofproto_ipfix_flow_exporter_options_clone(options);
+
+    return 0;
 }
 
 void
 dpif_ipfix_set_options(
     struct dpif_ipfix *di,
-    const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options)
+    const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options,
+    const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options,
+    size_t n_flow_exporters_options)
 {
-    dpif_ipfix_bridge_exporter_set_options(&(di->bridge_exporter),
-                                           bridge_exporter_options);
+    int i;
+    struct ofproto_ipfix_flow_exporter_options *options;
+    struct dpif_ipfix_flow_exporter_map_node *node, *next;
+
+    if (dpif_ipfix_bridge_exporter_set_options(&di->bridge_exporter,
+                                               bridge_exporter_options)) {
+        return;
+    }
+
+    /* Add new flow exporters and update current flow exporters. */
+    options = (struct ofproto_ipfix_flow_exporter_options *)
+        flow_exporters_options;
+    for (i = 0; i < n_flow_exporters_options; i++) {
+        node = dpif_ipfix_find_flow_exporter_map_node(
+            di, options->collector_set_id);
+        if (!node) {
+            node = xzalloc(sizeof *node);
+            hmap_insert(&di->flow_exporter_map, &node->node,
+                        COLLECTOR_SET_ID_HASH(options->collector_set_id));
+        }
+        if (dpif_ipfix_flow_exporter_set_options(&node->exporter, options)) {
+            return;
+        }
+        options++;
+    }
+
+    /*
+     * assert(hmap_count(&di->flow_exporter_map) >= n_flow_exporters_options);
+     */
+
+    /* Remove dropped flow exporters, if any needs to be removed. */
+    if (hmap_count(&di->flow_exporter_map) > n_flow_exporters_options) {
+        HMAP_FOR_EACH_SAFE (node, next, node, &di->flow_exporter_map) {
+            /* This is slow but doesn't take any extra memory, and
+             * this table is not supposed to contain many rows anyway. */
+            options = (struct ofproto_ipfix_flow_exporter_options *)
+                flow_exporters_options;
+            for (i = 0; i < n_flow_exporters_options; i++) {
+              if (node->exporter.options->collector_set_id
+                  == options->collector_set_id) {
+                  break;
+                  options++;
+              }
+            }
+            if (i == n_flow_exporters_options) {  // Not found.
+                hmap_remove(&di->flow_exporter_map, &node->node);
+                dpif_ipfix_flow_exporter_clear(&node->exporter);
+                free(node);
+            }
+        }
+    }
+
+    /*
+     * assert(hmap_count(&di->flow_exporter_map) == n_flow_exporters_options);
+     */
 }
 
 struct dpif_ipfix *
@@ -141,6 +331,7 @@ dpif_ipfix_create(void)
 {
     struct dpif_ipfix *di;
     di = xzalloc(sizeof *di);
+    hmap_init(&di->flow_exporter_map);
     return di;
 }
 
@@ -149,6 +340,7 @@ dpif_ipfix_destroy(struct dpif_ipfix *di)
 {
     if (di) {
         dpif_ipfix_clear(di);
+        hmap_destroy(&di->flow_exporter_map);
         free(di);
     }
 }
@@ -156,5 +348,13 @@ dpif_ipfix_destroy(struct dpif_ipfix *di)
 void
 dpif_ipfix_clear(struct dpif_ipfix *di)
 {
-    dpif_ipfix_bridge_exporter_clear(&(di->bridge_exporter));
+    struct dpif_ipfix_flow_exporter_map_node *node, *next;
+
+    dpif_ipfix_bridge_exporter_clear(&di->bridge_exporter);
+
+    HMAP_FOR_EACH_SAFE (node, next, node, &di->flow_exporter_map) {
+        hmap_remove(&di->flow_exporter_map, &node->node);
+        dpif_ipfix_flow_exporter_clear(&node->exporter);
+        free(node);
+    }
 }
diff --git a/ofproto/ofproto-dpif-ipfix.h b/ofproto/ofproto-dpif-ipfix.h
index 297be56..5b46c6b 100644
--- a/ofproto/ofproto-dpif-ipfix.h
+++ b/ofproto/ofproto-dpif-ipfix.h
@@ -17,13 +17,17 @@
 #ifndef OFPROTO_DPIF_IPFIX_H
 #define OFPROTO_DPIF_IPFIX_H 1
 
+#include <stddef.h>
+
 struct ofproto_ipfix_bridge_exporter_options;
+struct ofproto_ipfix_flow_exporter_options;
 
 struct dpif_ipfix *dpif_ipfix_create(void);
 void dpif_ipfix_destroy(struct dpif_ipfix *);
 void dpif_ipfix_set_options(
     struct dpif_ipfix *,
-    const struct ofproto_ipfix_bridge_exporter_options *);
+    const struct ofproto_ipfix_bridge_exporter_options *,
+    const struct ofproto_ipfix_flow_exporter_options *, size_t);
 void dpif_ipfix_clear(struct dpif_ipfix *);
 
 #endif /* ofproto/ofproto-dpif-ipfix.h */
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index bd92694..9d5fa74 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1793,16 +1793,20 @@ set_sflow(struct ofproto *ofproto_,
 static int
 set_ipfix(
     struct ofproto *ofproto_,
-    const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options)
+    const struct ofproto_ipfix_bridge_exporter_options *bridge_exporter_options,
+    const struct ofproto_ipfix_flow_exporter_options *flow_exporters_options,
+    size_t n_flow_exporters_options)
 {
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     struct dpif_ipfix *di = ofproto->ipfix;
 
-    if (bridge_exporter_options) {
+    if (bridge_exporter_options || flow_exporters_options) {
         if (!di) {
             di = ofproto->ipfix = dpif_ipfix_create();
         }
-        dpif_ipfix_set_options(di, bridge_exporter_options);
+        dpif_ipfix_set_options(
+            di, bridge_exporter_options, flow_exporters_options,
+            n_flow_exporters_options);
     } else {
         if (di) {
             dpif_ipfix_destroy(di);
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index 2272ff2..2f429e0 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -1110,15 +1110,18 @@ struct ofproto_class {
                      const struct ofproto_sflow_options *sflow_options);
 
     /* Configures IPFIX on 'ofproto' according to the options in
-     * 'bridge_exporter_options', or turns off IPFIX if
-     * 'bridge_exporter_options' is NULL.
+     * 'bridge_exporter_options' and the 'flow_exporters_options'
+     * array, or turns off IPFIX if 'bridge_exporter_options' and
+     * 'flow_exporters_options' is NULL.
      *
      * EOPNOTSUPP as a return value indicates that 'ofproto' does not support
      * IPFIX, as does a null pointer. */
     int (*set_ipfix)(
         struct ofproto *ofproto,
         const struct ofproto_ipfix_bridge_exporter_options
-            *bridge_exporter_options);
+            *bridge_exporter_options,
+        const struct ofproto_ipfix_flow_exporter_options
+            *flow_exporters_options, size_t n_flow_exporters_options);
 
     /* Configures connectivity fault management on 'ofport'.
      *
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index e142759..b85b3e0 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -643,13 +643,15 @@ ofproto_set_sflow(struct ofproto *ofproto,
 
 int
 ofproto_set_ipfix(struct ofproto *ofproto,
-                  const struct ofproto_ipfix_bridge_exporter_options *oibeo)
+                  const struct ofproto_ipfix_bridge_exporter_options *bo,
+                  const struct ofproto_ipfix_flow_exporter_options *fo,
+                  size_t n_fo)
 {
     /* TODO: Check the options. */
     if (ofproto->ofproto_class->set_ipfix) {
-        return ofproto->ofproto_class->set_ipfix(ofproto, oibeo);
+        return ofproto->ofproto_class->set_ipfix(ofproto, bo, fo, n_fo);
     } else {
-        return oibeo ? EOPNOTSUPP : 0;
+        return (bo || fo) ? EOPNOTSUPP : 0;
     }
 }
 
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 5cec201..d05b999 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -68,6 +68,7 @@ struct ofproto_sflow_options {
     char *control_ip;
 };
 
+
 struct ofproto_ipfix_bridge_exporter_options {
     struct sset targets;
     uint32_t sampling_rate;
@@ -75,6 +76,11 @@ struct ofproto_ipfix_bridge_exporter_options {
     uint32_t obs_point_id;  /* Bridge-wide Observation Point ID. */
 };
 
+struct ofproto_ipfix_flow_exporter_options {
+    uint32_t collector_set_id;
+    struct sset targets;
+};
+
 struct ofproto_stp_settings {
     stp_identifier system_id;
     uint16_t priority;
@@ -237,7 +243,9 @@ int ofproto_set_netflow(struct ofproto *,
                         const struct netflow_options *nf_options);
 int ofproto_set_sflow(struct ofproto *, const struct ofproto_sflow_options *);
 int ofproto_set_ipfix(struct ofproto *,
-                      const struct ofproto_ipfix_bridge_exporter_options *);
+                      const struct ofproto_ipfix_bridge_exporter_options *,
+                      const struct ofproto_ipfix_flow_exporter_options *,
+                      size_t);
 int ofproto_set_stp(struct ofproto *, const struct ofproto_stp_settings *);
 int ofproto_get_stp_status(struct ofproto *, struct ofproto_stp_status *);
 
diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in
index 003e99a..79e4b78 100644
--- a/utilities/ovs-vsctl.8.in
+++ b/utilities/ovs-vsctl.8.in
@@ -527,11 +527,14 @@ The global SSL configuration for \fBovs\-vswitchd\fR.  The record
 attached to the \fBOpen_vSwitch\fR table may be identified by
 specifying \fB.\fR as the record name.
 .IP "\fBsFlow\fR"
-An sFlow configuration attached to a bridge.  Records may be
+An sFlow exporter configuration attached to a bridge.  Records may be
 identified by bridge name.
 .IP "\fBIPFIX\fR"
-An IPFIX configuration attached to a bridge.  Records may be
+An IPFIX exporter configuration attached to a bridge.  Records may be
 identified by bridge name.
+.IP "\fBFlowSampleCollectorSet\fR"
+An IPFIX exporter configuration attached to a bridge for sampling
+packets on a per-flow basis using OpenFlow \fBsample\fR actions.
 .PP
 Record names must be specified in full and with correct
 capitalization.  Names of tables and columns are not case-sensitive,
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index 4c1ec17..ef7beec 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -1479,6 +1479,7 @@ cmd_emer_reset(struct vsctl_context *ctx)
     const struct ovsrec_ssl *ssl, *next_ssl;
     const struct ovsrec_sflow *sflow, *next_sflow;
     const struct ovsrec_ipfix *ipfix, *next_ipfix;
+    const struct ovsrec_flowsamplecollectorset *fscset, *next_fscset;
 
     /* Reset the Open_vSwitch table. */
     ovsrec_open_vswitch_set_manager_options(ctx->ovs, NULL, 0);
@@ -1546,6 +1547,10 @@ cmd_emer_reset(struct vsctl_context *ctx)
         ovsrec_ipfix_delete(ipfix);
     }
 
+    OVSREC_FLOWSAMPLECOLLECTORSET_FOR_EACH_SAFE (fscset, next_fscset, idl) {
+        ovsrec_flowsamplecollectorset_delete(fscset);
+    }
+
     vsctl_context_invalidate_cache(ctx);
 }
 
@@ -2471,7 +2476,8 @@ struct vsctl_table_class {
 static const struct vsctl_table_class tables[] = {
     {&ovsrec_table_bridge,
      {{&ovsrec_table_bridge, &ovsrec_bridge_col_name, NULL},
-      {NULL, NULL, NULL}}},
+      {&ovsrec_table_flowsamplecollectorset, NULL,
+       &ovsrec_flowsamplecollectorset_col_bridge}}},
 
     {&ovsrec_table_controller,
      {{&ovsrec_table_bridge,
@@ -2529,6 +2535,11 @@ static const struct vsctl_table_class tables[] = {
      {{&ovsrec_table_bridge,
        &ovsrec_bridge_col_name,
        &ovsrec_bridge_col_ipfix},
+      {&ovsrec_table_flowsamplecollectorset, NULL,
+       &ovsrec_flowsamplecollectorset_col_ipfix}}},
+
+    {&ovsrec_table_flowsamplecollectorset,
+     {{NULL, NULL, NULL},
       {NULL, NULL, NULL}}},
 
     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index d2c3907..15c2aeb 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -374,6 +374,7 @@ bridge_init(const char *remote)
     ovsdb_idl_omit(idl, &ovsrec_netflow_col_external_ids);
     ovsdb_idl_omit(idl, &ovsrec_sflow_col_external_ids);
     ovsdb_idl_omit(idl, &ovsrec_ipfix_col_external_ids);
+    ovsdb_idl_omit(idl, &ovsrec_flowsamplecollectorset_col_external_ids);
 
     ovsdb_idl_omit(idl, &ovsrec_manager_col_external_ids);
     ovsdb_idl_omit(idl, &ovsrec_manager_col_inactivity_probe);
@@ -942,30 +943,79 @@ bridge_configure_sflow(struct bridge *br, int *sflow_bridge_number)
 static void
 bridge_configure_ipfix(struct bridge *br)
 {
-    const struct ovsrec_ipfix *cfg = br->cfg->ipfix;
-    struct ofproto_ipfix_bridge_exporter_options oibeo;
+    const struct ovsrec_ipfix *bridge_exporter_cfg = br->cfg->ipfix;
+    const struct ovsrec_flowsamplecollectorset *flow_exporter_cfg;
+    struct ofproto_ipfix_bridge_exporter_options bridge_exporter_options;
+    struct ofproto_ipfix_flow_exporter_options *flow_exporters_options = NULL;
+    size_t n_flow_exporters_options = 0;
+
+    OVSREC_FLOWSAMPLECOLLECTORSET_FOR_EACH(flow_exporter_cfg, idl) {
+        if (flow_exporter_cfg->bridge == br->cfg) {
+            n_flow_exporters_options++;
+        }
+    }
 
-    if (!cfg) {
-        ofproto_set_ipfix(br->ofproto, NULL);
+    if (!bridge_exporter_cfg && n_flow_exporters_options == 0) {
+        ofproto_set_ipfix(br->ofproto, NULL, NULL, 0);
         return;
     }
 
-    memset(&oibeo, 0, sizeof oibeo);
+    if (bridge_exporter_cfg) {
+        memset(&bridge_exporter_options, 0, sizeof bridge_exporter_options);
 
-    /* Collectors. */
-    sset_init(&oibeo.targets);
-    sset_add_array(&oibeo.targets, cfg->targets, cfg->n_targets);
+        sset_init(&bridge_exporter_options.targets);
+        sset_add_array(&bridge_exporter_options.targets,
+                       bridge_exporter_cfg->targets,
+                       bridge_exporter_cfg->n_targets);
 
-    oibeo.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
-    if (cfg->sampling) {
-        oibeo.sampling_rate = *cfg->sampling;
+        bridge_exporter_options.sampling_rate = SFL_DEFAULT_SAMPLING_RATE;
+        if (bridge_exporter_cfg->sampling) {
+            bridge_exporter_options.sampling_rate =
+                *bridge_exporter_cfg->sampling;
+        }
+        bridge_exporter_options.obs_domain_id =
+            *bridge_exporter_cfg->obs_domain_id;
+        bridge_exporter_options.obs_point_id =
+            *bridge_exporter_cfg->obs_point_id;
+    }
+
+    if (n_flow_exporters_options > 0) {
+        struct ofproto_ipfix_flow_exporter_options *options;
+        flow_exporters_options = xcalloc(n_flow_exporters_options,
+                                         sizeof *flow_exporters_options);
+        options = flow_exporters_options;
+        OVSREC_FLOWSAMPLECOLLECTORSET_FOR_EACH(flow_exporter_cfg, idl) {
+            if (flow_exporter_cfg->bridge == br->cfg) {
+                options->collector_set_id = flow_exporter_cfg->id;
+                sset_init(&options->targets);
+                sset_add_array(&options->targets,
+                               flow_exporter_cfg->ipfix->targets,
+                               flow_exporter_cfg->ipfix->n_targets);
+
+                options++;
+            }
+        }
     }
-    oibeo.obs_domain_id = *cfg->obs_domain_id;
-    oibeo.obs_point_id = *cfg->obs_point_id;
 
-    ofproto_set_ipfix(br->ofproto, &oibeo);
+    ofproto_set_ipfix(
+        br->ofproto,
+        bridge_exporter_cfg ? &bridge_exporter_options : NULL,
+        flow_exporters_options, n_flow_exporters_options);
+
+    if (bridge_exporter_cfg) {
+        sset_destroy(&bridge_exporter_options.targets);
+    }
 
-    sset_destroy(&oibeo.targets);
+    if (n_flow_exporters_options > 0) {
+        struct ofproto_ipfix_flow_exporter_options *options =
+            flow_exporters_options;
+        size_t i;
+        for (i = 0; i < n_flow_exporters_options; i++) {
+            sset_destroy(&options->targets);
+            options++;
+        }
+        free(flow_exporters_options);
+    }
 }
 
 static void
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 0d1f4fd..2cf6ce5 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
  "version": "7.1.0",
- "cksum": "4242091849 18457",
+ "cksum": "1616135014 19188",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -409,6 +409,26 @@
        "external_ids": {
          "type": {"key": "string", "value": "string",
                   "min": 0, "max": "unlimited"}}}},
+   "FlowSampleCollectorSet": {
+     "columns": {
+       "id": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4294967295},
+                  "min": 1, "max": 1}},
+       "bridge": {
+         "type": {"key": {"type": "uuid",
+                          "refTable": "Bridge"},
+                  "min": 1, "max": 1}},
+       "ipfix": {
+         "type": {"key": {"type": "uuid",
+                          "refTable": "IPFIX"},
+                  "min": 0, "max": 1}},
+       "external_ids": {
+         "type": {"key": "string", "value": "string",
+                  "min": 0, "max": "unlimited"}}},
+     "isRoot": true,
+     "indexes": [["id", "bridge"]]},
    "Controller": {
      "columns": {
        "target": {
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 24452b1..415717c 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -3207,4 +3207,27 @@
     </group>
   </table>
 
+  <table name="FlowSampleCollectorSet">
+    <p>A set of IPFIX collectors of packet samples generated by
+    OpenFlow <code>sample</code> actions.</p>
+
+    <column name="id">
+      Unique ID of this collector set, to be used as the
+      <code>collector_set_id</code> in OpenFlow <code>sample</code>
+      actions.
+    </column>
+
+    <column name="ipfix">
+      Configuration of the set of IPFIX collectors to send one flow
+      record per sampled packet to.
+    </column>
+
+    <group title="Common Columns">
+      The overall purpose of these columns is described under <code>Common
+      Columns</code> at the beginning of this document.
+
+      <column name="external_ids"/>
+    </group>
+  </table>
+
 </database>
-- 
1.8.1.3




More information about the dev mailing list