[ovs-dev] [PATCH/RFC 5/8] ofproto-dpif: MPLS recirculation

Simon Horman horms at verge.net.au
Tue Mar 25 21:24:38 UTC 2014


In some cases an pop MPLS action changes a packet to be a non-mpls packet.
In this case subsequent any L3+ actions require access to portions
of the packet which were not decoded as they were opaque when the
packet was MPLS. Allow such actions to be translated by
first recirculating the packet.

Signed-off-by: Simon Horman <horms at verge.net.au>
---
 ofproto/ofproto-dpif-xlate.c | 178 +++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 172 insertions(+), 6 deletions(-)

diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 8011b8d..4f3e31a 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2209,7 +2209,73 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
     ofpbuf_delete(packet);
 }
 
+// XXX: Also present in bond.c
+#define RECIRC_RULE_PRIORITY 20
+
+// XXX: Put somewhere else
+#define RECIRC_TIMEOUT 60
+
 static void
+compose_recirculate_action(struct xlate_ctx *ctx,
+                           const struct ofpact *ofpacts_base,
+                           const struct ofpact *ofpact_current,
+                           size_t ofpacts_base_len)
+{
+    int error;
+    struct match match;
+    struct ofpbuf ofpacts;
+    struct rule *rule;
+    uint32_t id;
+    unsigned len;
+
+    if (ctx->rule) {
+        id = rule_dpif_get_recirc_id(ctx->rule);
+    } else {
+        /* XXX: This leaks recirculation ids */
+        id = ofproto_dpif_alloc_recirc_id(ctx->xbridge->ofproto);
+    }
+
+    if (!id) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_ERR_RL(&rl, "Failed to allocate recirculation id");
+        ctx->exit = true;
+        return;
+    }
+
+    /* XXX: Common with update_recirc_rules() */
+    match_init_catchall(&match);
+    /* recirc_id -> metadata to speed up look ups. */
+    match_set_metadata(&match, htonll(id));
+    match_set_recirc_id(&match, id);
+    /* XXX: Use a mask?
+     * match_set_dp_hash_masked(&match, i, BOND_MASK); */
+
+    len = ofpacts_base_len -
+        ((uint8_t *)ofpact_current - (uint8_t *)ofpacts_base);
+
+    ofpbuf_use_const(&ofpacts, ofpact_current, len);
+
+    error = ofproto_dpif_add_internal_flow(ctx->xbridge->ofproto, &match,
+                                           RECIRC_RULE_PRIORITY,
+                                           RECIRC_TIMEOUT, &ofpacts,
+                                           &rule);
+    if (error) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_ERR_RL(&rl, "Failed to add post recirculation flow %s",
+                    match_to_string(&match, 0));
+        ctx->exit = true;
+        return;
+    }
+
+    ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
+                                          &ctx->xout->odp_actions,
+                                          &ctx->xout->wc);
+    /* XXX: Use better hash_alg and hash_bias parameters ? */
+    odp_put_recirc_action(id, OVS_RECIRC_HASH_ALG_NONE, 0,
+                          &ctx->xout->odp_actions);
+}
+
+static bool
 compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls)
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
@@ -2220,6 +2286,16 @@ compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls)
 
     n = flow_count_mpls_labels(flow, wc);
     if (!n) {
+        if (flow->nw_ttl == 0 &&
+            (flow->dl_type == htons(ETH_TYPE_IP) ||
+             flow->dl_type == htons(ETH_TYPE_IPV6))) {
+            /* Recirculate if it is an IP packet with a zero ttl.
+             * This may indicate that the packet was previously MPLS
+             * and an MPLS pop action converted it to IP. In this case
+             * recirculating should reveal the IP TTL which is used
+             * as the basis for a new MPLS LSE. */
+            return true;
+        }
         ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
                                               &ctx->xout->odp_actions,
                                               &ctx->xout->wc);
@@ -2232,13 +2308,15 @@ compose_mpls_push_action(struct xlate_ctx *ctx, struct ofpact_push_mpls *mpls)
                          ctx->xbridge->name, FLOW_MAX_MPLS_LABELS);
         }
         ctx->exit = true;
-        return;
+        return false;
     } else if (n >= ctx->xbridge->max_mpls_depth) {
         COVERAGE_INC(xlate_actions_mpls_overflow);
         ctx->xout->slow |= SLOW_ACTION;
     }
 
     flow_push_mpls(flow, n, mpls->ethertype, wc);
+
+    return false;
 }
 
 static void
