On 28 February 2017 at 17:17, Jarno Rajahalme <ja...@ovn.org> 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 <ja...@ovn.org> > --- > 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 d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev