From: Jan Scheurich <jan.scheur...@ericsson.com> 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 | 435 +++++++++++++++++++++++-------------------- 1 file changed, 235 insertions(+), 200 deletions(-) diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 64a33ba..7dc5220 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -3258,39 +3258,28 @@ 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; - union flow_vlan_hdr flow_vlans[FLOW_MAX_VLAN_HEADERS]; - 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 == 39); - 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) && @@ -3304,7 +3293,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->cfm && cfm_should_process_flow(xport->cfm, flow, wc)) || (xport->bfd && bfd_should_process_flow(xport->bfd, flow, @@ -3319,162 +3308,214 @@ 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 == htonl(PT_ETH) && xport->is_layer3 ) { - /* Ethernet packet to L3 outport -> pop ethernet header. */ - flow->packet_type = PACKET_TYPE_BE(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, + false); + 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, + false); + 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 != htonl(PT_ETH) && !xport->is_layer3) { - /* L2 outport and non-ethernet packet_type -> add dummy eth header. */ - flow->packet_type = htonl(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, - false); - 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, - false); - 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; + union flow_vlan_hdr flow_vlans[FLOW_MAX_VLAN_HEADERS]; + uint8_t flow_nw_tos; + odp_port_t out_port, odp_port, odp_tnl_port; + bool is_native_tunnel = 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 == 39); + memset(&flow_tnl, 0, sizeof flow_tnl); + + if (!check_output_prerequisites(ctx, xport, flow, check_stp)) { + return; + } + + if (xport->peer) { + xlate_output_patch_port(ctx, xport); return; } memcpy(flow_vlans, flow->vlans, sizeof flow_vlans); flow_nw_tos = flow->nw_tos; + if (flow->packet_type == htonl(PT_ETH) && xport->is_layer3 ) { + /* Ethernet packet to L3 outport -> pop ethernet header. */ + flow->packet_type = htonl(PACKET_TYPE(OFPHTN_ETHERTYPE, + ntohs(flow->dl_type))); + } + else if (flow->packet_type != htonl(PT_ETH) && !xport->is_layer3) { + /* L2 outport and non-ethernet packet_type -> add dummy eth header. */ + flow->packet_type = htonl(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)) { @@ -3513,7 +3554,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); @@ -3525,9 +3566,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. */ @@ -3540,50 +3584,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)) { - - odp_tnl_port = tnl_port_map_lookup(flow, wc); - } + } 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 */ - 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; - } - } + } 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); - 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; -- 2.7.4 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev