X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=4cc720667d143dff167fd4ae42bdcb8c95fbe46b;hb=80740385d2700b1a03d28a02338d02509fd0b697;hp=a638349bd6630448bc0d72adec413b9862ba186d;hpb=62cd70721ac27719a1d41225563e2496f49f0312;p=openvswitch diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index a638349b..4cc72066 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -63,7 +63,7 @@ COVERAGE_DEFINE(facet_unexpected); /* Maximum depth of flow table recursion (due to resubmit actions) in a * flow translation. */ -#define MAX_RESUBMIT_RECURSION 16 +#define MAX_RESUBMIT_RECURSION 32 /* Number of implemented OpenFlow tables. */ enum { N_TABLES = 255 }; @@ -155,11 +155,15 @@ struct ofbundle { }; static void bundle_remove(struct ofport *); +static void bundle_update(struct ofbundle *); 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 void stp_run(struct ofproto_dpif *ofproto); +static void stp_wait(struct ofproto_dpif *ofproto); + struct action_xlate_ctx { /* action_xlate_ctx_init() initializes these members. */ @@ -201,13 +205,13 @@ struct action_xlate_ctx { * reason to look at them. */ int recurse; /* Recursion level, via xlate_table_action. */ - 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. */ + uint32_t original_priority; /* Priority when packet arrived. */ uint8_t table_id; /* OpenFlow table ID where flow was found. */ uint32_t sflow_n_outputs; /* Number of output ports. */ uint16_t sflow_odp_port; /* Output port for composing sFlow action. */ uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */ + bool exit; /* No further actions should be processed. */ }; static void action_xlate_ctx_init(struct action_xlate_ctx *, @@ -256,8 +260,7 @@ struct facet { struct netflow_flow nf_flow; /* Per-flow NetFlow tracking data. */ }; -static struct facet *facet_create(struct rule_dpif *, const struct flow *, - const struct ofpbuf *packet); +static struct facet *facet_create(struct rule_dpif *, const struct flow *); static void facet_remove(struct ofproto_dpif *, struct facet *); static void facet_free(struct facet *); @@ -266,6 +269,11 @@ 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); static void facet_execute(struct ofproto_dpif *, struct facet *, struct ofpbuf *packet); @@ -309,6 +317,10 @@ struct ofport_dpif { 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. */ + + struct stp_port *stp_port; /* Spanning Tree Protocol, if any. */ + enum stp_state stp_state; /* Always STP_DISABLED if STP not in use. */ + long long int stp_state_entered; }; static struct ofport_dpif * @@ -369,6 +381,10 @@ struct ofproto_dpif { struct list completions; bool has_bundle_action; /* True when the first bundle action appears. */ + + /* Spanning tree. */ + struct stp *stp; + long long int stp_last_tick; }; /* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only @@ -396,7 +412,12 @@ static void update_learning_table(struct ofproto_dpif *, static bool is_admissible(struct ofproto_dpif *, const struct flow *, bool have_packet, tag_type *, int *vlanp, struct ofbundle **in_bundlep); + +/* Upcalls. */ +#define FLOW_MISS_MAX_BATCH 50 static void handle_upcall(struct ofproto_dpif *, struct dpif_upcall *); +static void handle_miss_upcalls(struct ofproto_dpif *, + struct dpif_upcall *, size_t n); /* Flow expiration. */ static int expire(struct ofproto_dpif *); @@ -485,6 +506,7 @@ construct(struct ofproto *ofproto_, int *n_tablesp) ofproto->netflow = NULL; ofproto->sflow = NULL; + ofproto->stp = NULL; hmap_init(&ofproto->bundles); ofproto->ml = mac_learning_create(); for (i = 0; i < MAX_MIRRORS; i++) { @@ -565,8 +587,10 @@ static int run(struct ofproto *ofproto_) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + struct dpif_upcall misses[FLOW_MISS_MAX_BATCH]; struct ofport_dpif *ofport; struct ofbundle *bundle; + size_t n_misses; int i; if (!clogged) { @@ -574,22 +598,29 @@ run(struct ofproto *ofproto_) } dpif_run(ofproto->dpif); - for (i = 0; i < 50; i++) { - struct dpif_upcall packet; + n_misses = 0; + for (i = 0; i < FLOW_MISS_MAX_BATCH; i++) { + struct dpif_upcall *upcall = &misses[n_misses]; int error; - error = dpif_recv(ofproto->dpif, &packet); + error = dpif_recv(ofproto->dpif, upcall); if (error) { - if (error == ENODEV) { - /* Datapath destroyed. */ + if (error == ENODEV && n_misses == 0) { return error; } break; } - handle_upcall(ofproto, &packet); + if (upcall->type == DPIF_UC_MISS) { + /* Handle it later. */ + n_misses++; + } else { + handle_upcall(ofproto, upcall); + } } + handle_miss_upcalls(ofproto, misses, n_misses); + if (timer_expired(&ofproto->next_expiration)) { int delay = expire(ofproto); timer_set_duration(&ofproto->next_expiration, delay); @@ -609,6 +640,7 @@ run(struct ofproto *ofproto_) bundle_run(bundle); } + stp_run(ofproto); mac_learning_run(ofproto->ml, &ofproto->revalidate_set); /* Now revalidate if there's anything to do. */ @@ -659,6 +691,7 @@ wait(struct ofproto *ofproto_) bundle_wait(bundle); } mac_learning_wait(ofproto->ml); + stp_wait(ofproto); 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()"); @@ -764,6 +797,8 @@ port_construct(struct ofport *port_) port->cfm = NULL; port->tag = tag_create_random(); port->may_enable = true; + port->stp_port = NULL; + port->stp_state = STP_DISABLED; if (ofproto->sflow) { dpif_sflow_add_port(ofproto->sflow, port->odp_port, @@ -807,6 +842,10 @@ port_reconfigured(struct ofport *port_, ovs_be32 old_config) if (changed & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP | OFPPC_NO_FWD | OFPPC_NO_FLOOD)) { ofproto->need_revalidate = true; + + if (changed & htonl(OFPPC_NO_FLOOD) && port->bundle) { + bundle_update(port->bundle); + } } } @@ -889,6 +928,249 @@ get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps, } } +/* Spanning Tree. */ + +static void +send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_) +{ + struct ofproto_dpif *ofproto = ofproto_; + struct stp_port *sp = stp_get_port(ofproto->stp, port_num); + struct ofport_dpif *ofport; + + ofport = stp_port_get_aux(sp); + if (!ofport) { + VLOG_WARN_RL(&rl, "%s: cannot send BPDU on unknown port %d", + ofproto->up.name, port_num); + } else { + struct eth_header *eth = pkt->l2; + + netdev_get_etheraddr(ofport->up.netdev, eth->eth_src); + if (eth_addr_is_zero(eth->eth_src)) { + VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d " + "with unknown MAC", ofproto->up.name, port_num); + } else { + send_packet(ofproto_dpif_cast(ofport->up.ofproto), + ofport->odp_port, pkt); + } + } + ofpbuf_delete(pkt); +} + +/* Configures STP on 'ofproto_' using the settings defined in 's'. */ +static int +set_stp(struct ofproto *ofproto_, const struct ofproto_stp_settings *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + /* Only revalidate flows if the configuration changed. */ + if (!s != !ofproto->stp) { + ofproto->need_revalidate = true; + } + + if (s) { + if (!ofproto->stp) { + ofproto->stp = stp_create(ofproto_->name, s->system_id, + send_bpdu_cb, ofproto); + ofproto->stp_last_tick = time_msec(); + } + + stp_set_bridge_id(ofproto->stp, s->system_id); + stp_set_bridge_priority(ofproto->stp, s->priority); + stp_set_hello_time(ofproto->stp, s->hello_time); + stp_set_max_age(ofproto->stp, s->max_age); + stp_set_forward_delay(ofproto->stp, s->fwd_delay); + } else { + stp_destroy(ofproto->stp); + ofproto->stp = NULL; + } + + return 0; +} + +static int +get_stp_status(struct ofproto *ofproto_, struct ofproto_stp_status *s) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + + if (ofproto->stp) { + s->enabled = true; + s->bridge_id = stp_get_bridge_id(ofproto->stp); + s->designated_root = stp_get_designated_root(ofproto->stp); + s->root_path_cost = stp_get_root_path_cost(ofproto->stp); + } else { + s->enabled = false; + } + + return 0; +} + +static void +update_stp_port_state(struct ofport_dpif *ofport) +{ + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + enum stp_state state; + + /* Figure out new state. */ + state = ofport->stp_port ? stp_port_get_state(ofport->stp_port) + : STP_DISABLED; + + /* Update state. */ + if (ofport->stp_state != state) { + ovs_be32 of_state; + bool fwd_change; + + VLOG_DBG_RL(&rl, "port %s: STP state changed from %s to %s", + netdev_get_name(ofport->up.netdev), + stp_state_name(ofport->stp_state), + stp_state_name(state)); + if (stp_learn_in_state(ofport->stp_state) + != stp_learn_in_state(state)) { + /* xxx Learning action flows should also be flushed. */ + mac_learning_flush(ofproto->ml); + } + fwd_change = stp_forward_in_state(ofport->stp_state) + != stp_forward_in_state(state); + + ofproto->need_revalidate = true; + ofport->stp_state = state; + ofport->stp_state_entered = time_msec(); + + if (fwd_change) { + bundle_update(ofport->bundle); + } + + /* Update the STP state bits in the OpenFlow port description. */ + of_state = (ofport->up.opp.state & htonl(~OFPPS_STP_MASK)) + | htonl(state == STP_LISTENING ? OFPPS_STP_LISTEN + : state == STP_LEARNING ? OFPPS_STP_LEARN + : state == STP_FORWARDING ? OFPPS_STP_FORWARD + : state == STP_BLOCKING ? OFPPS_STP_BLOCK + : 0); + ofproto_port_set_state(&ofport->up, of_state); + } +} + +/* Configures STP on 'ofport_' using the settings defined in 's'. The + * caller is responsible for assigning STP port numbers and ensuring + * there are no duplicates. */ +static int +set_stp_port(struct ofport *ofport_, + const struct ofproto_port_stp_settings *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct stp_port *sp = ofport->stp_port; + + if (!s || !s->enable) { + if (sp) { + ofport->stp_port = NULL; + stp_port_disable(sp); + update_stp_port_state(ofport); + } + return 0; + } else if (sp && stp_port_no(sp) != s->port_num + && ofport == stp_port_get_aux(sp)) { + /* The port-id changed, so disable the old one if it's not + * already in use by another port. */ + stp_port_disable(sp); + } + + sp = ofport->stp_port = stp_get_port(ofproto->stp, s->port_num); + stp_port_enable(sp); + + stp_port_set_aux(sp, ofport); + stp_port_set_priority(sp, s->priority); + stp_port_set_path_cost(sp, s->path_cost); + + update_stp_port_state(ofport); + + return 0; +} + +static int +get_stp_port_status(struct ofport *ofport_, + struct ofproto_port_stp_status *s) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofport->up.ofproto); + struct stp_port *sp = ofport->stp_port; + + if (!ofproto->stp || !sp) { + s->enabled = false; + return 0; + } + + s->enabled = true; + s->port_id = stp_port_get_id(sp); + s->state = stp_port_get_state(sp); + s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000; + s->role = stp_port_get_role(sp); + stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count); + + return 0; +} + +static void +stp_run(struct ofproto_dpif *ofproto) +{ + if (ofproto->stp) { + long long int now = time_msec(); + long long int elapsed = now - ofproto->stp_last_tick; + struct stp_port *sp; + + if (elapsed > 0) { + stp_tick(ofproto->stp, MIN(INT_MAX, elapsed)); + ofproto->stp_last_tick = now; + } + while (stp_get_changed_port(ofproto->stp, &sp)) { + struct ofport_dpif *ofport = stp_port_get_aux(sp); + + if (ofport) { + update_stp_port_state(ofport); + } + } + } +} + +static void +stp_wait(struct ofproto_dpif *ofproto) +{ + if (ofproto->stp) { + poll_timer_wait(1000); + } +} + +/* Returns true if STP should process 'flow'. */ +static bool +stp_should_process_flow(const struct flow *flow) +{ + return eth_addr_equals(flow->dl_dst, eth_addr_stp); +} + +static void +stp_process_packet(const struct ofport_dpif *ofport, + const struct ofpbuf *packet) +{ + struct ofpbuf payload = *packet; + struct eth_header *eth = payload.data; + struct stp_port *sp = ofport->stp_port; + + /* Sink packets on ports that have STP disabled when the bridge has + * STP enabled. */ + if (!sp || stp_port_get_state(sp) == STP_DISABLED) { + return; + } + + /* Trim off padding on payload. */ + if (payload.size > ntohs(eth->eth_type) + ETH_HEADER_LEN) { + payload.size = ntohs(eth->eth_type) + ETH_HEADER_LEN; + } + + if (ofpbuf_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) { + stp_received_bpdu(sp, payload.data, payload.size); + } +} + /* Bundles. */ /* Expires all MAC learning entries associated with 'port' and forces ofproto @@ -940,6 +1222,21 @@ bundle_lookup_multiple(struct ofproto_dpif *ofproto, } } +static void +bundle_update(struct ofbundle *bundle) +{ + struct ofport_dpif *port; + + bundle->floodable = true; + LIST_FOR_EACH (port, bundle_node, &bundle->ports) { + if (port->up.opp.config & htonl(OFPPC_NO_FLOOD) + || !stp_forward_in_state(port->stp_state)) { + bundle->floodable = false; + break; + } + } +} + static void bundle_del_port(struct ofport_dpif *port) { @@ -957,12 +1254,7 @@ bundle_del_port(struct ofport_dpif *port) bond_slave_unregister(bundle->bond, port); } - bundle->floodable = true; - LIST_FOR_EACH (port, bundle_node, &bundle->ports) { - if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) { - bundle->floodable = false; - } - } + bundle_update(bundle); } static bool @@ -985,7 +1277,8 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port, port->bundle = bundle; list_push_back(&bundle->ports, &port->bundle_node); - if (port->up.opp.config & htonl(OFPPC_NO_FLOOD)) { + if (port->up.opp.config & htonl(OFPPC_NO_FLOOD) + || !stp_forward_in_state(port->stp_state)) { bundle->floodable = false; } } @@ -1253,12 +1546,8 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size) pdu_size); memcpy(packet_pdu, pdu, pdu_size); - error = netdev_send(port->up.netdev, &packet); - if (error) { - VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed " - "(%s)", port->bundle->name, - netdev_get_name(port->up.netdev), strerror(error)); - } + send_packet(ofproto_dpif_cast(port->up.ofproto), port->odp_port, + &packet); ofpbuf_uninit(&packet); } else { VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface " @@ -1277,7 +1566,16 @@ bundle_send_learning_packets(struct ofbundle *bundle) error = n_packets = n_errors = 0; LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) { if (e->port.p != bundle) { - int ret = bond_send_learning_packet(bundle->bond, e->mac, e->vlan); + struct ofpbuf *learning_packet; + struct ofport_dpif *port; + int ret; + + learning_packet = bond_compose_learning_packet(bundle->bond, e->mac, + e->vlan, + (void **)&port); + ret = send_packet(ofproto_dpif_cast(port->up.ofproto), + port->odp_port, learning_packet); + ofpbuf_delete(learning_packet); if (ret) { error = ret; n_errors++; @@ -1511,7 +1809,7 @@ set_flood_vlans(struct ofproto *ofproto_, unsigned long *flood_vlans) } static bool -is_mirror_output_bundle(struct ofproto *ofproto_, void *aux) +is_mirror_output_bundle(const struct ofproto *ofproto_, void *aux) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct ofbundle *bundle = bundle_lookup(ofproto, aux); @@ -1715,6 +2013,28 @@ port_is_lacp_current(const struct ofport *ofport_) /* Upcall handling. */ +/* Flow miss batching. + * + * Some dpifs implement operations faster when you hand them off in a batch. + * To allow batching, "struct flow_miss" queues the dpif-related work needed + * for a given flow. Each "struct flow_miss" corresponds to sending one or + * more packets, plus possibly installing the flow in the dpif. + * + * So far we only batch the operations that affect flow setup time the most. + * It's possible to batch more than that, but the benefit might be minimal. */ +struct flow_miss { + struct hmap_node hmap_node; + struct flow flow; + const struct nlattr *key; + size_t key_len; + struct list packets; +}; + +struct flow_miss_op { + union dpif_op dpif_op; + struct facet *facet; +}; + /* Sends an OFPT_PACKET_IN message for 'packet' of type OFPR_NO_MATCH to each * OpenFlow controller as necessary according to their individual * configurations. @@ -1784,75 +2104,216 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow, lacp_process_packet(ofport->bundle->lacp, ofport, packet); } return true; + } else if (ofproto->stp && stp_should_process_flow(flow)) { + if (packet) { + stp_process_packet(ofport, packet); + } + return true; } return false; } -static void -handle_miss_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +static struct flow_miss * +flow_miss_create(struct hmap *todo, const struct flow *flow, + const struct nlattr *key, size_t key_len) { - struct facet *facet; - struct flow flow; + uint32_t hash = flow_hash(flow, 0); + struct flow_miss *miss; - /* Obtain in_port and tun_id, at least. */ - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + HMAP_FOR_EACH_WITH_HASH (miss, hmap_node, hash, todo) { + if (flow_equal(&miss->flow, flow)) { + return miss; + } + } - /* Set header pointers in 'flow'. */ - flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow); + miss = xmalloc(sizeof *miss); + hmap_insert(todo, &miss->hmap_node, hash); + miss->flow = *flow; + miss->key = key; + miss->key_len = key_len; + list_init(&miss->packets); + return miss; +} - /* Handle 802.1ag and LACP. */ - if (process_special(ofproto, &flow, upcall->packet)) { - ofpbuf_delete(upcall->packet); - ofproto->n_matches++; - return; - } +static void +handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss, + struct flow_miss_op *ops, size_t *n_ops) +{ + const struct flow *flow = &miss->flow; + struct ofpbuf *packet, *next_packet; + struct facet *facet; - facet = facet_lookup_valid(ofproto, &flow); + facet = facet_lookup_valid(ofproto, flow); if (!facet) { - struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow, 0); + struct rule_dpif *rule; + + 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); + struct ofport_dpif *port = get_ofp_port(ofproto, flow->in_port); if (port) { if (port->up.opp.config & htonl(OFPPC_NO_PACKET_IN)) { COVERAGE_INC(ofproto_dpif_no_packet_in); /* XXX install 'drop' flow entry */ - ofpbuf_delete(upcall->packet); return; } } else { VLOG_WARN_RL(&rl, "packet-in on unknown port %"PRIu16, - flow.in_port); + 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); } - send_packet_in_miss(ofproto, upcall->packet, &flow, false); return; } - facet = facet_create(rule, &flow, upcall->packet); - } else if (!facet->may_install) { - /* The facet is not installable, that is, we need to process every - * packet, so process the current packet's actions into 'facet'. */ - facet_make_actions(ofproto, facet, upcall->packet); + facet = facet_create(rule, flow); } - if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) { - /* - * Extra-special case for fail-open mode. - * - * We are in fail-open mode and the packet matched the fail-open rule, - * but we are connected to a controller too. We should send the packet - * up to the controller in the hope that it will try to set up a flow - * and thereby allow us to exit fail-open. - * - * See the top-level comment in fail-open.c for more information. - */ - send_packet_in_miss(ofproto, upcall->packet, &flow, true); + LIST_FOR_EACH_SAFE (packet, next_packet, list_node, &miss->packets) { + list_remove(&packet->list_node); + ofproto->n_matches++; + + if (facet->rule->up.cr.priority == FAIL_OPEN_PRIORITY) { + /* + * Extra-special case for fail-open mode. + * + * We are in fail-open mode and the packet matched the fail-open + * rule, but we are connected to a controller too. We should send + * the packet up to the controller in the hope that it will try to + * set up a flow and thereby allow us to exit fail-open. + * + * See the top-level comment in fail-open.c for more information. + */ + send_packet_in_miss(ofproto, packet, flow, true); + } + + if (!facet->may_install) { + facet_make_actions(ofproto, facet, packet); + } + if (!execute_controller_action(ofproto, &facet->flow, + facet->actions, facet->actions_len, + packet)) { + struct flow_miss_op *op = &ops[(*n_ops)++]; + struct dpif_execute *execute = &op->dpif_op.execute; + + op->facet = facet; + execute->type = DPIF_OP_EXECUTE; + execute->key = miss->key; + execute->key_len = miss->key_len; + execute->actions + = (facet->may_install + ? facet->actions + : xmemdup(facet->actions, facet->actions_len)); + execute->actions_len = facet->actions_len; + execute->packet = packet; + } } - facet_execute(ofproto, facet, upcall->packet); - facet_install(ofproto, facet, false); - ofproto->n_matches++; + if (facet->may_install) { + struct flow_miss_op *op = &ops[(*n_ops)++]; + struct dpif_flow_put *put = &op->dpif_op.flow_put; + + op->facet = facet; + put->type = DPIF_OP_FLOW_PUT; + put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY; + put->key = miss->key; + put->key_len = miss->key_len; + put->actions = facet->actions; + put->actions_len = facet->actions_len; + put->stats = NULL; + } +} + +static void +handle_miss_upcalls(struct ofproto_dpif *ofproto, struct dpif_upcall *upcalls, + size_t n_upcalls) +{ + struct dpif_upcall *upcall; + struct flow_miss *miss, *next_miss; + struct flow_miss_op flow_miss_ops[FLOW_MISS_MAX_BATCH * 2]; + union dpif_op *dpif_ops[FLOW_MISS_MAX_BATCH * 2]; + struct hmap todo; + size_t n_ops; + size_t i; + + if (!n_upcalls) { + return; + } + + /* Construct the to-do list. + * + * This just amounts to extracting the flow from each packet and sticking + * the packets that have the same flow in the same "flow_miss" structure so + * that we can process them together. */ + hmap_init(&todo); + for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) { + struct flow_miss *miss; + struct flow flow; + + /* Obtain in_port and tun_id, at least, then set 'flow''s header + * pointers. */ + odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + flow_extract(upcall->packet, flow.priority, flow.tun_id, + flow.in_port, &flow); + + /* Handle 802.1ag, LACP, and STP specially. */ + if (process_special(ofproto, &flow, upcall->packet)) { + ofpbuf_delete(upcall->packet); + ofproto->n_matches++; + continue; + } + + /* Add other packets to a to-do list. */ + miss = flow_miss_create(&todo, &flow, upcall->key, upcall->key_len); + list_push_back(&miss->packets, &upcall->packet->list_node); + } + + /* 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) { + 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++) { + dpif_ops[i] = &flow_miss_ops[i].dpif_op; + } + dpif_operate(ofproto->dpif, dpif_ops, n_ops); + + /* Free memory and update facets. */ + for (i = 0; i < n_ops; i++) { + struct flow_miss_op *op = &flow_miss_ops[i]; + struct dpif_execute *execute; + struct dpif_flow_put *put; + + switch (op->dpif_op.type) { + case DPIF_OP_EXECUTE: + execute = &op->dpif_op.execute; + if (op->facet->actions != execute->actions) { + free((struct nlattr *) execute->actions); + } + ofpbuf_delete((struct ofpbuf *) execute->packet); + break; + + case DPIF_OP_FLOW_PUT: + put = &op->dpif_op.flow_put; + if (!put->error) { + op->facet->installed = true; + } + break; + } + } } static void @@ -1890,8 +2351,8 @@ handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) break; case DPIF_UC_MISS: - handle_miss_upcall(ofproto, upcall); - break; + /* The caller handles these. */ + NOT_REACHED(); case DPIF_N_UC_TYPES: default: @@ -2179,15 +2640,16 @@ rule_expire(struct rule_dpif *rule) /* Facets. */ -/* Creates and returns a new facet owned by 'rule', given a 'flow' and an - * example 'packet' within that flow. +/* Creates and returns a new facet owned by 'rule', given a 'flow'. * * The caller must already have determined that no facet with an identical * 'flow' exists in 'ofproto' and that 'flow' is the best match for 'rule' in - * the ofproto's classifier table. */ + * the ofproto's classifier table. + * + * The facet will initially have no ODP actions. The caller should fix that + * by calling facet_make_actions(). */ static struct facet * -facet_create(struct rule_dpif *rule, const struct flow *flow, - const struct ofpbuf *packet) +facet_create(struct rule_dpif *rule, const struct flow *flow) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); struct facet *facet; @@ -2201,8 +2663,6 @@ facet_create(struct rule_dpif *rule, const struct flow *flow, netflow_flow_init(&facet->nf_flow); netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used); - facet_make_actions(ofproto, facet, packet); - return facet; } @@ -2213,23 +2673,15 @@ facet_free(struct facet *facet) free(facet); } -/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on - * 'packet', which arrived on 'in_port'. - * - * Takes ownership of 'packet'. */ static bool -execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, - const struct nlattr *odp_actions, size_t actions_len, - struct ofpbuf *packet) -{ - struct odputil_keybuf keybuf; - struct ofpbuf key; - int error; - - if (actions_len == 0) { - return true; - } else if (odp_actions->nla_type == OVS_ACTION_ATTR_USERSPACE - && NLA_ALIGN(odp_actions->nla_len) == actions_len) { +execute_controller_action(struct ofproto_dpif *ofproto, + const struct flow *flow, + const struct nlattr *odp_actions, size_t actions_len, + struct ofpbuf *packet) +{ + 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. @@ -2243,6 +2695,27 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, send_packet_in_action(ofproto, packet, nl_attr_get_u64(nla), flow, false); return true; + } else { + return false; + } +} + +/* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on + * 'packet', which arrived on 'in_port'. + * + * Takes ownership of 'packet'. */ +static bool +execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, + const struct nlattr *odp_actions, size_t actions_len, + struct ofpbuf *packet) +{ + struct odputil_keybuf keybuf; + struct ofpbuf key; + int error; + + if (execute_controller_action(ofproto, flow, odp_actions, actions_len, + packet)) { + return true; } ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); @@ -2413,6 +2886,7 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) * hash bucket.) */ vlan_tci = facet->flow.vlan_tci; NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) { + const struct ovs_action_push_vlan *vlan; struct ofport_dpif *port; switch (nl_attr_type(a)) { @@ -2429,7 +2903,8 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) break; case OVS_ACTION_ATTR_PUSH_VLAN: - vlan_tci = nl_attr_get_be16(a); + vlan = nl_attr_get(a); + vlan_tci = vlan->vlan_tci; break; } } @@ -2759,13 +3234,26 @@ static struct rule_dpif * rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow, uint8_t table_id) { + struct cls_rule *cls_rule; + struct classifier *cls; + if (table_id >= N_TABLES) { return NULL; } - return rule_dpif_cast(rule_from_cls_rule( - classifier_lookup(&ofproto->up.tables[table_id], - flow))); + cls = &ofproto->up.tables[table_id]; + if (flow->nw_frag & FLOW_NW_FRAG_ANY + && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) { + /* For OFPC_NORMAL frag_handling, we must pretend that transport ports + * are unavailable. */ + struct flow ofpc_normal_flow = *flow; + ofpc_normal_flow.tp_src = htons(0); + ofpc_normal_flow.tp_dst = htons(0); + cls_rule = classifier_lookup(cls, &ofpc_normal_flow); + } else { + cls_rule = classifier_lookup(cls, flow); + } + return rule_dpif_cast(rule_from_cls_rule(cls_rule)); } static void @@ -2904,7 +3392,8 @@ 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, 0) == rule) { - facet = facet_create(rule, flow, packet); + facet = facet_create(rule, flow); + facet_make_actions(ofproto, facet, packet); facet_execute(ofproto, facet, packet); facet_install(ofproto, facet, true); return 0; @@ -2955,7 +3444,7 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, struct flow flow; int error; - flow_extract((struct ofpbuf *) packet, 0, 0, &flow); + flow_extract((struct ofpbuf *) packet, 0, 0, 0, &flow); ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); odp_flow_key_from_flow(&key, &flow); @@ -3091,85 +3580,163 @@ fix_sflow_action(struct action_xlate_ctx *ctx) } static void -commit_vlan_tci(struct action_xlate_ctx *ctx, ovs_be16 vlan_tci) +commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type, + const void *key, size_t key_size) { - struct flow *base = &ctx->base_flow; - struct ofpbuf *odp_actions = ctx->odp_actions; + size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET); + nl_msg_put_unspec(odp_actions, key_type, key, key_size); + nl_msg_end_nested(odp_actions, offset); +} - if (base->vlan_tci != vlan_tci) { - if (!(vlan_tci & htons(VLAN_CFI))) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } else { - if (base->vlan_tci != htons(0)) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } - nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, - vlan_tci & ~htons(VLAN_CFI)); - } - base->vlan_tci = vlan_tci; +static void +commit_set_tun_id_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + if (base->tun_id == flow->tun_id) { + return; } + base->tun_id = flow->tun_id; + + commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID, + &base->tun_id, sizeof(base->tun_id)); } static void -commit_odp_actions(struct action_xlate_ctx *ctx) +commit_set_ether_addr_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) { - const struct flow *flow = &ctx->flow; - struct flow *base = &ctx->base_flow; - struct ofpbuf *odp_actions = ctx->odp_actions; + struct ovs_key_ethernet eth_key; - if (base->tun_id != flow->tun_id) { - nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id); - base->tun_id = flow->tun_id; + if (eth_addr_equals(base->dl_src, flow->dl_src) && + eth_addr_equals(base->dl_dst, flow->dl_dst)) { + return; } - if (base->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; + memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN); + memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN); + + memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN); + memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN); + + commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET, + ð_key, sizeof(eth_key)); +} + +static void +commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci) +{ + struct flow *base = &ctx->base_flow; + + if (base->vlan_tci == new_tci) { + return; } - if (base->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->vlan_tci & htons(VLAN_CFI)) { + nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN); } - 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 (new_tci & htons(VLAN_CFI)) { + struct ovs_action_push_vlan vlan; + + vlan.vlan_tpid = htons(ETH_TYPE_VLAN); + vlan.vlan_tci = new_tci; + nl_msg_put_unspec(ctx->odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, + &vlan, sizeof vlan); } + base->vlan_tci = new_tci; +} - commit_vlan_tci(ctx, flow->vlan_tci); +static void +commit_set_nw_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + struct ovs_key_ipv4 ipv4_key; - if (base->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->dl_type != htons(ETH_TYPE_IP) || + !base->nw_src || !base->nw_dst) { + return; } - if (base->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 (base->nw_src == flow->nw_src && + base->nw_dst == flow->nw_dst && + base->nw_tos == flow->nw_tos && + base->nw_ttl == flow->nw_ttl && + base->nw_frag == flow->nw_frag) { + return; + } + + ipv4_key.ipv4_src = base->nw_src = flow->nw_src; + ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst; + ipv4_key.ipv4_proto = base->nw_proto; + ipv4_key.ipv4_tos = flow->nw_tos; + ipv4_key.ipv4_ttl = flow->nw_ttl; + ipv4_key.ipv4_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE + : base->nw_frag == FLOW_NW_FRAG_ANY + ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); + + commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4, + &ipv4_key, sizeof(ipv4_key)); +} + +static void +commit_set_port_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + if (!base->tp_src || !base->tp_dst) { + return; } - if (!eth_addr_equals(base->dl_src, flow->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 (base->tp_src == flow->tp_src && + base->tp_dst == flow->tp_dst) { + return; } - if (!eth_addr_equals(base->dl_dst, flow->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 (flow->nw_proto == IPPROTO_TCP) { + struct ovs_key_tcp port_key; + + port_key.tcp_src = base->tp_src = flow->tp_src; + port_key.tcp_dst = base->tp_dst = flow->tp_dst; + + commit_set_action(odp_actions, OVS_KEY_ATTR_TCP, + &port_key, sizeof(port_key)); + + } else if (flow->nw_proto == IPPROTO_UDP) { + struct ovs_key_udp port_key; + + port_key.udp_src = base->tp_src = flow->tp_src; + port_key.udp_dst = base->tp_dst = flow->tp_dst; + + commit_set_action(odp_actions, OVS_KEY_ATTR_UDP, + &port_key, sizeof(port_key)); } +} - if (ctx->base_priority != ctx->priority) { - if (ctx->priority) { - nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY, - ctx->priority); - } else { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY); - } - ctx->base_priority = ctx->priority; +static void +commit_set_priority_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions) +{ + if (base->priority == flow->priority) { + return; } + base->priority = flow->priority; + + commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY, + &base->priority, sizeof(base->priority)); +} + +static void +commit_odp_actions(struct action_xlate_ctx *ctx) +{ + const struct flow *flow = &ctx->flow; + struct flow *base = &ctx->base_flow; + struct ofpbuf *odp_actions = ctx->odp_actions; + + commit_set_tun_id_action(flow, base, odp_actions); + commit_set_ether_addr_action(flow, base, odp_actions); + commit_vlan_action(ctx, flow->vlan_tci); + commit_set_nw_action(flow, base, odp_actions); + commit_set_port_action(flow, base, odp_actions); + commit_set_priority_action(flow, base, odp_actions); } static void @@ -3187,7 +3754,8 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) uint16_t odp_port = ofp_port_to_odp_port(ofp_port); if (ofport) { - if (ofport->up.opp.config & htonl(OFPPC_NO_FWD)) { + if (ofport->up.opp.config & htonl(OFPPC_NO_FWD) + || !stp_forward_in_state(ofport->stp_state)) { /* Forwarding disabled on port. */ return; } @@ -3280,7 +3848,9 @@ flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask) commit_odp_actions(ctx); 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)) { + if (ofp_port != ctx->flow.in_port + && !(ofport->up.opp.config & mask) + && stp_forward_in_state(ofport->stp_state)) { compose_output_action(ctx, ofport->odp_port); } } @@ -3375,7 +3945,7 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, const struct ofp_action_enqueue *oae) { uint16_t ofp_port, odp_port; - uint32_t ctx_priority, priority; + uint32_t flow_priority, priority; int error; error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id), @@ -3390,14 +3960,16 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, ofp_port = ntohs(oae->port); if (ofp_port == OFPP_IN_PORT) { ofp_port = ctx->flow.in_port; + } else if (ofp_port == ctx->flow.in_port) { + return; } odp_port = ofp_port_to_odp_port(ofp_port); /* Add datapath actions. */ - ctx_priority = ctx->priority; - ctx->priority = priority; + flow_priority = ctx->flow.priority; + ctx->flow.priority = priority; add_output_action(ctx, odp_port); - ctx->priority = ctx_priority; + ctx->flow.priority = flow_priority; /* Update NetFlow output port. */ if (ctx->nf_output_iface == NF_OUT_DROP) { @@ -3422,7 +3994,7 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx, return; } - ctx->priority = priority; + ctx->flow.priority = priority; } struct xlate_reg_state { @@ -3462,7 +4034,7 @@ slave_enabled_cb(uint16_t ofp_port, void *ofproto_) case OFPP_NORMAL: case OFPP_FLOOD: case OFPP_ALL: - case OFPP_LOCAL: + case OFPP_NONE: return true; case OFPP_CONTROLLER: /* Not supported by the bundle action. */ return false; @@ -3492,6 +4064,27 @@ xlate_learn_action(struct action_xlate_ctx *ctx, free(fm.actions); } +static bool +may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx) +{ + if (port->up.opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp) + ? htonl(OFPPC_NO_RECV_STP) + : htonl(OFPPC_NO_RECV))) { + return false; + } + + /* Only drop packets here if both forwarding and learning are + * disabled. If just learning is enabled, we need to have + * OFPP_NORMAL and the learning action have a look at the packet + * before we can drop it. */ + if (!stp_forward_in_state(port->stp_state) + && !stp_learn_in_state(port->stp_state)) { + return false; + } + + return true; +} + static void do_xlate_actions(const union ofp_action *in, size_t n_in, struct action_xlate_ctx *ctx) @@ -3501,11 +4094,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, size_t left; port = get_ofp_port(ctx->ofproto, ctx->flow.in_port); - if (port - && port->up.opp.config & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) && - port->up.opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp) - ? htonl(OFPPC_NO_RECV_STP) - : htonl(OFPPC_NO_RECV))) { + if (port && !may_receive(port, ctx)) { /* Drop this flow. */ return; } @@ -3522,6 +4111,10 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, enum ofputil_action_code code; ovs_be64 tun_id; + if (ctx->exit) { + break; + } + code = ofputil_decode_action_unsafe(ia); switch (code) { case OFPUTIL_OFPAT_OUTPUT: @@ -3562,7 +4155,8 @@ 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 & IP_DSCP_MASK; + 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: @@ -3598,7 +4192,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, break; case OFPUTIL_NXAST_POP_QUEUE: - ctx->priority = 0; + ctx->flow.priority = ctx->original_priority; break; case OFPUTIL_NXAST_REG_MOVE: @@ -3656,8 +4250,19 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, xlate_learn_action(ctx, (const struct nx_action_learn *) ia); } break; + + case OFPUTIL_NXAST_EXIT: + ctx->exit = true; + break; } } + + /* We've let OFPP_NORMAL and the learning action look at the packet, + * so drop it now if forwarding is disabled. */ + if (port && !stp_forward_in_state(port->stp_state)) { + ofpbuf_clear(ctx->odp_actions); + add_sflow_action(ctx); + } } static void @@ -3686,11 +4291,31 @@ xlate_actions(struct action_xlate_ctx *ctx, ctx->has_normal = false; ctx->nf_output_iface = NF_OUT_DROP; ctx->recurse = 0; - ctx->priority = 0; - ctx->base_priority = 0; + ctx->original_priority = ctx->flow.priority; ctx->base_flow = ctx->flow; ctx->base_flow.tun_id = 0; ctx->table_id = 0; + ctx->exit = false; + + if (ctx->flow.nw_frag & FLOW_NW_FRAG_ANY) { + switch (ctx->ofproto->up.frag_handling) { + case OFPC_FRAG_NORMAL: + /* We must pretend that transport ports are unavailable. */ + ctx->flow.tp_src = ctx->base_flow.tp_src = htons(0); + ctx->flow.tp_dst = ctx->base_flow.tp_dst = htons(0); + break; + + case OFPC_FRAG_DROP: + return ctx->odp_actions; + + case OFPC_FRAG_REASM: + NOT_REACHED(); + + case OFPC_FRAG_NX_MATCH: + /* Nothing to do. */ + break; + } + } if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) { ctx->may_set_up_flow = false; @@ -3698,7 +4323,6 @@ xlate_actions(struct action_xlate_ctx *ctx, } else { add_sflow_action(ctx); do_xlate_actions(in, n_in, ctx); - fix_sflow_action(ctx); if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow, ctx->odp_actions->data, @@ -3707,10 +4331,10 @@ xlate_actions(struct action_xlate_ctx *ctx, if (ctx->packet && connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow, ctx->packet)) { - nl_msg_push_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, - OVSP_LOCAL); + compose_output_action(ctx, OVSP_LOCAL); } } + fix_sflow_action(ctx); } return ctx->odp_actions; @@ -4051,7 +4675,7 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, if (tci) { tci |= htons(VLAN_CFI); } - commit_vlan_tci(ctx, tci); + commit_vlan_action(ctx, tci); cur_vid = dst->vid; } @@ -4096,6 +4720,14 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, return -1; } } else { + if (flow->dl_type == htons(ETH_TYPE_VLAN) && + !(flow->vlan_tci & htons(VLAN_CFI))) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial " + "VLAN tag received on port %s", + ofproto->up.name, in_bundle->name); + return -1; + } if (in_bundle->vlan_mode != PORT_VLAN_TRUNK) { return in_bundle->vlan; } else { @@ -4214,10 +4846,9 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow, return false; } - /* 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) { + /* 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; } @@ -4422,21 +5053,17 @@ rule_invalidate(const struct rule_dpif *rule) } static bool -get_drop_frags(struct ofproto *ofproto_) +set_frag_handling(struct ofproto *ofproto_, + enum ofp_config_flags frag_handling) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - bool drop_frags; - - dpif_get_drop_frags(ofproto->dpif, &drop_frags); - return drop_frags; -} -static void -set_drop_frags(struct ofproto *ofproto_, bool drop_frags) -{ - struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - - dpif_set_drop_frags(ofproto->dpif, drop_frags); + if (frag_handling != OFPC_FRAG_REASM) { + ofproto->need_revalidate = true; + return true; + } else { + return false; + } } static int @@ -4447,6 +5074,10 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); int error; + if (flow->in_port >= ofproto->max_ports && flow->in_port < OFPP_MAX) { + return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT); + } + error = validate_actions(ofp_actions, n_ofp_actions, flow, ofproto->max_ports); if (!error) { @@ -4485,6 +5116,22 @@ ofproto_dpif_lookup(const char *name) : NULL); } +static void +ofproto_unixctl_fdb_flush(struct unixctl_conn *conn, + const char *args, void *aux OVS_UNUSED) +{ + const struct ofproto_dpif *ofproto; + + ofproto = ofproto_dpif_lookup(args); + if (!ofproto) { + unixctl_command_reply(conn, 501, "no such bridge"); + return; + } + mac_learning_flush(ofproto->ml); + + unixctl_command_reply(conn, 200, "table successfully flushed"); +} + static void ofproto_unixctl_fdb_show(struct unixctl_conn *conn, const char *args, void *aux OVS_UNUSED) @@ -4582,7 +5229,7 @@ static void ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, void *aux OVS_UNUSED) { - char *dpname, *arg1, *arg2, *arg3; + char *dpname, *arg1, *arg2, *arg3, *arg4; char *args = xstrdup(args_); char *save_ptr = NULL; struct ofproto_dpif *ofproto; @@ -4600,7 +5247,8 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, dpname = strtok_r(args, " ", &save_ptr); arg1 = strtok_r(NULL, " ", &save_ptr); arg2 = strtok_r(NULL, " ", &save_ptr); - arg3 = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */ + arg3 = strtok_r(NULL, " ", &save_ptr); + arg4 = strtok_r(NULL, "", &save_ptr); /* Get entire rest of line. */ if (dpname && arg1 && (!arg2 || !strcmp(arg2, "-generate")) && !arg3) { /* ofproto/trace dpname flow [-generate] */ int error; @@ -4625,18 +5273,20 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, packet = ofpbuf_new(0); flow_compose(packet, &flow); } - } else if (dpname && arg1 && arg2 && arg3) { - /* ofproto/trace dpname tun_id in_port packet */ + } else if (dpname && arg1 && arg2 && arg3 && arg4) { + /* ofproto/trace dpname priority tun_id in_port packet */ uint16_t in_port; ovs_be64 tun_id; + uint32_t priority; - tun_id = htonll(strtoull(arg1, NULL, 0)); - in_port = ofp_port_to_odp_port(atoi(arg2)); + priority = atoi(arg1); + tun_id = htonll(strtoull(arg2, NULL, 0)); + in_port = ofp_port_to_odp_port(atoi(arg3)); packet = ofpbuf_new(strlen(args) / 2); - arg3 = ofpbuf_put_hex(packet, arg3, NULL); - arg3 += strspn(arg3, " "); - if (*arg3 != '\0') { + arg4 = ofpbuf_put_hex(packet, arg4, NULL); + arg4 += strspn(arg4, " "); + if (*arg4 != '\0') { unixctl_command_reply(conn, 501, "Trailing garbage in command"); goto exit; } @@ -4651,7 +5301,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_, ds_put_cstr(&result, s); free(s); - flow_extract(packet, tun_id, in_port, &flow); + flow_extract(packet, priority, tun_id, in_port, &flow); } else { unixctl_command_reply(conn, 501, "Bad command syntax"); goto exit; @@ -4734,6 +5384,8 @@ ofproto_dpif_unixctl_init(void) unixctl_command_register("ofproto/trace", "bridge {tun_id in_port packet | odp_flow [-generate]}", ofproto_unixctl_trace, NULL); + unixctl_command_register("fdb/flush", "bridge", ofproto_unixctl_fdb_flush, + NULL); unixctl_command_register("fdb/show", "bridge", ofproto_unixctl_fdb_show, NULL); unixctl_command_register("ofproto/clog", "", ofproto_dpif_clog, NULL); @@ -4776,8 +5428,7 @@ const struct ofproto_class ofproto_dpif_class = { rule_get_stats, rule_execute, rule_modify_actions, - get_drop_frags, - set_drop_frags, + set_frag_handling, packet_out, set_netflow, get_netflow_ids, @@ -4785,6 +5436,10 @@ const struct ofproto_class ofproto_dpif_class = { set_cfm, get_cfm_fault, get_cfm_remote_mpids, + set_stp, + get_stp_status, + set_stp_port, + get_stp_port_status, bundle_set, bundle_remove, mirror_set,