[ovs-dev] [PATCH 2/4] Translation of generic encap and decap actions

Zoltán Balogh zoltan.balogh at ericsson.com
Fri Jun 30 15:29:32 UTC 2017


From: Jan Scheurich <jan.scheurich at ericsson.com>

This commit implements a skeleton for the translation of generic encap
and decap actions in ofproto-dpif and adds support to encap and decap an
Ethernet header.

In general translation of encap commits pending actions and then rewrites
struct flow in accordance with the new packet type and header. In the
case of encap(ethernet) it suffices to change the packet type from
(1, Ethertype) to (0,0) and set the dl_type accordingly. A new
pending_encap flag in xlate ctx is set to mark that an corresponding
datapath encap action must be triggered at the next commit. In the
case of encap(ethernet) ofproto generetas a push_eth action.

The general case for translation of decap() is to emit a datapath action
to decap the current outermost header and then recirculate the packet
to reparse the inner headers. In the special case of an Ethernet packet,
decap() just changes the packet type from (0,0) to (1, dl_type) without
a need to recirculate. The emission of the pop_eth action for the
datapath is postponed to the next commit.

Hence encap(ethernet) and decap() on an Ethernet packet are OF octions
that only incur a cost in the dataplane when a modifed packet is
actually committed, e.g. because it is sent out. They can freely be
used for normalizing the packet type in the OF pipeline without
degrading performance.

Signed-off-by: Jan Scheurich <jan.scheurich at ericsson.com>
Signed-off-by: Yi Yang <yi.y.yang at intel.com>
---
 lib/odp-util.c               | 84 +++++++++++++++++++++++++-------------
 lib/odp-util.h               |  3 +-
 ofproto/ofproto-dpif-xlate.c | 97 +++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 153 insertions(+), 31 deletions(-)

diff --git a/lib/odp-util.c b/lib/odp-util.c
index f4c0b66..d9fee4f 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -5862,13 +5862,17 @@ put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow)
 }
 
 static void
-commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
-                             struct ofpbuf *odp_actions,
-                             struct flow_wildcards *wc,
-                             bool use_masked)
+commit_set_ether_action(const struct flow *flow, struct flow *base_flow,
+                        struct ofpbuf *odp_actions,
+                        struct flow_wildcards *wc,
+                        bool use_masked)
 {
     struct ovs_key_ethernet key, base, mask;
 
+    if (flow->packet_type != htonl(PT_ETH)) {
+        return;
+    }
+
     get_ethernet_key(flow, &key);
     get_ethernet_key(base_flow, &base);
     get_ethernet_key(&wc->masks, &mask);
@@ -5881,29 +5885,6 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
 }
 
 static void
-commit_ether_action(const struct flow *flow, struct flow *base_flow,
-                    struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                    bool use_masked)
-{
-    if (flow->packet_type == htonl(PT_ETH)) {
-        if (base_flow->packet_type != htonl(PT_ETH)) {
-            odp_put_push_eth_action(odp_actions, &flow->dl_src, &flow->dl_dst);
-            base_flow->packet_type = flow->packet_type;
-            base_flow->dl_src = flow->dl_src;
-            base_flow->dl_dst = flow->dl_dst;
-        } else {
-            commit_set_ether_addr_action(flow, base_flow, odp_actions, wc,
-                                         use_masked);
-        }
-    } else {
-        if (base_flow->packet_type == htonl(PT_ETH)) {
-            odp_put_pop_eth_action(odp_actions);
-            base_flow->packet_type = flow->packet_type;
-        }
-    }
-}
-
-static void
 commit_vlan_action(const struct flow* flow, struct flow *base,
                    struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
@@ -6340,6 +6321,50 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
     }
 }
 
+static void
+commit_packet_type_change(const struct flow *flow,
+                          struct flow *base_flow,
+                          struct ofpbuf *odp_actions,
+                          struct flow_wildcards *wc,
+                          bool pending_encap)
+{
+    if (flow->packet_type == base_flow->packet_type) {
+        return;
+    }
+
+    if (pending_encap) {
+        switch (ntohl(flow->packet_type)) {
+        case PT_ETH: {
+            /* push_eth */
+            odp_put_push_eth_action(odp_actions, &flow->dl_src,
+                    &flow->dl_dst);
+            base_flow->packet_type = flow->packet_type;
+            base_flow->dl_src = flow->dl_src;
+            base_flow->dl_dst = flow->dl_dst;
+            break;
+        }
+        default:
+            /* Only the above protocols are supported for encap. The check
+             * is done at action decoding. */
+            OVS_NOT_REACHED();
+        }
+    } else {
+        if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE &&
+            base_flow->packet_type == htonl(PT_ETH)) {
+            /* pop_eth */
+            odp_put_pop_eth_action(odp_actions);
+            base_flow->packet_type = flow->packet_type;
+            base_flow->dl_src = eth_addr_zero;
+            base_flow->dl_dst = eth_addr_zero;
+        } else {
+            /* All other cases are handled through recirculation. */
+            OVS_NOT_REACHED();
+        }
+    }
+
+    wc->masks.packet_type = OVS_BE32_MAX;
+}
+
 /* If any of the flow key data that ODP actions can modify are different in
  * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
  * key from 'base' into 'flow', and then changes 'base' the same way.  Does not
@@ -6352,12 +6377,13 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
 enum slow_path_reason
 commit_odp_actions(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions, struct flow_wildcards *wc,
-                   bool use_masked)
+                   bool use_masked, bool pending_encap)
 {
     enum slow_path_reason slow1, slow2;
     bool mpls_done = false;
 
-    commit_ether_action(flow, base, odp_actions, wc, use_masked);
+    commit_packet_type_change(flow, base, odp_actions, wc, pending_encap);
+    commit_set_ether_action(flow, base, odp_actions, wc, use_masked);
     /* Make packet a non-MPLS packet before committing L3/4 actions,
      * which would otherwise do nothing. */
     if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index fe183a9..f01c069 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -274,7 +274,8 @@ enum slow_path_reason commit_odp_actions(const struct flow *,
                                          struct flow *base,
                                          struct ofpbuf *odp_actions,
                                          struct flow_wildcards *wc,
-                                         bool use_masked);
+                                         bool use_masked,
+                                         bool pending_encap);
 

 /* ofproto-dpif interface.
  *
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index e2247c8..575c59e 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -234,6 +234,8 @@ struct xlate_ctx {
     bool in_action_set;         /* Currently translating action_set, if true. */
     bool in_packet_out;         /* Currently translating a packet_out msg, if
                                  * true. */
