netflow: Set Engine and Add Alternate Port ID Flag
authorJustin Pettit <jpettit@nicira.com>
Fri, 15 May 2009 01:11:57 +0000 (18:11 -0700)
committerJustin Pettit <jpettit@nicira.com>
Fri, 15 May 2009 17:28:38 +0000 (10:28 -0700)
Allow the Engine Type and Engine ID to be configured in NetFlow
messages.  Previously, they were always set to zero.  Now, by default,
they are both set to the datapath index. These can be overridden with
the "netflow.bridge.engine-type" and "netflow.bridge.engine-id" keys,
respectively.

Add the ability to allow collectors to distinguish between virtual
switches sending message from the host.  When the
"netflow.bridge.add-id-to-iface" flag is enabled, the least significant
7 bits of the engine id are placed into the most significant bits of
the  ingress  and  egress  interface fields of flow records.  This
mimics the behavior of VMware ESX when the "-p" option is given to the
NetFlow configuration program.

(Addresses Bug #1222 and Bug #1223)

secchan/main.c
secchan/netflow.c
secchan/netflow.h
secchan/ofproto.c
secchan/ofproto.h
vswitchd/bridge.c
vswitchd/vswitchd.conf.5.in

index cd6a88b3658bd99bd5d8fad42c9388e3badd8129..b9ba4d44554c19a6bb5e997ae73103806a284872 100644 (file)
@@ -185,7 +185,7 @@ main(int argc, char *argv[])
         ovs_fatal(error,
                   "failed to configure controller snooping connections");
     }
-    error = ofproto_set_netflow(ofproto, &s.netflow);
+    error = ofproto_set_netflow(ofproto, &s.netflow, 0, 0, false);
     if (error) {
         ovs_fatal(error, "failed to configure NetFlow collectors");
     }
index b68a51ca8435a25694b2fc3ab3cf3832d64fef3c..49e65ccde541d5aee16ae671678f0a1d3275b92b 100644 (file)
@@ -67,8 +67,8 @@ struct netflow_v5_header {
                                       after epoch seconds. */
     uint32_t flow_seq;             /* Number of flows since sending
                                       messages began. */
-    uint8_t  engine_type;          /* Set to zero. */
-    uint8_t  engine_id;            /* Set to zero. */
+    uint8_t  engine_type;          /* Engine type. */
+    uint8_t  engine_id;            /* Engine id. */
     uint16_t sampling_interval;    /* Set to zero. */
 };
 BUILD_ASSERT_DECL(sizeof(struct netflow_v5_header) == 24);
@@ -107,9 +107,14 @@ struct netflow_v5_record {
 BUILD_ASSERT_DECL(sizeof(struct netflow_v5_record) == 48);
 
 struct netflow {
+    uint8_t engine_type;          /* Value of engine_type to use. */
+    uint8_t engine_id;            /* Value of engine_id to use. */
     long long int boot_time;      /* Time when netflow_create() was called. */
     int *fds;                     /* Sockets for NetFlow collectors. */
     size_t n_fds;                 /* Number of Netflow collectors. */
+    bool add_id_to_iface;         /* Put the 7 least signficiant bits of 
+                                   * 'engine_id' into the most signficant 
+                                   * bits of the interface fields. */
     uint32_t netflow_cnt;         /* Flow sequence number for NetFlow. */
     struct ofpbuf packet;         /* NetFlow packet being accumulated. */
 };
@@ -193,8 +198,8 @@ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
         nf_hdr->unix_secs = htonl(now.tv_sec);
         nf_hdr->unix_nsecs = htonl(now.tv_usec * 1000);
         nf_hdr->flow_seq = htonl(nf->netflow_cnt++);
-        nf_hdr->engine_type = 0;
-        nf_hdr->engine_id = 0;
+        nf_hdr->engine_type = nf->engine_type;
+        nf_hdr->engine_id = nf->engine_id;
         nf_hdr->sampling_interval = htons(0);
     }
 
@@ -205,8 +210,15 @@ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
     nf_rec->src_addr = expired->flow.nw_src;
     nf_rec->dst_addr = expired->flow.nw_dst;
     nf_rec->nexthop = htons(0);
