On 28 February 2017 at 17:17, Jarno Rajahalme <[email protected]> wrote:
> Add resubmit option to use the Conntrack original direction tuple
> swapped with the corresponding packet header fields during the lookup.
> This could allow the same ACL table be used for admitting return
> and/or related traffic as is used for admitting the original direction
> traffic.
>
> Signed-off-by: Jarno Rajahalme <[email protected]>
> ---
> include/openvswitch/ofp-actions.h | 4 +-
> lib/ofp-actions.c | 82 +++++++++++++++++++------
> ofproto/ofproto-dpif-xlate.c | 68 ++++++++++++++++++---
> tests/ofp-actions.at | 6 ++
> tests/ofproto-dpif.at | 89 +++++++++++++++++++++------
> tests/system-traffic.at | 122
> ++++++++++++++++++++++++++++----------
> utilities/ovs-ofctl.8.in | 19 +++++-
> 7 files changed, 310 insertions(+), 80 deletions(-)
>
> diff --git a/include/openvswitch/ofp-actions.h
> b/include/openvswitch/ofp-actions.h
> index 53d6b44..5ea0763 100644
> --- a/include/openvswitch/ofp-actions.h
> +++ b/include/openvswitch/ofp-actions.h
> @@ -640,11 +640,13 @@ struct ofpact_nat {
>
> /* OFPACT_RESUBMIT.
> *
> - * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
> + * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE, NXAST_RESUBMIT_TABLE_CT. */
> struct ofpact_resubmit {
> struct ofpact ofpact;
> ofp_port_t in_port;
> uint8_t table_id;
> + bool with_ct_orig; /* Resubmit with Conntrack original direction tuple
> + * fields in place of IP header fields. */
> };
>
> /* Bits for 'flags' in struct nx_action_learn.
> diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> index 2869e0f..4d35a77 100644
> --- a/lib/ofp-actions.c
> +++ b/lib/ofp-actions.c
> @@ -265,6 +265,8 @@ enum ofp_raw_action_type {
> NXAST_RAW_RESUBMIT,
> /* NX1.0+(14): struct nx_action_resubmit. */
> NXAST_RAW_RESUBMIT_TABLE,
> + /* NX1.0+(44): struct nx_action_resubmit. */
> + NXAST_RAW_RESUBMIT_TABLE_CT,
>
> /* NX1.0+(2): uint32_t. */
> NXAST_RAW_SET_TUNNEL,
> @@ -3850,19 +3852,20 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout
> *a, struct ds *s)
> ds_put_format(s, "%s)%s", colors.paren, colors.end);
> }
>
> -/* Action structures for NXAST_RESUBMIT and NXAST_RESUBMIT_TABLE.
> +/* Action structures for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE, and
> + * NXAST_RESUBMIT_TABLE_CT.
> *
> * These actions search one of the switch's flow tables:
> *
> - * - For NXAST_RESUBMIT_TABLE only, if the 'table' member is not 255, then
> - * it specifies the table to search.
> + * - For NXAST_RESUBMIT_TABLE and NXAST_RESUBMIT_TABLE_CT only, if the
> + * 'table' member is not 255, then it specifies the table to search.
'only' is a bit superfluous - it's now for 2 of the 3 cases.
> *
> - * - Otherwise (for NXAST_RESUBMIT_TABLE with a 'table' of 255, or for
> - * NXAST_RESUBMIT regardless of 'table'), it searches the current flow
> - * table, that is, the OpenFlow flow table that contains the flow from
> - * which this action was obtained. If this action did not come from a
> - * flow table (e.g. it came from an OFPT_PACKET_OUT message), then
> table 0
> - * is the current table.
> + * - Otherwise (for NXAST_RESUBMIT_TABLE or NXAST_RESUBMIT_TABLE_CT with a
> + * 'table' of 255, or for NXAST_RESUBMIT regardless of 'table'), it
> + * searches the current flow table, that is, the OpenFlow flow table
> that
> + * contains the flow from which this action was obtained. If this
> action
> + * did not come from a flow table (e.g. it came from an OFPT_PACKET_OUT
> + * message), then table 0 is the current table.
> *
> * The flow table lookup uses a flow that may be slightly modified from the
> * original lookup:
> @@ -3870,9 +3873,12 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a,
> struct ds *s)
> * - For NXAST_RESUBMIT, the 'in_port' member of struct nx_action_resubmit
> * is used as the flow's in_port.
> *
> - * - For NXAST_RESUBMIT_TABLE, if the 'in_port' member is not
> OFPP_IN_PORT,
> - * then its value is used as the flow's in_port. Otherwise, the
> original
> - * in_port is used.
> + * - For NXAST_RESUBMIT_TABLE and NXAST_RESUBMIT_TABLE_CT, if the
> 'in_port'
> + * member is not OFPP_IN_PORT, then its value is used as the flow's
> + * in_port. Otherwise, the original in_port is used.
> + *
> + * - For NXAST_RESUBMIT_TABLE_CT the Conntrack 5-tuple fields are used as
> + * the packets IP header fields during the lookup.
> *
> * - If actions that modify the flow (e.g. OFPAT_SET_VLAN_VID) precede the
> * resubmit action, then the flow is updated with the new values.
> @@ -3905,11 +3911,12 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout
> *a, struct ds *s)
> * a total limit of 4,096 resubmits per flow translation (earlier
> versions
> * did not impose any total limit).
> *
> - * NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE requires
> - * 'pad' to be all-bits-zero.
> + * NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE and
> + * NXAST_RESUBMIT_TABLE_CT require 'pad' to be all-bits-zero.
> *
> * Open vSwitch 1.0.1 and earlier did not support recursion. Open vSwitch
> - * before 1.2.90 did not support NXAST_RESUBMIT_TABLE.
> + * before 1.2.90 did not support NXAST_RESUBMIT_TABLE. Open vSwitch before
> + * 2.7.0 did not support NXAST_RESUBMIT_TABLE_CT.
2.8.0.
> */
> struct nx_action_resubmit {
> ovs_be16 type; /* OFPAT_VENDOR. */
> @@ -3954,6 +3961,21 @@ decode_NXAST_RAW_RESUBMIT_TABLE(const struct
> nx_action_resubmit *nar,
> return 0;
> }
>
> +static enum ofperr
> +decode_NXAST_RAW_RESUBMIT_TABLE_CT(const struct nx_action_resubmit *nar,
> + enum ofp_version ofp_version OVS_UNUSED,
> + struct ofpbuf *out)
> +{
> + enum ofperr error = decode_NXAST_RAW_RESUBMIT_TABLE(nar, ofp_version,
> out);
> + if (error) {
> + return error;
> + }
> + struct ofpact_resubmit *resubmit = out->header;
> + resubmit->ofpact.raw = NXAST_RAW_RESUBMIT_TABLE_CT;
> + resubmit->with_ct_orig = true;
> + return 0;
> +}
> +
> static void
> encode_RESUBMIT(const struct ofpact_resubmit *resubmit,
> enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
> @@ -3961,10 +3983,12 @@ encode_RESUBMIT(const struct ofpact_resubmit
> *resubmit,
> uint16_t in_port = ofp_to_u16(resubmit->in_port);
>
> if (resubmit->table_id == 0xff
> - && resubmit->ofpact.raw != NXAST_RAW_RESUBMIT_TABLE) {
> + && resubmit->ofpact.raw == NXAST_RAW_RESUBMIT) {
> put_NXAST_RESUBMIT(out, in_port);
> } else {
> - struct nx_action_resubmit *nar = put_NXAST_RESUBMIT_TABLE(out);
> + struct nx_action_resubmit *nar;
> + nar = resubmit->with_ct_orig
> + ? put_NXAST_RESUBMIT_TABLE_CT(out) :
> put_NXAST_RESUBMIT_TABLE(out);
> nar->table = resubmit->table_id;
> nar->in_port = htons(in_port);
> }
> @@ -3975,7 +3999,7 @@ parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts,
> enum ofputil_protocol *usable_protocols OVS_UNUSED)
> {
> struct ofpact_resubmit *resubmit;
> - char *in_port_s, *table_s;
> + char *in_port_s, *table_s, *ct_s;
>
> resubmit = ofpact_put_RESUBMIT(ofpacts);
>
> @@ -4002,6 +4026,16 @@ parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts,
> resubmit->table_id = 255;
> }
>
> + ct_s = strsep(&arg, ",");
> + if (ct_s && ct_s[0]) {
> + if (strcmp(ct_s, "ct")) {
> + return xasprintf("%s: unknown parameter", ct_s);
> + }
> + resubmit->with_ct_orig = true;
> + } else {
> + resubmit->with_ct_orig = false;
> + }
> +
> if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
> return xstrdup("at least one \"in_port\" or \"table\" must be "
> "specified on resubmit");
> @@ -4024,6 +4058,9 @@ format_RESUBMIT(const struct ofpact_resubmit *a, struct
> ds *s)
> if (a->table_id != 255) {
> ds_put_format(s, "%"PRIu8, a->table_id);
> }
> + if (a->with_ct_orig) {
> + ds_put_cstr(s, ",ct");
> + }
If no table is used, this could serialize two commas.
> ds_put_format(s, "%s)%s", colors.paren, colors.end);
> }
> }
> @@ -7220,9 +7257,16 @@ ofpact_check__(enum ofputil_protocol
> *usable_protocols, struct ofpact *a,
> case OFPACT_SET_TUNNEL:
> case OFPACT_SET_QUEUE:
> case OFPACT_POP_QUEUE:
> - case OFPACT_RESUBMIT:
> return 0;
>
> + case OFPACT_RESUBMIT: {
> + struct ofpact_resubmit *resubmit = ofpact_get_RESUBMIT(a);
> +
> + if (resubmit->with_ct_orig && !is_ct_valid(flow, &match->wc, NULL)) {
> + return OFPERR_OFPBAC_MATCH_INCONSISTENT;
> + }
> + return 0;
> + }
> case OFPACT_FIN_TIMEOUT:
> if (flow->nw_proto != IPPROTO_TCP) {
> inconsistent_match(usable_protocols);
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 0a6b730..257b736 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -483,7 +483,7 @@ static void do_xlate_actions(const struct ofpact *,
> size_t ofpacts_len,
> static void xlate_normal(struct xlate_ctx *);
> static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
> uint8_t table_id, bool may_packet_in,
> - bool honor_table_miss);
> + bool honor_table_miss, bool with_ct_orig);
> static bool input_vid_is_valid(const struct xlate_ctx *,
> uint16_t vid, struct xbundle *);
> static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid);
> @@ -3204,7 +3204,8 @@ compose_output_action__(struct xlate_ctx *ctx,
> ofp_port_t ofp_port,
>
> 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);
> + xlate_table_action(ctx, flow->in_port.ofp_port, 0, true,
> true,
> + false);
> if (!ctx->freezing) {
> xlate_action_set(ctx);
> }
> @@ -3218,7 +3219,8 @@ compose_output_action__(struct xlate_ctx *ctx,
> ofp_port_t ofp_port,
> 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);
> + 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;
> @@ -3473,8 +3475,50 @@ xlate_resubmit_resource_check(struct xlate_ctx *ctx)
> }
>
> static void
> +tuple_swap(struct flow *flow, const struct flow *key)
> +{
> + /* Do not swap if there is no CT tuple. */
> + if (flow == key && flow->ct_nw_proto == 0) {
> + OVS_NOT_REACHED(); /* Prerequisite check should take care of this!
> */
I'm not sure I follow the reasoning behind the flow / key pointer check here.
It seems to me like this function is always called if someone writes
the resubmit(...,ct) parameter, is there a guarantee that they've run
the connection through ct() action before running this action?
> + }
> +
> + uint8_t nw_proto = flow->nw_proto;
> + flow->nw_proto = flow->ct_nw_proto;
> + flow->ct_nw_proto = nw_proto;
> +
> + if (key->dl_type == htons(ETH_TYPE_IP)) {
> + ovs_be32 nw_src = flow->nw_src;
> + flow->nw_src = flow->ct_nw_src;
> + flow->ct_nw_src = nw_src;
> +
> + ovs_be32 nw_dst = flow->nw_dst;
> + flow->nw_dst = flow->ct_nw_dst;
> + flow->ct_nw_dst = nw_dst;
> + } else if (key->dl_type == htons(ETH_TYPE_IPV6)) {
> + struct in6_addr ipv6_src = flow->ipv6_src;
> + flow->ipv6_src = flow->ct_ipv6_src;
> + flow->ct_ipv6_src = ipv6_src;
> +
> + struct in6_addr ipv6_dst = flow->ipv6_dst;
> + flow->ipv6_dst = flow->ct_ipv6_dst;
> + flow->ct_ipv6_dst = ipv6_dst;
> + } else {
> + OVS_NOT_REACHED();
Remind me, does prerequisite check ensure the ethertype will be
IP/IPV6? - I recall CT having something like this, but this is the
resubmit action so I'm not sure we have as many guarantees on our
prerequisites.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev