Previously NetFlow expiration messages always contained 0 as the
output interface index. This changes that to report the OpenFlow
interface instead.
Feature #1202
if (nf->add_id_to_iface) {
uint16_t iface = (nf->engine_id & 0x7f) << 9;
nf_rec->input = htons(iface | (expired->flow.in_port & 0x1ff));
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);
} 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));
}
nf_rec->packet_count = htonl(MIN(expired->packet_count, UINT32_MAX));
nf_rec->byte_count = htonl(MIN(expired->byte_count, UINT32_MAX));
struct ofexpired;
struct svec;
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);
struct netflow *netflow_create(void);
void netflow_destroy(struct netflow *);
int netflow_set_collectors(struct netflow *, const struct svec *collectors);
const flow_t *flow, struct ofproto *ofproto,
const struct ofpbuf *packet,
struct odp_actions *out, tag_type *tags,
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;
struct rule {
struct cls_rule cr;
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). */
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
/* 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
int error;
error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
int error;
error = xlate_actions(actions, n_actions, flow, p, packet, &odp_actions,
if (error) {
return error;
}
if (error) {
return error;
}
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,
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;
return;
}
actions = a.actions;
super = rule->super ? rule->super : rule;
rule->tags = 0;
xlate_actions(super->actions, super->n_actions, &rule->cr.flow, p,
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
actions_len = a.n_actions * sizeof *a.actions;
if (rule->n_odp_actions != a.n_actions
rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
{
struct rule *super = rule->super;
rule_post_uninstall(struct ofproto *ofproto, struct rule *rule)
{
struct rule *super = rule->super;
+ bool controller_action;
rule_account(ofproto, rule, 0);
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;
struct ofexpired expired;
expired.flow = rule->cr.flow;
expired.packet_count = rule->packet_count;
expired.created = rule->created;
expired.tcp_flags = rule->tcp_flags;
expired.ip_tos = rule->ip_tos;
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) {
netflow_expire(ofproto->netflow, &expired);
}
if (super) {
-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;
{
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;
+ }
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. */
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,
};
static void do_xlate_actions(const union ofp_action *in, size_t n_in,
}
odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
}
odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
+ ctx->nf_output_iface = port;
const struct ofp_action_output *oao)
{
uint16_t odp_port;
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:
switch (ntohs(oao->port)) {
case OFPP_IN_PORT:
case OFPP_NORMAL:
if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
ctx->out, ctx->tags,
case OFPP_NORMAL:
if (!ctx->ofproto->ofhooks->normal_cb(ctx->flow, ctx->packet,
ctx->out, ctx->tags,
ctx->ofproto->aux)) {
COVERAGE_INC(ofproto_uninstallable);
ctx->may_set_up_flow = false;
}
break;
case OFPP_FLOOD:
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);
- 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);
break;
case OFPP_CONTROLLER:
add_controller_action(ctx->out, oao);
+
+ 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;
+ }
xlate_actions(const union ofp_action *in, size_t n_in,
const flow_t *flow, struct ofproto *ofproto,
const struct ofpbuf *packet,
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;
{
tag_type no_tags = 0;
struct action_xlate_ctx ctx;
ctx.out = out;
ctx.tags = tags ? tags : &no_tags;
ctx.may_set_up_flow = true;
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
do_xlate_actions(in, n_in, &ctx);
/* Check with in-band control to see if we're allowed to set up this
if (may_set_up_flow) {
*may_set_up_flow = ctx.may_set_up_flow;
}
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);
if (odp_actions_overflow(out)) {
odp_actions_init(out);
return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_TOO_MANY);
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_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;
}
if (error) {
return error;
}
static bool
default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
struct odp_actions *actions, tag_type *tags,
static bool
default_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
struct odp_actions *actions, tag_type *tags,
+ uint16_t *nf_output_iface, void *ofproto_)
{
struct ofproto *ofproto = ofproto_;
int out_port;
{
struct ofproto *ofproto = ofproto_;
int out_port;
/* Determine output port. */
out_port = mac_learning_lookup_tag(ofproto->ml, flow->dl_dst, 0, tags);
if (out_port < 0) {
/* 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;
} else if (out_port != flow->in_port) {
odp_actions_add(actions, ODPAT_OUTPUT)->output.port = out_port;
+ *nf_output_iface = out_port;
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. */
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,
};
int ofproto_create(const char *datapath, const struct ofhooks *, void *aux,
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,
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);
void (*account_flow_cb)(const flow_t *, const union odp_action *,
size_t n_actions, unsigned long long int n_bytes,
void *aux);
#include "port-array.h"
#include "proc-net-compat.h"
#include "process.h"
#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"
#include "secchan/ofproto.h"
#include "socket-util.h"
#include "stp.h"
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,
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;
{
mirror_mask_t mirrors = in_port->src_mirrors;
struct dst *dst = dsts;
+ *nf_output_iface = NF_OUT_FLOOD;
} else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
} 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++;
}
mirrors |= out_port->dst_mirrors;
dst++;
}
static void
compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
const struct port *in_port, const struct port *out_port,
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;
{
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++) {
cur_vlan = ntohs(flow->dl_vlan);
for (p = dsts; p < &dsts[n_dsts]; p++) {
static bool
process_flow(struct bridge *br, const flow_t *flow,
const struct ofpbuf *packet, struct odp_actions *actions,
static bool
process_flow(struct bridge *br, const flow_t *flow,
const struct ofpbuf *packet, struct odp_actions *actions,
+ tag_type *tags, uint16_t *nf_output_iface)
{
struct iface *in_iface;
struct port *in_port;
{
struct iface *in_iface;
struct port *in_port;
- 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);
static bool
bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
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_;
{
struct bridge *br = br_;
#endif
COVERAGE_INC(bridge_process_flow);
#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);
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
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:
The following syntax sends NetFlow records for \fBmybr\fR to the NetFlow
collector \fBnflow.example.com\fR on UDP port \fB9995\fR: