X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif-xlate.c;h=a6bfc8702888416ea193f3e7bdcc50ce573d36b5;hb=1839c35676b5c3a2a70e83477a6ce3d3c7d0d245;hp=848c7780bebffb07af4c560941e911413cea6c63;hpb=60d02c72941d5a4aeea85090f120a0cc03e945f2;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 848c7780b..a6bfc8702 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. +/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,7 @@ COVERAGE_DEFINE(xlate_actions); COVERAGE_DEFINE(xlate_actions_oversize); +COVERAGE_DEFINE(xlate_actions_mpls_overflow); VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate); @@ -87,6 +88,15 @@ struct xbridge { enum ofp_config_flags frag; /* Fragmentation handling. */ bool has_in_band; /* Bridge has in band control? */ bool forward_bpdu; /* Bridge forwards STP BPDUs? */ + + /* 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. */ + bool variable_length_userdata; + + /* Number of MPLS label stack entries that the datapath supports + * in matches. */ + size_t max_mpls_depth; }; struct xbundle { @@ -165,16 +175,10 @@ struct xlate_ctx { /* The rule that we are currently translating, or NULL. */ struct rule_dpif *rule; - int mpls_depth_delta; /* Delta of the mpls stack depth since - * actions were last committed. - * Must be between -1 and 1 inclusive. */ - ovs_be32 pre_push_mpls_lse; /* Used to record the top-most MPLS LSE - * prior to an mpls_push so that it may be - * used for a subsequent mpls_pop. */ - /* 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. */ @@ -197,9 +201,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 @@ -222,8 +228,9 @@ 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_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 *, @@ -249,7 +256,9 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, const struct dpif_sflow *sflow, const struct dpif_ipfix *ipfix, const struct netflow *netflow, enum ofp_config_flags frag, - bool forward_bpdu, bool has_in_band) + bool forward_bpdu, bool has_in_band, + bool variable_length_userdata, + size_t max_mpls_depth) { struct xbridge *xbridge = xbridge_lookup(ofproto); @@ -301,6 +310,8 @@ 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->variable_length_userdata = variable_length_userdata; + xbridge->max_mpls_depth = max_mpls_depth; } void @@ -513,12 +524,10 @@ xlate_ofport_remove(struct ofport_dpif *ofport) /* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key' * respectively), populates 'flow' with the result of odp_flow_key_to_flow(). - * Optionally, if nonnull, populates 'fitnessp' with the fitness of 'flow' as - * returned by odp_flow_key_to_flow(). Also, optionally populates 'ofproto' - * with the ofproto_dpif, 'odp_in_port' with the datapath in_port, that - * 'packet' ingressed, and 'ipfix', 'sflow', and 'netflow' with the appropriate - * handles for those protocols if they're enabled. Caller is responsible for - * unrefing them. + * Optionally populates 'ofproto' with the ofproto_dpif, 'odp_in_port' with + * the datapath in_port, that 'packet' ingressed, and 'ipfix', 'sflow', and + * 'netflow' with the appropriate handles for those protocols if they're + * enabled. Caller is responsible for unrefing them. * * If 'ofproto' is nonnull, requires 'flow''s in_port to exist. Otherwise sets * 'flow''s in_port to OFPP_NONE. @@ -538,19 +547,16 @@ xlate_ofport_remove(struct ofport_dpif *ofport) * or some other positive errno if there are other problems. */ int xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet, - const struct nlattr *key, size_t key_len, - struct flow *flow, enum odp_key_fitness *fitnessp, + const struct nlattr *key, size_t key_len, struct flow *flow, struct ofproto_dpif **ofproto, struct dpif_ipfix **ipfix, struct dpif_sflow **sflow, struct netflow **netflow, odp_port_t *odp_in_port) { - enum odp_key_fitness fitness; const struct xport *xport; int error = ENODEV; ovs_rwlock_rdlock(&xlate_rwlock); - fitness = odp_flow_key_to_flow(key, key_len, flow); - if (fitness == ODP_FIT_ERROR) { + if (odp_flow_key_to_flow(key, key_len, flow) == ODP_FIT_ERROR) { error = EINVAL; goto exit; } @@ -574,10 +580,8 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet, * an OpenFlow controller properly, so that it looks correct * for sFlow, and so that flow_extract() will get the correct * vlan_tci if it is called on 'packet'. */ - eth_push_vlan(packet, flow->vlan_tci); + eth_push_vlan(packet, htons(ETH_TYPE_VLAN), flow->vlan_tci); } - /* We can't reproduce 'key' from 'flow'. */ - fitness = fitness == ODP_FIT_PERFECT ? ODP_FIT_TOO_MUCH : fitness; } error = 0; @@ -598,9 +602,6 @@ xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet, } exit: - if (fitnessp) { - *fitnessp = fitness; - } ovs_rwlock_unlock(&xlate_rwlock); return error; } @@ -667,7 +668,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); @@ -681,6 +682,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 @@ -695,7 +703,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. */ @@ -704,12 +712,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)); } } @@ -889,8 +897,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; } @@ -1486,7 +1492,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); @@ -1687,7 +1693,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 == 23); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 25); if (!xport) { xlate_report(ctx, "Nonexistent output port"); @@ -1695,9 +1701,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) { @@ -1722,17 +1737,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); } } @@ -1783,27 +1798,25 @@ 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); + } } } if (out_port != ODPP_NONE) { ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow, &ctx->xout->odp_actions, - &ctx->xout->wc, - &ctx->mpls_depth_delta); + &ctx->xout->wc); nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port); @@ -1840,7 +1853,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--; } @@ -1855,9 +1867,9 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx) MAX_RESUBMIT_RECURSION); } else if (ctx->resubmits >= MAX_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; @@ -1867,14 +1879,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; @@ -1882,29 +1896,42 @@ 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; + 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); @@ -1928,7 +1955,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); @@ -1974,7 +2001,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); @@ -1985,6 +2012,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: @@ -2000,12 +2029,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; @@ -2037,7 +2092,7 @@ 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, false, false); } static void @@ -2067,7 +2122,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len, { struct ofproto_packet_in *pin; struct ofpbuf *packet; - struct flow key; + struct pkt_metadata md = PKT_METADATA_INITIALIZER(0); ctx->xout->slow |= SLOW_CONTROLLER; if (!ctx->xin->packet) { @@ -2076,20 +2131,16 @@ execute_controller_action(struct xlate_ctx *ctx, int len, packet = ofpbuf_clone(ctx->xin->packet); - key.skb_priority = 0; - key.pkt_mark = 0; - memset(&key.tunnel, 0, sizeof key.tunnel); - ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow, &ctx->xout->odp_actions, - &ctx->xout->wc, - &ctx->mpls_depth_delta); + &ctx->xout->wc); - odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data, - ctx->xout->odp_actions.size, NULL, 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; @@ -2101,105 +2152,76 @@ 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); } -static bool -compose_mpls_push_action(struct xlate_ctx *ctx, ovs_be16 eth_type) +static void +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; + int n; - ovs_assert(eth_type_mpls(eth_type)); - - /* If mpls_depth_delta is negative then an MPLS POP action has been - * composed and the resulting MPLS label stack is unknown. This means - * an MPLS PUSH action can't be composed as it needs to know either the - * top-most MPLS LSE to use as a template for the new MPLS LSE, or that - * there is no MPLS label stack present. Thus, stop processing. - * - * If mpls_depth_delta is positive then an MPLS PUSH action has been - * composed and no further MPLS PUSH action may be performed without - * losing MPLS LSE and ether type information held in xtx->xin->flow. - * Thus, stop processing. - * - * If the MPLS LSE of the flow and base_flow differ then the MPLS LSE - * has been updated. Performing a MPLS PUSH action may be would result in - * losing MPLS LSE and ether type information held in xtx->xin->flow. - * Thus, stop processing. - * - * It is planned that in the future this case will be handled - * by recirculation */ - if (ctx->mpls_depth_delta || - ctx->xin->flow.mpls_lse != ctx->base_flow.mpls_lse) { - return true; - } - - memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse); - - ctx->pre_push_mpls_lse = ctx->xin->flow.mpls_lse; + ovs_assert(eth_type_mpls(mpls->ethertype)); - if (eth_type_mpls(ctx->xin->flow.dl_type)) { - flow->mpls_lse &= ~htonl(MPLS_BOS_MASK); - } else { - ovs_be32 label; - uint8_t tc, ttl; - - if (flow->dl_type == htons(ETH_TYPE_IPV6)) { - label = htonl(0x2); /* IPV6 Explicit Null. */ - } else { - label = htonl(0x0); /* IPV4 Explicit Null. */ + n = flow_count_mpls_labels(flow, wc); + if (!n) { + ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow, + &ctx->xout->odp_actions, + &ctx->xout->wc); + } else if (n >= FLOW_MAX_MPLS_LABELS) { + if (ctx->xin->packet != NULL) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping packet on which an " + "MPLS push action can't be performed as it would " + "have more MPLS LSEs than the %d supported.", + ctx->xbridge->name, FLOW_MAX_MPLS_LABELS); } - wc->masks.nw_tos |= IP_DSCP_MASK; - wc->masks.nw_ttl = 0xff; - tc = (flow->nw_tos & IP_DSCP_MASK) >> 2; - ttl = flow->nw_ttl ? flow->nw_ttl : 0x40; - flow->mpls_lse = set_mpls_lse_values(ttl, tc, 1, label); + ctx->exit = true; + return; + } else if (n >= ctx->xbridge->max_mpls_depth) { + COVERAGE_INC(xlate_actions_mpls_overflow); + ctx->xout->slow |= SLOW_ACTION; } - flow->dl_type = eth_type; - ctx->mpls_depth_delta++; - return false; + flow_push_mpls(flow, n, mpls->ethertype, wc); } -static bool +static void compose_mpls_pop_action(struct xlate_ctx *ctx, ovs_be16 eth_type) { struct flow_wildcards *wc = &ctx->xout->wc; + struct flow *flow = &ctx->xin->flow; + int n = flow_count_mpls_labels(flow, wc); - if (!eth_type_mpls(ctx->xin->flow.dl_type)) { - return true; - } - - /* If mpls_depth_delta is negative then an MPLS POP action has been - * composed. Performing another MPLS POP action - * would result in losing ether type that results from - * the already composed MPLS POP. Thus, stop processing. - * - * It is planned that in the future this case will be handled - * by recirculation */ - if (ctx->mpls_depth_delta < 0) { - return true; - } - - memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse); - - /* If mpls_depth_delta is positive then an MPLS PUSH action has been - * executed and the previous MPLS LSE saved in ctx->pre_push_mpls_lse. The - * flow's MPLS LSE should be restored to that value to allow any - * subsequent actions that update of the LSE to be executed correctly. - */ - if (ctx->mpls_depth_delta > 0) { - ctx->xin->flow.mpls_lse = ctx->pre_push_mpls_lse; + if (!flow_pop_mpls(flow, n, eth_type, wc) && n >= FLOW_MAX_MPLS_LABELS) { + if (ctx->xin->packet != NULL) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + VLOG_WARN_RL(&rl, "bridge %s: dropping packet on which an " + "MPLS pop action can't be performed as it has " + "more MPLS LSEs than the %d supported.", + ctx->xbridge->name, FLOW_MAX_MPLS_LABELS); + } + ctx->exit = true; + ofpbuf_clear(&ctx->xout->odp_actions); } - - ctx->xin->flow.dl_type = eth_type; - ctx->mpls_depth_delta--; - - return false; } static bool @@ -2228,99 +2250,53 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids) } } -static bool +static void compose_set_mpls_label_action(struct xlate_ctx *ctx, ovs_be32 label) { - if (!eth_type_mpls(ctx->xin->flow.dl_type)) { - return true; - } - - /* If mpls_depth_delta is negative then an MPLS POP action has been - * executed and the resulting MPLS label stack is unknown. This means - * a SET MPLS LABEL action can't be executed as it needs to manipulate - * the top-most MPLS LSE. Thus, stop processing. - * - * It is planned that in the future this case will be handled - * by recirculation. - */ - if (ctx->mpls_depth_delta < 0) { - return true; + if (eth_type_mpls(ctx->xin->flow.dl_type)) { + ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_LABEL_MASK); + set_mpls_lse_label(&ctx->xin->flow.mpls_lse[0], label); } - - ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK); - set_mpls_lse_label(&ctx->xin->flow.mpls_lse, label); - return false; } -static bool +static void compose_set_mpls_tc_action(struct xlate_ctx *ctx, uint8_t tc) { - if (!eth_type_mpls(ctx->xin->flow.dl_type)) { - return true; - } - - /* If mpls_depth_delta is negative then an MPLS POP action has been - * executed and the resulting MPLS label stack is unknown. This means - * a SET MPLS TC action can't be executed as it needs to manipulate - * the top-most MPLS LSE. Thus, stop processing. - * - * It is planned that in the future this case will be handled - * by recirculation. - */ - if (ctx->mpls_depth_delta < 0) { - return true; + if (eth_type_mpls(ctx->xin->flow.dl_type)) { + ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_TC_MASK); + set_mpls_lse_tc(&ctx->xin->flow.mpls_lse[0], tc); } - - ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK); - set_mpls_lse_tc(&ctx->xin->flow.mpls_lse, tc); - return false; } -static bool +static void compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl) { - if (!eth_type_mpls(ctx->xin->flow.dl_type)) { - return true; - } - - /* If mpls_depth_delta is negative then an MPLS POP action has been - * executed and the resulting MPLS label stack is unknown. This means - * a SET MPLS TTL push action can't be executed as it needs to manipulate - * the top-most MPLS LSE. Thus, stop processing. - * - * It is planned that in the future this case will be handled - * by recirculation. - */ - if (ctx->mpls_depth_delta < 0) { - return true; + if (eth_type_mpls(ctx->xin->flow.dl_type)) { + ctx->xout->wc.masks.mpls_lse[0] |= htonl(MPLS_TTL_MASK); + set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse[0], ttl); } - - ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TTL_MASK); - set_mpls_lse_ttl(&ctx->xin->flow.mpls_lse, ttl); - return false; } static bool compose_dec_mpls_ttl_action(struct xlate_ctx *ctx) { struct flow *flow = &ctx->xin->flow; - uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse); + uint8_t ttl = mpls_lse_to_ttl(flow->mpls_lse[0]); struct flow_wildcards *wc = &ctx->xout->wc; memset(&wc->masks.mpls_lse, 0xff, sizeof wc->masks.mpls_lse); + if (eth_type_mpls(flow->dl_type)) { + if (ttl > 1) { + ttl--; + set_mpls_lse_ttl(&flow->mpls_lse[0], ttl); + return false; + } else { + execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0); - if (!eth_type_mpls(flow->dl_type)) { - return false; - } - - if (ttl > 1) { - ttl--; - set_mpls_lse_ttl(&flow->mpls_lse, ttl); - return false; + /* Stop processing for current table. */ + return true; + } } else { - execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0); - - /* Stop processing for current table. */ return true; } } @@ -2339,7 +2315,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); @@ -2521,10 +2497,18 @@ xlate_sample_action(struct xlate_ctx *ctx, * the same percentage. */ uint32_t probability = (os->probability << 16) | os->probability; + if (!ctx->xbridge->variable_length_userdata) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_ERR_RL(&rl, "ignoring NXAST_SAMPLE action because datapath " + "lacks support (needs Linux 3.10+ or kernel module from " + "OVS 1.11+)"); + return; + } + ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow, &ctx->xout->odp_actions, - &ctx->xout->wc, - &ctx->mpls_depth_delta); + &ctx->xout->wc); compose_flow_sample_cookie(os->probability, os->collector_set_id, os->obs_domain_id, os->obs_point_id, &cookie); @@ -2568,7 +2552,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); } @@ -2742,7 +2726,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, * 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) - || flow->mpls_lse)) { + || eth_type_mpls(flow->dl_type))) { mf_set_flow_value(mf, &set_field->value, flow); } break; @@ -2758,38 +2742,24 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_PUSH_MPLS: - if (compose_mpls_push_action(ctx, - ofpact_get_PUSH_MPLS(a)->ethertype)) { - return; - } + compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)); break; case OFPACT_POP_MPLS: - if (compose_mpls_pop_action(ctx, - ofpact_get_POP_MPLS(a)->ethertype)) { - return; - } + compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype); break; case OFPACT_SET_MPLS_LABEL: - if (compose_set_mpls_label_action(ctx, - ofpact_get_SET_MPLS_LABEL(a)->label)) { - return; - } - break; + compose_set_mpls_label_action( + ctx, ofpact_get_SET_MPLS_LABEL(a)->label); + break; case OFPACT_SET_MPLS_TC: - if (compose_set_mpls_tc_action(ctx, - ofpact_get_SET_MPLS_TC(a)->tc)) { - return; - } + compose_set_mpls_tc_action(ctx, ofpact_get_SET_MPLS_TC(a)->tc); break; case OFPACT_SET_MPLS_TTL: - if (compose_set_mpls_ttl_action(ctx, - ofpact_get_SET_MPLS_TTL(a)->ttl)) { - return; - } + compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl); break; case OFPACT_DEC_MPLS_TTL: @@ -2858,7 +2828,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; } @@ -2928,8 +2898,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 * @@ -2974,8 +2944,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; @@ -2987,6 +2957,7 @@ actions_output_to_local_port(const struct xlate_ctx *ctx) /* Thread safe call to xlate_actions__(). */ void xlate_actions(struct xlate_in *xin, struct xlate_out *xout) + OVS_EXCLUDED(xlate_rwlock) { ovs_rwlock_rdlock(&xlate_rwlock); xlate_actions__(xin, xout); @@ -3014,6 +2985,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); @@ -3065,7 +3037,10 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) memset(&wc->masks.in_port, 0xff, sizeof wc->masks.in_port); memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority); memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type); - wc->masks.nw_frag |= FLOW_NW_FRAG_MASK; + 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) { @@ -3074,14 +3049,15 @@ 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; - ctx.mpls_depth_delta = 0; 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); } @@ -3156,7 +3132,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); @@ -3164,11 +3140,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); } @@ -3185,7 +3161,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 @@ -3226,8 +3202,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); } @@ -3240,13 +3230,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_; - int error; 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); @@ -3256,9 +3244,9 @@ xlate_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) } output.port = xport->ofp_port; output.max_len = 0; - error = ofproto_dpif_execute_actions(xport->xbridge->ofproto, &flow, NULL, - &output.ofpact, sizeof output, - packet); ovs_rwlock_unlock(&xlate_rwlock); - return error; + + return ofproto_dpif_execute_actions(xport->xbridge->ofproto, &flow, NULL, + &output.ofpact, sizeof output, + packet); }