X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=f09c230d3c7b574149d287ddd014d363b5c0a4c5;hb=df2c07f4338faac04f4969f243fe4e8083b309ac;hp=1e77cda23e083efdff2f4eb2281935b648e81b73;hpb=38f2e36072c9065cae3d4fbab4a70e4f502706cd;p=openvswitch diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 1e77cda2..f09c230d 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -16,12 +16,13 @@ #include -#include "ofproto/private.h" +#include "ofproto/ofproto-provider.h" #include #include "autopath.h" #include "bond.h" +#include "bundle.h" #include "byte-order.h" #include "connmgr.h" #include "coverage.h" @@ -59,7 +60,7 @@ COVERAGE_DEFINE(facet_invalidated); COVERAGE_DEFINE(facet_revalidate); COVERAGE_DEFINE(facet_unexpected); -/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a +/* Maximum depth of flow table recursion (due to resubmit actions) in a * flow translation. */ #define MAX_RESUBMIT_RECURSION 16 @@ -95,8 +96,8 @@ static struct rule_dpif *rule_dpif_cast(const struct rule *rule) return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL; } -static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *ofproto, - const struct flow *flow); +static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *, + const struct flow *, uint8_t table); #define MAX_MIRRORS 32 typedef uint32_t mirror_mask_t; @@ -187,6 +188,7 @@ struct action_xlate_ctx { uint32_t priority; /* Current flow priority. 0 if none. */ struct flow base_flow; /* Flow at the last commit. */ uint32_t base_priority; /* Priority at the last commit. */ + uint8_t table_id; /* OpenFlow table ID where flow was found. */ }; static void action_xlate_ctx_init(struct action_xlate_ctx *, @@ -205,12 +207,8 @@ struct facet { * dpif_execute(). * * - Do include packets and bytes that were obtained from the datapath - * when a flow was deleted (e.g. dpif_flow_del()) or when its - * statistics were reset (e.g. dpif_flow_put() with + * when its statistics were reset (e.g. dpif_flow_put() with * DPIF_FP_ZERO_STATS). - * - * - Do not include any packets or bytes that can currently be obtained - * from the datapath by, e.g., dpif_flow_get(). */ uint64_t packet_count; /* Number of packets received. */ uint64_t byte_count; /* Number of bytes received. */ @@ -222,10 +220,7 @@ struct facet { uint64_t rs_byte_count; /* Bytes pushed to resubmit children. */ long long int rs_used; /* Used time pushed to resubmit children. */ - /* Number of bytes passed to account_cb. This may include bytes that can - * currently obtained from the datapath (thus, it can be greater than - * byte_count). */ - uint64_t accounted_bytes; + uint64_t accounted_bytes; /* Bytes processed by facet_account(). */ struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */ struct list list_node; /* In owning rule's 'facets' list. */ @@ -267,10 +262,10 @@ static void facet_update_time(struct ofproto_dpif *, struct facet *, long long int used); static void facet_update_stats(struct ofproto_dpif *, struct facet *, const struct dpif_flow_stats *); +static void facet_reset_counters(struct facet *); static void facet_reset_dp_stats(struct facet *, struct dpif_flow_stats *); static void facet_push_stats(struct facet *); -static void facet_account(struct ofproto_dpif *, struct facet *, - uint64_t extra_bytes); +static void facet_account(struct ofproto_dpif *, struct facet *); static bool facet_is_controller_flow(struct facet *); @@ -287,6 +282,7 @@ struct ofport_dpif { struct cfm *cfm; /* Connectivity Fault Management, if any. */ tag_type tag; /* Tag associated with this port. */ uint32_t bond_stable_id; /* stable_id to use as bond slave, or 0. */ + bool may_enable; /* May be enabled in bonds. */ }; static struct ofport_dpif * @@ -331,6 +327,8 @@ struct ofproto_dpif { /* Support for debugging async flow mods. */ struct list completions; + + bool has_bundle_action; /* True when the first bundle action appears. */ }; /* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only @@ -415,7 +413,7 @@ dealloc(struct ofproto *ofproto_) } static int -construct(struct ofproto *ofproto_) +construct(struct ofproto *ofproto_, int *n_tablesp) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); const char *name = ofproto->up.name; @@ -460,12 +458,11 @@ construct(struct ofproto *ofproto_) list_init(&ofproto->completions); - ofproto->up.tables = xmalloc(sizeof *ofproto->up.tables); - classifier_init(&ofproto->up.tables[0]); - ofproto->up.n_tables = 1; - ofproto_dpif_unixctl_init(); + ofproto->has_bundle_action = false; + + *n_tablesp = 255; return 0; } @@ -486,14 +483,18 @@ destruct(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct rule_dpif *rule, *next_rule; - struct cls_cursor cursor; + struct classifier *table; int i; complete_operations(ofproto); - cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL); - CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) { - ofproto_rule_destroy(&rule->up); + OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { + struct cls_cursor cursor; + + cls_cursor_init(&cursor, table, NULL); + CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) { + ofproto_rule_destroy(&rule->up); + } } for (i = 0; i < MAX_MIRRORS; i++) { @@ -558,6 +559,8 @@ run(struct ofproto *ofproto_) bundle_run(bundle); } + mac_learning_run(ofproto->ml, &ofproto->revalidate_set); + /* Now revalidate if there's anything to do. */ if (ofproto->need_revalidate || !tag_set_is_empty(&ofproto->revalidate_set)) { @@ -605,6 +608,7 @@ wait(struct ofproto *ofproto_) HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { bundle_wait(bundle); } + mac_learning_wait(ofproto->ml); if (ofproto->need_revalidate) { /* Shouldn't happen, but if it does just go around again. */ VLOG_DBG_RL(&rl, "need revalidate in ofproto_wait_cb()"); @@ -656,7 +660,7 @@ static void get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct odp_stats s; + struct ovs_dp_stats s; strcpy(ots->name, "classifier"); @@ -708,6 +712,7 @@ port_construct(struct ofport *port_) port->bundle = NULL; port->cfm = NULL; port->tag = tag_create_random(); + port->may_enable = true; if (ofproto->sflow) { dpif_sflow_add_port(ofproto->sflow, port->odp_port, @@ -1176,12 +1181,7 @@ bundle_run(struct ofbundle *bundle) struct ofport_dpif *port; LIST_FOR_EACH (port, bundle_node, &bundle->ports) { - bool may_enable = lacp_slave_may_enable(bundle->lacp, port); - - if (may_enable && port->cfm) { - may_enable = !cfm_get_fault(port->cfm); - } - bond_slave_set_may_enable(bundle->bond, port, may_enable); + bond_slave_set_may_enable(bundle->bond, port, port->may_enable); } bond_run(bundle->bond, &bundle->ofproto->revalidate_set, @@ -1265,6 +1265,7 @@ mirror_set(struct ofproto *ofproto_, void *aux, mirror = ofproto->mirrors[idx] = xzalloc(sizeof *mirror); mirror->ofproto = ofproto; mirror->idx = idx; + mirror->aux = aux; mirror->out_vlan = -1; mirror->name = NULL; } @@ -1390,6 +1391,14 @@ is_mirror_output_bundle(struct ofproto *ofproto_, void *aux) struct ofbundle *bundle = bundle_lookup(ofproto, aux); return bundle && bundle->mirror_out != 0; } + +static void +forward_bpdu_changed(struct ofproto *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + /* Revalidate cached flows whenever forward_bpdu option changes. */ + ofproto->need_revalidate = true; +} /* Ports. */ @@ -1418,6 +1427,8 @@ ofproto_port_from_dpif_port(struct ofproto_port *ofproto_port, static void port_run(struct ofport_dpif *ofport) { + bool enable = netdev_get_carrier(ofport->up.netdev); + if (ofport->cfm) { cfm_run(ofport->cfm); @@ -1430,7 +1441,23 @@ port_run(struct ofport_dpif *ofport) ofport->odp_port, &packet); ofpbuf_uninit(&packet); } + + enable = enable && !cfm_get_fault(ofport->cfm); + } + + if (ofport->bundle) { + enable = enable && lacp_slave_may_enable(ofport->bundle->lacp, ofport); } + + if (ofport->may_enable != enable) { + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + + if (ofproto->has_bundle_action) { + ofproto->need_revalidate = true; + } + } + + ofport->may_enable = enable; } static void @@ -1627,12 +1654,12 @@ handle_miss_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) /* Check with in-band control to see if this packet should be sent * to the local port regardless of the flow table. */ if (connmgr_msg_in_hook(ofproto->up.connmgr, &flow, upcall->packet)) { - send_packet(ofproto, ODPP_LOCAL, upcall->packet); + send_packet(ofproto, OVSP_LOCAL, upcall->packet); } facet = facet_lookup_valid(ofproto, &flow); if (!facet) { - struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow); + struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow, 0); if (!rule) { /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */ struct ofport_dpif *port = get_ofp_port(ofproto, flow.in_port); @@ -1726,7 +1753,7 @@ static int expire(struct ofproto_dpif *ofproto) { struct rule_dpif *rule, *next_rule; - struct cls_cursor cursor; + struct classifier *table; int dp_max_idle; /* Update stats for each flow in the datapath. */ @@ -1737,9 +1764,13 @@ expire(struct ofproto_dpif *ofproto) expire_facets(ofproto, dp_max_idle); /* Expire OpenFlow flows whose idle_timeout or hard_timeout has passed. */ - cls_cursor_init(&cursor, &ofproto->up.tables[0], NULL); - CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) { - rule_expire(rule); + OFPROTO_FOR_EACH_TABLE (table, &ofproto->up) { + struct cls_cursor cursor; + + cls_cursor_init(&cursor, table, NULL); + CLS_CURSOR_FOR_EACH_SAFE (rule, next_rule, up.cr, &cursor) { + rule_expire(rule); + } } /* All outstanding data in existing flows has been accounted, so it's a @@ -1786,7 +1817,7 @@ update_stats(struct ofproto_dpif *p) ds_init(&s); odp_flow_key_format(key, key_len, &s); - VLOG_WARN_RL(&rl, "failed to convert ODP flow key to flow: %s", + VLOG_WARN_RL(&rl, "failed to convert datapath flow key to flow: %s", ds_cstr(&s)); ds_destroy(&s); @@ -1813,7 +1844,7 @@ update_stats(struct ofproto_dpif *p) facet->dp_byte_count = stats->n_bytes; facet_update_time(p, facet, stats->used); - facet_account(p, facet, stats->n_bytes); + facet_account(p, facet); facet_push_stats(facet); } else { /* There's a flow in the datapath that we know nothing about. @@ -1850,11 +1881,12 @@ facet_max_idle(const struct ofproto_dpif *ofproto) * N_BUCKETS buckets whose width is BUCKET_WIDTH msecs each. Each facet * that is installed in the kernel gets dropped in the appropriate bucket. * After the histogram has been built, we compute the cutoff so that only - * the most-recently-used 1% of facets (but at least 1000 flows) are kept - * cached. At least the most-recently-used bucket of facets is kept, so - * actually an arbitrary number of facets can be kept in any given - * expiration run (though the next run will delete most of those unless - * they receive additional data). + * the most-recently-used 1% of facets (but at least + * ofproto->up.flow_eviction_threshold flows) are kept cached. At least + * the most-recently-used bucket of facets is kept, so actually an + * arbitrary number of facets can be kept in any given expiration run + * (though the next run will delete most of those unless they receive + * additional data). * * This requires a second pass through the facets, in addition to the pass * made by update_stats(), because the former function never looks @@ -1869,7 +1901,7 @@ facet_max_idle(const struct ofproto_dpif *ofproto) int i; total = hmap_count(&ofproto->facets); - if (total <= 1000) { + if (total <= ofproto->up.flow_eviction_threshold) { return N_BUCKETS * BUCKET_WIDTH; } @@ -1887,7 +1919,8 @@ facet_max_idle(const struct ofproto_dpif *ofproto) subtotal = bucket = 0; do { subtotal += buckets[bucket++]; - } while (bucket < N_BUCKETS && subtotal < MAX(1000, total / 100)); + } while (bucket < N_BUCKETS && + subtotal < MAX(ofproto->up.flow_eviction_threshold, total / 100)); if (VLOG_IS_DBG_ENABLED()) { struct ds s; @@ -2026,7 +2059,7 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, struct ofpbuf *packet) { if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t)) - && odp_actions->nla_type == ODP_ACTION_ATTR_CONTROLLER) { + && odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE) { /* 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. */ @@ -2068,8 +2101,8 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, * applying flow_extract() to 'packet' would yield the same flow as * 'facet->flow'. * - * 'facet' must have accurately composed ODP actions; that is, it must not be - * in need of revalidation. + * 'facet' must have accurately composed datapath actions; that is, it must + * not be in need of revalidation. * * Takes ownership of 'packet'. */ static void @@ -2105,7 +2138,7 @@ facet_remove(struct ofproto_dpif *ofproto, struct facet *facet) facet_free(facet); } -/* Composes the ODP actions for 'facet' based on its rule's actions. */ +/* Composes the datapath actions for 'facet' based on its rule's actions. */ static void facet_make_actions(struct ofproto_dpif *p, struct facet *facet, const struct ofpbuf *packet) @@ -2186,10 +2219,9 @@ vlan_tci_to_openflow_vlan(ovs_be16 vlan_tci) } static void -facet_account(struct ofproto_dpif *ofproto, - struct facet *facet, uint64_t extra_bytes) +facet_account(struct ofproto_dpif *ofproto, struct facet *facet) { - uint64_t total_bytes, n_bytes; + uint64_t n_bytes; struct ofbundle *in_bundle; const struct nlattr *a; tag_type dummy = 0; @@ -2197,12 +2229,11 @@ facet_account(struct ofproto_dpif *ofproto, ovs_be16 vlan_tci; int vlan; - total_bytes = facet->byte_count + extra_bytes; - if (total_bytes <= facet->accounted_bytes) { + if (facet->byte_count <= facet->accounted_bytes) { return; } - n_bytes = total_bytes - facet->accounted_bytes; - facet->accounted_bytes = total_bytes; + n_bytes = facet->byte_count - facet->accounted_bytes; + facet->accounted_bytes = facet->byte_count; /* Test that 'tags' is nonzero to ensure that only flows that include an * OFPP_NORMAL action are used for learning and bond slave rebalancing. @@ -2233,7 +2264,7 @@ facet_account(struct ofproto_dpif *ofproto, struct ofport_dpif *port; switch (nl_attr_type(a)) { - case ODP_ACTION_ATTR_OUTPUT: + case OVS_ACTION_ATTR_OUTPUT: port = get_odp_port(ofproto, nl_attr_get_u32(a)); if (port && port->bundle && port->bundle->bond) { bond_account(port->bundle->bond, &facet->flow, @@ -2241,11 +2272,11 @@ facet_account(struct ofproto_dpif *ofproto, } break; - case ODP_ACTION_ATTR_STRIP_VLAN: + case OVS_ACTION_ATTR_STRIP_VLAN: vlan_tci = htons(0); break; - case ODP_ACTION_ATTR_SET_DL_TCI: + case OVS_ACTION_ATTR_SET_DL_TCI: vlan_tci = nl_attr_get_be16(a); break; } @@ -2318,7 +2349,7 @@ facet_flush_stats(struct ofproto_dpif *ofproto, struct facet *facet) assert(!facet->dp_packet_count); facet_push_stats(facet); - facet_account(ofproto, facet, 0); + facet_account(ofproto, facet); if (ofproto->netflow && !facet_is_controller_flow(facet)) { struct ofexpired expired; @@ -2334,11 +2365,7 @@ facet_flush_stats(struct ofproto_dpif *ofproto, struct facet *facet) /* Reset counters to prevent double counting if 'facet' ever gets * reinstalled. */ - facet->packet_count = 0; - facet->byte_count = 0; - facet->rs_packet_count = 0; - facet->rs_byte_count = 0; - facet->accounted_bytes = 0; + facet_reset_counters(facet); netflow_flow_clear(&facet->nf_flow); } @@ -2406,14 +2433,14 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) COVERAGE_INC(facet_revalidate); /* Determine the new rule. */ - new_rule = rule_dpif_lookup(ofproto, &facet->flow); + new_rule = rule_dpif_lookup(ofproto, &facet->flow, 0); if (!new_rule) { /* No new rule, so delete the facet. */ facet_remove(ofproto, facet); return false; } - /* Calculate new ODP actions. + /* Calculate new datapath actions. * * We do not modify any 'facet' state yet, because we might need to, e.g., * emit a NetFlow expiration and, if so, we need to have the old state @@ -2425,8 +2452,8 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet) || memcmp(facet->actions, odp_actions->data, facet->actions_len)); - /* If the ODP actions changed or the installability changed, then we need - * to talk to the datapath. */ + /* If the datapath actions changed or the installability changed, + * then we need to talk to the datapath. */ if (actions_changed || ctx.may_set_up_flow != facet->installed) { if (ctx.may_set_up_flow) { struct dpif_flow_stats stats; @@ -2500,6 +2527,16 @@ facet_update_stats(struct ofproto_dpif *ofproto, struct facet *facet, } } +static void +facet_reset_counters(struct facet *facet) +{ + facet->packet_count = 0; + facet->byte_count = 0; + facet->rs_packet_count = 0; + facet->rs_byte_count = 0; + facet->accounted_bytes = 0; +} + static void facet_push_stats(struct facet *facet) { @@ -2564,10 +2601,11 @@ flow_push_stats(const struct rule_dpif *rule, /* Rules. */ static struct rule_dpif * -rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow) +rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow, + uint8_t table_id) { return rule_dpif_cast(rule_from_cls_rule( - classifier_lookup(&ofproto->up.tables[0], + classifier_lookup(&ofproto->up.tables[table_id], flow))); } @@ -2625,6 +2663,13 @@ rule_construct(struct rule *rule_) rule->facets = victim->facets; list_moved(&rule->facets); LIST_FOR_EACH (facet, list_node, &rule->facets) { + /* XXX: We're only clearing our local counters here. It's possible + * that quite a few packets are unaccounted for in the datapath + * statistics. These will be accounted to the new rule instead of + * cleared as required. This could be fixed by clearing out the + * datapath statistics for this facet, but currently it doesn't + * seem worth it. */ + facet_reset_counters(facet); facet->rule = rule; } } else { @@ -2689,7 +2734,7 @@ rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet) /* Otherwise, if 'rule' is in fact the correct rule for 'packet', then * create a new facet for it and use that. */ - if (rule_dpif_lookup(ofproto, flow) == rule) { + if (rule_dpif_lookup(ofproto, flow, 0) == rule) { facet = facet_create(rule, flow, packet); facet_execute(ofproto, facet, packet); facet_install(ofproto, facet, true); @@ -2746,7 +2791,7 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, odp_flow_key_from_flow(&key, &flow); ofpbuf_init(&odp_actions, 32); - nl_msg_put_u32(&odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port); + nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); error = dpif_execute(ofproto->dpif, key.data, key.size, odp_actions.data, odp_actions.size, @@ -2760,11 +2805,11 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, return error; } -/* OpenFlow to ODP action translation. */ +/* OpenFlow to datapath action translation. */ static void do_xlate_actions(const union ofp_action *in, size_t n_in, struct action_xlate_ctx *ctx); -static bool xlate_normal(struct action_xlate_ctx *); +static void xlate_normal(struct action_xlate_ctx *); static void commit_odp_actions(struct action_xlate_ctx *ctx) @@ -2774,58 +2819,63 @@ commit_odp_actions(struct action_xlate_ctx *ctx) struct ofpbuf *odp_actions = ctx->odp_actions; if (base->tun_id != flow->tun_id) { - nl_msg_put_be64(odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, flow->tun_id); + nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id); base->tun_id = flow->tun_id; } if (base->nw_src != flow->nw_src) { - nl_msg_put_be32(odp_actions, ODP_ACTION_ATTR_SET_NW_SRC, flow->nw_src); + nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_SRC, flow->nw_src); base->nw_src = flow->nw_src; } if (base->nw_dst != flow->nw_dst) { - nl_msg_put_be32(odp_actions, ODP_ACTION_ATTR_SET_NW_DST, flow->nw_dst); + nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_DST, flow->nw_dst); base->nw_dst = flow->nw_dst; } + if (base->nw_tos != flow->nw_tos) { + nl_msg_put_u8(odp_actions, OVS_ACTION_ATTR_SET_NW_TOS, flow->nw_tos); + base->nw_tos = flow->nw_tos; + } + if (base->vlan_tci != flow->vlan_tci) { if (!(flow->vlan_tci & htons(VLAN_CFI))) { - nl_msg_put_flag(odp_actions, ODP_ACTION_ATTR_STRIP_VLAN); + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_STRIP_VLAN); } else { - nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_DL_TCI, + nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_DL_TCI, flow->vlan_tci & ~htons(VLAN_CFI)); } base->vlan_tci = flow->vlan_tci; } if (base->tp_src != flow->tp_src) { - nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_TP_SRC, flow->tp_src); + nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src); base->tp_src = flow->tp_src; } if (base->tp_dst != flow->tp_dst) { - nl_msg_put_be16(odp_actions, ODP_ACTION_ATTR_SET_TP_DST, flow->tp_dst); + nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_DST, flow->tp_dst); base->tp_dst = flow->tp_dst; } if (!eth_addr_equals(base->dl_src, flow->dl_src)) { - nl_msg_put_unspec(odp_actions, ODP_ACTION_ATTR_SET_DL_SRC, + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_SRC, flow->dl_src, ETH_ADDR_LEN); memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN); } if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) { - nl_msg_put_unspec(odp_actions, ODP_ACTION_ATTR_SET_DL_DST, + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_DST, flow->dl_dst, ETH_ADDR_LEN); memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN); } if (ctx->base_priority != ctx->priority) { if (ctx->priority) { - nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, + nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY, ctx->priority); } else { - nl_msg_put_flag(odp_actions, ODP_ACTION_ATTR_POP_PRIORITY); + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY); } ctx->base_priority = ctx->priority; } @@ -2851,23 +2901,28 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) } commit_odp_actions(ctx); - nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, odp_port); + nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); ctx->nf_output_iface = ofp_port; } static void -xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port) +xlate_table_action(struct action_xlate_ctx *ctx, + uint16_t in_port, uint8_t table_id) { if (ctx->recurse < MAX_RESUBMIT_RECURSION) { struct rule_dpif *rule; uint16_t old_in_port; + uint8_t old_table_id; + + old_table_id = ctx->table_id; + ctx->table_id = table_id; /* Look up a flow with 'in_port' as the input port. Then restore the * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will * have surprising behavior). */ old_in_port = ctx->flow.in_port; ctx->flow.in_port = in_port; - rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow); + rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow, table_id); ctx->flow.in_port = old_in_port; if (ctx->resubmit_hook) { @@ -2879,14 +2934,31 @@ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port) do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx); ctx->recurse--; } + + ctx->table_id = old_table_id; } else { static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1); - VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times", + VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times", MAX_RESUBMIT_RECURSION); } } +static void +xlate_resubmit_table(struct action_xlate_ctx *ctx, + const struct nx_action_resubmit *nar) +{ + uint16_t in_port; + uint8_t table_id; + + in_port = (nar->in_port == htons(OFPP_IN_PORT) + ? ctx->flow.in_port + : ntohs(nar->in_port)); + table_id = nar->table == 255 ? ctx->table_id : nar->table; + + xlate_table_action(ctx, in_port, table_id); +} + static void flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask) { @@ -2896,7 +2968,7 @@ flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask) HMAP_FOR_EACH (ofport, up.hmap_node, &ctx->ofproto->up.ports) { uint16_t ofp_port = ofport->up.ofp_port; if (ofp_port != ctx->flow.in_port && !(ofport->up.opp.config & mask)) { - nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, + nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, ofport->odp_port); } } @@ -2917,7 +2989,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx, add_output_action(ctx, ctx->flow.in_port); break; case OFPP_TABLE: - xlate_table_action(ctx, ctx->flow.in_port); + xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id); break; case OFPP_NORMAL: xlate_normal(ctx); @@ -2930,11 +3002,13 @@ xlate_output_action__(struct action_xlate_ctx *ctx, break; case OFPP_CONTROLLER: commit_odp_actions(ctx); - nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, max_len); + nl_msg_put_u64(ctx->odp_actions, OVS_ACTION_ATTR_USERSPACE, max_len); break; case OFPP_LOCAL: add_output_action(ctx, OFPP_LOCAL); break; + case OFPP_NONE: + break; default: if (port != ctx->flow.in_port) { add_output_action(ctx, port); @@ -2952,6 +3026,19 @@ xlate_output_action__(struct action_xlate_ctx *ctx, } } +static void +xlate_output_reg_action(struct action_xlate_ctx *ctx, + const struct nx_action_output_reg *naor) +{ + uint64_t ofp_port; + + ofp_port = nxm_read_field_bits(naor->src, naor->ofs_nbits, &ctx->flow); + + if (ofp_port <= UINT16_MAX) { + xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len)); + } +} + static void xlate_output_action(struct action_xlate_ctx *ctx, const struct ofp_action_output *oao) @@ -2975,14 +3062,14 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, return; } - /* Figure out ODP output port. */ + /* Figure out datapath output port. */ ofp_port = ntohs(oae->port); if (ofp_port == OFPP_IN_PORT) { ofp_port = ctx->flow.in_port; } odp_port = ofp_port_to_odp_port(ofp_port); - /* Add ODP actions. */ + /* Add datapath actions. */ ctx_priority = ctx->priority; ctx->priority = priority; add_output_action(ctx, odp_port); @@ -3039,6 +3126,28 @@ xlate_autopath(struct action_xlate_ctx *ctx, autopath_execute(naa, &ctx->flow, ofp_port); } +static bool +slave_enabled_cb(uint16_t ofp_port, void *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_; + struct ofport_dpif *port; + + switch (ofp_port) { + case OFPP_IN_PORT: + case OFPP_TABLE: + case OFPP_NORMAL: + case OFPP_FLOOD: + case OFPP_ALL: + case OFPP_LOCAL: + return true; + case OFPP_CONTROLLER: /* Not supported by the bundle action. */ + return false; + default: + port = get_ofp_port(ofproto, ofp_port); + return port ? port->may_enable : false; + } +} + static void do_xlate_actions(const union ofp_action *in, size_t n_in, struct action_xlate_ctx *ctx) @@ -3064,6 +3173,8 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, const struct nx_action_set_queue *nasq; const struct nx_action_multipath *nam; const struct nx_action_autopath *naa; + const struct nx_action_bundle *nab; + const struct nx_action_output_reg *naor; enum ofputil_action_code code; ovs_be64 tun_id; @@ -3107,7 +3218,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPUTIL_OFPAT_SET_NW_TOS: - ctx->flow.nw_tos = ia->nw_tos.nw_tos; + ctx->flow.nw_tos = ia->nw_tos.nw_tos & IP_DSCP_MASK; break; case OFPUTIL_OFPAT_SET_TP_SRC: @@ -3124,7 +3235,11 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, case OFPUTIL_NXAST_RESUBMIT: nar = (const struct nx_action_resubmit *) ia; - xlate_table_action(ctx, ntohs(nar->in_port)); + xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id); + break; + + case OFPUTIL_NXAST_RESUBMIT_TABLE: + xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia); break; case OFPUTIL_NXAST_SET_TUNNEL: @@ -3170,6 +3285,26 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, naa = (const struct nx_action_autopath *) ia; xlate_autopath(ctx, naa); break; + + case OFPUTIL_NXAST_BUNDLE: + ctx->ofproto->has_bundle_action = true; + nab = (const struct nx_action_bundle *) ia; + xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow, + slave_enabled_cb, + ctx->ofproto), 0); + break; + + case OFPUTIL_NXAST_BUNDLE_LOAD: + ctx->ofproto->has_bundle_action = true; + nab = (const struct nx_action_bundle *) ia; + bundle_execute_load(nab, &ctx->flow, slave_enabled_cb, + ctx->ofproto); + break; + + case OFPUTIL_NXAST_OUTPUT_REG: + naor = (const struct nx_action_output_reg *) ia; + xlate_output_reg_action(ctx, naor); + break; } } } @@ -3199,6 +3334,7 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->priority = 0; ctx->base_priority = 0; ctx->base_flow = ctx->flow; + ctx->table_id = 0; if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) { ctx->may_set_up_flow = false; @@ -3311,7 +3447,8 @@ dst_is_duplicate(const struct dst_set *set, const struct dst *test) static bool ofbundle_trunks_vlan(const struct ofbundle *bundle, uint16_t vlan) { - return bundle->vlan < 0 && vlan_bitmap_contains(bundle->trunks, vlan); + return (bundle->vlan < 0 + && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan))); } static bool @@ -3357,7 +3494,48 @@ compose_dsts(struct action_xlate_ctx *ctx, uint16_t vlan, static bool vlan_is_mirrored(const struct ofmirror *m, int vlan) { - return vlan_bitmap_contains(m->vlans, vlan); + return !m->vlans || bitmap_is_set(m->vlans, vlan); +} + +/* Returns true if a packet with Ethernet destination MAC 'dst' may be mirrored + * to a VLAN. In general most packets may be mirrored but we want to drop + * protocols that may confuse switches. */ +static bool +eth_dst_may_rspan(const uint8_t dst[ETH_ADDR_LEN]) +{ + /* If you change this function's behavior, please update corresponding + * documentation in vswitch.xml at the same time. */ + if (dst[0] != 0x01) { + /* All the currently banned MACs happen to start with 01 currently, so + * this is a quick way to eliminate most of the good ones. */ + } else { + if (eth_addr_is_reserved(dst)) { + /* Drop STP, IEEE pause frames, and other reserved protocols + * (01-80-c2-00-00-0x). */ + return false; + } + + if (dst[0] == 0x01 && dst[1] == 0x00 && dst[2] == 0x0c) { + /* Cisco OUI. */ + if ((dst[3] & 0xfe) == 0xcc && + (dst[4] & 0xfe) == 0xcc && + (dst[5] & 0xfe) == 0xcc) { + /* Drop the following protocols plus others following the same + pattern: + + CDP, VTP, DTP, PAgP (01-00-0c-cc-cc-cc) + Spanning Tree PVSTP+ (01-00-0c-cc-cc-cd) + STP Uplink Fast (01-00-0c-cd-cd-cd) */ + return false; + } + + if (!(dst[3] | dst[4] | dst[5])) { + /* Drop Inter Switch Link packets (01-00-0c-00-00-00). */ + return false; + } + } + } + return true; } static void @@ -3394,7 +3572,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, && !dst_is_duplicate(set, &dst)) { dst_set_add(set, &dst); } - } else { + } else if (eth_dst_may_rspan(ctx->flow.dl_dst)) { struct ofbundle *bundle; HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) { @@ -3450,7 +3628,7 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, continue; } nl_msg_put_u32(ctx->odp_actions, - ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port); + OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port); } /* Then output the rest. */ @@ -3461,18 +3639,18 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, } if (dst->vlan != cur_vlan) { if (dst->vlan == OFP_VLAN_NONE) { - nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN); + nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_STRIP_VLAN); } else { ovs_be16 tci; tci = htons(dst->vlan & VLAN_VID_MASK); tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK); nl_msg_put_be16(ctx->odp_actions, - ODP_ACTION_ATTR_SET_DL_TCI, tci); + OVS_ACTION_ATTR_SET_DL_TCI, tci); } cur_vlan = dst->vlan; } nl_msg_put_u32(ctx->odp_actions, - ODP_ACTION_ATTR_OUTPUT, dst->port->odp_port); + OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port); } dst_set_free(&set); @@ -3625,8 +3803,10 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, return false; } - /* Drop frames for reserved multicast addresses. */ - if (eth_addr_is_reserved(flow->dl_dst)) { + /* Drop frames for reserved multicast addresses + * only if forward_bpdu option is absent. */ + if (eth_addr_is_reserved(flow->dl_dst) && + !ofproto->up.forward_bpdu) { return false; } @@ -3666,10 +3846,7 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, return true; } -/* If the composed actions may be applied to any packet in the given 'flow', - * returns true. Otherwise, the actions should only be applied to 'packet', or - * not at all, if 'packet' was NULL. */ -static bool +static void xlate_normal(struct action_xlate_ctx *ctx) { struct ofbundle *in_bundle; @@ -3700,7 +3877,8 @@ xlate_normal(struct action_xlate_ctx *ctx) * 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. */ - return false; + ctx->may_set_up_flow = false; + return; } else { out_bundle = OFBUNDLE_FLOOD; } @@ -3714,8 +3892,6 @@ done: if (in_bundle) { compose_actions(ctx, vlan, in_bundle, out_bundle); } - - return true; } static bool @@ -3814,7 +3990,8 @@ struct ofproto_trace { }; static void -trace_format_rule(struct ds *result, int level, const struct rule *rule) +trace_format_rule(struct ds *result, uint8_t table_id, int level, + const struct rule_dpif *rule) { ds_put_char_multiple(result, '\t', level); if (!rule) { @@ -3822,14 +3999,14 @@ trace_format_rule(struct ds *result, int level, const struct rule *rule) return; } - ds_put_format(result, "Rule: cookie=%#"PRIx64" ", - ntohll(rule->flow_cookie)); - cls_rule_format(&rule->cr, result); + ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ", + table_id, ntohll(rule->up.flow_cookie)); + cls_rule_format(&rule->up.cr, result); ds_put_char(result, '\n'); ds_put_char_multiple(result, '\t', level); ds_put_cstr(result, "OpenFlow "); - ofp_print_actions(result, rule->actions, rule->n_actions); + ofp_print_actions(result, rule->up.actions, rule->up.n_actions); ds_put_char(result, '\n'); } @@ -3848,6 +4025,20 @@ trace_format_flow(struct ds *result, int level, const char *title, ds_put_char(result, '\n'); } +static void +trace_format_regs(struct ds *result, int level, const char *title, + struct ofproto_trace *trace) +{ + size_t i; + + ds_put_char_multiple(result, '\t', level); + ds_put_format(result, "%s:", title); + for (i = 0; i < FLOW_N_REGS; i++) { + ds_put_format(result, " reg%zu=0x%"PRIx32, i, trace->flow.regs[i]); + } + ds_put_char(result, '\n'); +} + static void trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule) { @@ -3856,33 +4047,79 @@ trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule) ds_put_char(result, '\n'); trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace); - trace_format_rule(result, ctx->recurse + 1, &rule->up); + trace_format_regs(result, ctx->recurse + 1, "Resubmitted regs", trace); + trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule); } static void ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, void *aux OVS_UNUSED) { - char *dpname, *in_port_s, *tun_id_s, *packet_s; + char *dpname, *arg1, *arg2, *arg3; char *args = xstrdup(args_); char *save_ptr = NULL; struct ofproto_dpif *ofproto; - struct ofpbuf packet; + struct ofpbuf odp_key; + struct ofpbuf *packet; struct rule_dpif *rule; struct ds result; struct flow flow; - uint16_t in_port; - ovs_be64 tun_id; char *s; - ofpbuf_init(&packet, strlen(args) / 2); + packet = NULL; + ofpbuf_init(&odp_key, 0); ds_init(&result); dpname = strtok_r(args, " ", &save_ptr); - tun_id_s = strtok_r(NULL, " ", &save_ptr); - in_port_s = strtok_r(NULL, " ", &save_ptr); - packet_s = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */ - if (!dpname || !in_port_s || !packet_s) { + arg1 = strtok_r(NULL, " ", &save_ptr); + arg2 = strtok_r(NULL, " ", &save_ptr); + arg3 = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */ + if (dpname && arg1 && !arg2 && !arg3) { + /* ofproto/trace dpname flow */ + int error; + + /* Convert string to datapath key. */ + ofpbuf_init(&odp_key, 0); + error = odp_flow_key_from_string(arg1, &odp_key); + if (error) { + unixctl_command_reply(conn, 501, "Bad flow syntax"); + goto exit; + } + + /* Convert odp_key to flow. */ + error = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); + if (error) { + unixctl_command_reply(conn, 501, "Invalid flow"); + goto exit; + } + } else if (dpname && arg1 && arg2 && arg3) { + /* ofproto/trace dpname tun_id in_port packet */ + uint16_t in_port; + ovs_be64 tun_id; + + tun_id = htonll(strtoull(arg1, NULL, 0)); + in_port = ofp_port_to_odp_port(atoi(arg2)); + + packet = ofpbuf_new(strlen(args) / 2); + arg3 = ofpbuf_put_hex(packet, arg3, NULL); + arg3 += strspn(arg3, " "); + if (*arg3 != '\0') { + unixctl_command_reply(conn, 501, "Trailing garbage in command"); + goto exit; + } + if (packet->size < ETH_HEADER_LEN) { + unixctl_command_reply(conn, 501, + "Packet data too short for Ethernet"); + goto exit; + } + + ds_put_cstr(&result, "Packet: "); + s = ofp_packet_to_string(packet->data, packet->size, packet->size); + ds_put_cstr(&result, s); + free(s); + + flow_extract(packet, tun_id, in_port, &flow); + } else { unixctl_command_reply(conn, 501, "Bad command syntax"); goto exit; } @@ -3894,39 +4131,19 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, goto exit; } - tun_id = htonll(strtoull(tun_id_s, NULL, 0)); - in_port = ofp_port_to_odp_port(atoi(in_port_s)); - - packet_s = ofpbuf_put_hex(&packet, packet_s, NULL); - packet_s += strspn(packet_s, " "); - if (*packet_s != '\0') { - unixctl_command_reply(conn, 501, "Trailing garbage in command"); - goto exit; - } - if (packet.size < ETH_HEADER_LEN) { - unixctl_command_reply(conn, 501, "Packet data too short for Ethernet"); - goto exit; - } - - ds_put_cstr(&result, "Packet: "); - s = ofp_packet_to_string(packet.data, packet.size, packet.size); - ds_put_cstr(&result, s); - free(s); - - flow_extract(&packet, tun_id, in_port, &flow); ds_put_cstr(&result, "Flow: "); flow_format(&result, &flow); ds_put_char(&result, '\n'); - rule = rule_dpif_lookup(ofproto, &flow); - trace_format_rule(&result, 0, &rule->up); + rule = rule_dpif_lookup(ofproto, &flow, 0); + trace_format_rule(&result, 0, 0, rule); if (rule) { struct ofproto_trace trace; struct ofpbuf *odp_actions; trace.result = &result; trace.flow = flow; - action_xlate_ctx_init(&trace.ctx, ofproto, &flow, &packet); + action_xlate_ctx_init(&trace.ctx, ofproto, &flow, packet); trace.ctx.resubmit_hook = trace_resubmit; odp_actions = xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions); @@ -3936,13 +4153,23 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, ds_put_cstr(&result, "Datapath actions: "); format_odp_actions(&result, odp_actions->data, odp_actions->size); ofpbuf_delete(odp_actions); + + if (!trace.ctx.may_set_up_flow) { + if (packet) { + ds_put_cstr(&result, "\nThis flow is not cachable."); + } else { + ds_put_cstr(&result, "\nThe datapath actions are incomplete--" + "for complete actions, please supply a packet."); + } + } } unixctl_command_reply(conn, 200, ds_cstr(&result)); exit: ds_destroy(&result); - ofpbuf_uninit(&packet); + ofpbuf_delete(packet); + ofpbuf_uninit(&odp_key); free(args); } @@ -4027,4 +4254,5 @@ const struct ofproto_class ofproto_dpif_class = { mirror_set, set_flood_vlans, is_mirror_output_bundle, + forward_bpdu_changed, };