-    nf_rec->input = htons(expired->flow.in_port);
-    nf_rec->output = htons(0);
+    if (nf->add_id_to_iface) {
+        uint16_t iface = (nf->engine_id & 0x7f) << 9;
+        nf_rec->input = htons(iface | (expired->flow.in_port & 0x1ff));
+        nf_rec->output = htons(iface);
+        printf("input: %x\n", ntohs(nf_rec->input));
+    } else {
+        nf_rec->input = htons(expired->flow.in_port);
+        nf_rec->output = htons(0);
+    }
     nf_rec->packet_count = htonl(MIN(expired->packet_count, UINT32_MAX));
     nf_rec->byte_count = htonl(MIN(expired->byte_count, UINT32_MAX));
     nf_rec->init_time = htonl(expired->created - nf->boot_time);
@@ -298,13 +310,25 @@ netflow_set_collectors(struct netflow *nf, const struct svec *collectors_)
     return error;
 }
 
+void 
+netflow_set_engine(struct netflow *nf, uint8_t engine_type, 
+        uint8_t engine_id, bool add_id_to_iface)
+{
+    nf->engine_type = engine_type;
+    nf->engine_id = engine_id;
+    nf->add_id_to_iface = add_id_to_iface;
+}
+
 struct netflow *
 netflow_create(void)
 {
     struct netflow *nf = xmalloc(sizeof *nf);
+    nf->engine_type = 0;
+    nf->engine_id = 0;
     nf->boot_time = time_msec();
     nf->fds = NULL;
     nf->n_fds = 0;
+    nf->add_id_to_iface = false;
     nf->netflow_cnt = 0;
     ofpbuf_init(&nf->packet, 1500);
     return nf;
index 3e7f51c6e1eee0ef1ef96ab04d0a3fd199c76b59..c1a5a6efddf017b484aca0a8677edd9e93825758 100644 (file)
@@ -42,6 +42,8 @@ struct svec;
 struct netflow *netflow_create(void);
 void netflow_destroy(struct netflow *);
 int netflow_set_collectors(struct netflow *, const struct svec *collectors);
+void netflow_set_engine(struct netflow *nf, uint8_t engine_type, 
+        uint8_t engine_id, bool add_id_to_iface);
 void netflow_expire(struct netflow *, const struct ofexpired *);
 void netflow_run(struct netflow *);
 
index 7a58247890ab068b15b6405baad99f63d6b9688c..9cdf5d288a5a577e2b79893b6306e3e5ad2bb3ed 100644 (file)
@@ -545,12 +545,15 @@ ofproto_set_snoops(struct ofproto *ofproto, const struct svec *snoops)
 }
 
 int
-ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors)
+ofproto_set_netflow(struct ofproto *ofproto, const struct svec *collectors,
+        uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface)
 {
     if (collectors && collectors->n) {
         if (!ofproto->netflow) {
             ofproto->netflow = netflow_create();
         }
+        netflow_set_engine(ofproto->netflow, engine_type, engine_id, 
+                add_id_to_iface);
         return netflow_set_collectors(ofproto->netflow, collectors);
     } else {
         netflow_destroy(ofproto->netflow);
index 91c51c410674324586a7c341f7c930e16937ddf1..83c66a7b46affe87f1d4aabcaa59df7666d0c7e6 100644 (file)
@@ -79,7 +79,8 @@ int ofproto_set_discovery(struct ofproto *, bool discovery,
 int ofproto_set_controller(struct ofproto *, const char *controller);
 int ofproto_set_listeners(struct ofproto *, const struct svec *listeners);
 int ofproto_set_snoops(struct ofproto *, const struct svec *snoops);
-int ofproto_set_netflow(struct ofproto *, const struct svec *collectors);
+int ofproto_set_netflow(struct ofproto *, const struct svec *collectors,
+        uint8_t engine_type, uint8_t engine_id, bool add_id_to_iface);
 void ofproto_set_failure(struct ofproto *, bool fail_open);
 void ofproto_set_rate_limit(struct ofproto *, int rate_limit, int burst_limit);
 int ofproto_set_stp(struct ofproto *, bool enable_stp);
index cd4dfe737b0a3ab140d3782cc25de0e38b56d455..fa2463764cc8423c346553c7faa8cde032661d04 100644 (file)
@@ -471,8 +471,12 @@ bridge_reconfigure(void)
         uint64_t dpid;
         struct iface *local_iface = NULL;
         const char *devname;
+        uint8_t engine_type = br->dpif.minor;
+        uint8_t engine_id = br->dpif.minor;
+        bool add_id_to_iface = false;
         struct svec nf_hosts;
 
+
         bridge_fetch_dp_ifaces(br);
         for (i = 0; i < br->n_ports; ) {
             struct port *port = br->ports[i];
@@ -516,9 +520,31 @@ bridge_reconfigure(void)
         ofproto_set_datapath_id(br->ofproto, dpid);
 
         /* Set NetFlow configuration on this bridge. */
+        if (cfg_has("netflow.%s.engine-type", br->name)) {
+            engine_type = cfg_get_int(0, "netflow.%s.engine-type", 
+                    br->name);
+        }
+        if (cfg_has("netflow.%s.engine-id", br->name)) {
+            engine_id = cfg_get_int(0, "netflow.%s.engine-id", br->name);
+        }
+        if (cfg_has("netflow.%s.add-id-to-iface", br->name)) {
+            add_id_to_iface = cfg_get_bool(0, "netflow.%s.add-id-to-iface",
+                    br->name);
+        }
+        if (add_id_to_iface && engine_id > 0x7f) {
+            VLOG_WARN("bridge %s: netflow port mangling may conflict with "
+                    "another vswitch, choose an engine id less than 128", 
+                    br->name);
+        }
+        if (add_id_to_iface && br->n_ports > 0x1ff) {
+            VLOG_WARN("bridge %s: netflow port mangling will conflict with "
+                    "another port when 512 or more ports are used", 
+                    br->name);
+        }
         svec_init(&nf_hosts);
         cfg_get_all_keys(&nf_hosts, "netflow.%s.host", br->name);
-        if (ofproto_set_netflow(br->ofproto, &nf_hosts)) {
+        if (ofproto_set_netflow(br->ofproto, &nf_hosts,  engine_type, 
+                    engine_id, add_id_to_iface)) {
             VLOG_ERR("bridge %s: problem setting netflow collectors", 
                     br->name);
         }
index bc3a7f56bf1f8557a035e368ef0081af2a1a530e..68dc20345ce3fb28e5adc194bd8a70b7cf12bdf2 100644 (file)
@@ -304,12 +304,26 @@ port.eth1.ingress.policing-burst=20
 .fi
 .SS "NetFlow v5 Flow Logging"
 NetFlow is a protocol that exports a number of details about terminating 
-flows, such as the principals involved and duration.  A bridge may be 
+IP flows, such as the principals involved and duration.  A bridge may be 
 configured to send NetFlow v5 records to NetFlow collectors when flows 
 end.  To enable, define the key \fBnetflow.\fIbridge\fB.host\fR for each 
 collector in the form \fIhost\fB:\fIport\fR.  Records from \fIbridge\fR 
 will be sent to each \fIhost\fR on UDP \fIport\fR.
-.PP
+
+The NetFlow messages will use the datapath index for the engine type and id.  
+This can be overridden with the \fBnetflow.\fIbridge\fB.engine-type\fR and 
+\fBnetflow.\fIbridge\fB.engine-id\fR, respectively.  Each takes a value
+between 0 and 255, inclusive. 
+
+Many NetFlow collectors do not expect multiple virtual switches to be
+sending messages from the same host, and they do not store the engine
+information which could be used to disambiguate the traffic.  To prevent
+flows from multiple switches appearing as if they came on the interface,
+add \fBnetflow.\fIbridge\fB.add-id-to-iface=true\fR to the configuration
+file.  This will place the least significant 7 bits of the engine id
+into the most significant bits of the ingress and egress interface fields 
+of flow records.  By default, this behavior is disabled.
+
 The following syntax sends NetFlow records for \fBmybr\fR to the NetFlow
 collector \fBnflow.example.com\fR on UDP port \fB9995\fR:
 .PP