X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto-dpif-xlate.c;h=ac273e9444e2bfeccc36cfccc2c54ed16c53b1fb;hb=ce3955bed734b7459a8bdb3180284bf35dddaa7b;hp=1eb09532e0606dec4944cdde0d395996b0bdeea7;hpb=cfa955b083c5617212a29a03423e063ff6cb350a;p=sliver-openvswitch.git diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 1eb09532e..ac273e944 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -49,6 +49,7 @@ #include "vlog.h" COVERAGE_DEFINE(xlate_actions); +COVERAGE_DEFINE(xlate_actions_oversize); VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate); @@ -75,6 +76,7 @@ struct xbridge { struct mbridge *mbridge; /* Mirroring. */ struct dpif_sflow *sflow; /* SFlow handle, or null. */ struct dpif_ipfix *ipfix; /* Ipfix handle, or null. */ + struct netflow *netflow; /* Netflow handle, or null. */ struct stp *stp; /* STP or null if disabled. */ /* Special rules installed by ofproto-dpif. */ @@ -82,7 +84,6 @@ struct xbridge { struct rule_dpif *no_packet_in_rule; enum ofp_config_flags frag; /* Fragmentation handling. */ - bool has_netflow; /* Bridge runs netflow? */ bool has_in_band; /* Bridge has in band control? */ bool forward_bpdu; /* Bridge forwards STP BPDUs? */ }; @@ -126,6 +127,7 @@ struct xport { struct xport *peer; /* Patch port peer or null. */ enum ofputil_port_config config; /* OpenFlow port configuration. */ + enum ofputil_port_state state; /* OpenFlow port state. */ int stp_port_no; /* STP port number or -1 if not in use. */ struct hmap skb_priorities; /* Map of 'skb_priority_to_dscp's. */ @@ -244,8 +246,9 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, const struct mac_learning *ml, struct stp *stp, const struct mbridge *mbridge, const struct dpif_sflow *sflow, - const struct dpif_ipfix *ipfix, enum ofp_config_flags frag, - bool forward_bpdu, bool has_in_band, bool has_netflow) + const struct dpif_ipfix *ipfix, + const struct netflow *netflow, enum ofp_config_flags frag, + bool forward_bpdu, bool has_in_band) { struct xbridge *xbridge = xbridge_lookup(ofproto); @@ -283,13 +286,17 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name, xbridge->stp = stp_ref(stp); } + if (xbridge->netflow != netflow) { + netflow_unref(xbridge->netflow); + xbridge->netflow = netflow_ref(netflow); + } + free(xbridge->name); xbridge->name = xstrdup(name); xbridge->dpif = dpif; xbridge->forward_bpdu = forward_bpdu; xbridge->has_in_band = has_in_band; - xbridge->has_netflow = has_netflow; xbridge->frag = frag; xbridge->miss_rule = miss_rule; xbridge->no_packet_in_rule = no_packet_in_rule; @@ -396,7 +403,8 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle, const struct cfm *cfm, const struct bfd *bfd, struct ofport_dpif *peer, int stp_port_no, const struct ofproto_port_queue *qdscp_list, size_t n_qdscp, - enum ofputil_port_config config, bool is_tunnel, + enum ofputil_port_config config, + enum ofputil_port_state state, bool is_tunnel, bool may_enable) { struct xport *xport = xport_lookup(ofport); @@ -417,6 +425,7 @@ xlate_ofport_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle, ovs_assert(xport->ofp_port == ofp_port); xport->config = config; + xport->state = state; xport->stp_port_no = stp_port_no; xport->is_tunnel = is_tunnel; xport->may_enable = may_enable; @@ -719,6 +728,106 @@ ofp_port_to_odp_port(const struct xbridge *xbridge, ofp_port_t ofp_port) return xport ? xport->odp_port : ODPP_NONE; } +static bool +odp_port_is_alive(const struct xlate_ctx *ctx, ofp_port_t ofp_port) +{ + struct xport *xport; + + xport = get_ofp_port(ctx->xbridge, ofp_port); + if (!xport || xport->config & OFPUTIL_PC_PORT_DOWN || + xport->state & OFPUTIL_PS_LINK_DOWN) { + return false; + } + + return true; +} + +static const struct ofputil_bucket * +group_first_live_bucket(const struct xlate_ctx *, const struct group_dpif *, + int depth); + +static bool +group_is_alive(const struct xlate_ctx *ctx, uint32_t group_id, int depth) +{ + struct group_dpif *group; + bool hit; + + hit = group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group); + if (!hit) { + return false; + } + + hit = group_first_live_bucket(ctx, group, depth) != NULL; + + group_dpif_release(group); + return hit; +} + +#define MAX_LIVENESS_RECURSION 128 /* Arbitrary limit */ + +static bool +bucket_is_alive(const struct xlate_ctx *ctx, + const struct ofputil_bucket *bucket, int depth) +{ + if (depth >= MAX_LIVENESS_RECURSION) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + + VLOG_WARN_RL(&rl, "bucket chaining exceeded %d links", + MAX_LIVENESS_RECURSION); + return false; + } + + return !ofputil_bucket_has_liveness(bucket) || + (bucket->watch_port != OFPP_ANY && + odp_port_is_alive(ctx, bucket->watch_port)) || + (bucket->watch_group != OFPG_ANY && + group_is_alive(ctx, bucket->watch_group, depth + 1)); +} + +static const struct ofputil_bucket * +group_first_live_bucket(const struct xlate_ctx *ctx, + const struct group_dpif *group, int depth) +{ + struct ofputil_bucket *bucket; + const struct list *buckets; + + group_dpif_get_buckets(group, &buckets); + LIST_FOR_EACH (bucket, list_node, buckets) { + if (bucket_is_alive(ctx, bucket, depth)) { + return bucket; + } + } + + return NULL; +} + +static const struct ofputil_bucket * +group_best_live_bucket(const struct xlate_ctx *ctx, + const struct group_dpif *group, + uint32_t basis) +{ + const struct ofputil_bucket *best_bucket = NULL; + uint32_t best_score = 0; + int i = 0; + + const struct ofputil_bucket *bucket; + const struct list *buckets; + + group_dpif_get_buckets(group, &buckets); + LIST_FOR_EACH (bucket, list_node, buckets) { + if (bucket_is_alive(ctx, bucket, 0)) { + uint32_t score = (hash_int(i, basis) & 0xffff) * bucket->weight; + if (score >= best_score) { + best_bucket = bucket; + best_score = score; + } + } + i++; + } + + return best_bucket; +} + static bool xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan) { @@ -1559,7 +1668,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 == 21); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23); if (!xport) { xlate_report(ctx, "Nonexistent output port"); @@ -1614,6 +1723,9 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, if (ctx->xin->resubmit_stats) { netdev_vport_inc_tx(xport->netdev, ctx->xin->resubmit_stats); netdev_vport_inc_rx(peer->netdev, ctx->xin->resubmit_stats); + if (peer->bfd) { + bfd_account_rx(peer->bfd, ctx->xin->resubmit_stats); + } } return; @@ -1714,9 +1826,8 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule) ctx->recurse--; } -static void -xlate_table_action(struct xlate_ctx *ctx, - ofp_port_t in_port, uint8_t table_id, bool may_packet_in) +static bool +xlate_resubmit_resource_check(struct xlate_ctx *ctx) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); @@ -1730,6 +1841,17 @@ xlate_table_action(struct xlate_ctx *ctx, } else if (ctx->stack.size >= 65536) { VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack"); } else { + return true; + } + + return false; +} + +static void +xlate_table_action(struct xlate_ctx *ctx, + ofp_port_t in_port, uint8_t table_id, bool may_packet_in) +{ + if (xlate_resubmit_resource_check(ctx)) { struct rule_dpif *rule; ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port; uint8_t old_table_id = ctx->table_id; @@ -1754,10 +1876,10 @@ xlate_table_action(struct xlate_ctx *ctx, /* XXX * check if table configuration flags - * OFPTC_TABLE_MISS_CONTROLLER, default. - * OFPTC_TABLE_MISS_CONTINUE, - * OFPTC_TABLE_MISS_DROP - * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do? */ + * 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, @@ -1775,6 +1897,109 @@ xlate_table_action(struct xlate_ctx *ctx, ctx->exit = true; } +static void +xlate_group_bucket(struct xlate_ctx *ctx, const struct ofputil_bucket *bucket) +{ + uint64_t action_list_stub[1024 / 8]; + struct ofpbuf action_list, action_set; + + ofpbuf_use_const(&action_set, bucket->ofpacts, bucket->ofpacts_len); + ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub); + + ofpacts_execute_action_set(&action_list, &action_set); + ctx->recurse++; + do_xlate_actions(action_list.data, action_list.size, ctx); + ctx->recurse--; + + ofpbuf_uninit(&action_set); + ofpbuf_uninit(&action_list); +} + +static void +xlate_all_group(struct xlate_ctx *ctx, struct group_dpif *group) +{ + const struct ofputil_bucket *bucket; + const struct list *buckets; + struct flow old_flow = ctx->xin->flow; + + group_dpif_get_buckets(group, &buckets); + + LIST_FOR_EACH (bucket, list_node, buckets) { + xlate_group_bucket(ctx, bucket); + /* Roll back flow to previous state. + * This is equivalent to cloning the packet for each bucket. + * + * As a side effect any subsequently applied actions will + * also effectively be applied to a clone of the packet taken + * just before applying the all or indirect group. */ + ctx->xin->flow = old_flow; + } +} + +static void +xlate_ff_group(struct xlate_ctx *ctx, struct group_dpif *group) +{ + const struct ofputil_bucket *bucket; + + bucket = group_first_live_bucket(ctx, group, 0); + if (bucket) { + xlate_group_bucket(ctx, bucket); + } +} + +static void +xlate_select_group(struct xlate_ctx *ctx, struct group_dpif *group) +{ + struct flow_wildcards *wc = &ctx->xout->wc; + const struct ofputil_bucket *bucket; + uint32_t basis; + + basis = hash_bytes(ctx->xin->flow.dl_dst, sizeof ctx->xin->flow.dl_dst, 0); + bucket = group_best_live_bucket(ctx, group, basis); + if (bucket) { + memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); + xlate_group_bucket(ctx, bucket); + } +} + +static void +xlate_group_action__(struct xlate_ctx *ctx, struct group_dpif *group) +{ + switch (group_dpif_get_type(group)) { + case OFPGT11_ALL: + case OFPGT11_INDIRECT: + xlate_all_group(ctx, group); + break; + case OFPGT11_SELECT: + xlate_select_group(ctx, group); + break; + case OFPGT11_FF: + xlate_ff_group(ctx, group); + break; + default: + NOT_REACHED(); + } + group_dpif_release(group); +} + +static bool +xlate_group_action(struct xlate_ctx *ctx, uint32_t group_id) +{ + if (xlate_resubmit_resource_check(ctx)) { + struct group_dpif *group; + bool got_group; + + got_group = group_dpif_lookup(ctx->xbridge->ofproto, group_id, &group); + if (got_group) { + xlate_group_action__(ctx, group); + } else { + return true; + } + } + + return false; +} + static void xlate_ofpact_resubmit(struct xlate_ctx *ctx, const struct ofpact_resubmit *resubmit) @@ -1983,6 +2208,54 @@ compose_dec_ttl(struct xlate_ctx *ctx, struct ofpact_cnt_ids *ids) } } +static bool +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; + } + + ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK); + set_mpls_lse_label(&ctx->xin->flow.mpls_lse, label); + return false; +} + +static bool +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; + } + + ctx->xout->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK); + set_mpls_lse_tc(&ctx->xin->flow.mpls_lse, tc); + return false; +} + static bool compose_set_mpls_ttl_action(struct xlate_ctx *ctx, uint8_t ttl) { @@ -2292,6 +2565,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { struct ofpact_controller *controller; const struct ofpact_metadata *metadata; + const struct ofpact_set_field *set_field; + const struct mf_field *mf; if (ctx->exit) { break; @@ -2304,7 +2579,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_GROUP: - /* XXX not yet implemented */ + if (xlate_group_action(ctx, ofpact_get_GROUP(a)->group_id)) { + return; + } break; case OFPACT_CONTROLLER: @@ -2320,17 +2597,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_VLAN_VID: wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); - flow->vlan_tci &= ~htons(VLAN_VID_MASK); - flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid) - | htons(VLAN_CFI)); + if (flow->vlan_tci & htons(VLAN_CFI) || + ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) { + flow->vlan_tci &= ~htons(VLAN_VID_MASK); + flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid) + | htons(VLAN_CFI)); + } break; case OFPACT_SET_VLAN_PCP: wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI); - flow->vlan_tci &= ~htons(VLAN_PCP_MASK); - flow->vlan_tci |= - htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp << VLAN_PCP_SHIFT) - | VLAN_CFI); + if (flow->vlan_tci & htons(VLAN_CFI) || + ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) { + flow->vlan_tci &= ~htons(VLAN_PCP_MASK); + flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp + << VLAN_PCP_SHIFT) | VLAN_CFI); + } break; case OFPACT_STRIP_VLAN: @@ -2368,12 +2650,26 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, } break; - case OFPACT_SET_IPV4_DSCP: - /* OpenFlow 1.0 only supports IPv4. */ - if (flow->dl_type == htons(ETH_TYPE_IP)) { + case OFPACT_SET_IP_DSCP: + if (is_ip_any(flow)) { wc->masks.nw_tos |= IP_DSCP_MASK; flow->nw_tos &= ~IP_DSCP_MASK; - flow->nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp; + flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp; + } + break; + + case OFPACT_SET_IP_ECN: + if (is_ip_any(flow)) { + wc->masks.nw_tos |= IP_ECN_MASK; + flow->nw_tos &= ~IP_ECN_MASK; + flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn; + } + break; + + case OFPACT_SET_IP_TTL: + if (is_ip_any(flow)) { + wc->masks.nw_ttl = 0xff; + flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl; } break; @@ -2417,6 +2713,20 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow, wc); break; + 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) + || flow->mpls_lse)) { + mf_set_flow_value(mf, &set_field->value, flow); + } + break; + case OFPACT_STACK_PUSH: nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc, &ctx->stack); @@ -2441,6 +2751,20 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, } break; + case OFPACT_SET_MPLS_LABEL: + if (compose_set_mpls_label_action(ctx, + ofpact_get_SET_MPLS_LABEL(a)->label)) { + return; + } + break; + + case OFPACT_SET_MPLS_TC: + if (compose_set_mpls_tc_action(ctx, + ofpact_get_SET_MPLS_TC(a)->tc)) { + return; + } + break; + case OFPACT_SET_MPLS_TTL: if (compose_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl)) { @@ -2510,7 +2834,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_GOTO_TABLE: { - /* It is assumed that goto-table is the last action. */ struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a); ovs_assert(ctx->table_id < ogt->table_id); @@ -2529,7 +2852,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, void xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto, const struct flow *flow, struct rule_dpif *rule, - uint8_t tcp_flags, const struct ofpbuf *packet) + uint16_t tcp_flags, const struct ofpbuf *packet) { xin->ofproto = ofproto; xin->flow = *flow; @@ -2762,7 +3085,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) wc->masks.nw_frag |= FLOW_NW_FRAG_MASK; tnl_may_send = tnl_xlate_init(&ctx.base_flow, flow, wc); - if (ctx.xbridge->has_netflow) { + if (ctx.xbridge->netflow) { netflow_mask_wc(flow, wc); } @@ -2873,11 +3196,11 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) if (nl_attr_oversized(ctx.xout->odp_actions.size)) { /* These datapath actions are too big for a Netlink attribute, so we - * can't execute them. */ - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - - VLOG_ERR_RL(&rl, "discarding oversize datapath actions"); - ofpbuf_clear(&ctx.xout->odp_actions); + * 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 + * prevent the flow from being installed. */ + COVERAGE_INC(xlate_actions_oversize); + ctx.xout->slow |= SLOW_ACTION; } ofpbuf_uninit(&ctx.stack); @@ -2885,8 +3208,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout) /* Clear the metadata and register wildcard masks, because we won't * use non-header fields as part of the cache. */ - memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata); - memset(&wc->masks.regs, 0, sizeof wc->masks.regs); + flow_wildcards_clear_non_packet_fields(wc); out: rule_actions_unref(actions);