X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=ofproto%2Fofproto-dpif.c;h=211577b6fb494b1b7602bde4ee299adece342f51;hb=8ba855c1ab62dbc356071794f4681e9f6ba7505a;hp=98bcc74701e2d6ce0ce32733c7702098b4f4875e;hpb=8c977421e48b18398137bcbbf447e372ac61b6a6;p=openvswitch diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 98bcc747..211577b6 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -138,6 +138,7 @@ struct ofbundle { /* Configuration. */ struct list ports; /* Contains "struct ofport"s. */ + enum port_vlan_mode vlan_mode; /* VLAN mode */ int vlan; /* -1=trunk port, else a 12-bit VLAN ID. */ unsigned long *trunks; /* Bitmap of trunked VLANs, if 'vlan' == -1. * NULL if all VLANs are trunked. */ @@ -204,6 +205,9 @@ struct action_xlate_ctx { 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. */ + 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. */ }; static void action_xlate_ctx_init(struct action_xlate_ctx *, @@ -252,8 +256,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 *); @@ -262,6 +265,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); @@ -392,7 +400,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 *); @@ -400,7 +413,9 @@ static int expire(struct ofproto_dpif *); /* Utilities. */ static int send_packet(struct ofproto_dpif *, uint32_t odp_port, const struct ofpbuf *packet); - +static size_t +compose_sflow_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions, + const struct flow *, uint32_t odp_port); /* Global variables. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); @@ -465,17 +480,17 @@ construct(struct ofproto *ofproto_, int *n_tablesp) ofproto->max_ports = dpif_get_max_ports(ofproto->dpif); ofproto->n_matches = 0; + dpif_flow_flush(ofproto->dpif); + dpif_recv_purge(ofproto->dpif); + error = dpif_recv_set_mask(ofproto->dpif, ((1u << DPIF_UC_MISS) | - (1u << DPIF_UC_ACTION) | - (1u << DPIF_UC_SAMPLE))); + (1u << DPIF_UC_ACTION))); if (error) { VLOG_ERR("failed to listen on datapath %s: %s", name, strerror(error)); dpif_close(ofproto->dpif); return error; } - dpif_flow_flush(ofproto->dpif); - dpif_recv_purge(ofproto->dpif); ofproto->netflow = NULL; ofproto->sflow = NULL; @@ -559,8 +574,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) { @@ -568,22 +585,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); @@ -704,7 +728,7 @@ static void get_tables(struct ofproto *ofproto_, struct ofp_table_stats *ots) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); - struct ovs_dp_stats s; + struct dpif_dp_stats s; strcpy(ots->name, "classifier"); @@ -752,6 +776,7 @@ port_construct(struct ofport *port_) struct ofport_dpif *port = ofport_dpif_cast(port_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); + ofproto->need_revalidate = true; port->odp_port = ofp_port_to_odp_port(port->up.ofp_port); port->bundle = NULL; port->cfm = NULL; @@ -772,6 +797,7 @@ port_destruct(struct ofport *port_) struct ofport_dpif *port = ofport_dpif_cast(port_); struct ofproto_dpif *ofproto = ofproto_dpif_cast(port->up.ofproto); + ofproto->need_revalidate = true; bundle_remove(port_); set_cfm(port_, NULL); if (ofproto->sflow) { @@ -808,6 +834,7 @@ set_sflow(struct ofproto *ofproto_, { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); struct dpif_sflow *ds = ofproto->sflow; + if (sflow_options) { if (!ds) { struct ofport_dpif *ofport; @@ -817,11 +844,15 @@ set_sflow(struct ofproto *ofproto_, dpif_sflow_add_port(ds, ofport->odp_port, netdev_get_name(ofport->up.netdev)); } + ofproto->need_revalidate = true; } dpif_sflow_set_options(ds, sflow_options); } else { - dpif_sflow_destroy(ds); - ofproto->sflow = NULL; + if (ds) { + dpif_sflow_destroy(ds); + ofproto->need_revalidate = true; + ofproto->sflow = NULL; + } } return 0; } @@ -977,6 +1008,7 @@ bundle_add_port(struct ofbundle *bundle, uint32_t ofp_port, } } if (lacp) { + port->bundle->ofproto->need_revalidate = true; lacp_slave_register(bundle->lacp, port, lacp); } @@ -1028,9 +1060,10 @@ bundle_set(struct ofproto *ofproto_, void *aux, { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); bool need_flush = false; - const unsigned long *trunks; struct ofport_dpif *port; struct ofbundle *bundle; + unsigned long *trunks; + int vlan; size_t i; bool ok; @@ -1053,6 +1086,7 @@ bundle_set(struct ofproto *ofproto_, void *aux, bundle->name = NULL; list_init(&bundle->ports); + bundle->vlan_mode = PORT_VLAN_TRUNK; bundle->vlan = -1; bundle->trunks = NULL; bundle->lacp = NULL; @@ -1112,19 +1146,65 @@ bundle_set(struct ofproto *ofproto_, void *aux, return EINVAL; } + /* Set VLAN tagging mode */ + if (s->vlan_mode != bundle->vlan_mode) { + bundle->vlan_mode = s->vlan_mode; + need_flush = true; + } + /* Set VLAN tag. */ - if (s->vlan != bundle->vlan) { - bundle->vlan = s->vlan; + vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1 + : s->vlan >= 0 && s->vlan <= 4095 ? s->vlan + : 0); + if (vlan != bundle->vlan) { + bundle->vlan = vlan; need_flush = true; } /* Get trunked VLANs. */ - trunks = s->vlan == -1 ? NULL : s->trunks; + switch (s->vlan_mode) { + case PORT_VLAN_ACCESS: + trunks = NULL; + break; + + case PORT_VLAN_TRUNK: + trunks = (unsigned long *) s->trunks; + break; + + case PORT_VLAN_NATIVE_UNTAGGED: + case PORT_VLAN_NATIVE_TAGGED: + if (vlan != 0 && (!s->trunks + || !bitmap_is_set(s->trunks, vlan) + || bitmap_is_set(s->trunks, 0))) { + /* Force trunking the native VLAN and prohibit trunking VLAN 0. */ + if (s->trunks) { + trunks = bitmap_clone(s->trunks, 4096); + } else { + trunks = bitmap_allocate1(4096); + } + bitmap_set1(trunks, vlan); + bitmap_set0(trunks, 0); + } else { + trunks = (unsigned long *) s->trunks; + } + break; + + default: + NOT_REACHED(); + } if (!vlan_bitmap_equal(trunks, bundle->trunks)) { free(bundle->trunks); - bundle->trunks = vlan_bitmap_clone(trunks); + if (trunks == s->trunks) { + bundle->trunks = vlan_bitmap_clone(trunks); + } else { + bundle->trunks = trunks; + trunks = NULL; + } need_flush = true; } + if (trunks != s->trunks) { + free(trunks); + } /* Bonding. */ if (!list_is_short(&bundle->ports)) { @@ -1506,7 +1586,8 @@ port_run(struct ofport_dpif *ofport) ofpbuf_uninit(&packet); } - enable = enable && !cfm_get_fault(ofport->cfm); + enable = enable && !cfm_get_fault(ofport->cfm) + && cfm_get_opup(ofport->cfm); } if (ofport->bundle) { @@ -1652,25 +1733,74 @@ port_is_lacp_current(const struct ofport *ofport_) /* Upcall handling. */ -/* Given 'upcall', of type DPIF_UC_ACTION or DPIF_UC_MISS, sends an - * OFPT_PACKET_IN message to each OpenFlow controller as necessary according to - * their individual configurations. +/* 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. + * + * If 'clone' is true, the caller retains ownership of 'packet'. Otherwise, + * ownership is transferred to this function. */ +static void +send_packet_in_miss(struct ofproto_dpif *ofproto, struct ofpbuf *packet, + const struct flow *flow, bool clone) +{ + struct ofputil_packet_in pin; + + pin.packet = packet; + pin.in_port = flow->in_port; + pin.reason = OFPR_NO_MATCH; + 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(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall, - const struct flow *flow, bool clone) +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; - pin.packet = upcall->packet; + memcpy(&cookie, &userdata, sizeof(cookie)); + + pin.packet = packet; pin.in_port = flow->in_port; - pin.reason = upcall->type == DPIF_UC_MISS ? OFPR_NO_MATCH : OFPR_ACTION; + pin.reason = OFPR_ACTION; pin.buffer_id = 0; /* not yet known */ - pin.send_len = upcall->userdata; + pin.send_len = cookie.data; connmgr_send_packet_in(ofproto->up.connmgr, &pin, flow, - clone ? NULL : upcall->packet); + clone ? NULL : packet); } static bool @@ -1698,102 +1828,245 @@ process_special(struct ofproto_dpif *ofproto, const struct flow *flow, 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; - - /* Obtain in_port and tun_id, at least. */ - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + uint32_t hash = flow_hash(flow, 0); + struct flow_miss *miss; - /* Set header pointers in 'flow'. */ - flow_extract(upcall->packet, flow.tun_id, flow.in_port, &flow); - - /* Handle 802.1ag and LACP. */ - if (process_special(ofproto, &flow, upcall->packet)) { - ofpbuf_delete(upcall->packet); - ofproto->n_matches++; - return; + HMAP_FOR_EACH_WITH_HASH (miss, hmap_node, hash, todo) { + if (flow_equal(&miss->flow, flow)) { + return miss; + } } - /* 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, OVSP_LOCAL, upcall->packet); - } + 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; +} - facet = facet_lookup_valid(ofproto, &flow); +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); 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(ofproto, upcall, &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(ofproto, upcall, &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_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +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.tun_id, flow.in_port, &flow); + + /* Handle 802.1ag and LACP 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 +handle_userspace_upcall(struct ofproto_dpif *ofproto, + struct dpif_upcall *upcall) { struct flow flow; + struct user_action_cookie cookie; - switch (upcall->type) { - case DPIF_UC_ACTION: - COVERAGE_INC(ofproto_dpif_ctlr_action); - odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - send_packet_in(ofproto, upcall, &flow, false); - break; + memcpy(&cookie, &upcall->userdata, sizeof(cookie)); - case DPIF_UC_SAMPLE: + if (cookie.type == USER_ACTION_COOKIE_SFLOW) { if (ofproto->sflow) { odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); - dpif_sflow_received(ofproto->sflow, upcall, &flow); + 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); + odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow); + send_packet_in_action(ofproto, upcall->packet, upcall->userdata, + &flow, false); + } else { + VLOG_WARN_RL(&rl, "invalid user cookie : 0x%"PRIx64, upcall->userdata); + } +} + +static void +handle_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall) +{ + switch (upcall->type) { + case DPIF_UC_ACTION: + handle_userspace_upcall(ofproto, upcall); break; case DPIF_UC_MISS: - handle_miss_upcall(ofproto, upcall); - break; + /* The caller handles these. */ + NOT_REACHED(); case DPIF_N_UC_TYPES: default: @@ -2081,15 +2354,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; @@ -2103,8 +2377,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; } @@ -2115,6 +2387,33 @@ facet_free(struct facet *facet) free(facet); } +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) +{ + 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, + false); + return true; + } else { + return false; + } +} + /* Executes, within 'ofproto', the 'n_actions' actions in 'actions' on * 'packet', which arrived on 'in_port'. * @@ -2124,39 +2423,23 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, const struct nlattr *odp_actions, size_t actions_len, struct ofpbuf *packet) { - if (actions_len == NLA_ALIGN(NLA_HDRLEN + sizeof(uint64_t)) - && 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. */ - struct dpif_upcall upcall; - - upcall.type = DPIF_UC_ACTION; - upcall.packet = packet; - upcall.key = NULL; - upcall.key_len = 0; - upcall.userdata = nl_attr_get_u64(odp_actions); - upcall.sample_pool = 0; - upcall.actions = NULL; - upcall.actions_len = 0; - - send_packet_in(ofproto, &upcall, flow, false); + struct odputil_keybuf keybuf; + struct ofpbuf key; + int error; + if (execute_controller_action(ofproto, flow, odp_actions, actions_len, + packet)) { return true; - } else { - struct odputil_keybuf keybuf; - struct ofpbuf key; - int error; + } - ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); - odp_flow_key_from_flow(&key, flow); + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); + odp_flow_key_from_flow(&key, flow); - error = dpif_execute(ofproto->dpif, key.data, key.size, - odp_actions, actions_len, packet); + error = dpif_execute(ofproto->dpif, key.data, key.size, + odp_actions, actions_len, packet); - ofpbuf_delete(packet); - return !error; - } + ofpbuf_delete(packet); + return !error; } /* Executes the actions indicated by 'facet' on 'packet' and credits 'facet''s @@ -2179,7 +2462,7 @@ facet_execute(struct ofproto_dpif *ofproto, struct facet *facet, assert(ofpbuf_headroom(packet) >= sizeof(struct ofp_packet_in)); - flow_extract_stats(&facet->flow, packet, &stats); + dpif_flow_stats_extract(&facet->flow, packet, &stats); stats.used = time_msec(); if (execute_odp_actions(ofproto, &facet->flow, facet->actions, facet->actions_len, packet)) { @@ -2280,12 +2563,6 @@ facet_install(struct ofproto_dpif *p, struct facet *facet, bool zero_stats) } } -static int -vlan_tci_to_openflow_vlan(ovs_be16 vlan_tci) -{ - return vlan_tci != htons(0) ? vlan_tci_to_vid(vlan_tci) : OFP_VLAN_NONE; -} - static void facet_account(struct ofproto_dpif *ofproto, struct facet *facet) { @@ -2330,7 +2607,7 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet) port = get_odp_port(ofproto, nl_attr_get_u32(a)); if (port && port->bundle && port->bundle->bond) { bond_account(port->bundle->bond, &facet->flow, - vlan_tci_to_openflow_vlan(vlan_tci), n_bytes); + vlan_tci_to_vid(vlan_tci), n_bytes); } break; @@ -2464,7 +2741,8 @@ facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow) /* The facet we found might not be valid, since we could be in need of * revalidation. If it is not valid, don't return it. */ if (facet - && ofproto->need_revalidate + && (ofproto->need_revalidate + || tag_set_intersects(&ofproto->revalidate_set, facet->tags)) && !facet_revalidate(ofproto, facet)) { COVERAGE_INC(facet_invalidated); return NULL; @@ -2803,6 +3081,9 @@ rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet) /* First look for a related facet. If we find one, account it to that. */ facet = facet_lookup_valid(ofproto, flow); if (facet && facet->rule == rule) { + if (!facet->may_install) { + facet_make_actions(ofproto, facet, packet); + } facet_execute(ofproto, facet, packet); return 0; } @@ -2810,7 +3091,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; @@ -2850,7 +3132,7 @@ rule_modify_actions(struct rule *rule_) complete_operation(rule); } -/* Sends 'packet' out of port 'odp_port' within 'p'. +/* Sends 'packet' out of port 'odp_port' within 'ofproto'. * Returns 0 if successful, otherwise a positive errno value. */ static int send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, @@ -2866,6 +3148,8 @@ send_packet(struct ofproto_dpif *ofproto, uint32_t odp_port, odp_flow_key_from_flow(&key, &flow); ofpbuf_init(&odp_actions, 32); + compose_sflow_action(ofproto, &odp_actions, &flow, odp_port); + nl_msg_put_u32(&odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); error = dpif_execute(ofproto->dpif, key.data, key.size, @@ -2886,6 +3170,134 @@ static void do_xlate_actions(const union ofp_action *in, size_t n_in, struct action_xlate_ctx *ctx); static void xlate_normal(struct action_xlate_ctx *); +static size_t +put_userspace_action(const struct ofproto_dpif *ofproto, + struct ofpbuf *odp_actions, + const struct flow *flow, + const struct user_action_cookie *cookie) +{ + size_t offset; + uint32_t pid; + + pid = dpif_port_get_pid(ofproto->dpif, + ofp_port_to_odp_port(flow->in_port)); + + offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE); + nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid); + nl_msg_put_unspec(odp_actions, OVS_USERSPACE_ATTR_USERDATA, + cookie, sizeof *cookie); + nl_msg_end_nested(odp_actions, offset); + + return odp_actions->size - NLA_ALIGN(sizeof *cookie); +} + +/* Compose SAMPLE action for sFlow. */ +static size_t +compose_sflow_action(const struct ofproto_dpif *ofproto, + struct ofpbuf *odp_actions, + const struct flow *flow, + uint32_t odp_port) +{ + uint32_t port_ifindex; + uint32_t probability; + struct user_action_cookie cookie; + size_t sample_offset, actions_offset; + int cookie_offset, n_output; + + if (!ofproto->sflow || flow->in_port == OFPP_NONE) { + return 0; + } + + if (odp_port == OVSP_NONE) { + port_ifindex = 0; + n_output = 0; + } else { + port_ifindex = dpif_sflow_odp_port_to_ifindex(ofproto->sflow, odp_port); + n_output = 1; + } + + sample_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SAMPLE); + + /* Number of packets out of UINT_MAX to sample. */ + probability = dpif_sflow_get_probability(ofproto->sflow); + nl_msg_put_u32(odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, probability); + + actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS); + + cookie.type = USER_ACTION_COOKIE_SFLOW; + cookie.data = port_ifindex; + cookie.n_output = n_output; + cookie.vlan_tci = 0; + cookie_offset = put_userspace_action(ofproto, odp_actions, flow, &cookie); + + nl_msg_end_nested(odp_actions, actions_offset); + nl_msg_end_nested(odp_actions, sample_offset); + return cookie_offset; +} + +/* SAMPLE action must be first action in any given list of actions. + * At this point we do not have all information required to build it. So try to + * build sample action as complete as possible. */ +static void +add_sflow_action(struct action_xlate_ctx *ctx) +{ + ctx->user_cookie_offset = compose_sflow_action(ctx->ofproto, + ctx->odp_actions, + &ctx->flow, OVSP_NONE); + ctx->sflow_odp_port = 0; + ctx->sflow_n_outputs = 0; +} + +/* Fix SAMPLE action according to data collected while composing ODP actions. + * We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested + * USERSPACE action's user-cookie which is required for sflow. */ +static void +fix_sflow_action(struct action_xlate_ctx *ctx) +{ + const struct flow *base = &ctx->base_flow; + struct user_action_cookie *cookie; + + if (!ctx->user_cookie_offset) { + return; + } + + cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset, + sizeof(*cookie)); + assert(cookie != NULL); + assert(cookie->type == USER_ACTION_COOKIE_SFLOW); + + if (ctx->sflow_n_outputs) { + cookie->data = dpif_sflow_odp_port_to_ifindex(ctx->ofproto->sflow, + ctx->sflow_odp_port); + } + if (ctx->sflow_n_outputs >= 255) { + cookie->n_output = 255; + } else { + cookie->n_output = ctx->sflow_n_outputs; + } + cookie->vlan_tci = base->vlan_tci; +} + +static void +commit_vlan_tci(struct action_xlate_ctx *ctx, ovs_be16 vlan_tci) +{ + struct flow *base = &ctx->base_flow; + struct ofpbuf *odp_actions = ctx->odp_actions; + + 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_odp_actions(struct action_xlate_ctx *ctx) { @@ -2913,18 +3325,7 @@ commit_odp_actions(struct action_xlate_ctx *ctx) 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, 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, - flow->vlan_tci & ~htons(VLAN_CFI)); - } - base->vlan_tci = flow->vlan_tci; - } + commit_vlan_tci(ctx, flow->vlan_tci); if (base->tp_src != flow->tp_src) { nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src); @@ -2959,6 +3360,14 @@ commit_odp_actions(struct action_xlate_ctx *ctx) } } +static void +compose_output_action(struct action_xlate_ctx *ctx, uint16_t odp_port) +{ + nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); + ctx->sflow_odp_port = odp_port; + ctx->sflow_n_outputs++; +} + static void add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) { @@ -2979,7 +3388,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port) } commit_odp_actions(ctx); - nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port); + compose_output_action(ctx, odp_port); ctx->nf_output_iface = ofp_port; } @@ -3060,14 +3469,25 @@ 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, OVS_ACTION_ATTR_OUTPUT, - ofport->odp_port); + compose_output_action(ctx, ofport->odp_port); } } ctx->nf_output_iface = NF_OUT_FLOOD; } +static void +compose_controller_action(struct action_xlate_ctx *ctx, int len) +{ + struct user_action_cookie cookie; + + 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); +} + static void xlate_output_action__(struct action_xlate_ctx *ctx, uint16_t port, uint16_t max_len) @@ -3094,7 +3514,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx, break; case OFPP_CONTROLLER: commit_odp_actions(ctx); - nl_msg_put_u64(ctx->odp_actions, OVS_ACTION_ATTR_USERSPACE, max_len); + compose_controller_action(ctx, max_len); break; case OFPP_LOCAL: add_output_action(ctx, OFPP_LOCAL); @@ -3158,6 +3578,8 @@ 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); @@ -3210,7 +3632,7 @@ xlate_autopath(struct action_xlate_ctx *ctx, } else if (port->bundle->bond) { /* Autopath does not support VLAN hashing. */ struct ofport_dpif *slave = bond_choose_output_slave( - port->bundle->bond, &ctx->flow, OFP_VLAN_NONE, &ctx->tags); + port->bundle->bond, &ctx->flow, 0, &ctx->tags); if (slave) { ofp_port = slave->up.ofp_port; } @@ -3447,6 +3869,7 @@ xlate_actions(struct action_xlate_ctx *ctx, COVERAGE_INC(ofproto_dpif_xlate); ctx->odp_actions = ofpbuf_new(512); + ofpbuf_reserve(ctx->odp_actions, NL_A_U32_SIZE); ctx->tags = 0; ctx->may_set_up_flow = true; ctx->has_learn = false; @@ -3461,16 +3884,22 @@ xlate_actions(struct action_xlate_ctx *ctx, if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) { ctx->may_set_up_flow = false; + return ctx->odp_actions; } else { + add_sflow_action(ctx); do_xlate_actions(in, n_in, ctx); - } - /* Check with in-band control to see if we're allowed to set up this - * flow. */ - if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow, - ctx->odp_actions->data, - ctx->odp_actions->size)) { - ctx->may_set_up_flow = false; + if (!connmgr_may_set_up_flow(ctx->ofproto->up.connmgr, &ctx->flow, + ctx->odp_actions->data, + ctx->odp_actions->size)) { + ctx->may_set_up_flow = false; + if (ctx->packet + && connmgr_msg_in_hook(ctx->ofproto->up.connmgr, &ctx->flow, + ctx->packet)) { + compose_output_action(ctx, OVSP_LOCAL); + } + } + fix_sflow_action(ctx); } return ctx->odp_actions; @@ -3480,7 +3909,7 @@ xlate_actions(struct action_xlate_ctx *ctx, struct dst { struct ofport_dpif *port; - uint16_t vlan; + uint16_t vid; }; struct dst_set { @@ -3495,20 +3924,71 @@ static void dst_set_free(struct dst_set *); static struct ofport_dpif *ofbundle_get_a_port(const struct ofbundle *); +/* Given 'vid', the VID obtained from the 802.1Q header that was received as + * part of a packet (specify 0 if there was no 802.1Q header), and 'in_bundle', + * the bundle on which the packet was received, returns the VLAN to which the + * packet belongs. + * + * Both 'vid' and the return value are in the range 0...4095. */ +static uint16_t +input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid) +{ + switch (in_bundle->vlan_mode) { + case PORT_VLAN_ACCESS: + return in_bundle->vlan; + break; + + case PORT_VLAN_TRUNK: + return vid; + + case PORT_VLAN_NATIVE_UNTAGGED: + case PORT_VLAN_NATIVE_TAGGED: + return vid ? vid : in_bundle->vlan; + + default: + NOT_REACHED(); + } +} + +/* Given 'vlan', the VLAN that a packet belongs to, and + * 'out_bundle', a bundle on which the packet is to be output, returns the VID + * that should be included in the 802.1Q header. (If the return value is 0, + * then the 802.1Q header should only be included in the packet if there is a + * nonzero PCP.) + * + * Both 'vlan' and the return value are in the range 0...4095. */ +static uint16_t +output_vlan_to_vid(const struct ofbundle *out_bundle, uint16_t vlan) +{ + switch (out_bundle->vlan_mode) { + case PORT_VLAN_ACCESS: + return 0; + + case PORT_VLAN_TRUNK: + case PORT_VLAN_NATIVE_TAGGED: + return vlan; + + case PORT_VLAN_NATIVE_UNTAGGED: + return vlan == out_bundle->vlan ? 0 : vlan; + + default: + NOT_REACHED(); + } +} + static bool set_dst(struct action_xlate_ctx *ctx, struct dst *dst, const struct ofbundle *in_bundle, const struct ofbundle *out_bundle) { - dst->vlan = (out_bundle->vlan >= 0 ? OFP_VLAN_NONE - : in_bundle->vlan >= 0 ? in_bundle->vlan - : ctx->flow.vlan_tci == 0 ? OFP_VLAN_NONE - : vlan_tci_to_vid(ctx->flow.vlan_tci)); + uint16_t vlan; + + vlan = input_vid_to_vlan(in_bundle, vlan_tci_to_vid(ctx->flow.vlan_tci)); + dst->vid = output_vlan_to_vid(out_bundle, vlan); dst->port = (!out_bundle->bond ? ofbundle_get_a_port(out_bundle) : bond_choose_output_slave(out_bundle->bond, &ctx->flow, - dst->vlan, &ctx->tags)); - + dst->vid, &ctx->tags)); return dst->port != NULL; } @@ -3559,7 +4039,7 @@ dst_is_duplicate(const struct dst_set *set, const struct dst *test) { size_t i; for (i = 0; i < set->n; i++) { - if (set->dsts[i].vlan == test->vlan + if (set->dsts[i].vid == test->vid && set->dsts[i].port == test->port) { return true; } @@ -3570,7 +4050,7 @@ 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 + return (bundle->vlan_mode != PORT_VLAN_ACCESS && (!bundle->trunks || bitmap_is_set(bundle->trunks, vlan))); } @@ -3668,7 +4148,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, { struct ofproto_dpif *ofproto = ctx->ofproto; mirror_mask_t mirrors; - int flow_vlan; + uint16_t flow_vid; size_t i; mirrors = in_bundle->src_mirrors; @@ -3680,11 +4160,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, return; } - flow_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci); - if (flow_vlan == 0) { - flow_vlan = OFP_VLAN_NONE; - } - + flow_vid = vlan_tci_to_vid(ctx->flow.vlan_tci); while (mirrors) { struct ofmirror *m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1]; if (vlan_is_mirrored(m, vlan)) { @@ -3702,20 +4178,15 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx, if (ofbundle_includes_vlan(bundle, m->out_vlan) && set_dst(ctx, &dst, in_bundle, bundle)) { - if (bundle->vlan < 0) { - dst.vlan = m->out_vlan; - } + /* set_dst() got dst->vid from the input packet's VLAN, + * not from m->out_vlan, so recompute it. */ + dst.vid = output_vlan_to_vid(bundle, m->out_vlan); + if (dst_is_duplicate(set, &dst)) { continue; } - /* Use the vlan tag on the original flow instead of - * the one passed in the vlan parameter. This ensures - * that we compare the vlan from before any implicit - * tagging tags place. This is necessary because - * dst->vlan is the final vlan, after removing implicit - * tags. */ - if (bundle == in_bundle && dst.vlan == flow_vlan) { + if (bundle == in_bundle && dst.vid == flow_vid) { /* Don't send out input port on same VLAN. */ continue; } @@ -3733,51 +4204,47 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan, const struct ofbundle *in_bundle, const struct ofbundle *out_bundle) { - uint16_t initial_vlan, cur_vlan; + uint16_t initial_vid, cur_vid; const struct dst *dst; struct dst_set set; dst_set_init(&set); compose_dsts(ctx, vlan, in_bundle, out_bundle, &set); compose_mirror_dsts(ctx, vlan, in_bundle, &set); + if (!set.n) { + dst_set_free(&set); + return; + } /* Output all the packets we can without having to change the VLAN. */ - initial_vlan = vlan_tci_to_vid(ctx->flow.vlan_tci); - if (initial_vlan == 0) { - initial_vlan = OFP_VLAN_NONE; - } + commit_odp_actions(ctx); + initial_vid = vlan_tci_to_vid(ctx->flow.vlan_tci); for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) { - if (dst->vlan != initial_vlan) { + if (dst->vid != initial_vid) { continue; } - nl_msg_put_u32(ctx->odp_actions, - OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port); + compose_output_action(ctx, dst->port->odp_port); } /* Then output the rest. */ - cur_vlan = initial_vlan; + cur_vid = initial_vid; for (dst = set.dsts; dst < &set.dsts[set.n]; dst++) { - if (dst->vlan == initial_vlan) { + if (dst->vid == initial_vid) { continue; } - if (dst->vlan != cur_vlan) { - if (dst->vlan == OFP_VLAN_NONE) { - nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } else { - ovs_be16 tci; + if (dst->vid != cur_vid) { + ovs_be16 tci; - if (cur_vlan != OFP_VLAN_NONE) { - nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN); - } - tci = htons(dst->vlan & VLAN_VID_MASK); - tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK); - nl_msg_put_be16(ctx->odp_actions, - OVS_ACTION_ATTR_PUSH_VLAN, tci); + tci = htons(dst->vid); + tci |= ctx->flow.vlan_tci & htons(VLAN_PCP_MASK); + if (tci) { + tci |= htons(VLAN_CFI); } - cur_vlan = dst->vlan; + commit_vlan_tci(ctx, tci); + + cur_vid = dst->vid; } - nl_msg_put_u32(ctx->odp_actions, - OVS_ACTION_ATTR_OUTPUT, dst->port->odp_port); + compose_output_action(ctx, dst->port->odp_port); } dst_set_free(&set); @@ -3792,8 +4259,9 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, struct ofbundle *in_bundle, bool have_packet) { int vlan = vlan_tci_to_vid(flow->vlan_tci); - if (in_bundle->vlan >= 0) { - if (vlan) { + if (vlan) { + if (in_bundle->vlan_mode == PORT_VLAN_ACCESS) { + /* Drop tagged packet on access port */ if (have_packet) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " @@ -3803,10 +4271,10 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, in_bundle->name, in_bundle->vlan); } return -1; - } - vlan = in_bundle->vlan; - } else { - if (!ofbundle_includes_vlan(in_bundle, vlan)) { + } else if (ofbundle_includes_vlan(in_bundle, vlan)) { + return vlan; + } else { + /* Drop packets from a VLAN not member of the trunk */ if (have_packet) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged " @@ -3816,9 +4284,13 @@ flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow, } return -1; } + } else { + if (in_bundle->vlan_mode != PORT_VLAN_TRUNK) { + return in_bundle->vlan; + } else { + return ofbundle_includes_vlan(in_bundle, 0) ? 0 : -1; + } } - - return vlan; } /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after @@ -4448,11 +4920,13 @@ ofproto_dpif_unixctl_init(void) } registered = true; - unixctl_command_register("ofproto/trace", ofproto_unixctl_trace, NULL); - unixctl_command_register("fdb/show", ofproto_unixctl_fdb_show, NULL); - - unixctl_command_register("ofproto/clog", ofproto_dpif_clog, NULL); - unixctl_command_register("ofproto/unclog", ofproto_dpif_unclog, NULL); + unixctl_command_register("ofproto/trace", + "bridge {tun_id in_port packet | odp_flow [-generate]}", + ofproto_unixctl_trace, NULL); + unixctl_command_register("fdb/show", "bridge", ofproto_unixctl_fdb_show, + NULL); + unixctl_command_register("ofproto/clog", "", ofproto_dpif_clog, NULL); + unixctl_command_register("ofproto/unclog", "", ofproto_dpif_unclog, NULL); } const struct ofproto_class ofproto_dpif_class = {