The very long function compose_output_action__() has been re-factored to make
the different cases for output to patch-port, native tunnel port, kernel tunnel
port, recirculation, or termination of a native tunnel at output to LOCAL port
clearer. Larger, self-contained blocks have been split out into separate
functions.

Signed-off-by; Jan Scheurich <jan.scheur...@ericsson.com>
Co-authored-by: Zoltan Balogh <zoltan.bal...@ericsson.com>
---
 ofproto/ofproto-dpif-xlate.c | 432 +++++++++++++++++++++++--------------------
 1 file changed, 230 insertions(+), 202 deletions(-)

diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 72dabf8..a19b9f2 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3072,40 +3072,26 @@ xlate_flow_is_protected(const struct xlate_ctx *ctx, 
const struct flow *flow, co
             xport_in->xbundle->protected && xport_out->xbundle->protected);
 }
 
-static void
-compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
-                        const struct xlate_bond_recirc *xr, bool check_stp)
+static bool
+check_output_prerequisites(struct xlate_ctx *ctx,
+                           const struct xport *xport,
+                           struct flow *flow,
+                           bool check_stp)
 {
-    const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
-    struct flow_wildcards *wc = ctx->wc;
-    struct flow *flow = &ctx->xin->flow;
-    struct flow_tnl flow_tnl;
-    ovs_be16 flow_vlan_tci;
-    uint32_t flow_pkt_mark;
-    uint8_t flow_nw_tos;
-    odp_port_t out_port, odp_port;
-    bool tnl_push_pop_send = false;
-    uint8_t dscp;
-
-    /* If 'struct flow' gets additional metadata, we'll need to zero it out
-     * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
-    memset(&flow_tnl, 0, sizeof flow_tnl);
-
     if (!xport) {
         xlate_report(ctx, OFT_WARN, "Nonexistent output port");
-        return;
+        return false;
     } else if (xport->config & OFPUTIL_PC_NO_FWD) {
         xlate_report(ctx, OFT_DETAIL, "OFPPC_NO_FWD set, skipping output");
-        return;
+        return false;
     } else if (ctx->mirror_snaplen != 0 && xport->odp_port == ODPP_NONE) {
         xlate_report(ctx, OFT_WARN,
                      "Mirror truncate to ODPP_NONE, skipping output");
-        return;
+        return false;
     } else if (xlate_flow_is_protected(ctx, flow, xport)) {
         xlate_report(ctx, OFT_WARN,
                      "Flow is between protected ports, skipping output.");
-        return;
+        return false;
     } else if (check_stp) {
         if (is_stp(&ctx->base_flow)) {
             if (!xport_stp_should_forward_bpdu(xport) &&
@@ -3119,7 +3105,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
                                  "RSTP not managing BPDU in this state, "
                                  "skipping bpdu output");
                 }
-                return;
+                return false;
             }
         } else if (!xport_stp_forward_state(xport) ||
                    !xport_rstp_forward_state(xport)) {
@@ -3130,153 +3116,190 @@ compose_output_action__(struct xlate_ctx *ctx, 
ofp_port_t ofp_port,
                 xlate_report(ctx, OFT_WARN,
                              "RSTP not in forwarding state, skipping output");
             }
-            return;
+            return false;
         }
     }
+    return true;
+}
 
-    if (flow->packet_type == PT_ETH && xport->is_layer3 ) {
-        /* Ethernet packet to L3 outport -> pop ethernet header. */
-        flow->packet_type = PACKET_TYPE(OFPHTN_ETHERTYPE, 
ntohs(flow->dl_type));
+static void
+xlate_output_patch_port(struct xlate_ctx *ctx,
+                        const struct xport *xport)
+{
+    const struct xport *peer = xport->peer;
+    struct flow *flow = &ctx->xin->flow;
+    struct flow old_flow = ctx->xin->flow;
+    struct flow_tnl old_flow_tnl_wc = ctx->wc->masks.tunnel;
+    bool old_conntrack = ctx->conntracked;
+    bool old_was_mpls = ctx->was_mpls;
+    ovs_version_t old_version = ctx->xin->tables_version;
+    struct ofpbuf old_stack = ctx->stack;
+    uint8_t new_stack[1024];
+    struct ofpbuf old_action_set = ctx->action_set;
+    struct ovs_list *old_trace = ctx->xin->trace;
+    uint64_t actset_stub[1024 / 8];
+
+    ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
+    ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
+    flow->in_port.ofp_port = peer->ofp_port;
+    flow->metadata = htonll(0);
+    memset(&flow->tunnel, 0, sizeof flow->tunnel);
+    flow->tunnel.metadata.tab = ofproto_get_tun_tab(
+        &peer->xbridge->ofproto->up);
+    ctx->wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
+    memset(flow->regs, 0, sizeof flow->regs);
+    flow->actset_output = OFPP_UNSET;
+    clear_conntrack(ctx);
+    ctx->xin->trace = xlate_report(ctx, OFT_BRIDGE,
+                                   "bridge(\"%s\")", peer->xbridge->name);
+
+    /* When the patch port points to a different bridge, then the mirrors
+     * for that bridge clearly apply independently to the packet, so we
+     * reset the mirror bitmap to zero and then restore it after the packet
+     * returns.
+     *
+     * When the patch port points to the same bridge, this is more of a
+     * design decision: can mirrors be re-applied to the packet after it
+     * re-enters the bridge, or should we treat that as doubly mirroring a
+     * single packet?  The former may be cleaner, since it respects the
+     * model in which a patch port is like a physical cable plugged from
+     * one switch port to another, but the latter may be less surprising to
+     * users.  We take the latter choice, for now at least.  (To use the
+     * former choice, hard-code 'independent_mirrors' to "true".) */
+    mirror_mask_t old_mirrors = ctx->mirrors;
+    bool independent_mirrors = peer->xbridge != ctx->xbridge;
+    if (independent_mirrors) {
+        ctx->mirrors = 0;
+    }
+    ctx->xbridge = peer->xbridge;
+
+    /* The bridge is now known so obtain its table version. */
+    ctx->xin->tables_version
+        = ofproto_dpif_get_tables_version(ctx->xbridge->ofproto);
+
+    if (!process_special(ctx, peer) && may_receive(peer, ctx)) {
+        if (xport_stp_forward_state(peer) && xport_rstp_forward_state(peer)) {
+            xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
+            if (!ctx->freezing) {
+                xlate_action_set(ctx);
+            }
+            if (ctx->freezing) {
+                finish_freezing(ctx);
+            }
+        } else {
+            /* Forwarding is disabled by STP and RSTP.  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->odp_actions->size;
+            mirror_mask_t old_mirrors2 = ctx->mirrors;
+
+            xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
+            ctx->mirrors = old_mirrors2;
+            ctx->base_flow = old_base_flow;
+            ctx->odp_actions->size = old_size;
+
+            /* Undo changes that may have been done for freezing. */
+            ctx_cancel_freeze(ctx);
+        }
     }
-    else if (flow->packet_type != PT_ETH && !xport->is_layer3) {
-        /* L2 outport and non-ethernet packet_type -> add dummy eth header. */
-        flow->packet_type = PT_ETH;
-        memset(&flow->dl_dst, 0,ETH_ADDR_LEN);
-        memset(&flow->dl_src, 0,ETH_ADDR_LEN);
+
+    ctx->xin->trace = old_trace;
+    if (independent_mirrors) {
+        ctx->mirrors = old_mirrors;
     }
+    ctx->xin->flow = old_flow;
+    ctx->xbridge = xport->xbridge;
+    ofpbuf_uninit(&ctx->action_set);
+    ctx->action_set = old_action_set;
+    ofpbuf_uninit(&ctx->stack);
+    ctx->stack = old_stack;
 
-    if (xport->peer) {
-        const struct xport *peer = xport->peer;
-        struct flow old_flow = ctx->xin->flow;
-        struct flow_tnl old_flow_tnl_wc = ctx->wc->masks.tunnel;
-        bool old_conntrack = ctx->conntracked;
-        bool old_was_mpls = ctx->was_mpls;
-        ovs_version_t old_version = ctx->xin->tables_version;
-        struct ofpbuf old_stack = ctx->stack;
-        uint8_t new_stack[1024];
-        struct ofpbuf old_action_set = ctx->action_set;
-        struct ovs_list *old_trace = ctx->xin->trace;
-        uint64_t actset_stub[1024 / 8];
+    /* Restore calling bridge's lookup version. */
+    ctx->xin->tables_version = old_version;
 
-        ofpbuf_use_stub(&ctx->stack, new_stack, sizeof new_stack);
-        ofpbuf_use_stub(&ctx->action_set, actset_stub, sizeof actset_stub);
-        flow->in_port.ofp_port = peer->ofp_port;
-        flow->metadata = htonll(0);
-        memset(&flow->tunnel, 0, sizeof flow->tunnel);
-        flow->tunnel.metadata.tab = ofproto_get_tun_tab(
-            &peer->xbridge->ofproto->up);
-        ctx->wc->masks.tunnel.metadata.tab = flow->tunnel.metadata.tab;
-        memset(flow->regs, 0, sizeof flow->regs);
-        flow->actset_output = OFPP_UNSET;
-        clear_conntrack(ctx);
-        ctx->xin->trace = xlate_report(ctx, OFT_BRIDGE,
-                                       "bridge(\"%s\")", peer->xbridge->name);
-
-        /* When the patch port points to a different bridge, then the mirrors
-         * for that bridge clearly apply independently to the packet, so we
-         * reset the mirror bitmap to zero and then restore it after the packet
-         * returns.
-         *
-         * When the patch port points to the same bridge, this is more of a
-         * design decision: can mirrors be re-applied to the packet after it
-         * re-enters the bridge, or should we treat that as doubly mirroring a
-         * single packet?  The former may be cleaner, since it respects the
-         * model in which a patch port is like a physical cable plugged from
-         * one switch port to another, but the latter may be less surprising to
-         * users.  We take the latter choice, for now at least.  (To use the
-         * former choice, hard-code 'independent_mirrors' to "true".) */
-        mirror_mask_t old_mirrors = ctx->mirrors;
-        bool independent_mirrors = peer->xbridge != ctx->xbridge;
-        if (independent_mirrors) {
-            ctx->mirrors = 0;
-        }
-        ctx->xbridge = peer->xbridge;
-
-        /* The bridge is now known so obtain its table version. */
-        ctx->xin->tables_version
-            = ofproto_dpif_get_tables_version(ctx->xbridge->ofproto);
-
-        if (!process_special(ctx, peer) && may_receive(peer, ctx)) {
-            if (xport_stp_forward_state(peer) && 
xport_rstp_forward_state(peer)) {
-                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
-                if (!ctx->freezing) {
-                    xlate_action_set(ctx);
-                }
-                if (ctx->freezing) {
-                    finish_freezing(ctx);
-                }
-            } else {
-                /* Forwarding is disabled by STP and RSTP.  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->odp_actions->size;
-                mirror_mask_t old_mirrors2 = ctx->mirrors;
-
-                xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
-                ctx->mirrors = old_mirrors2;
-                ctx->base_flow = old_base_flow;
-                ctx->odp_actions->size = old_size;
-
-                /* Undo changes that may have been done for freezing. */
-                ctx_cancel_freeze(ctx);
-            }
-        }
+    /* Since this packet came in on a patch port (from the perspective of
+     * the peer bridge), it cannot have useful tunnel information. As a
+     * result, any wildcards generated on that tunnel also cannot be valid.
+     * The tunnel wildcards must be restored to their original version since
+     * the peer bridge uses a separate tunnel metadata table and therefore
+     * any generated wildcards will be garbage in the context of our
+     * metadata table. */
+    ctx->wc->masks.tunnel = old_flow_tnl_wc;
 
-        ctx->xin->trace = old_trace;
-        if (independent_mirrors) {
-            ctx->mirrors = old_mirrors;
-        }
-        ctx->xin->flow = old_flow;
-        ctx->xbridge = xport->xbridge;
-        ofpbuf_uninit(&ctx->action_set);
-        ctx->action_set = old_action_set;
-        ofpbuf_uninit(&ctx->stack);
-        ctx->stack = old_stack;
-
-        /* Restore calling bridge's lookup version. */
-        ctx->xin->tables_version = old_version;
-
-        /* Since this packet came in on a patch port (from the perspective of
-         * the peer bridge), it cannot have useful tunnel information. As a
-         * result, any wildcards generated on that tunnel also cannot be valid.
-         * The tunnel wildcards must be restored to their original version 
since
-         * the peer bridge uses a separate tunnel metadata table and therefore
-         * any generated wildcards will be garbage in the context of our
-         * metadata table. */
-        ctx->wc->masks.tunnel = old_flow_tnl_wc;
-
-        /* The peer bridge popping MPLS should have no effect on the original
-         * bridge. */
-        ctx->was_mpls = old_was_mpls;
-
-        /* The peer bridge's conntrack execution should have no effect on the
-         * original bridge. */
-        ctx->conntracked = old_conntrack;
-
-        /* The fact that the peer bridge exits (for any reason) does not mean
-         * that the original bridge should exit.  Specifically, if the peer
-         * bridge freezes translation, the original bridge must continue
-         * processing with the original, not the frozen packet! */
-        ctx->exit = false;
-
-        /* Peer bridge errors do not propagate back. */
-        ctx->error = XLATE_OK;
+    /* The peer bridge popping MPLS should have no effect on the original
+     * bridge. */
+    ctx->was_mpls = old_was_mpls;
 
-        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);
-            }
-        }
-        if (ctx->xin->xcache) {
-            struct xc_entry *entry;
+    /* The peer bridge's conntrack execution should have no effect on the
+     * original bridge. */
+    ctx->conntracked = old_conntrack;
 
-            entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV);
-            entry->dev.tx = netdev_ref(xport->netdev);
-            entry->dev.rx = netdev_ref(peer->netdev);
-            entry->dev.bfd = bfd_ref(peer->bfd);
+    /* The fact that the peer bridge exits (for any reason) does not mean
+     * that the original bridge should exit.  Specifically, if the peer
+     * bridge freezes translation, the original bridge must continue
+     * processing with the original, not the frozen packet! */
+    ctx->exit = false;
+
+    /* Peer bridge errors do not propagate back. */
+    ctx->error = XLATE_OK;
+
+    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);
         }
