X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=ofproto%2Fofproto-dpif-xlate.c;h=91ce7b71d14993e80e1fe1b2bc7c96dea41d9cd6;hb=8d25251929c8f325bed0fff24192d5a87034b32e;hp=ad4458240d31c950adf60f6272c9d353144a99d1;hpb=bdd534291ff72077390f7b1745c4443ad3833d99;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index ad4458240..91ce7b71d 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -58,6 +58,8 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate); /* Maximum depth of flow table recursion (due to resubmit actions) in a * flow translation. */ #define MAX_RESUBMIT_RECURSION 64 +#define MAX_INTERNAL_RESUBMITS 1 /* Max resbmits allowed using rules in + internal table. */ /* Maximum number of resubmit actions in a flow translation, whether they are * recursive or not. */ @@ -89,6 +91,9 @@ struct xbridge { bool has_in_band; /* Bridge has in band control? */ bool forward_bpdu; /* Bridge forwards STP BPDUs? */ + /* True if the datapath supports recirculation. */ + bool enable_recirc; + /* True if the datapath supports variable-length * OVS_USERSPACE_ATTR_USERDATA in OVS_ACTION_ATTR_USERSPACE actions. * False if the datapath supports only 8-byte (or shorter) userdata. */ @@ -178,6 +183,7 @@ struct xlate_ctx { /* Resubmit statistics, via xlate_table_action(). */ int recurse; /* Current resubmit nesting depth. */ int resubmits; /* Total number of resubmits. */ + bool in_group; /* Currently translating ofgroup, if true. */ uint32_t orig_skb_priority; /* Priority when packet arrived. */ uint8_t table_id; /* OpenFlow table ID where flow was found. */ @@ -200,9 +206,11 @@ struct xlate_ctx { * it did not arrive on a "real" port. 'ofpp_none_bundle' exists for * when an input bundle is needed for validation (e.g., mirroring or * OFPP_NORMAL processing). It is not connected to an 'ofproto' or have - * any 'port' structs, so care must be taken when dealing with it. - * The bundle's name and vlan mode are initialized in lookup_input_bundle() */ -static struct xbundle ofpp_none_bundle; + * any 'port' structs, so care must be taken when dealing with it. */ +static struct xbundle ofpp_none_bundle = { + .name = "OFPP_NONE", + .vlan_mode = PORT_VLAN_TRUNK +}; /* Node in 'xport''s 'skb_priorities' map. Used to maintain a map from * 'priority' (the datapath's term for QoS queue) to the dscp bits which all @@ -223,10 +231,11 @@ static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len, struct xlate_ctx *); static void xlate_actions__(struct xlate_in *, struct xlate_out *) OVS_REQ_RDLOCK(xlate_rwlock); - static void xlate_normal(struct xlate_ctx *); - static void xlate_report(struct xlate_ctx *, const char *); - static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port, - uint8_t table_id, bool may_packet_in); +static void xlate_normal(struct xlate_ctx *); +static void xlate_report(struct xlate_ctx *, const char *); +static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port, + uint8_t table_id, bool may_packet_in, + bool honor_table_miss); static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn); static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid); static void output_normal(struct xlate_ctx *, const struct xbundle *, @@ -253,6 +262,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, const struct dpif_ipfix *ipfix, const struct netflow *netflow, enum ofp_config_flags frag, bool forward_bpdu, bool has_in_band, + bool enable_recirc, bool variable_length_userdata, size_t max_mpls_depth) { @@ -306,6 +316,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, xbridge->frag = frag; xbridge->miss_rule = miss_rule; xbridge->no_packet_in_rule = no_packet_in_rule; + xbridge->enable_recirc = enable_recirc; xbridge->variable_length_userdata = variable_length_userdata; xbridge->max_mpls_depth = max_mpls_depth; } @@ -664,7 +675,7 @@ xport_get_stp_port(const struct xport *xport) : NULL; } -static enum stp_state +static bool xport_stp_learn_state(const struct xport *xport) { struct stp_port *sp = xport_get_stp_port(xport); @@ -678,6 +689,13 @@ xport_stp_forward_state(const struct xport *xport) return stp_forward_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED); } +static bool +xport_stp_listen_state(const struct xport *xport) +{ + struct stp_port *sp = xport_get_stp_port(xport); + return stp_listen_in_state(sp ? stp_port_get_state(sp) : STP_DISABLED); +} + /* Returns true if STP should process 'flow'. Sets fields in 'wc' that * were used to make the determination.*/ static bool @@ -692,7 +710,7 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet) { struct stp_port *sp = xport_get_stp_port(xport); struct ofpbuf payload = *packet; - struct eth_header *eth = payload.data; + struct eth_header *eth = ofpbuf_data(&payload); /* Sink packets on ports that have STP disabled when the bridge has * STP enabled. */ @@ -701,12 +719,12 @@ stp_process_packet(const struct xport *xport, const struct ofpbuf *packet) } /* 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_size(&payload) > ntohs(eth->eth_type) + ETH_HEADER_LEN) { + ofpbuf_set_size(&payload, 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); + stp_received_bpdu(sp, ofpbuf_data(&payload), ofpbuf_size(&payload)); } } @@ -886,8 +904,6 @@ lookup_input_bundle(const struct xbridge *xbridge, ofp_port_t in_port, /* Special-case OFPP_NONE, which a controller may use as the ingress * port for traffic that it is sourcing. */ if (in_port == OFPP_NONE) { - ofpp_none_bundle.name = "OFPP_NONE"; - ofpp_none_bundle.vlan_mode = PORT_VLAN_TRUNK; return &ofpp_none_bundle; } @@ -1122,13 +1138,30 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle, /* Partially configured bundle with no slaves. Drop the packet. */ return; } else if (!out_xbundle->bond) { + ctx->xout->use_recirc = false; xport = CONTAINER_OF(list_front(&out_xbundle->xports), struct xport, bundle_node); } else { struct ofport_dpif *ofport; + struct xlate_recirc *xr = &ctx->xout->recirc; + struct flow_wildcards *wc = &ctx->xout->wc; + + if (ctx->xbridge->enable_recirc) { + ctx->xout->use_recirc = bond_may_recirc( + out_xbundle->bond, &xr->recirc_id, &xr->hash_bias); - ofport = bond_choose_output_slave(out_xbundle->bond, &ctx->xin->flow, - &ctx->xout->wc, vid); + if (ctx->xout->use_recirc) { + /* Only TCP mode uses recirculation. */ + xr->hash_alg = OVS_RECIRC_HASH_ALG_L4; + bond_update_post_recirc_rules(out_xbundle->bond, false); + + /* Recirculation does not require unmasking hash fields. */ + wc = NULL; + } + } + + ofport = bond_choose_output_slave(out_xbundle->bond, + &ctx->xin->flow, wc, vid); xport = xport_lookup(ofport); if (!xport) { @@ -1483,7 +1516,7 @@ compose_sample_action(const struct xbridge *xbridge, actions_offset = nl_msg_start_nested(odp_actions, OVS_SAMPLE_ATTR_ACTIONS); odp_port = ofp_port_to_odp_port(xbridge, flow->in_port.ofp_port); - pid = dpif_port_get_pid(xbridge->dpif, odp_port); + pid = dpif_port_get_pid(xbridge->dpif, odp_port, 0); cookie_offset = odp_put_userspace_action(pid, cookie, cookie_size, odp_actions); nl_msg_end_nested(odp_actions, actions_offset); @@ -1684,7 +1717,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 24); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25); if (!xport) { xlate_report(ctx, "Nonexistent output port"); @@ -1692,9 +1725,18 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, } else if (xport->config & OFPUTIL_PC_NO_FWD) { xlate_report(ctx, "OFPPC_NO_FWD set, skipping output"); return; - } else if (check_stp && !xport_stp_forward_state(xport)) { - xlate_report(ctx, "STP not in forwarding state, skipping output"); - return; + } else if (check_stp) { + if (eth_addr_equals(ctx->base_flow.dl_dst, eth_addr_stp)) { + if (!xport_stp_listen_state(xport)) { + xlate_report(ctx, "STP not in listening state, " + "skipping bpdu output"); + return; + } + } else if (!xport_stp_forward_state(xport)) { + xlate_report(ctx, "STP not in forwarding state, " + "skipping output"); + return; + } } if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) { @@ -1719,17 +1761,17 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, ctx->xout->slow |= special; } else if (may_receive(peer, ctx)) { if (xport_stp_forward_state(peer)) { - xlate_table_action(ctx, flow->in_port.ofp_port, 0, true); + xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true); } else { /* Forwarding is disabled by STP. Let OFPP_NORMAL and the * learning action look at the packet, then drop it. */ struct flow old_base_flow = ctx->base_flow; - size_t old_size = ctx->xout->odp_actions.size; + size_t old_size = ofpbuf_size(&ctx->xout->odp_actions); mirror_mask_t old_mirrors = ctx->xout->mirrors; - xlate_table_action(ctx, flow->in_port.ofp_port, 0, true); + xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true); ctx->xout->mirrors = old_mirrors; ctx->base_flow = old_base_flow; - ctx->xout->odp_actions.size = old_size; + ofpbuf_set_size(&ctx->xout->odp_actions, old_size); } } @@ -1752,7 +1794,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, flow_nw_tos = flow->nw_tos; if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) { - wc->masks.nw_tos |= IP_ECN_MASK; + wc->masks.nw_tos |= IP_DSCP_MASK; flow->nw_tos &= ~IP_DSCP_MASK; flow->nw_tos |= dscp; } @@ -1780,19 +1822,18 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, &ctx->xout->odp_actions); flow->tunnel = flow_tnl; /* Restore tunnel metadata */ } else { - ofp_port_t vlandev_port; - odp_port = xport->odp_port; + out_port = odp_port; if (ofproto_has_vlan_splinters(ctx->xbridge->ofproto)) { + ofp_port_t vlandev_port; + wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); - } - vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, ofp_port, - flow->vlan_tci); - if (vlandev_port == ofp_port) { - out_port = odp_port; - } else { - out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port); - flow->vlan_tci = htons(0); + vlandev_port = vsp_realdev_to_vlandev(ctx->xbridge->ofproto, + ofp_port, flow->vlan_tci); + if (vlandev_port != ofp_port) { + out_port = ofp_port_to_odp_port(ctx->xbridge, vlandev_port); + flow->vlan_tci = htons(0); + } } } @@ -1800,8 +1841,20 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow, &ctx->xout->odp_actions, &ctx->xout->wc); - nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT, - out_port); + + if (ctx->xout->use_recirc) { + struct ovs_action_recirc *act_recirc; + struct xlate_recirc *xr = &ctx->xout->recirc; + + act_recirc = nl_msg_put_unspec_uninit(&ctx->xout->odp_actions, + OVS_ACTION_ATTR_RECIRC, sizeof *act_recirc); + act_recirc->recirc_id = xr->recirc_id; + act_recirc->hash_alg = xr->hash_alg; + act_recirc->hash_bias = xr->hash_bias; + } else { + nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT, + out_port); + } ctx->sflow_odp_port = odp_port; ctx->sflow_n_outputs++; @@ -1836,7 +1889,6 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule) ctx->rule = rule; actions = rule_dpif_get_actions(rule); do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx); - rule_actions_unref(actions); ctx->rule = old_rule; ctx->recurse--; } @@ -1846,14 +1898,14 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - if (ctx->recurse >= MAX_RESUBMIT_RECURSION) { + if (ctx->recurse >= MAX_RESUBMIT_RECURSION + MAX_INTERNAL_RESUBMITS) { VLOG_ERR_RL(&rl, "resubmit actions recursed over %d times", MAX_RESUBMIT_RECURSION); - } else if (ctx->resubmits >= MAX_RESUBMITS) { + } else if (ctx->resubmits >= MAX_RESUBMITS + MAX_INTERNAL_RESUBMITS) { VLOG_ERR_RL(&rl, "over %d resubmit actions", MAX_RESUBMITS); - } else if (ctx->xout->odp_actions.size > UINT16_MAX) { + } else if (ofpbuf_size(&ctx->xout->odp_actions) > UINT16_MAX) { VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of actions"); - } else if (ctx->stack.size >= 65536) { + } else if (ofpbuf_size(&ctx->stack) >= 65536) { VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack"); } else { return true; @@ -1863,14 +1915,16 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx) } static void -xlate_table_action(struct xlate_ctx *ctx, - ofp_port_t in_port, uint8_t table_id, bool may_packet_in) +xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id, + bool may_packet_in, bool honor_table_miss) { if (xlate_resubmit_resource_check(ctx)) { ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port; bool skip_wildcards = ctx->xin->skip_wildcards; uint8_t old_table_id = ctx->table_id; struct rule_dpif *rule; + enum rule_dpif_lookup_verdict verdict; + enum ofputil_port_config config = 0; ctx->table_id = table_id; @@ -1878,29 +1932,47 @@ xlate_table_action(struct xlate_ctx *ctx, * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will * have surprising behavior). */ ctx->xin->flow.in_port.ofp_port = in_port; - rule_dpif_lookup_in_table(ctx->xbridge->ofproto, &ctx->xin->flow, - !skip_wildcards ? &ctx->xout->wc : NULL, - table_id, &rule); + verdict = rule_dpif_lookup_from_table(ctx->xbridge->ofproto, + &ctx->xin->flow, + !skip_wildcards + ? &ctx->xout->wc : NULL, + honor_table_miss, + &ctx->table_id, &rule); ctx->xin->flow.in_port.ofp_port = old_in_port; if (ctx->xin->resubmit_hook) { ctx->xin->resubmit_hook(ctx->xin, rule, ctx->recurse); } - if (!rule && may_packet_in) { - struct xport *xport; - - /* XXX - * check if table configuration flags - * OFPTC11_TABLE_MISS_CONTROLLER, default. - * OFPTC11_TABLE_MISS_CONTINUE, - * OFPTC11_TABLE_MISS_DROP - * When OF1.0, OFPTC11_TABLE_MISS_CONTINUE is used. What to do? */ - xport = get_ofp_port(ctx->xbridge, ctx->xin->flow.in_port.ofp_port); - choose_miss_rule(xport ? xport->config : 0, - ctx->xbridge->miss_rule, - ctx->xbridge->no_packet_in_rule, &rule); + switch (verdict) { + case RULE_DPIF_LOOKUP_VERDICT_MATCH: + goto match; + case RULE_DPIF_LOOKUP_VERDICT_CONTROLLER: + if (may_packet_in) { + struct xport *xport; + + xport = get_ofp_port(ctx->xbridge, + ctx->xin->flow.in_port.ofp_port); + config = xport ? xport->config : 0; + break; + } + /* Fall through to drop */ + case RULE_DPIF_LOOKUP_VERDICT_DROP: + config = OFPUTIL_PC_NO_PACKET_IN; + break; + case RULE_DPIF_LOOKUP_VERDICT_DEFAULT: + if (!ofproto_dpif_wants_packet_in_on_miss(ctx->xbridge->ofproto)) { + config = OFPUTIL_PC_NO_PACKET_IN; + } + break; + default: + OVS_NOT_REACHED(); } + + choose_miss_rule(config, ctx->xbridge->miss_rule, + ctx->xbridge->no_packet_in_rule, &rule); + +match: if (rule) { xlate_recursively(ctx, rule); rule_dpif_unref(rule); @@ -1924,7 +1996,7 @@ xlate_group_bucket(struct xlate_ctx *ctx, const struct ofputil_bucket *bucket) ofpacts_execute_action_set(&action_list, &action_set); ctx->recurse++; - do_xlate_actions(action_list.data, action_list.size, ctx); + do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx); ctx->recurse--; ofpbuf_uninit(&action_set); @@ -1970,7 +2042,7 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group) const struct ofputil_bucket *bucket; uint32_t basis; - basis = hash_bytes(ctx->xin->flow.dl_dst, sizeof ctx->xin->flow.dl_dst, 0); + basis = hash_mac(ctx->xin->flow.dl_dst, 0, 0); bucket = group_best_live_bucket(ctx, group, basis); if (bucket) { memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); @@ -1981,6 +2053,8 @@ xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group) static void xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group) { + ctx->in_group = true; + switch (group_dpif_get_type(group)) { case OFPGT11_ALL: case OFPGT11_INDIRECT: @@ -1996,12 +2070,38 @@ xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group) OVS_NOT_REACHED(); } group_dpif_release(group); + + ctx->in_group = false; +} + +static bool +xlate_group_resource_check(struct xlate_ctx *ctx) +{ + if (!xlate_resubmit_resource_check(ctx)) { + return false; + } else if (ctx->in_group) { + /* Prevent nested translation of OpenFlow groups. + * + * OpenFlow allows this restriction. We enforce this restriction only + * because, with the current architecture, we would otherwise have to + * take a possibly recursive read lock on the ofgroup rwlock, which is + * unsafe given that POSIX allows taking a read lock to block if there + * is a thread blocked on taking the write lock. Other solutions + * without this restriction are also possible, but seem unwarranted + * given the current limited use of groups. */ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_ERR_RL(&rl, "cannot recursively translate OpenFlow group"); + return false; + } else { + return true; + } } static bool xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id) { - if (xlate_resubmit_resource_check(ctx)) { + if (xlate_group_resource_check(ctx)) { struct group_dpif *group; bool got_group; @@ -2022,6 +2122,15 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx, { ofp_port_t in_port; uint8_t table_id; + bool may_packet_in = false; + bool honor_table_miss = false; + + if (ctx->rule && rule_dpif_is_internal(ctx->rule)) { + /* Still allow missed packets to be sent to the controller + * if resubmitting from an internal table. */ + may_packet_in = true; + honor_table_miss = true; + } in_port = resubmit->in_port; if (in_port == OFPP_IN_PORT) { @@ -2033,7 +2142,8 @@ xlate_ofpact_resubmit(struct xlate_ctx *ctx, table_id = ctx->table_id; } - xlate_table_action(ctx, in_port, table_id, false); + xlate_table_action(ctx, in_port, table_id, may_packet_in, + honor_table_miss); } static void @@ -2076,11 +2186,12 @@ execute_controller_action(struct xlate_ctx *ctx, int len, &ctx->xout->odp_actions, &ctx->xout->wc); - odp_execute_actions(NULL, packet, &md, ctx->xout->odp_actions.data, - ctx->xout->odp_actions.size, NULL); + odp_execute_actions(NULL, packet, false, &md, + ofpbuf_data(&ctx->xout->odp_actions), + ofpbuf_size(&ctx->xout->odp_actions), NULL); pin = xmalloc(sizeof *pin); - pin->up.packet_len = packet->size; + pin->up.packet_len = ofpbuf_size(packet); pin->up.packet = ofpbuf_steal_data(packet); pin->up.reason = reason; pin->up.table_id = ctx->table_id; @@ -2092,8 +2203,22 @@ execute_controller_action(struct xlate_ctx *ctx, int len, pin->controller_id = controller_id; pin->send_len = len; - pin->generated_by_table_miss = (ctx->rule - && rule_dpif_is_table_miss(ctx->rule)); + /* If a rule is a table-miss rule then this is + * a table-miss handled by a table-miss rule. + * + * Else, if rule is internal and has a controller action, + * the later being implied by the rule being processed here, + * then this is a table-miss handled without a table-miss rule. + * + * Otherwise this is not a table-miss. */ + pin->miss_type = OFPROTO_PACKET_IN_NO_MISS; + if (ctx->rule) { + if (rule_dpif_is_table_miss(ctx->rule)) { + pin->miss_type = OFPROTO_PACKET_IN_MISS_FLOW; + } else if (rule_dpif_is_internal(ctx->rule)) { + pin->miss_type = OFPROTO_PACKET_IN_MISS_WITHOUT_FLOW; + } + } ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin); ofpbuf_delete(packet); } @@ -2103,18 +2228,12 @@ compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls) { struct flow_wildcards *wc = &ctx->xout->wc; struct flow *flow = &ctx->xin->flow; - ovs_be16 vlan_tci = flow->vlan_tci; int n; ovs_assert(eth_type_mpls(mpls->ethertype)); n = flow_count_mpls_labels(flow, wc); if (!n) { - if (mpls->position == OFPACT_MPLS_BEFORE_VLAN) { - vlan_tci = 0; - } else { - flow->vlan_tci = 0; - } ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow, &ctx->xout->odp_actions, &ctx->xout->wc); @@ -2134,7 +2253,6 @@ compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls) } flow_push_mpls(flow, n, mpls->ethertype, wc); - flow->vlan_tci = vlan_tci; } static void @@ -2248,7 +2366,7 @@ xlate_output_action(struct xlate_ctx *ctx, break; case OFPP_TABLE: xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, - 0, may_packet_in); + 0, may_packet_in, true); break; case OFPP_NORMAL: xlate_normal(ctx); @@ -2485,7 +2603,7 @@ xlate_action_set(struct xlate_ctx *ctx) ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub); ofpacts_execute_action_set(&action_list, &ctx->action_set); - do_xlate_actions(action_list.data, action_list.size, ctx); + do_xlate_actions(ofpbuf_data(&action_list), ofpbuf_size(&action_list), ctx); ofpbuf_uninit(&action_list); } @@ -2653,15 +2771,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_FIELD: set_field = ofpact_get_SET_FIELD(a); mf = set_field->field; - mf_mask_field_and_prereqs(mf, &wc->masks); /* Set field action only ever overwrites packet's outermost * applicable header fields. Do nothing if no header exists. */ - if ((mf->id != MFF_VLAN_VID || flow->vlan_tci & htons(VLAN_CFI)) - && ((mf->id != MFF_MPLS_LABEL && mf->id != MFF_MPLS_TC) - || eth_type_mpls(flow->dl_type))) { - mf_set_flow_value(mf, &set_field->value, flow); + if (mf->id == MFF_VLAN_VID) { + wc->masks.vlan_tci |= htons(VLAN_CFI); + if (!(flow->vlan_tci & htons(VLAN_CFI))) { + break; + } + } else if ((mf->id == MFF_MPLS_LABEL || mf->id == MFF_MPLS_TC) + /* 'dl_type' is already unwildcarded. */ + && !eth_type_mpls(flow->dl_type)) { + break; } + + mf_mask_field_and_prereqs(mf, &wc->masks); + mf_set_flow_value(mf, &set_field->value, flow); break; case OFPACT_STACK_PUSH: @@ -2761,7 +2886,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, ovs_assert(ctx->table_id < ogt->table_id); xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port, - ogt->table_id, true); + ogt->table_id, true, true); break; } @@ -2831,8 +2956,8 @@ xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src) ofpbuf_use_stub(&dst->odp_actions, dst->odp_actions_stub, sizeof dst->odp_actions_stub); - ofpbuf_put(&dst->odp_actions, src->odp_actions.data, - src->odp_actions.size); + ofpbuf_put(&dst->odp_actions, ofpbuf_data(&src->odp_actions), + ofpbuf_size(&src->odp_actions)); } static struct skb_priority_to_dscp * @@ -2877,8 +3002,8 @@ actions_output_to_local_port(const struct xlate_ctx *ctx) const struct nlattr *a; unsigned int left; - NL_ATTR_FOR_EACH_UNSAFE (a, left, ctx->xout->odp_actions.data, - ctx->xout->odp_actions.size) { + NL_ATTR_FOR_EACH_UNSAFE (a, left, ofpbuf_data(&ctx->xout->odp_actions), + ofpbuf_size(&ctx->xout->odp_actions)) { if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT && nl_attr_get_odp_port(a) == local_odp_port) { return true; @@ -2918,6 +3043,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) struct xlate_ctx ctx; size_t ofpacts_len; bool tnl_may_send; + bool is_icmp; COVERAGE_INC(xlate_actions); @@ -2972,6 +3098,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) if (is_ip_any(flow)) { wc->masks.nw_frag |= FLOW_NW_FRAG_MASK; } + is_icmp = is_icmpv4(flow) || is_icmpv6(flow); tnl_may_send = tnl_xlate_init(&ctx.base_flow, flow, wc); if (ctx.xbridge->netflow) { @@ -2980,19 +3107,22 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) ctx.recurse = 0; ctx.resubmits = 0; + ctx.in_group = false; ctx.orig_skb_priority = flow->skb_priority; ctx.table_id = 0; ctx.exit = false; if (!xin->ofpacts && !ctx.rule) { - rule_dpif_lookup(ctx.xbridge->ofproto, flow, - !xin->skip_wildcards ? wc : NULL, &rule); + ctx.table_id = rule_dpif_lookup(ctx.xbridge->ofproto, flow, + !xin->skip_wildcards ? wc : NULL, + &rule); if (ctx.xin->resubmit_stats) { rule_dpif_credit_stats(rule, ctx.xin->resubmit_stats); } ctx.rule = rule; } xout->fail_open = ctx.rule && rule_dpif_is_fail_open(ctx.rule); + xout->use_recirc = false; if (xin->ofpacts) { ofpacts = xin->ofpacts; @@ -3061,7 +3191,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) add_sflow_action(&ctx); add_ipfix_action(&ctx); - sample_actions_len = ctx.xout->odp_actions.size; + sample_actions_len = ofpbuf_size(&ctx.xout->odp_actions); if (tnl_may_send && (!in_port || may_receive(in_port, &ctx))) { do_xlate_actions(ofpacts, ofpacts_len, &ctx); @@ -3069,11 +3199,11 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) /* We've let OFPP_NORMAL and the learning action look at the * packet, so drop it now if forwarding is disabled. */ if (in_port && !xport_stp_forward_state(in_port)) { - ctx.xout->odp_actions.size = sample_actions_len; + ofpbuf_set_size(&ctx.xout->odp_actions, sample_actions_len); } } - if (ctx.action_set.size) { + if (ofpbuf_size(&ctx.action_set)) { xlate_action_set(&ctx); } @@ -3090,7 +3220,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) } } - if (nl_attr_oversized(ctx.xout->odp_actions.size)) { + if (nl_attr_oversized(ofpbuf_size(&ctx.xout->odp_actions))) { /* These datapath actions are too big for a Netlink attribute, so we * can't hand them to the kernel directly. dpif_execute() can execute * them one by one with help, so just mark the result as SLOW_ACTION to @@ -3131,8 +3261,22 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) * use non-header fields as part of the cache. */ flow_wildcards_clear_non_packet_fields(wc); + /* ICMPv4 and ICMPv6 have 8-bit "type" and "code" fields. struct flow uses + * the low 8 bits of the 16-bit tp_src and tp_dst members to represent + * these fields. The datapath interface, on the other hand, represents + * them with just 8 bits each. This means that if the high 8 bits of the + * masks for these fields somehow become set, then they will get chopped + * off by a round trip through the datapath, and revalidation will spot + * that as an inconsistency and delete the flow. Avoid the problem here by + * making sure that only the low 8 bits of either field can be unwildcarded + * for ICMP. + */ + if (is_icmp) { + wc->masks.tp_src &= htons(UINT8_MAX); + wc->masks.tp_dst &= htons(UINT8_MAX); + } + out: - rule_actions_unref(actions); rule_dpif_unref(rule); } @@ -3145,12 +3289,11 @@ xlate_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) struct xport *xport; struct ofpact_output output; struct flow flow; - union flow_in_port in_port_; ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output); /* Use OFPP_NONE as the in_port to avoid special packet processing. */ - in_port_.ofp_port = OFPP_NONE; - flow_extract(packet, 0, 0, NULL, &in_port_, &flow); + flow_extract(packet, NULL, &flow); + flow.in_port.ofp_port = OFPP_NONE; ovs_rwlock_rdlock(&xlate_rwlock); xport = xport_lookup(ofport);