X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=ab6e901e04a6cebfbbf8ad67693e834496c9905b;hb=522f8f6ff0af5eb56197adb309cf56b4800756f8;hp=df7a56a6516bd8a18fec3b9bf0130a685260b822;hpb=4dd1e3cabbd109e322553a7b3d4c875e697d2069;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index df7a56a65..ab6e901e0 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -176,11 +176,23 @@ static void bundle_destroy(struct ofbundle *); static void bundle_del_port(struct ofport_dpif *); static void bundle_run(struct ofbundle *); static void bundle_wait(struct ofbundle *); -static struct ofport_dpif *lookup_input_bundle(struct ofproto_dpif *, - uint16_t in_port, bool warn); +static struct ofbundle *lookup_input_bundle(struct ofproto_dpif *, + uint16_t in_port, bool warn); + +/* A controller may use OFPP_NONE as the ingress port to indicate that + * it did not arrive on a "real" port. 'ofpp_none_bundle' exists for + * when an input bundle is needed for validation (e.g., mirroring or + * OFPP_NORMAL processing). It is not connected to an 'ofproto' or have + * any 'port' structs, so care must be taken when dealing with it. */ +static struct ofbundle ofpp_none_bundle = { + .name = "OFPP_NONE", + .vlan_mode = PORT_VLAN_TRUNK +}; static void stp_run(struct ofproto_dpif *ofproto); static void stp_wait(struct ofproto_dpif *ofproto); +static int set_stp_port(struct ofport *, + const struct ofproto_port_stp_settings *); static bool ofbundle_includes_vlan(const struct ofbundle *, uint16_t vlan); @@ -204,6 +216,9 @@ struct action_xlate_ctx { * we are just revalidating. */ bool may_learn; + /* Cookie of the currently matching rule, or 0. */ + ovs_be64 cookie; + /* If nonnull, called just before executing a resubmit action. * * This is normally null so the client has to set it manually after @@ -237,7 +252,8 @@ struct action_xlate_ctx { static void action_xlate_ctx_init(struct action_xlate_ctx *, struct ofproto_dpif *, const struct flow *, - ovs_be16 initial_tci, const struct ofpbuf *); + ovs_be16 initial_tci, ovs_be64 cookie, + const struct ofpbuf *); static struct ofpbuf *xlate_actions(struct action_xlate_ctx *, const union ofp_action *in, size_t n_in); @@ -314,12 +330,6 @@ static struct facet *facet_lookup_valid(struct ofproto_dpif *, const struct flow *); static bool facet_revalidate(struct ofproto_dpif *, struct facet *); -static bool execute_controller_action(struct ofproto_dpif *, - const struct flow *, - const struct nlattr *odp_actions, - size_t actions_len, - struct ofpbuf *packet, bool clone); - static void facet_flush_stats(struct ofproto_dpif *, struct facet *); static void facet_update_time(struct ofproto_dpif *, struct facet *, @@ -643,7 +653,7 @@ construct(struct ofproto *ofproto_, int *n_tablesp) ofproto->sflow = NULL; ofproto->stp = NULL; hmap_init(&ofproto->bundles); - ofproto->ml = mac_learning_create(); + ofproto->ml = mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME); for (i = 0; i < MAX_MIRRORS; i++) { ofproto->mirrors[i] = NULL; } @@ -1128,6 +1138,12 @@ set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s) stp_set_max_age(ofproto->stp, s->max_age); stp_set_forward_delay(ofproto->stp, s->fwd_delay); } else { + struct ofport *ofport; + + HMAP_FOR_EACH (ofport, hmap_node, &ofproto->up.ports) { + set_stp_port(ofport, NULL); + } + stp_destroy(ofproto->stp); ofproto->stp = NULL; } @@ -2128,6 +2144,13 @@ forward_bpdu_changed(struct ofproto *ofproto_) /* Revalidate cached flows whenever forward_bpdu option changes. */ ofproto->need_revalidate = true; } + +static void +set_mac_idle_time(struct ofproto *ofproto_, unsigned int idle_time) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + mac_learning_set_idle_time(ofproto->ml, idle_time); +} /* Ports. */ @@ -2400,50 +2423,30 @@ struct flow_miss_op { /* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_NO_MATCH to each * OpenFlow controller as necessary according to their individual - * configurations. - * - * If 'clone' is true, the caller retains ownership of 'packet'. Otherwise, - * ownership is transferred to this function. */ + * configurations. */ static void send_packet_in_miss(struct ofproto_dpif *ofproto, struct ofpbuf *packet, - const struct flow *flow, bool clone) + const struct flow *flow) { struct ofputil_packet_in pin; - pin.packet = packet; - pin.in_port = flow->in_port; + pin.packet = packet->data; + pin.packet_len = packet->size; + pin.total_len = packet->size; pin.reason = OFPR_NO_MATCH; + + pin.table_id = 0; + pin.cookie = 0; + pin.buffer_id = 0; /* not yet known */ pin.send_len = 0; /* not used for flow table misses */ - connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow, - clone ? NULL : packet); -} -/* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_ACTION to each - * OpenFlow controller as necessary according to their individual - * configurations. - * - * 'send_len' should be the number of bytes of 'packet' to send to the - * controller, as specified in the action that caused the packet to be sent. - * - * If 'clone' is true, the caller retains ownership of 'upcall->packet'. - * Otherwise, ownership is transferred to this function. */ -static void -send_packet_in_action(struct ofproto_dpif *ofproto, struct ofpbuf *packet, - uint64_t userdata, const struct flow *flow, bool clone) -{ - struct ofputil_packet_in pin; - struct user_action_cookie cookie; + flow_get_metadata(flow, &pin.fmd); - memcpy(&cookie, &userdata, sizeof(cookie)); + /* Registers aren't meaningful on a miss. */ + memset(pin.fmd.reg_masks, 0, sizeof pin.fmd.reg_masks); - pin.packet = packet; - pin.in_port = flow->in_port; - pin.reason = OFPR_ACTION; - pin.buffer_id = 0; /* not yet known */ - pin.send_len = cookie.data; - connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow, - clone ? NULL : packet); + connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow); } static bool @@ -2530,10 +2533,8 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, flow->in_port); } - LIST_FOR_EACH_SAFE (packet, next_packet, list_node, - &miss->packets) { - list_remove(&packet->list_node); - send_packet_in_miss(ofproto, packet, flow, false); + LIST_FOR_EACH (packet, list_node, &miss->packets) { + send_packet_in_miss(ofproto, packet, flow); } return; @@ -2548,8 +2549,9 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, LIST_FOR_EACH_SAFE (packet, next_packet, list_node, &miss->packets) { struct dpif_flow_stats stats; + struct flow_miss_op *op; + struct dpif_execute *execute; - list_remove(&packet->list_node); ofproto->n_matches++; if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) { @@ -2563,45 +2565,37 @@ handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, * * See the top-level comment in fail-open.c for more information. */ - send_packet_in_miss(ofproto, packet, flow, true); + send_packet_in_miss(ofproto, packet, flow); } if (!facet->may_install || !subfacet->actions) { subfacet_make_actions(ofproto, subfacet, packet); } - /* Credit statistics to subfacet for this packet. We must do this now - * because execute_controller_action() below may destroy 'packet'. */ dpif_flow_stats_extract(&facet->flow, packet, &stats); subfacet_update_stats(ofproto, subfacet, &stats); - if (!execute_controller_action(ofproto, &facet->flow, - subfacet->actions, - subfacet->actions_len, packet, true) - && subfacet->actions_len > 0) { - struct flow_miss_op *op = &ops[(*n_ops)++]; - struct dpif_execute *execute = &op->dpif_op.execute; - - if (flow->vlan_tci != subfacet->initial_tci) { - /* This packet was received on a VLAN splinter port. We added - * a VLAN to the packet to make the packet resemble the flow, - * but the actions were composed assuming that the packet - * contained no VLAN. So, we must remove the VLAN header from - * the packet before trying to execute the actions. */ - eth_pop_vlan(packet); - } - - op->subfacet = subfacet; - execute->type = DPIF_OP_EXECUTE; - execute->key = miss->key; - execute->key_len = miss->key_len; - execute->actions - = (facet->may_install - ? subfacet->actions - : xmemdup(subfacet->actions, subfacet->actions_len)); - execute->actions_len = subfacet->actions_len; - execute->packet = packet; + if (flow->vlan_tci != subfacet->initial_tci) { + /* This packet was received on a VLAN splinter port. We added + * a VLAN to the packet to make the packet resemble the flow, + * but the actions were composed assuming that the packet + * contained no VLAN. So, we must remove the VLAN header from + * the packet before trying to execute the actions. */ + eth_pop_vlan(packet); } + + op = &ops[(*n_ops)++]; + execute = &op->dpif_op.execute; + op->subfacet = subfacet; + execute->type = DPIF_OP_EXECUTE; + execute->key = miss->key; + execute->key_len = miss->key_len; + execute->actions = (facet->may_install + ? subfacet->actions + : xmemdup(subfacet->actions, + subfacet->actions_len)); + execute->actions_len = subfacet->actions_len; + execute->packet = packet; } if (facet->may_install && subfacet->key_fitness != ODP_FIT_TOO_LITTLE) { @@ -2743,14 +2737,10 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, /* Process each element in the to-do list, constructing the set of * operations to batch. */ n_ops = 0; - HMAP_FOR_EACH_SAFE (miss, next_miss, hmap_node, &todo) { + HMAP_FOR_EACH (miss, hmap_node, &todo) { handle_flow_miss(ofproto, miss, flow_miss_ops, &n_ops); - ofpbuf_list_delete(&miss->packets); - hmap_remove(&todo, &miss->hmap_node); - free(miss); } assert(n_ops <= ARRAY_SIZE(flow_miss_ops)); - hmap_destroy(&todo); /* Execute batch. */ for (i = 0; i < n_ops; i++) { @@ -2770,7 +2760,6 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, if (op->subfacet->actions != execute->actions) { free((struct nlattr *) execute->actions); } - ofpbuf_delete((struct ofpbuf *) execute->packet); break; case DPIF_OP_FLOW_PUT: @@ -2781,6 +2770,12 @@ handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, break; } } + HMAP_FOR_EACH_SAFE (miss, next_miss, hmap_node, &todo) { + ofpbuf_list_delete(&miss->packets); + hmap_remove(&todo, &miss->hmap_node); + free(miss); + } + hmap_destroy(&todo); } static void @@ -2807,15 +2802,10 @@ handle_userspace_upcall(struct ofproto_dpif *ofproto, dpif_sflow_received(ofproto->sflow, upcall->packet, &flow, &cookie); } - ofpbuf_delete(upcall->packet); - } else if (cookie.type == USER_ACTION_COOKIE_CONTROLLER) { - COVERAGE_INC(ofproto_dpif_ctlr_action); - send_packet_in_action(ofproto, upcall->packet, upcall->userdata, - &flow, false); } else { VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata); - ofpbuf_delete(upcall->packet); } + ofpbuf_delete(upcall->packet); } static int @@ -3148,39 +3138,6 @@ facet_free(struct facet *facet) free(facet); } -/* If the 'actions_len' bytes of actions in 'odp_actions' are just a single - * OVS_ACTION_ATTR_USERSPACE action, executes it internally and returns true. - * Otherwise, returns false without doing anything. - * - * If 'clone' is true, the caller always retains ownership of 'packet'. - * Otherwise, ownership is transferred to this function if it returns true. */ -static bool -execute_controller_action(struct ofproto_dpif *ofproto, - const struct flow *flow, - const struct nlattr *odp_actions, size_t actions_len, - struct ofpbuf *packet, bool clone) -{ - if (actions_len - && odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE - && NLA_ALIGN(odp_actions->nla_len) == actions_len) { - /* As an optimization, avoid a round-trip from userspace to kernel to - * userspace. This also avoids possibly filling up kernel packet - * buffers along the way. - * - * This optimization will not accidentally catch sFlow - * OVS_ACTION_ATTR_USERSPACE actions, since those are encapsulated - * inside OVS_ACTION_ATTR_SAMPLE. */ - const struct nlattr *nla; - - nla = nl_attr_find_nested(odp_actions, OVS_USERSPACE_ATTR_USERDATA); - send_packet_in_action(ofproto, packet, nl_attr_get_u64(nla), flow, - clone); - return true; - } else { - return false; - } -} - /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on * 'packet', which arrived on 'in_port'. * @@ -3194,11 +3151,6 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, struct ofpbuf key; int error; - if (execute_controller_action(ofproto, flow, odp_actions, actions_len, - packet, false)) { - return true; - } - ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, flow); @@ -3221,12 +3173,25 @@ facet_remove(struct ofproto_dpif *ofproto, struct facet *facet) { struct subfacet *subfacet, *next_subfacet; + assert(!list_is_empty(&facet->subfacets)); + + /* First uninstall all of the subfacets to get final statistics. */ + LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) { + subfacet_uninstall(ofproto, subfacet); + } + + /* Flush the final stats to the rule. + * + * This might require us to have at least one subfacet around so that we + * can use its actions for accounting in facet_account(), which is why we + * have uninstalled but not yet destroyed the subfacets. */ + facet_flush_stats(ofproto, facet); + + /* Now we're really all done so destroy everything. */ LIST_FOR_EACH_SAFE (subfacet, next_subfacet, list_node, &facet->subfacets) { subfacet_destroy__(ofproto, subfacet); } - - facet_flush_stats(ofproto, facet); hmap_remove(&ofproto->facets, &facet->hmap_node); list_remove(&facet->list_node); facet_free(facet); @@ -3254,7 +3219,8 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) struct action_xlate_ctx ctx; action_xlate_ctx_init(&ctx, ofproto, &facet->flow, - facet->flow.vlan_tci, NULL); + facet->flow.vlan_tci, + facet->rule->up.flow_cookie, NULL); ctx.may_learn = true; ofpbuf_delete(xlate_actions(&ctx, facet->rule->up.actions, facet->rule->up.n_actions)); @@ -3443,7 +3409,8 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) bool should_install; action_xlate_ctx_init(&ctx, ofproto, &facet->flow, - subfacet->initial_tci, NULL); + subfacet->initial_tci, new_rule->up.flow_cookie, + NULL); odp_actions = xlate_actions(&ctx, new_rule->up.actions, new_rule->up.n_actions); actions_changed = (subfacet->actions_len != odp_actions->size @@ -3593,7 +3560,8 @@ flow_push_stats(const struct rule_dpif *rule, push.bytes = bytes; push.used = used; - action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, NULL); + action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, + rule->up.flow_cookie, NULL); push.ctx.resubmit_hook = push_resubmit; ofpbuf_delete(xlate_actions(&push.ctx, rule->up.actions, rule->up.n_actions)); @@ -3701,9 +3669,11 @@ subfacet_destroy(struct ofproto_dpif *ofproto, struct subfacet *subfacet) { struct facet *facet = subfacet->facet; - subfacet_destroy__(ofproto, subfacet); - if (list_is_empty(&facet->subfacets)) { + if (list_is_singleton(&facet->subfacets)) { + /* facet_remove() needs at least one subfacet (it will remove it). */ facet_remove(ofproto, facet); + } else { + subfacet_destroy__(ofproto, subfacet); } } @@ -3733,7 +3703,7 @@ subfacet_make_actions(struct ofproto_dpif *p, struct subfacet *subfacet, struct action_xlate_ctx ctx; action_xlate_ctx_init(&ctx, p, &facet->flow, subfacet->initial_tci, - packet); + rule->up.flow_cookie, packet); odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions); facet->tags = ctx.tags; facet->may_install = ctx.may_set_up_flow; @@ -4011,7 +3981,8 @@ rule_execute(struct rule *rule_, const struct flow *flow, struct ofpbuf *odp_actions; size_t size; - action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, packet); + action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, + rule->up.flow_cookie, packet); odp_actions = xlate_actions(&ctx, rule->up.actions, rule->up.n_actions); size = packet->size; if (execute_odp_actions(ofproto, flow, odp_actions->data, @@ -4281,8 +4252,12 @@ xlate_table_action(struct action_xlate_ctx *ctx, } if (rule) { + ovs_be64 old_cookie = ctx->cookie; + ctx->recurse++; + ctx->cookie = rule->up.flow_cookie; do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx); + ctx->cookie = old_cookie; ctx->recurse--; } @@ -4333,16 +4308,62 @@ flood_packets(struct action_xlate_ctx *ctx, bool all) } static void -compose_controller_action(struct action_xlate_ctx *ctx, int len) +execute_controller_action(struct action_xlate_ctx *ctx, int len) { - struct user_action_cookie cookie; + struct ofputil_packet_in pin; + struct ofpbuf *packet; - commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions); - cookie.type = USER_ACTION_COOKIE_CONTROLLER; - cookie.data = len; - cookie.n_output = 0; - cookie.vlan_tci = 0; - put_userspace_action(ctx->ofproto, ctx->odp_actions, &ctx->flow, &cookie); + ctx->may_set_up_flow = false; + if (!ctx->packet) { + return; + } + + packet = ofpbuf_clone(ctx->packet); + + if (packet->l2 && packet->l3) { + struct eth_header *eh; + + eth_pop_vlan(packet); + eh = packet->l2; + assert(eh->eth_type == ctx->flow.dl_type); + memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src); + memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst); + + if (ctx->flow.vlan_tci & htons(VLAN_CFI)) { + eth_push_vlan(packet, ctx->flow.vlan_tci); + } + + if (packet->l4) { + if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) { + packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst, + ctx->flow.nw_tos, ctx->flow.nw_ttl); + } + + if (packet->l7) { + if (ctx->flow.nw_proto == IPPROTO_TCP) { + packet_set_tcp_port(packet, ctx->flow.tp_src, + ctx->flow.tp_dst); + } else if (ctx->flow.nw_proto == IPPROTO_UDP) { + packet_set_udp_port(packet, ctx->flow.tp_src, + ctx->flow.tp_dst); + } + } + } + } + + pin.packet = packet->data; + pin.packet_len = packet->size; + pin.reason = OFPR_ACTION; + pin.table_id = ctx->table_id; + pin.cookie = ctx->cookie; + + pin.buffer_id = 0; + pin.send_len = len; + pin.total_len = packet->size; + flow_get_metadata(&ctx->flow, &pin.fmd); + + connmgr_send_packet_in(ctx->ofproto->up.connmgr, &pin, &ctx->flow); + ofpbuf_delete(packet); } static void @@ -4370,13 +4391,11 @@ xlate_output_action__(struct action_xlate_ctx *ctx, flood_packets(ctx, true); break; case OFPP_CONTROLLER: - compose_controller_action(ctx, max_len); - break; - case OFPP_LOCAL: - compose_output_action(ctx, OFPP_LOCAL); + execute_controller_action(ctx, max_len); break; case OFPP_NONE: break; + case OFPP_LOCAL: default: if (port != ctx->flow.in_port) { compose_output_action(ctx, port); @@ -4628,8 +4647,11 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPUTIL_OFPAT_SET_NW_TOS: - ctx->flow.nw_tos &= ~IP_DSCP_MASK; - ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK; + /* OpenFlow 1.0 only supports IPv4. */ + if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) { + ctx->flow.nw_tos &= ~IP_DSCP_MASK; + ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK; + } break; case OFPUTIL_OFPAT_SET_TP_SRC: @@ -4741,13 +4763,15 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, static void action_xlate_ctx_init(struct action_xlate_ctx *ctx, struct ofproto_dpif *ofproto, const struct flow *flow, - ovs_be16 initial_tci, const struct ofpbuf *packet) + ovs_be16 initial_tci, ovs_be64 cookie, + const struct ofpbuf *packet) { ctx->ofproto = ofproto; ctx->flow = *flow; ctx->base_flow = ctx->flow; ctx->base_flow.tun_id = 0; ctx->base_flow.vlan_tci = initial_tci; + ctx->cookie = cookie; ctx->packet = packet; ctx->may_learn = packet != NULL; ctx->resubmit_hook = NULL; @@ -4858,6 +4882,11 @@ input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid) static bool input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn) { + /* Allow any VID on the OFPP_NONE port. */ + if (in_bundle == &ofpp_none_bundle) { + return true; + } + switch (in_bundle->vlan_mode) { case PORT_VLAN_ACCESS: if (vid) { @@ -5040,22 +5069,17 @@ add_mirror_actions(struct action_xlate_ctx *ctx, const struct flow *orig_flow) { struct ofproto_dpif *ofproto = ctx->ofproto; mirror_mask_t mirrors; - struct ofport_dpif *in_port; struct ofbundle *in_bundle; uint16_t vlan; uint16_t vid; const struct nlattr *a; size_t left; - /* Obtain in_port from orig_flow.in_port. - * - * lookup_input_bundle() also ensures that in_port belongs to a bundle. */ - in_port = lookup_input_bundle(ctx->ofproto, orig_flow->in_port, - ctx->packet != NULL); - if (!in_port) { + in_bundle = lookup_input_bundle(ctx->ofproto, orig_flow->in_port, + ctx->packet != NULL); + if (!in_bundle) { return; } - in_bundle = in_port->bundle; mirrors = in_bundle->src_mirrors; /* Drop frames on bundles reserved for mirroring. */ @@ -5176,6 +5200,11 @@ update_learning_table(struct ofproto_dpif *ofproto, { struct mac_entry *mac; + /* Don't learn the OFPP_NONE port. */ + if (in_bundle == &ofpp_none_bundle) { + return; + } + if (!mac_learning_may_learn(ofproto->ml, flow->dl_src, vlan)) { return; } @@ -5206,15 +5235,21 @@ update_learning_table(struct ofproto_dpif *ofproto, } } -static struct ofport_dpif * +static struct ofbundle * lookup_input_bundle(struct ofproto_dpif *ofproto, uint16_t in_port, bool warn) { struct ofport_dpif *ofport; + /* Special-case OFPP_NONE, which a controller may use as the ingress + * port for traffic that it is sourcing. */ + if (in_port == OFPP_NONE) { + return &ofpp_none_bundle; + } + /* Find the port and bundle for the received packet. */ ofport = get_ofp_port(ofproto, in_port); if (ofport && ofport->bundle) { - return ofport; + return ofport->bundle; } /* Odd. A few possible reasons here: @@ -5298,15 +5333,15 @@ xlate_normal(struct action_xlate_ctx *ctx) ctx->has_normal = true; - /* Obtain in_port from ctx->flow.in_port. - * - * lookup_input_bundle() also ensures that in_port belongs to a bundle. */ - in_port = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port, + in_bundle = lookup_input_bundle(ctx->ofproto, ctx->flow.in_port, ctx->packet != NULL); - if (!in_port) { + if (!in_bundle) { return; } - in_bundle = in_port->bundle; + + /* We know 'in_port' exists unless it is "ofpp_none_bundle", + * since lookup_input_bundle() succeeded. */ + in_port = get_ofp_port(ctx->ofproto, ctx->flow.in_port); /* Drop malformed frames. */ if (ctx->flow.dl_type == htons(ETH_TYPE_VLAN) && @@ -5339,7 +5374,8 @@ xlate_normal(struct action_xlate_ctx *ctx) vlan = input_vid_to_vlan(in_bundle, vid); /* Check other admissibility requirements. */ - if (!is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) { + if (in_port && + !is_admissible(ctx->ofproto, &ctx->flow, in_port, vlan, &ctx->tags)) { return; } @@ -5355,14 +5391,6 @@ xlate_normal(struct action_xlate_ctx *ctx) if (mac->port.p != in_bundle) { output_normal(ctx, mac->port.p, vlan); } - } else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) { - /* If we are revalidating but don't have a learning entry then eject - * the flow. Installing a flow that floods packets opens up a window - * of time where we could learn from a packet reflected on a bond and - * blackhole packets before the learning table is updated to reflect - * the correct port. */ - ctx->may_set_up_flow = false; - return; } else { struct ofbundle *bundle; @@ -5522,15 +5550,24 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, ofproto->max_ports); if (!error) { struct odputil_keybuf keybuf; - struct action_xlate_ctx ctx; struct ofpbuf *odp_actions; + struct ofproto_push push; struct ofpbuf key; ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, flow); - action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, packet); - odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions); + action_xlate_ctx_init(&push.ctx, ofproto, flow, flow->vlan_tci, 0, + packet); + + /* Ensure that resubmits in 'ofp_actions' get accounted to their + * matching rules. */ + push.packets = 1; + push.bytes = packet->size; + push.used = time_msec(); + push.ctx.resubmit_hook = push_resubmit; + + odp_actions = xlate_actions(&push.ctx, ofp_actions, n_ofp_actions); dpif_execute(ofproto->dpif, key.data, key.size, odp_actions->data, odp_actions->size, packet); ofpbuf_delete(odp_actions); @@ -5618,17 +5655,25 @@ ofproto_dpif_lookup(const char *name) } static void -ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc OVS_UNUSED, +ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) { - const struct ofproto_dpif *ofproto; + struct ofproto_dpif *ofproto; - ofproto = ofproto_dpif_lookup(argv[1]); - if (!ofproto) { - unixctl_command_reply(conn, 501, "no such bridge"); - return; + if (argc > 1) { + ofproto = ofproto_dpif_lookup(argv[1]); + if (!ofproto) { + unixctl_command_reply(conn, 501, "no such bridge"); + return; + } + mac_learning_flush(ofproto->ml); + ofproto->need_revalidate = true; + } else { + HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { + mac_learning_flush(ofproto->ml); + ofproto->need_revalidate = true; + } } - mac_learning_flush(ofproto->ml); unixctl_command_reply(conn, 200, "table successfully flushed"); } @@ -5652,7 +5697,8 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED, struct ofbundle *bundle = e->port.p; ds_put_format(&ds, "%5d %4d "ETH_ADDR_FMT" %3d\n", ofbundle_get_a_port(bundle)->odp_port, - e->vlan, ETH_ADDR_ARGS(e->mac), mac_entry_age(e)); + e->vlan, ETH_ADDR_ARGS(e->mac), + mac_entry_age(ofproto->ml, e)); } unixctl_command_reply(conn, 200, ds_cstr(&ds)); ds_destroy(&ds); @@ -5796,7 +5842,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], } ds_put_cstr(&result, "Packet: "); - s = ofp_packet_to_string(packet->data, packet->size, packet->size); + s = ofp_packet_to_string(packet->data, packet->size); ds_put_cstr(&result, s); free(s); @@ -5819,7 +5865,8 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], trace.result = &result; trace.flow = flow; - action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci, packet); + action_xlate_ctx_init(&trace.ctx, ofproto, &flow, initial_tci, + rule->up.flow_cookie, packet); trace.ctx.resubmit_hook = trace_resubmit; odp_actions = xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions); @@ -5876,8 +5923,8 @@ ofproto_dpif_unixctl_init(void) unixctl_command_register( "ofproto/trace", "bridge {tun_id in_port packet | odp_flow [-generate]}", - 2, 4, ofproto_unixctl_trace, NULL); - unixctl_command_register("fdb/flush", "bridge", 1, 1, + 2, 5, ofproto_unixctl_trace, NULL); + unixctl_command_register("fdb/flush", "[bridge]", 0, 1, ofproto_unixctl_fdb_flush, NULL); unixctl_command_register("fdb/show", "bridge", 1, 1, ofproto_unixctl_fdb_show, NULL); @@ -6087,5 +6134,6 @@ const struct ofproto_class ofproto_dpif_class = { set_flood_vlans, is_mirror_output_bundle, forward_bpdu_changed, + set_mac_idle_time, set_realdev, };