From 6a07af3678afc64e39a330fc07362a940ef1ecce Mon Sep 17 00:00:00 2001 From: Jesse Gross Date: Thu, 22 Oct 2009 17:51:05 -0700 Subject: [PATCH] netflow: Populate NetFlow output interface field. 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 | 4 +-- secchan/netflow.h | 6 ++++ secchan/ofproto.c | 62 ++++++++++++++++++++++++++------- secchan/ofproto.h | 4 ++- vswitchd/bridge.c | 21 +++++++---- vswitchd/ovs-vswitchd.conf.5.in | 9 ++++- 6 files changed, 83 insertions(+), 23 deletions(-) diff --git a/secchan/netflow.c b/secchan/netflow.c index 7912b4b88..282fd8342 100644 --- a/secchan/netflow.c +++ b/secchan/netflow.c @@ -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)); diff --git a/secchan/netflow.h b/secchan/netflow.h index 13be90b49..a5ad3360e 100644 --- a/secchan/netflow.h +++ b/secchan/netflow.h @@ -22,6 +22,12 @@ 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); diff --git a/secchan/ofproto.c b/secchan/ofproto.c index 2453055b8..62a37bf42 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -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. */ } diff --git a/secchan/ofproto.h b/secchan/ofproto.h index 398cac4fd..8847deed0 100644 --- a/secchan/ofproto.h +++ b/secchan/ofproto.h @@ -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); diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 157dc6866..fcdd86603 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -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 diff --git a/vswitchd/ovs-vswitchd.conf.5.in b/vswitchd/ovs-vswitchd.conf.5.in index 7f989b40f..f998e4900 100644 --- a/vswitchd/ovs-vswitchd.conf.5.in +++ b/vswitchd/ovs-vswitchd.conf.5.in @@ -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: -- 2.43.0