+    bool pending_encap;         /* Waiting to commit a pending encap
+                                 * action, if true. */
 
     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
     ovs_be64 rule_cookie;       /* Cookie of the rule being translated. */
@@ -3233,7 +3235,8 @@ xlate_commit_actions(struct xlate_ctx *ctx)
 
     ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
                                           ctx->odp_actions, ctx->wc,
-                                          use_masked);
+                                          use_masked, ctx->pending_encap);
+    ctx->pending_encap = false;
 }
 
 static void
@@ -5431,6 +5434,85 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
 }
 
 static void
+rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
+                            struct flow *flow,
+                            struct flow_wildcards *wc)
+{
+    wc->masks.packet_type = OVS_BE32_MAX;
+    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+        /* Only adjust the packet_type and zero the dummy Ethernet addresses. */
+        ovs_be16 ethertype = pt_ns_type_be(flow->packet_type);
+        flow->packet_type = htonl(PT_ETH);
+        flow->dl_src = eth_addr_zero;
+        flow->dl_dst = eth_addr_zero;
+        flow->dl_type = ethertype;
+    } else {
+        xlate_report_debug(ctx, OFT_ACTION,
+                           "encap(ethernet) unsupported for packet type "
+                           "ethernet");
+        /* TODO: Error handling: drop packet. */
+        ctx->error = 1;
+    }
+}
+
+static void
+xlate_generic_encap_action(struct xlate_ctx *ctx,
+                           const struct ofpact_encap *encap)
+{
+    struct flow *flow = &ctx->xin->flow;
+    struct flow_wildcards *wc = ctx->wc;
+
+    /* Ensure that any pending actions on the inner packet are applied before
+     * rewriting the flow */
+    xlate_commit_actions(ctx);
+
+    /* Rewrite the flow to reflect the effect of pushing the new encap header. */
+    switch (ntohl(encap->new_pkt_type)) {
+        case PT_ETH:
+            rewrite_flow_encap_ethernet(ctx, flow, wc);
+            break;
+        default:
+            /* TODO: Error handling: Should not happen if the PT is checked
+             * at decoding */
+            break;
+    }
+
+    if (!ctx->error) {
+        /* The actual encap datapath action will be generated at next commit. */
+        ctx->pending_encap = true;
+    }
+}
+
+/* Returns true if packet must be recirculated after decapsulation. */
+static bool
+xlate_generic_decap_action(struct xlate_ctx *ctx,
+                           const struct ofpact_decap *decap OVS_UNUSED)
+{
+    struct flow *flow = &ctx->xin->flow;
+
+    /* Ensure that any pending actions on the current packet are applied
+     * before generating the decap action. */
+    xlate_commit_actions(ctx);
+
+    /* We assume for now that the new_pkt_type is PT_USE_NEXT_PROTO. */
+    switch (ntohl(flow->packet_type)) {
+        case PT_ETH:
+            /* Just change the packet_type.
+             * Delay generating pop_eth to the next commit. */
+            flow->packet_type = htonl(PACKET_TYPE(OFPHTN_ETHERTYPE,
+                                                  ntohs(flow->dl_type)));
+            return false;
+        default:
+            xlate_report_debug(ctx, OFT_ACTION,
+                               "decap() for unsupported packet type %x",
+                               ntohl(flow->packet_type));
+            /* TODO: Error handling: drop packet. */
+            ctx->error = 1;
+            return false;
+    }
+}
+
+static void
 recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
 {
     /* No need to recirculate if already exiting. */
@@ -5922,7 +6004,18 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_ENCAP:
+            xlate_generic_encap_action(ctx, ofpact_get_ENCAP(a));
+            break;
+
         case OFPACT_DECAP: {
+            bool recirc_needed =
+                    xlate_generic_decap_action(ctx, ofpact_get_DECAP(a));
+            if (!ctx->error && recirc_needed) {
+                /* Recirculate for parsing of inner packet. */
+                ctx_trigger_freeze(ctx);
+                /* Then continue with next action. */
+                a = ofpact_next(a);
+            }
             break;
         }
 
@@ -6255,6 +6348,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         .in_group = false,
         .in_action_set = false,
         .in_packet_out = xin->in_packet_out,
+        .pending_encap = false,
 
         .table_id = 0,
         .rule_cookie = OVS_BE64_MAX,
@@ -6413,6 +6507,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         flow->packet_type = htonl(PT_ETH);
         flow->dl_src = eth_addr_zero;
         flow->dl_dst = eth_addr_zero;
+        ctx.pending_encap = true;
     }
 
     if (!xin->ofpacts && !ctx.rule) {
-- 
1.9.1



More information about the dev mailing list