netflow: Populate NetFlow output interface field.
authorJesse Gross <jesse@nicira.com>
Fri, 23 Oct 2009 00:51:05 +0000 (17:51 -0700)
committerJesse Gross <jesse@nicira.com>
Tue, 27 Oct 2009 20:19:15 +0000 (13:19 -0700)
Previously NetFlow expiration messages always contained 0 as the
output interface index.  This changes that to report the OpenFlow
interface instead.

Feature #1202

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

index 7912b4b..282fd83 100644 (file)
@@ -196,10 +196,10 @@ netflow_expire(struct netflow *nf, const struct ofexpired *expired)
     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);
+        nf_rec->output = htons(iface | (expired->output_iface & 0x1ff));
     } else {
         nf_rec->input = htons(expired->flow.in_port);
-        nf_rec->output = htons(0);
+        nf_rec->output = htons(expired->output_iface);
     }
     nf_rec->packet_count = htonl(MIN(expired->packet_count, UINT32_MAX));
     nf_rec->byte_count = htonl(MIN(expired->byte_count, UINT32_MAX));
index 13be90b..a5ad336 100644 (file)
 struct ofexpired;
 struct svec;
 
+enum netflow_output_ports {
+    NF_OUT_FLOOD = UINT16_MAX,
+    NF_OUT_MULTI = UINT16_MAX - 1,
+    NF_OUT_DROP = UINT16_MAX - 2
+};
+
 struct netflow *netflow_create(void);
 void netflow_destroy(struct netflow *);
 int netflow_set_collectors(struct netflow *, const struct svec *collectors);
index 2453055..62a37bf 100644 (file)
@@ -82,7 +82,7 @@ static int xlate_actions(const union ofp_action *in, size_t n_in,
                          const flow_t *flow, struct ofproto *ofproto,
                          const struct ofpbuf *packet,
                          struct odp_actions *out, tag_type *tags,
-                         bool *may_set_up_flow);
+                         bool *may_set_up_flow, uint16_t *nf_output_iface);
 
 struct rule {
     struct cls_rule cr;
@@ -97,6 +97,7 @@ struct rule {
     uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
     uint8_t ip_tos;             /* Last-seen IP type-of-service. */
     tag_type tags;              /* Tags (set only by hooks). */
+    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
 
     /* If 'super' is non-NULL, this rule is a subrule, that is, it is an
      * exact-match rule (having cr.wc.wildcards of 0) generated from the
@@ -971,7 +972,7 @@ ofproto_send_packet(struct ofproto *p, const flow_t *flow,
     int error;
 
     error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
-                          NULL, NULL);
+                          NULL, NULL, NULL);
     if (error) {
         return error;
     }
@@ -1486,7 +1487,7 @@ rule_execute(struct ofproto *ofproto, struct rule *rule,
     if (rule->cr.wc.wildcards || !flow_equal(flow, &rule->cr.flow)) {
         struct rule *super = rule->super ? rule->super : rule;
         if (xlate_actions(super->actions, super->n_actions, flow, ofproto,
-                          packet, &a, NULL, 0)) {
+                          packet, &a, NULL, 0, NULL)) {
             return;
         }
         actions = a.actions;
@@ -1582,7 +1583,8 @@ rule_make_actions(struct ofproto *p, struct rule *rule,
     super = rule->super ? rule->super : rule;
     rule->tags = 0;
     xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
-                  packet, &a, &rule->tags, &rule->may_install);
+                  packet, &a, &rule->tags, &rule->may_install,
+                  &rule->nf_output_iface);
 
     actions_len = a.n_actions * sizeof *a.actions;
     if (rule->n_odp_actions != a.n_actions
@@ -1700,9 +1702,17 @@ static void
 rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
 {
     struct rule *super = rule->super;
+    bool controller_action;
 
     rule_account(ofproto, rule, 0);
-    if (ofproto->netflow && rule->byte_count) {
+
+    /* If the only action is send to the controller then don't report
+     * NetFlow expiration messages since it is just part of the control
+     * logic for the network and not real traffic. */
+    controller_action = rule->n_odp_actions == 1 &&
+                        rule->odp_actions[0].type == ODPAT_CONTROLLER;
+
+    if (ofproto->netflow && rule->byte_count && !controller_action) {
         struct ofexpired expired;
         expired.flow = rule->cr.flow;
         expired.packet_count = rule->packet_count;
@@ -1711,6 +1721,7 @@ rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
         expired.created = rule->created;
         expired.tcp_flags = rule->tcp_flags;
         expired.ip_tos = rule->ip_tos;
+        expired.output_iface = rule->nf_output_iface;
         netflow_expire(ofproto->netflow, &expired);
     }
     if (super) {
@@ -1894,9 +1905,14 @@ handle_set_config(struct ofproto *p, struct ofconn *ofconn,
 }
 
 static void
-add_output_group_action(struct odp_actions *actions, uint16_t group)
+add_output_group_action(struct odp_actions *actions, uint16_t group,
+                        uint16_t *nf_output_iface)
 {
     odp_actions_add(actions, ODPAT_OUTPUT_GROUP)->output_group.group = group;
+
+    if (group == DP_GROUP_ALL || group == DP_GROUP_FLOOD) {
+        *nf_output_iface = NF_OUT_FLOOD;
+    }
 }
 
 static void
@@ -1921,6 +1937,7 @@ struct action_xlate_ctx {
     tag_type *tags;             /* Tags associated with OFPP_NORMAL actions. */
     bool may_set_up_flow;       /* True ordinarily; false if the actions must
                                  * be reassessed for every packet. */
+    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
 };
 
 static void do_xlate_actions(const union ofp_action *in, size_t n_in,
@@ -1945,6 +1962,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
     }
 
     odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+    ctx->nf_output_iface = port;
 }
 
 static struct rule *
@@ -1994,6 +2012,9 @@ xlate_output_action(struct action_xlate_ctx *ctx,
                     const struct ofp_action_output *oao)
 {
     uint16_t odp_port;
+    uint16_t prev_nf_output_iface = ctx->nf_output_iface;
+
+    ctx->nf_output_iface = NF_OUT_DROP;
 
     switch (ntohs(oao->port)) {
     case OFPP_IN_PORT:
@@ -2005,16 +2026,18 @@ xlate_output_action(struct action_xlate_ctx *ctx,
     case OFPP_NORMAL:
         if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
                                               ctx->out, ctx->tags,
+                                              &ctx->nf_output_iface,
                                               ctx->ofproto->aux)) {
             COVERAGE_INC(ofproto_uninstallable);
             ctx->may_set_up_flow = false;
         }
         break;
     case OFPP_FLOOD:
-        add_output_group_action(ctx->out, DP_GROUP_FLOOD);
+        add_output_group_action(ctx->out, DP_GROUP_FLOOD,
+                                &ctx->nf_output_iface);
         break;
     case OFPP_ALL:
-        add_output_group_action(ctx->out, DP_GROUP_ALL);
+        add_output_group_action(ctx->out, DP_GROUP_ALL, &ctx->nf_output_iface);
         break;
     case OFPP_CONTROLLER:
         add_controller_action(ctx->out, oao);
@@ -2029,6 +2052,15 @@ xlate_output_action(struct action_xlate_ctx *ctx,
         }
         break;
     }
+
+    if (prev_nf_output_iface == NF_OUT_FLOOD) {
+        ctx->nf_output_iface = NF_OUT_FLOOD;
+    } else if (ctx->nf_output_iface == NF_OUT_DROP) {
+        ctx->nf_output_iface = prev_nf_output_iface;
+    } else if (prev_nf_output_iface != NF_OUT_DROP &&
+               ctx->nf_output_iface != NF_OUT_FLOOD) {
+        ctx->nf_output_iface = NF_OUT_MULTI;
+    }
 }
 
 static void
@@ -2127,7 +2159,8 @@ static int
 xlate_actions(const union ofp_action *in, size_t n_in,
               const flow_t *flow, struct ofproto *ofproto,
               const struct ofpbuf *packet,
-              struct odp_actions *out, tag_type *tags, bool *may_set_up_flow)
+              struct odp_actions *out, tag_type *tags, bool *may_set_up_flow,
+              uint16_t *nf_output_iface)
 {
     tag_type no_tags = 0;
     struct action_xlate_ctx ctx;
@@ -2140,6 +2173,7 @@ xlate_actions(const union ofp_action *in, size_t n_in,
     ctx.out = out;
     ctx.tags = tags ? tags : &no_tags;
     ctx.may_set_up_flow = true;
+    ctx.nf_output_iface = NF_OUT_DROP;
     do_xlate_actions(in, n_in, &ctx);
 
     /* Check with in-band control to see if we're allowed to set up this
@@ -2151,6 +2185,9 @@ xlate_actions(const union ofp_action *in, size_t n_in,
     if (may_set_up_flow) {
         *may_set_up_flow = ctx.may_set_up_flow;
     }
+    if (nf_output_iface) {
+        *nf_output_iface = ctx.nf_output_iface;
+    }
     if (odp_actions_overflow(out)) {
         odp_actions_init(out);
         return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_TOO_MANY);
@@ -2190,7 +2227,7 @@ handle_packet_out(struct ofproto *p, struct ofconn *ofconn,
 
     flow_extract(&payload, ofp_port_to_odp_port(ntohs(opo->in_port)), &flow);
     error = xlate_actions((const union ofp_action *) opo->actions, n_actions,
-                          &flow, p, &payload, &actions, NULL, NULL);
+                          &flow, p, &payload, &actions, NULL, NULL, NULL);
     if (error) {
         return error;
     }
@@ -3395,7 +3432,7 @@ pick_fallback_dpid(void)
 static bool
 default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
                          struct odp_actions *actions, tag_type *tags,
-                         void *ofproto_)
+                         uint16_t *nf_output_iface, void *ofproto_)
 {
     struct ofproto *ofproto = ofproto_;
     int out_port;
@@ -3422,9 +3459,10 @@ default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
     /* Determine output port. */
     out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
     if (out_port < 0) {
-        add_output_group_action(actions, DP_GROUP_FLOOD);
+        add_output_group_action(actions, DP_GROUP_FLOOD, nf_output_iface);
     } else if (out_port != flow->in_port) {
         odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port;
+        *nf_output_iface = out_port;
     } else {
         /* Drop. */
     }
index 398cac4..8847dee 100644 (file)
@@ -36,6 +36,7 @@ struct ofexpired {
     long long int created;      /* Creation time. */
     uint8_t tcp_flags;          /* Bitwise-OR of all TCP flags seen. */
     uint8_t ip_tos;             /* Last-seen IP type-of-service. */
+    uint16_t output_iface;      /* Output interface index. */
 };
 
 int ofproto_create(const char *datapath, const struct ofhooks *, void *aux,
@@ -99,7 +100,8 @@ struct ofhooks {
     void (*port_changed_cb)(enum ofp_port_reason, const struct ofp_phy_port *,
                             void *aux);
     bool (*normal_cb)(const flow_t *, const struct ofpbuf *packet,
-                      struct odp_actions *, tag_type *, void *aux);
+                      struct odp_actions *, tag_type *,
+                      uint16_t *nf_output_iface, void *aux);
     void (*account_flow_cb)(const flow_t *, const union odp_action *,
                             size_t n_actions, unsigned long long int n_bytes,
                             void *aux);
index 157dc68..fcdd866 100644 (file)
@@ -48,6 +48,7 @@
 #include "port-array.h"
 #include "proc-net-compat.h"
 #include "process.h"
+#include "secchan/netflow.h"
 #include "secchan/ofproto.h"
 #include "socket-util.h"
 #include "stp.h"
@@ -1677,7 +1678,7 @@ port_includes_vlan(const struct port *port, uint16_t vlan)
 static size_t
 compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
              const struct port *in_port, const struct port *out_port,
-             struct dst dsts[], tag_type *tags)
+             struct dst dsts[], tag_type *tags, uint16_t *nf_output_iface)
 {
     mirror_mask_t mirrors = in_port->src_mirrors;
     struct dst *dst = dsts;
@@ -1696,7 +1697,9 @@ compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
                 dst++;
             }
         }
