> On Sep 9, 2015, at 7:00 PM, Joe Stringer <joestrin...@nicira.com> wrote:
> 
> 

(snip)

> diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
> b/datapath/linux/compat/include/linux/openvswitch.h
> index 578cd88..69bdf32 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -343,6 +343,8 @@ enum ovs_key_attr {
>       OVS_KEY_ATTR_MPLS,      /* array of struct ovs_key_mpls.
>                                * The implementation may restrict
>                                * the accepted length of the array. */
> +     OVS_KEY_ATTR_CT_STATE,  /* u8 bitmask of OVS_CS_F_* */
> +     OVS_KEY_ATTR_CT_ZONE,   /* u16 connection tracking zone. */
> 
> #ifdef __KERNEL__
>       /* Only used within kernel data path. */
> @@ -456,6 +458,15 @@ struct ovs_key_nd {
>       __u8    nd_tll[ETH_ALEN];
> };
> 
> +/* OVS_KEY_ATTR_CT_STATE flags */
> +#define OVS_CS_F_NEW               0x01 /* Beginning of a new connection. */
> +#define OVS_CS_F_ESTABLISHED       0x02 /* Part of an existing connection. */
> +#define OVS_CS_F_RELATED           0x04 /* Related to an established
> +                                      * connection. */
> +#define OVS_CS_F_INVALID           0x20 /* Could not track connection. */
> +#define OVS_CS_F_REPLY_DIR         0x40 /* Flow is in the reply direction. */
> +#define OVS_CS_F_TRACKED           0x80 /* Conntrack has occurred. */
> +
> /**
>  * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
>  * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
> @@ -642,6 +653,28 @@ struct ovs_action_push_tnl {
> #endif
> 
> /**
> + * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action.
> + * @OVS_CT_ATTR_FLAGS: u32 connection tracking flags.
> + * @OVS_CT_ATTR_ZONE: u16 connection tracking zone.
> + */
> +enum ovs_ct_attr {
> +     OVS_CT_ATTR_UNSPEC,
> +     OVS_CT_ATTR_FLAGS,      /* u32 bitmask of OVS_CT_F_*. */
> +     OVS_CT_ATTR_ZONE,       /* u16 zone id. */
> +     __OVS_CT_ATTR_MAX
> +};
> +
> +#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
> +
> +/*
> + * OVS_CT_ATTR_FLAGS flags - bitmask of %OVS_CT_F_*
> + * @OVS_CT_F_COMMIT: Commits the flow to the conntrack table. This allows
> + * future packets for the same connection to be identified as 'established'
> + * or 'related'.
> + */
> +#define OVS_CT_F_COMMIT              0x01
> +
> +/**
>  * enum ovs_action_attr - Action types.
>  *
>  * @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
> @@ -672,6 +705,8 @@ struct ovs_action_push_tnl {
>  * indicate the new packet contents. This could potentially still be
>  * %ETH_P_MPLS if the resulting MPLS label stack is not empty.  If there
>  * is no MPLS label stack, as determined by ethertype, no action is taken.
> + * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related
> + * entries in the flow key.
>  *
>  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not 
> all
>  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
> @@ -702,6 +737,7 @@ enum ovs_action_attr {
>                                      * data immediately followed by a mask.
>                                      * The data must be zero for the unmasked
>                                      * bits. */
> +     OVS_ACTION_ATTR_CT,           /* One nested OVS_CT_ATTR_* . */
> 

I guess this should be “Nested OVS_CT_ATTR_*”, as there can be multiple nested 
attributes?

> #ifndef __KERNEL__
>       OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
> 

(snip)

> diff --git a/lib/flow.h b/lib/flow.h
> index d8632ff..2d6d3ee 100644
> --- a/lib/flow.h
> +++ b/lib/flow.h
> @@ -103,8 +103,10 @@ struct flow {
>     union flow_in_port in_port; /* Input port.*/
>     uint32_t recirc_id;         /* Must be exact match. */
>     uint32_t conj_id;           /* Conjunction ID. */
> +    uint16_t ct_zone;           /* Connection Zone. */
> +    uint8_t ct_state;           /* Connection state. */
> +    uint8_t pad1[3];            /* Pad to 64 bits. */
>     ofp_port_t actset_output;   /* Output port in action set. */
> -    uint8_t pad1[6];            /* Pad to 64 bits. */
> 

It would be slightly better to put ct_zone and ct_state right after recirc_id. 
This would make miniflows smaller, as the recirc_id, ct_zone, and ct_state 
would all be in the same miniflow data unit (conj_id and actset_output are 
userspace-only fields, so they would be zeroes for datapath miniflows).

We should probably make ct_state a 16 bit field (uint16_t) in struct flow 
already, for future-proofness, even if the kernel interface is still 8 bits.

Also, you should bump FLOW_WC_SEQ to make sure all the relevant places are 
updated for the new fields.

>     /* L2, Order the same as in the Ethernet header! (64-bit aligned) */
>     struct eth_addr dl_dst;     /* Ethernet destination address. */
> @@ -980,6 +982,8 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const 
> struct flow *flow)
>     md->skb_priority = flow->skb_priority;
>     md->pkt_mark = flow->pkt_mark;
>     md->in_port = flow->in_port;
> +    md->ct_state = flow->ct_state;
> +    md->ct_zone = flow->ct_zone;
> }
> 
> static inline bool is_ip_any(const struct flow *flow)
> 