@@ -2599,6 +2677,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 {
     struct flow_wildcards *wc = &ctx->xout->wc;
     struct flow *flow = &ctx->xin->flow;
+    bool may_xlate_l3_actions = true;
     const struct ofpact *a;
 
     /* dl_type already in the mask, not set below. */
@@ -2607,6 +2686,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         struct ofpact_controller *controller;
         const struct ofpact_metadata *metadata;
         const struct ofpact_set_field *set_field;
+        const struct ofpact_reg_load *load;
+        const struct ofpact_reg_move *move;
         const struct mf_field *mf;
 
         if (ctx->exit) {
@@ -2637,6 +2718,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_SET_VLAN_VID:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
             if (flow->vlan_tci & htons(VLAN_CFI) ||
                 ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
@@ -2647,6 +2731,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_SET_VLAN_PCP:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
             if (flow->vlan_tci & htons(VLAN_CFI) ||
                 ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
@@ -2658,12 +2745,18 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         case OFPACT_STRIP_VLAN:
             memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             flow->vlan_tci = htons(0);
             break;
 
         case OFPACT_PUSH_VLAN:
             /* XXX 802.1AD(QinQ) */
             memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             flow->vlan_tci = htons(VLAN_CFI);
             break;
 
@@ -2680,6 +2773,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         case OFPACT_SET_IPV4_SRC:
             if (flow->dl_type == htons(ETH_TYPE_IP)) {
                 memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+                if (!may_xlate_l3_actions) {
+                    goto recirculate;
+                }
                 flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
             }
             break;
@@ -2687,11 +2783,17 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         case OFPACT_SET_IPV4_DST:
             if (flow->dl_type == htons(ETH_TYPE_IP)) {
                 memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+                if (!may_xlate_l3_actions) {
+                    goto recirculate;
+                }
                 flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
             }
             break;
 
         case OFPACT_SET_IP_DSCP:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             if (is_ip_any(flow)) {
                 wc->masks.nw_tos |= IP_DSCP_MASK;
                 flow->nw_tos &= ~IP_DSCP_MASK;
@@ -2718,6 +2820,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             if (is_ip_any(flow)) {
                 memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
                 memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+                if (!may_xlate_l3_actions) {
+                    goto recirculate;
+                }
                 flow->tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
             }
             break;
@@ -2726,11 +2831,17 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             if (is_ip_any(flow)) {
                 memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
                 memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+                if (!may_xlate_l3_actions) {
+                    goto recirculate;
+                }
                 flow->tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
             }
             break;
 
         case OFPACT_RESUBMIT:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
             break;
 
@@ -2747,16 +2858,33 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_REG_MOVE:
-            nxm_execute_reg_move(ofpact_get_REG_MOVE(a), flow, wc);
+            move = ofpact_get_REG_MOVE(a);
+            mf = move->dst.field;
+
+            if (!may_xlate_l3_actions && mf_is_l3_or_higher(mf)) {
+                goto recirculate;
+            }
+            nxm_execute_reg_move(move, flow, wc);
             break;
 
         case OFPACT_REG_LOAD:
-            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow, wc);
+            load = ofpact_get_REG_LOAD(a);
+            mf = load->dst.field;
+
+            if (!may_xlate_l3_actions && mf_is_l3_or_higher(mf)) {
+                goto recirculate;
+            }
+            nxm_execute_reg_load(load, flow, wc);
             break;
 
         case OFPACT_SET_FIELD:
             set_field = ofpact_get_SET_FIELD(a);
             mf = set_field->field;
+
+            if (!may_xlate_l3_actions && mf_is_l3_or_higher(mf)) {
+                goto recirculate;
+            }
+
             mf_mask_field_and_prereqs(mf, &wc->masks);
 
             /* Set field action only ever overwrites packet's outermost
@@ -2769,21 +2897,38 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_STACK_PUSH:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc,
                                    &ctx->stack);
             break;
 
         case OFPACT_STACK_POP:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             nxm_execute_stack_pop(ofpact_get_STACK_POP(a), flow, wc,
                                   &ctx->stack);
             break;
 
         case OFPACT_PUSH_MPLS:
-            compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a));
+            if (compose_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a))) {
+                goto recirculate;
+            }
             break;
 
         case OFPACT_POP_MPLS:
+            if (!eth_type_mpls(ctx->xin->flow.dl_type)) {
+                return;
+            }
             compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
+            if (ctx->xbridge->enable_recirc &&
+                (ctx->xin->flow.dl_type == htons(ETH_TYPE_IP) ||
+                 ctx->xin->flow.dl_type == htons(ETH_TYPE_IPV6) ||
+                 eth_type_mpls(ctx->xin->flow.dl_type))) {
+                may_xlate_l3_actions = false;
+            }
             break;
 
         case OFPACT_SET_MPLS_LABEL:
@@ -2807,7 +2952,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         case OFPACT_DEC_TTL:
             wc->masks.nw_ttl = 0xff;
-            if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
+
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            } else if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
                 return;
             }
             break;
@@ -2817,10 +2965,16 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_MULTIPATH:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             multipath_execute(ofpact_get_MULTIPATH(a), flow, wc);
             break;
 
         case OFPACT_BUNDLE:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
             break;
 
@@ -2829,6 +2983,9 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_LEARN:
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
             xlate_learn_action(ctx, ofpact_get_LEARN(a));
             break;
 
@@ -2861,8 +3018,12 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_GOTO_TABLE: {
-            struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
+            struct ofpact_goto_table *ogt;
 
+            if (!may_xlate_l3_actions) {
+                goto recirculate;
+            }
+            ogt = ofpact_get_GOTO_TABLE(a);
             ovs_assert(ctx->table_id < ogt->table_id);
             xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
                                ogt->table_id, true, true);
@@ -2874,6 +3035,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
         }
     }
+
+    return;
+
+recirculate:
+    compose_recirculate_action(ctx, ofpacts, a, ofpacts_len);
 }
 
 void
-- 
1.8.4




More information about the dev mailing list