+    }
+    if (ctx->xin->xcache) {
+        struct xc_entry *entry;
+
+        entry = xlate_cache_add_entry(ctx->xin->xcache, XC_NETDEV);
+        entry->dev.tx = netdev_ref(xport->netdev);
+        entry->dev.rx = netdev_ref(peer->netdev);
+        entry->dev.bfd = bfd_ref(peer->bfd);
+    }
+}
+
+static inline bool
+terminate_native_tunnel(struct xlate_ctx *ctx,
+                        ofp_port_t ofp_port,
+                        struct flow *flow,
+                        struct flow_wildcards *wc,
+                        odp_port_t *tnl_port)
+{
+    *tnl_port = ODPP_NONE;
+
+    /* XXX: Write better Filter for tunnel port. We can use in_port
+     * in tunnel-port flow to avoid these checks completely. */
+    if (ofp_port == OFPP_LOCAL &&
+        ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
+        *tnl_port = tnl_port_map_lookup(flow, wc);
+    }
+
+    return (*tnl_port != ODPP_NONE);
+}
+
+static void
+compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+                        const struct xlate_bond_recirc *xr, bool check_stp)
+{
+    const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
+    struct flow_wildcards *wc = ctx->wc;
+    struct flow *flow = &ctx->xin->flow;
+    struct flow_tnl flow_tnl;
+    ovs_be16 flow_vlan_tci;
+    uint32_t flow_pkt_mark;
+    uint8_t flow_nw_tos;
+    odp_port_t out_port, odp_port, odp_tnl_port;
+    bool is_native_tunnel = false;
+    uint8_t dscp;
+
+    if (!check_output_prerequisites(ctx, xport, flow, check_stp)) {
+        return;
+    }
+
+    if (xport->peer) {
+        xlate_output_patch_port(ctx, xport);
         return;
     }
 
@@ -3284,6 +3307,17 @@ compose_output_action__(struct xlate_ctx *ctx, 
ofp_port_t ofp_port,
     flow_pkt_mark = flow->pkt_mark;
     flow_nw_tos = flow->nw_tos;
 
+    if (flow->packet_type == PT_ETH && xport->is_layer3 ) {
+        /* Ethernet packet to L3 outport -> pop ethernet header. */
+        flow->packet_type = PACKET_TYPE(OFPHTN_ETHERTYPE, 
ntohs(flow->dl_type));
+    }
+    else if (flow->packet_type != PT_ETH && !xport->is_layer3) {
+        /* L2 outport and non-ethernet packet_type -> add dummy eth header. */
+        flow->packet_type = PT_ETH;
+        memset(&flow->dl_dst, 0,ETH_ADDR_LEN);
+        memset(&flow->dl_src, 0,ETH_ADDR_LEN);
+    }
+
     if (count_skb_priorities(xport)) {
         memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
         if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) {
@@ -3322,7 +3356,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
         out_port = odp_port;
         if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
             xlate_report(ctx, OFT_DETAIL, "output to native tunnel");
-            tnl_push_pop_send = true;
+            is_native_tunnel = true;
         } else {
             xlate_report(ctx, OFT_DETAIL, "output to kernel tunnel");
             commit_odp_tunnel_action(flow, &ctx->base_flow, ctx->odp_actions);
@@ -3334,9 +3368,12 @@ compose_output_action__(struct xlate_ctx *ctx, 
ofp_port_t ofp_port,
     }
 
     if (out_port != ODPP_NONE) {
+
+        /* Commit accumulated flow updates before output. */
         xlate_commit_actions(ctx);
 
         if (xr) {
+            /* Recirculate the packet */
             struct ovs_action_hash *act_hash;
 
             /* Hash action. */
@@ -3349,50 +3386,41 @@ compose_output_action__(struct xlate_ctx *ctx, 
ofp_port_t ofp_port,
             /* Recirc action. */
             nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_RECIRC,
                            xr->recirc_id);
-        } else {
-
-            if (tnl_push_pop_send) {
-                build_tunnel_send(ctx, xport, flow, odp_port);
-                flow->tunnel = flow_tnl; /* Restore tunnel metadata */
-            } else {
-                odp_port_t odp_tnl_port = ODPP_NONE;
 
-                /* XXX: Write better Filter for tunnel port. We can use inport
-                * int tunnel-port flow to avoid these checks completely. */
-                if (ofp_port == OFPP_LOCAL &&
-                    ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
+        } else if (is_native_tunnel) {
+            /* Output to native tunnel port. */
+            build_tunnel_send(ctx, xport, flow, odp_port);
+            flow->tunnel = flow_tnl; /* Restore tunnel metadata */
 
-                    odp_tnl_port = tnl_port_map_lookup(flow, wc);
-                }
+        } else if (terminate_native_tunnel(ctx, ofp_port, flow, wc,
+                                           &odp_tnl_port)) {
+            /* Intercept packet to be received on native tunnel port. */
+            nl_msg_put_odp_port(ctx->odp_actions,
+                                OVS_ACTION_ATTR_TUNNEL_POP,
+                                odp_tnl_port);
 
-                if (odp_tnl_port != ODPP_NONE) {
-                    nl_msg_put_odp_port(ctx->odp_actions,
-                                        OVS_ACTION_ATTR_TUNNEL_POP,
-                                        odp_tnl_port);
-                } else {
-                    /* Tunnel push-pop action is not compatible with
-                     * IPFIX action. */
-                    compose_ipfix_action(ctx, out_port);
-
-                    /* Handle truncation of the mirrored packet. */
-                    if (ctx->mirror_snaplen > 0 &&
-                        ctx->mirror_snaplen < UINT16_MAX) {
-                        struct ovs_action_trunc *trunc;
-
-                        trunc = nl_msg_put_unspec_uninit(ctx->odp_actions,
-                                                         OVS_ACTION_ATTR_TRUNC,
-                                                         sizeof *trunc);
-                        trunc->max_len = ctx->mirror_snaplen;
-                        if (!ctx->xbridge->support.trunc) {
-                            ctx->xout->slow |= SLOW_ACTION;
-                        }
-                    }
-
-                    nl_msg_put_odp_port(ctx->odp_actions,
-                                        OVS_ACTION_ATTR_OUTPUT,
-                                        out_port);
+        } else {
+            /* Tunnel push-pop action is not compatible with
+             * IPFIX action. */
+            compose_ipfix_action(ctx, out_port);
+
+            /* Handle truncation of the mirrored packet. */
+            if (ctx->mirror_snaplen > 0 &&
+                    ctx->mirror_snaplen < UINT16_MAX) {
+                struct ovs_action_trunc *trunc;
+
+                trunc = nl_msg_put_unspec_uninit(ctx->odp_actions,
+                                                 OVS_ACTION_ATTR_TRUNC,
+                                                 sizeof *trunc);
+                trunc->max_len = ctx->mirror_snaplen;
+                if (!ctx->xbridge->support.trunc) {
+                    ctx->xout->slow |= SLOW_ACTION;
                 }
             }
+
+            nl_msg_put_odp_port(ctx->odp_actions,
+                                OVS_ACTION_ATTR_OUTPUT,
+                                out_port);
         }
 
         ctx->sflow_odp_port = odp_port;
@@ -3402,8 +3430,8 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
 
     if (mbridge_has_mirrors(ctx->xbridge->mbridge) && xport->xbundle) {
         mirror_packet(ctx, xport->xbundle,
-                      xbundle_mirror_dst(xport->xbundle->xbridge,
-                                         xport->xbundle));
+                xbundle_mirror_dst(xport->xbundle->xbridge,
+                        xport->xbundle));
     }
 
  out:
-- 
1.9.1

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to