(snip)

> diff --git a/lib/meta-flow.h b/lib/meta-flow.h
> index 02272ef..44a29e9 100644
> --- a/lib/meta-flow.h
> +++ b/lib/meta-flow.h
> @@ -703,6 +703,51 @@ enum OVS_PACKED_ENUM mf_field_id {
>      */
>     MFF_PKT_MARK,
> 
> +    /* "ct_state".
> +     *
> +     * Connection tracking state.  The field is populated by the NXAST_CT
> +     * action. The following bit values describe the state of the connection:
> +     *
> +     *   - New (0x01): This is the beginning of a new connection.
> +     *   - Established (0x02): This is part of an already existing 
> connection.
> +     *   - Related (0x04): This is a separate connection that is related to 
> an
> +     *                     existing connection.
> +     *   - Invalid (0x20): This flow could not be associated with a 
> connection.
> +     *                     This could be set for a variety of reasons,
> +     *                     including (but not limited to):
> +     *                     - L3/L4 protocol handler is not 
> loaded/unavailable.
> +     *                     - L3/L4 protocol handler determines that the 
> packet
> +     *                       is malformed or invalid for the current FSM 
> stage.
> +     *                     - Packets are unexpected length for protocol.
> +     *   - Reply (0x40): This flow is in the reply direction, ie it did not
> +     *                   initiate the connection.
> +     *   - Tracked (0x80): Connection tracking has occurred.
> +     *

Have we documented anywhere which of the bits are mutually exclusive and how. 
E.g.,
- If -trk, all the other bits are also zeroes (also invalid?)
- new and established are mutually exclusive (+new+est is not possible)
- new and reply are mutually exclusive (reply direction is defined as opposite 
of the initial +new)
- related and new can be set at the same time (but does related remain set for 
the duration of the connection, or is it also mutually exclusive with 
established?)
- apparently related and reply are not mutually exclusive, but when are they 
set at the same time?
- are invalid and tracked mutually exclusive?

Also, what are the possible transitions from one state to another?

IMO this information is invaluable for anyone crafting the flows using these 
bits.

> +     * Type: u8.
> +     * Maskable: bitwise.
> +     * Formatting: conn state.
> +     * Prerequisites: none.
> +     * Access: read-only.
> +     * NXM: NXM_NX_CT_STATE(105) since v2.5.
> +     * OXM: none.
> +     */
> +    MFF_CT_STATE,
> +
> +    /* "ct_zone".
> +     *
> +     * Connection tracking zone.  The field is populated by the
> +     * NXAST_CT action.
> +     *
> +     * Type: be16.
> +     * Maskable: no.
> +     * Formatting: hexadecimal.
> +     * Prerequisites: none.
> +     * Access: read-only.
> +     * NXM: NXM_NX_CT_ZONE(106) since v2.5.
> +     * OXM: none.
> +     */
> +    MFF_CT_ZONE,
> +

(snip)

> diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> index c1e46b5..6ea421f 100644
> --- a/lib/ofp-actions.c
> +++ b/lib/ofp-actions.c
> @@ -286,6 +286,9 @@ enum ofp_raw_action_type {
>     /* NX1.0+(34): struct nx_action_conjunction. */
>     NXAST_RAW_CONJUNCTION,
> 
> +    /* NX1.0+(35): struct nx_action_conntrack, ... */
> +    NXAST_RAW_CT,
> +
> /* ## ------------------ ## */
> /* ## Debugging actions. ## */
> /* ## ------------------ ## */
> @@ -346,6 +349,10 @@ static void *ofpact_put_raw(struct ofpbuf *, enum 
> ofp_version,
> static char *OVS_WARN_UNUSED_RESULT ofpacts_parse(
>     char *str, struct ofpbuf *ofpacts, enum ofputil_protocol 
> *usable_protocols,
>     bool allow_instructions, enum ofpact_type outer_action);
> +static enum ofperr ofpacts_pull_openflow_actions__(
> +    struct ofpbuf *openflow, unsigned int actions_len,
> +    enum ofp_version version, uint32_t allowed_ovsinsts,
> +    struct ofpbuf *ofpacts, enum ofpact_type outer_action);
> 
> /* Pull off existing actions or instructions. Used by nesting actions to keep
>  * ofpacts_parse() oblivious of actions nesting. */
> @@ -4448,6 +4455,245 @@ format_DEBUG_RECIRC(const struct ofpact_null *a 
> OVS_UNUSED, struct ds *s)
> {
>     ds_put_cstr(s, "debug_recirc");
> }
> +
> +/* Action structure for NXAST_CT.
> + *
> + * Pass traffic to the connection tracker.
> + *
> + * The "zone" specifies a context within which the tracking is done:
> + *
> + *      If 'zone_src' is nonzero, this specifies that the zone should be
> + *      sourced from a field zone_src[ofs:ofs+nbits]. The format and 
> semantics
> + *      of 'zone_src' and 'zone_ofs_nbits' are similar to those for the
> + *      NXAST_REG_LOAD action. The acceptable nxm_header values for 
> 'zone_src'
> + *      are the same as the acceptable nxm_header values for the 'src' field 
> of
> + *      NXAST_REG_MOVE.
> + *
> + *      If 'zone_src' is zero, then the value of 'zone_imm' will be used as 
> the
> + *      connection tracking zone.
> + *
> + * If "recirc_table" has a value other than NX_CT_RECIRC_NONE, then this 
> field
> + * specifies that the packet should be logically cloned after the packet is

The packet is logically cloned *before* it is sent through the connection 
tracker,
as only the cloned packet gets the ct_state and ct_zone, right?

> + * sent through the connection tracker, and pipeline processing for the 
> cloned
> + * packet will continue in the OpenFlow table specified by this value. The
> + * NXM_NX_CT_* fields will be populated for the subsequent processing. It is
> + * strongly recommended that this table is later than the current table, to
> + * prevent loops.
> + *

‘alg’ and ofpacts are introduced in later patches, and I guess it is too much 
work to not deal with them here yet?

Anyway, a thought about the nested actions just popped in my mind: The fact 
that the actions are nested means that they all must be produced by a single 
OpenFlow rule. I.e., the same rule that does conntrack must also set the 
labels, etc. This means that either there will be a potentially huge number of 
rules with conntrack actions, or the values used must be passed through 
registers (which is kind of redundant). Also, a register may not be wide enough 
for the data in question (e.g., labels).

> + * A standard "resubmit" action is not sufficient to populate NXM_NX_CT_*
> + * fields, since connection tracking occurs outside of the classifier.
> + */
> +struct nx_action_conntrack {
> +    ovs_be16 type;              /* OFPAT_VENDOR. */
> +    ovs_be16 len;               /* At least 24. */
> +    ovs_be32 vendor;            /* NX_VENDOR_ID. */
> +    ovs_be16 subtype;           /* NXAST_CT. */
> +    ovs_be16 flags;             /* Zero or more NX_CT_F_* flags.
> +                                 * Unspecified flag bits must be zero. */
> +    ovs_be32 zone_src;          /* Connection tracking context. */
> +    union {
> +        ovs_be16 zone_ofs_nbits;/* Range to use from source field. */
> +        ovs_be16 zone_imm;      /* Immediate value for zone. */
> +    };
> +    uint8_t recirc_table;       /* Recirculate to a specific table, or
> +                                   NX_CT_RECIRC_NONE for no recirculation. */
> +    uint8_t pad[3];             /* Zeroes */
> +    ovs_be16 alg;               /* Well-known port number for the protocol.
> +                                 * 0 indicates no ALG is required. */
> +    /* Followed by a sequence of ofpact elements, until the end of the action
> +     * is reached. */
> +};
> +OFP_ASSERT(sizeof(struct nx_action_conntrack) == 24);
> +
> +static enum ofperr
> +decode_ct_zone(const struct nx_action_conntrack *nac,
> +               struct ofpact_conntrack *out)
> +{
> +    if (nac->zone_src) {
> +        enum ofperr error;
> +
> +        out->zone_src.field = mf_from_nxm_header(ntohl(nac->zone_src));
> +        out->zone_src.ofs = nxm_decode_ofs(nac->zone_ofs_nbits);
> +        out->zone_src.n_bits = nxm_decode_n_bits(nac->zone_ofs_nbits);
> +        error = mf_check_src(&out->zone_src, NULL);
> +        if (error) {
> +            return error;
> +        }
> +
> +        if (out->zone_src.n_bits != 16) {
> +            VLOG_WARN_RL(&rl, "zone n_bits %d not within valid range 
> [16..16]",
> +                         out->zone_src.n_bits);
> +            return OFPERR_OFPBAC_BAD_SET_LEN;
> +        }
> +    } else {
> +        out->zone_src.field = NULL;
> +        out->zone_imm = ntohs(nac->zone_imm);
> +    }
> +
> +    return 0;
> +}
> +
> +static enum ofperr
> +decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac, struct ofpbuf 
> *out)
> +{
> +    const size_t ct_offset = ofpacts_pull(out);
> +    struct ofpact_conntrack *conntrack;
> +    struct ofpbuf openflow;
> +    int error = 0;
> +
> +    conntrack = ofpact_put_CT(out);
> +    conntrack->flags = ntohs(nac->flags);
> +    error = decode_ct_zone(nac, conntrack);
> +    if (error) {
> +        goto out;
> +    }
> +    conntrack->recirc_table = nac->recirc_table;
> +    conntrack->alg = ntohs(nac->alg);
> +
> +    ofpbuf_pull(out, sizeof(*conntrack));
> +
> +    /* XXX: version */

What about the version?

> +    ofpbuf_use_const(&openflow, nac + 1, ntohs(nac->len) - sizeof(*nac));
> +    error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
> +                                            OFP10_VERSION,
> +                                            1u << 
> OVSINST_OFPIT11_APPLY_ACTIONS,
> +                                            out, OFPACT_CT);
> +    if (error) {
> +        goto out;
> +    }
> +
> +    conntrack = ofpbuf_push_uninit(out, sizeof(*conntrack));
> +    out->header = &conntrack->ofpact;
> +    ofpact_update_len(out, &conntrack->ofpact);
> +
> +out:
> +    ofpbuf_push_uninit(out, ct_offset);
> +    return error;
> +}
> +
> +static void
> +encode_CT(const struct ofpact_conntrack *conntrack,
> +          enum ofp_version ofp_version, struct ofpbuf *out)
> +{
> +    struct nx_action_conntrack *nac;
> +    const size_t ofs = out->size;
> +    size_t len;
> +
> +    nac = put_NXAST_CT(out);
> +    nac->flags = htons(conntrack->flags);
> +    if (conntrack->zone_src.field) {
> +        nac->zone_src = htonl(mf_nxm_header(conntrack->zone_src.field->id));
> +        nac->zone_ofs_nbits = nxm_encode_ofs_nbits(conntrack->zone_src.ofs,
> +                                                   
> conntrack->zone_src.n_bits);
> +    } else {
> +        nac->zone_src = htonl(0);
> +        nac->zone_imm = htons(conntrack->zone_imm);
> +    }
> +    nac->recirc_table = conntrack->recirc_table;
> +    nac->alg = htons(conntrack->alg);
> +
> +    len = ofpacts_put_openflow_actions(conntrack->actions,
> +                                       ofpact_ct_get_action_len(conntrack),
> +                                       out, ofp_version);
> +    len += sizeof(*nac);
> +    nac = ofpbuf_at(out, ofs, sizeof(*nac));
> +    nac->len = htons(len);

nac->len should include the conntrack action itself, too, not just the length 
of the actions?
 
> +}
> +
> +/* Parses 'arg' as the argument to a "ct" action, and appends such an
> + * action to 'ofpacts'.
> + *

(snip)

> diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
> index 818f5c8..38a9547 100644
> --- a/lib/ofp-actions.h
> +++ b/lib/ofp-actions.h
> @@ -106,6 +106,7 @@
>     OFPACT(EXIT,            ofpact_null,        ofpact, "exit")         \
>     OFPACT(SAMPLE,          ofpact_sample,      ofpact, "sample")       \
>     OFPACT(UNROLL_XLATE,    ofpact_unroll_xlate, ofpact, "unroll_xlate") \
> +    OFPACT(CT,              ofpact_conntrack,   ofpact, "ct")           \
>                                                                         \
>     /* Debugging actions.                                               \
>      *                                                                  \
> @@ -471,6 +472,52 @@ BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) 
> % OFPACT_ALIGNTO == 0);
> BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions)
>                   == sizeof(struct ofpact_nest));
> 
> +/* Bits for 'flags' in struct nx_action_conntrack.
> + *
> + * If NX_CT_F_COMMIT is set, then the connection entry is moved from the
> + * unconfirmed to confirmed list in the tracker. */
> +enum nx_conntrack_flags {
> +    NX_CT_F_COMMIT = 1 << 0,
> +};
> +
> +/* Magic value for struct nx_action_conntrack 'recirc_table' field, to 
> specify
> + * that the packet should not be recirculated. This value should commonly be
> + * used in conjunction with the NX_CT_F_COMMIT flag above. */
> +#define NX_CT_RECIRC_NONE 0xFF
> +

Maybe this should be defined as OFPTT_ALL, as that is the only invalid table 
number (0xff), i.e., NX_CT_RECIRC_NONE may not be defined as anything else, 
right?

> +#define CT_MEMBERS                      \
> +    uint16_t flags;                     \
> +    uint16_t zone_imm;                  \
> +    struct mf_subfield zone_src;        \
> +    uint8_t recirc_table;               \
> +    uint16_t alg
> +

Maybe put the struct member first to avoid internal padding (mf_subfield has a 
pointer, so it will be 64-bit aligned on 64-bit systems)?


> +/* Used to perform build-time size checking for ofpact_conntrack. */
> +struct ofpact_ct_size {
> +    CT_MEMBERS;
> +};
> +
> +/* OFPACT_CT.
> + *
> + * Used for NXAST_CT. */
> +struct ofpact_conntrack {
> +    struct ofpact ofpact;
> +    CT_MEMBERS;

This is slightly ugly, seems to be necessary.

> +    uint8_t pad[PAD_SIZE(sizeof(struct ofpact) + sizeof(struct 
> ofpact_ct_size),
> +                         OFPACT_ALIGNTO)];
> +    struct ofpact actions[];
> +};
> +BUILD_ASSERT_DECL(offsetof(struct ofpact_conntrack, actions)
> +                  % OFPACT_ALIGNTO == 0);
> +BUILD_ASSERT_DECL(offsetof(struct ofpact_conntrack, actions)
> +                  == sizeof(struct ofpact_conntrack));
> +
> 

(snip)

> bool dpid_from_string(const char *s, uint64_t *dpidp);
> @@ -710,6 +717,18 @@ struct tcp_header {
> };
> BUILD_ASSERT_DECL(TCP_HEADER_LEN == sizeof(struct tcp_header));
> 
> +/* Connection states */
> +#define CS_NEW               0x01
> +#define CS_ESTABLISHED       0x02
> +#define CS_RELATED           0x04
> +#define CS_INVALID           0x20
> +#define CS_REPLY_DIR         0x40
> +#define CS_TRACKED           0x80
> +

These seem to match the datapath interface flags. Is that an invariant? Which 
ones we use in struct flow?

> +/* Undefined connection state bits. */
> +#define CS_UNSUPPORTED_MASK  0x18
> +#define CS_SUPPORTED_MASK    (~CS_UNSUPPORTED_MASK & 0xFF)
> +
> 

(snip)

> diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h
> index e7d95bf..7754e68 100644
> --- a/ofproto/ofproto-dpif-rid.h
> +++ b/ofproto/ofproto-dpif-rid.h
> @@ -142,6 +142,7 @@ struct recirc_state {
>     struct recirc_metadata metadata; /* Flow metadata. */
>     struct ofpbuf *stack;         /* Stack if any. */
>     mirror_mask_t mirrors;        /* Mirrors already output. */
> +    bool conntrack;               /* Conntrack occurred prior to recirc. */
> 

Should this be also called ‘conntracked’?

>     /* Actions to be translated on recirculation. */
>     uint32_t action_set_len;      /* How much of 'ofpacts' consists of an

(snip)

> static void
> check_support(struct dpif_backer *backer)
> {
> @@ -1239,6 +1278,9 @@ check_support(struct dpif_backer *backer)
>     backer->support.masked_set_action = check_masked_set_action(backer);
>     backer->support.ufid = check_ufid(backer);
>     backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif);
> +
> +    backer->support.odp.ct_state = check_ct_state(backer);
> +    backer->support.odp.ct_zone = check_ct_zone(backer);
> }
> 
> static int
> @@ -3935,10 +3977,38 @@ rule_dealloc(struct rule *rule_)
> }
> 
> static enum ofperr
> +rule_check(struct rule *rule)
> +{
> +    struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->ofproto);
> +    const struct odp_support *support;
> +    struct match match;
> +
> +    minimatch_expand(&rule->cr.match, &match);
> +    support = &ofproto_dpif_get_support(ofproto)->odp;
> +
> +    if ((match.wc.masks.ct_state && !support->ct_state)
> +        || (match.wc.masks.ct_zone && !support->ct_zone)) {
> +        return OFPERR_OFPBMC_BAD_FIELD;
> +    }
> +    if (match.wc.masks.ct_state & CS_UNSUPPORTED_MASK) {
> +        return OFPERR_OFPBMC_BAD_MASK;
> +    }
> +
> +    return 0;
> +}

This check seems somewhat expensive. Maybe get the masks from the minimatch 
without expanding it (using MINIFLOW_GET_U8() and MINIFLOW_GET_U16()), like 
this:

static enum ofperr
rule_check(struct rule *rule)
{
    ct_state_mask = MINIFLOW_GET_U8(&rule->cr.match.mask.masks, ct_state);
    ct_zone_mask = MINIFLOW_GET_U16(&rule->cr.match.mask.masks, ct_zone);

    if (ct_state_mask || ct_zone_mask) {
        struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->ofproto);
        const struct odp_support *support = 
&ofproto_dpif_get_support(ofproto)->odp;

        if ((ct_state_mask && !support->ct_state)
            || (ct_zone_mask && !support->ct_zone)) {
            return OFPERR_OFPBMC_BAD_FIELD;
        }
        if (ct_state_mask & CS_UNSUPPORTED_MASK) {
            return OFPERR_OFPBMC_BAD_MASK;
        }
    }
    return 0;
}


> +
> +static enum ofperr
> rule_construct(struct rule *rule_)
>     OVS_NO_THREAD_SAFETY_ANALYSIS
> {
>     struct rule_dpif *rule = rule_dpif_cast(rule_);
> +    int error;
> +
> +    error = rule_check(rule_);
> +    if (error) {
> +        return error;
> +    }
> +
>     ovs_mutex_init_adaptive(&rule->stats_mutex);
>     rule->stats.n_packets = 0;
>     rule->stats.n_bytes = 0;

(snip)

> --- a/tests/system-traffic.at
> +++ b/tests/system-traffic.at
> @@ -139,3 +139,472 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 
> 2 10.1.1.100 | FORMAT_PI
> 
> OVS_TRAFFIC_VSWITCHD_STOP
> AT_CLEANUP
> +
> +AT_SETUP([conntrack - controller])
> +CHECK_CONNTRACK()
> +OVS_TRAFFIC_VSWITCHD_START(
> +   [set-fail-mode br0 standalone -- ])
> +
> +ADD_NAMESPACES(at_ns0, at_ns1)
> +
> +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
> +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
> +
> +dnl Allow any traffic from ns0->ns1. Only allow nd, return traffic from 
> ns1->ns0.
> +AT_DATA([flows.txt], [dnl
> +priority=1,action=drop
> +priority=10,arp,action=normal
> +in_port=1,udp,action=ct(commit),controller
> +in_port=2,ct_state=-trk,udp,action=ct(table=0)
> +in_port=2,ct_state=+trk+est-new,udp,action=controller

I forgot the default priority value, would be useful to specify the priority of 
all the flows to make this more readable.

I’ll check the rest later. However, I was not able to run any of the tests with 
the net-next openvswitch kernel module, so I need to look at that before going 
on.

  Jarno

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to