+        *nf_output_iface = NF_OUT_FLOOD;
     } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
+        *nf_output_iface = dst->dp_ifidx;
         mirrors |= out_port->dst_mirrors;
         dst++;
     }
@@ -1764,14 +1767,16 @@ print_dsts(const struct dst *dsts, size_t n)
 static void
 compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
                 const struct port *in_port, const struct port *out_port,
-                tag_type *tags, struct odp_actions *actions)
+                tag_type *tags, struct odp_actions *actions,
+                uint16_t *nf_output_iface)
 {
     struct dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
     size_t n_dsts;
     const struct dst *p;
     uint16_t cur_vlan;
 
-    n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags);
+    n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags,
+                          nf_output_iface);
 
     cur_vlan = ntohs(flow->dl_vlan);
     for (p = dsts; p < &dsts[n_dsts]; p++) {
@@ -1804,7 +1809,7 @@ is_bcast_arp_reply(const flow_t *flow)
 static bool
 process_flow(struct bridge *br, const flow_t *flow,
              const struct ofpbuf *packet, struct odp_actions *actions,
-             tag_type *tags)
+             tag_type *tags, uint16_t *nf_output_iface)
 {
     struct iface *in_iface;
     struct port *in_port;
@@ -1960,7 +1965,8 @@ process_flow(struct bridge *br, const flow_t *flow,
     }
 
 done:
-    compose_actions(br, flow, vlan, in_port, out_port, tags, actions);
+    compose_actions(br, flow, vlan, in_port, out_port, tags, actions,
+                    nf_output_iface);
 
     return true;
 }
@@ -2005,7 +2011,8 @@ bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
 
 static bool
 bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
-                        struct odp_actions *actions, tag_type *tags, void *br_)
+                        struct odp_actions *actions, tag_type *tags,
+                        uint16_t *nf_output_iface, void *br_)
 {
     struct bridge *br = br_;
 
@@ -2018,7 +2025,7 @@ bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
 #endif
 
     COVERAGE_INC(bridge_process_flow);
-    return process_flow(br, flow, packet, actions, tags);
+    return process_flow(br, flow, packet, actions, tags, nf_output_iface);
 }
 
 static void
index 7f989b4..f998e49 100644 (file)
@@ -432,7 +432,14 @@ 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.
+of flow records.  When this option is enabled, a maximum of 508 ports are
+supported.  By default, this behavior is disabled.
+
+The egress interface field normally contains the OpenFlow port number,
+however, certain port values have special meaning: 0xFFFF indicates
+flooding, 0xFFFE is multiple controller-specified output interfaces, and
+0xFFFD means that packets from the flow were dropped.  If add-id-to-iface
+is enabled then these values become 0x1FF, 0x1FE, and 0x1FD respectively.
 
 The following syntax sends NetFlow records for \fBmybr\fR to the NetFlow
 collector \fBnflow.example.com\fR on UDP port \fB9995\fR: