The conntrack action now accepts a 16-bit 'zone' parameter. Connection tracking
in each zone is independent from tracking in all other zones. However, these
zones are specified per-datapath, so if two separate OVS bridges on a single
host both send packets to conntrack with zone 1, then connection tracking for
these connections will not be independent.

The 'conn_zone' field is also a new NXM matchable (read-only) field.

Signed-off-by: Joe Stringer <joestrin...@nicira.com>
Co-authored-by: Thomas Graf <tg...@noironetworks.com>
---
v4: Split out from initial patch.
    Rebase.
---
 datapath/flow_netlink.c                           |    2 +-
 datapath/linux/compat/include/linux/openvswitch.h |    3 +
 lib/dpif-netdev.c                                 |    2 +-
 lib/flow.c                                        |   11 ++-
 lib/flow.h                                        |    9 ++-
 lib/match.c                                       |   26 ++++++
 lib/match.h                                       |    1 +
 lib/meta-flow.c                                   |   21 +++++
 lib/meta-flow.h                                   |   15 ++++
 lib/nx-match.c                                    |    4 +
 lib/odp-execute.c                                 |    2 +
 lib/odp-util.c                                    |   55 ++++++++++++-
 lib/odp-util.h                                    |    3 +-
 lib/ofp-print.c                                   |    3 +
 lib/ofp-util.c                                    |    4 +
 lib/packets.h                                     |    1 +
 ofproto/ofproto-dpif-xlate.c                      |    4 +
 ofproto/ofproto-dpif.c                            |    5 +-
 ofproto/ofproto-dpif.h                            |    1 +
 tests/dpif-netdev.at                              |    2 +-
 tests/kmod-traffic.at                             |   87 +++++++++++++++++++++
 tests/ofproto-dpif.at                             |    4 +-
 tests/ofproto.at                                  |    3 +-
 utilities/ovs-ofctl.8.in                          |    5 ++
 24 files changed, 258 insertions(+), 15 deletions(-)

diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index baf6539..1246370 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -281,7 +281,7 @@ size_t ovs_key_attr_size(void)
        /* Whenever adding new OVS_KEY_ FIELDS, we should consider
         * updating this function.
         */
-       BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 23);
+       BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 24);
 
        return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */
                + nla_total_size(0)   /* OVS_KEY_ATTR_TUNNEL */
diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
b/datapath/linux/compat/include/linux/openvswitch.h
index 6c857d5..d4b77b9 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -344,6 +344,7 @@ enum ovs_key_attr {
                                 * The implementation may restrict
                                 * the accepted length of the array. */
        OVS_KEY_ATTR_CONN_STATE,/* u8 of OVS_CS_F_* */
+       OVS_KEY_ATTR_CONN_ZONE, /* u16 connection tracking zone. */
 
 #ifdef __KERNEL__
        /* Only used within kernel data path. */
@@ -652,10 +653,12 @@ struct ovs_action_push_tnl {
 /**
  * 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,      /* u8 of OVS_CT_F_*. */
+       OVS_CT_ATTR_ZONE,       /* u16 zone id. */
        __OVS_CT_ATTR_MAX
 };
 
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 627679b..5995aa4 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1927,7 +1927,7 @@ dpif_netdev_flow_from_nlattrs(const struct nlattr *key, 
uint32_t key_len,
     }
 
     /* Userspace datapath doesn't support conntrack. */
-    if (flow->conn_state) {
+    if (flow->conn_state || flow->conn_zone) {
         return EINVAL;
     }
 
diff --git a/lib/flow.c b/lib/flow.c
index 840d051..03ef433 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -468,7 +468,8 @@ miniflow_extract(struct dp_packet *packet, struct miniflow 
*dst)
         miniflow_pad_to_64(mf, conj_id);
     }
 
-    if (md->conn_state) {
+    if (md->conn_zone || md->conn_state) {
+        miniflow_push_uint16(mf, conn_zone, md->conn_zone);
         miniflow_push_uint8(mf, conn_state, md->conn_state);
         miniflow_pad_to_64(mf, pad1);
     }
@@ -801,6 +802,7 @@ flow_get_metadata(const struct flow *flow, struct 
flow_metadata *fmd)
     memcpy(fmd->regs, flow->regs, sizeof fmd->regs);
     fmd->pkt_mark = flow->pkt_mark;
     fmd->conn_state = flow->conn_state;
+    fmd->conn_zone = flow->conn_zone;
     fmd->in_port = flow->in_port.ofp_port;
 }
 
@@ -909,6 +911,9 @@ flow_format(struct ds *ds, const struct flow *flow)
     if (!flow->conn_state) {
         WC_UNMASK_FIELD(wc, conn_state);
     }
+    if (!flow->conn_zone) {
+        WC_UNMASK_FIELD(wc, conn_zone);
+    }
     for (int i = 0; i < FLOW_N_REGS; i++) {
         if (!flow->regs[i]) {
             WC_UNMASK_FIELD(wc, regs[i]);
@@ -972,6 +977,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards 
*wc,
     WC_MASK_FIELD(wc, skb_priority);
     WC_MASK_FIELD(wc, pkt_mark);
     WC_MASK_FIELD(wc, conn_state);
+    WC_MASK_FIELD(wc, conn_zone);
     WC_MASK_FIELD(wc, recirc_id);
     WC_MASK_FIELD(wc, dp_hash);
     WC_MASK_FIELD(wc, in_port);
@@ -1055,7 +1061,8 @@ flow_wc_map(const struct flow *flow)
     /* Metadata fields that can appear on packet input. */
     map |= MINIFLOW_MAP(skb_priority) | MINIFLOW_MAP(pkt_mark)
         | MINIFLOW_MAP(recirc_id) | MINIFLOW_MAP(dp_hash)
-        | MINIFLOW_MAP(conn_state) | MINIFLOW_MAP(in_port)
+        | MINIFLOW_MAP(in_port)
+        | MINIFLOW_MAP(conn_zone) | MINIFLOW_MAP(conn_state)
         | MINIFLOW_MAP(dl_dst) | MINIFLOW_MAP(dl_src)
         | MINIFLOW_MAP(dl_type) | MINIFLOW_MAP(vlan_tci);
 
diff --git a/lib/flow.h b/lib/flow.h
index 783a9ca..cb2bcfa 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -106,8 +106,9 @@ struct flow {
     union flow_in_port in_port; /* Input port.*/
     uint32_t recirc_id;         /* Must be exact match. */
     uint32_t conj_id;           /* Conjunction ID. */
-    uint8_t conn_state ;        /* Connection state. */
-    uint8_t pad1[5];            /* Pad to 48 bits. */
+    uint16_t conn_zone;         /* Connection Zone. */
+    uint8_t conn_state;         /* Connection state. */
+    uint8_t pad1[3];            /* Pad to 32 bits. */
     ofp_port_t actset_output;   /* Output port in action set. */
 
     /* L2, Order the same as in the Ethernet header! (64-bit aligned) */
@@ -192,8 +193,9 @@ struct flow_metadata {
     ovs_be64 metadata;               /* OpenFlow 1.1+ metadata field. */
     uint32_t regs[FLOW_N_REGS];      /* Registers. */
     uint32_t pkt_mark;               /* Packet mark. */
-    uint8_t conn_state;              /* Connection state. */
     ofp_port_t in_port;              /* OpenFlow port or zero. */
+    uint16_t conn_zone;              /* Connection zone. */
+    uint8_t conn_state;              /* Connection state. */
 };
 
 void flow_extract(struct dp_packet *, struct flow *);
@@ -756,6 +758,7 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const 
struct flow *flow)
     md->pkt_mark = flow->pkt_mark;
     md->in_port = flow->in_port;
     md->conn_state = flow->conn_state;
+    md->conn_zone = flow->conn_zone;
 }
 
 static inline bool is_ip_any(const struct flow *flow)
diff --git a/lib/match.c b/lib/match.c
index d34cb64..c9e2676 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -292,6 +292,13 @@ match_set_conn_state_masked(struct match *match, uint8_t 
conn_state,
 }
 
 void
+match_set_conn_zone(struct match *match, uint16_t conn_zone)
+{
+    match->flow.conn_zone = conn_zone;
+    match->wc.masks.conn_zone = UINT16_MAX;
+}
+
+void
 match_set_dl_type(struct match *match, ovs_be16 dl_type)
 {
     match->wc.masks.dl_type = OVS_BE16_MAX;
@@ -825,6 +832,21 @@ format_ipv6_netmask(struct ds *s, const char *name,
 }
 
 static void
+format_uint16_masked(struct ds *s, const char *name,
+                   uint16_t value, uint16_t mask)
+{
+    if (mask != 0) {
+        ds_put_format(s, "%s=", name);
+        if (mask == UINT16_MAX) {
+            ds_put_format(s, "%"PRIu16, value);
+        } else {
+            ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16, value, mask);
+        }
+        ds_put_char(s, ',');
+    }
+}
+
+static void
 format_be16_masked(struct ds *s, const char *name,
                    ovs_be16 value, ovs_be16 mask)
 {
@@ -974,6 +996,10 @@ match_format(const struct match *match, struct ds *s, int 
priority)
         ds_put_char(s, ',');
     }
 
+    if (wc->masks.conn_zone) {
+        format_uint16_masked(s, "conn_zone", f->conn_zone, 
wc->masks.conn_zone);
+    }
+
     if (wc->masks.dl_type) {
         skip_type = true;
         if (f->dl_type == htons(ETH_TYPE_IP)) {
diff --git a/lib/match.h b/lib/match.h
index 37723d0..38c4768 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -81,6 +81,7 @@ void match_set_pkt_mark_masked(struct match *, uint32_t 
pkt_mark, uint32_t mask)
 void match_set_conn_state(struct match *, uint8_t conn_state);
 void match_set_conn_state_masked(struct match *, uint8_t conn_state,
                                  uint8_t mask);
+void match_set_conn_zone(struct match *, uint16_t conn_zone);
 void match_set_skb_priority(struct match *, uint32_t skb_priority);
 void match_set_dl_type(struct match *, ovs_be16);
 void match_set_dl_src(struct match *, const uint8_t[ETH_ADDR_LEN]);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index c64e605..c30d7ef 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -134,6 +134,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct 
flow_wildcards *wc)
         return !wc->masks.pkt_mark;
     case MFF_CONN_STATE:
         return !wc->masks.conn_state;
+    case MFF_CONN_ZONE:
+        return !wc->masks.conn_zone;
     CASE_MFF_REGS:
         return !wc->masks.regs[mf->id - MFF_REG0];
     CASE_MFF_XREGS:
@@ -420,6 +422,7 @@ mf_is_value_valid(const struct mf_field *mf, const union 
mf_value *value)
     case MFF_SKB_PRIORITY:
     case MFF_PKT_MARK:
     case MFF_CONN_STATE:
+    case MFF_CONN_ZONE:
     CASE_MFF_REGS:
     CASE_MFF_XREGS:
     case MFF_ETH_SRC:
@@ -565,6 +568,10 @@ mf_get_value(const struct mf_field *mf, const struct flow 
*flow,
         value->u8 = flow->conn_state;
         break;
 
+    case MFF_CONN_ZONE:
+        value->be16 = htons(flow->conn_zone);
+        break;
+
     CASE_MFF_REGS:
         value->be32 = htonl(flow->regs[mf->id - MFF_REG0]);
         break;
@@ -790,6 +797,10 @@ mf_set_value(const struct mf_field *mf,
         match_set_conn_state(match, value->u8);
         break;
 
+    case MFF_CONN_ZONE:
+        match_set_conn_zone(match, ntohs(value->be16));
+        break;
+
     CASE_MFF_REGS:
         match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32));
         break;
@@ -1026,6 +1037,10 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->conn_state = value->u8;
         break;
 
+    case MFF_CONN_ZONE:
+        flow->conn_zone = ntohs(value->be16);
+        break;
+
     CASE_MFF_REGS:
         flow->regs[mf->id - MFF_REG0] = ntohl(value->be32);
         break;
@@ -1296,6 +1311,11 @@ mf_set_wild(const struct mf_field *mf, struct match 
*match)
         match->wc.masks.conn_state = 0;
         break;
 
+    case MFF_CONN_ZONE:
+        match->flow.conn_zone = 0;
+        match->wc.masks.conn_zone = 0;
+        break;
+
     CASE_MFF_REGS:
         match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0);
         break;
@@ -1473,6 +1493,7 @@ mf_set(const struct mf_field *mf,
     }
 
     switch (mf->id) {
+    case MFF_CONN_ZONE:
     case MFF_RECIRC_ID:
     case MFF_CONJ_ID:
     case MFF_IN_PORT:
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index b4fc4d8..37e23ac 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -567,6 +567,21 @@ enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_CONN_STATE,
 
+    /* "conn_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_CONN_ZONE(40) since v2.4.
+     * OXM: none.
+     */
+    MFF_CONN_ZONE,
+
 #if FLOW_N_REGS == 8
     /* "reg<N>".
      *
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 162cd70..470ee92 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1002,6 +1002,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const 
struct match *match,
         nxm_put_8m(b, MFF_CONN_STATE, oxm, flow->conn_state,
                    match->wc.masks.conn_state);
     }
+    if (match->wc.masks.conn_zone) {
+        nxm_put_16m(b, MFF_CONN_ZONE, oxm, htons(flow->conn_zone),
+                    htons(match->wc.masks.conn_zone));
+    }
 
     /* OpenFlow 1.1+ Metadata. */
     nxm_put_64m(b, MFF_METADATA, oxm,
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 06aeb9b..c56841c 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -327,6 +327,7 @@ odp_execute_set_action(struct dp_packet *packet, const 
struct nlattr *a)
     case OVS_KEY_ATTR_ICMPV6:
     case OVS_KEY_ATTR_TCP_FLAGS:
     case OVS_KEY_ATTR_CONN_STATE:
+    case OVS_KEY_ATTR_CONN_ZONE:
     case __OVS_KEY_ATTR_MAX:
     default:
         OVS_NOT_REACHED();
@@ -416,6 +417,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
     case OVS_KEY_ATTR_TUNNEL:    /* Masked data not supported for tunnel. */
     case OVS_KEY_ATTR_UNSPEC:
     case OVS_KEY_ATTR_CONN_STATE:
+    case OVS_KEY_ATTR_CONN_ZONE:
     case OVS_KEY_ATTR_ENCAP:
     case OVS_KEY_ATTR_ETHERTYPE:
     case OVS_KEY_ATTR_IN_PORT:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index d4fca67..fa6f0a3 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -113,6 +113,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char 
*namebuf, size_t bufsize)
     case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
     case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
     case OVS_KEY_ATTR_CONN_STATE: return "conn_state";
+    case OVS_KEY_ATTR_CONN_ZONE: return "conn_zone";
     case OVS_KEY_ATTR_TUNNEL: return "tunnel";
     case OVS_KEY_ATTR_IN_PORT: return "in_port";
     case OVS_KEY_ATTR_ETHERNET: return "eth";
@@ -617,6 +618,7 @@ format_odp_conntrack_action(struct ds *ds, const struct 
nlattr *attr)
 {
     static const struct nl_policy ovs_conntrack_policy[] = {
         [OVS_CT_ATTR_FLAGS] = { .type = NL_A_U32, .optional = true },
+        [OVS_CT_ATTR_ZONE] = { .type = NL_A_U16, .optional = true },
     };
     struct nlattr *a[ARRAY_SIZE(ovs_conntrack_policy)];
     uint32_t flags;
@@ -628,8 +630,9 @@ format_odp_conntrack_action(struct ds *ds, const struct 
nlattr *attr)
 
     flags = nl_attr_get_u32(a[OVS_CT_ATTR_FLAGS]);
 
-    ds_put_format(ds, "ct(%s)",
-                  flags & OVS_CT_F_COMMIT ? "commit" : "");
+    ds_put_format(ds, "ct(%szone=%"PRIu16")",
+                  flags & OVS_CT_F_COMMIT ? "commit," : "",
+                  nl_attr_get_u16(a[OVS_CT_ATTR_ZONE]));
 }
 
 static void
@@ -1033,6 +1036,7 @@ parse_conntrack_action(const char *s_, struct ofpbuf 
*actions)
 
     if (ovs_scan(s, "ct(")) {
         uint32_t flags = 0;
+        uint16_t zone = 0;
         size_t start;
         char *end;
 
@@ -1050,6 +1054,9 @@ parse_conntrack_action(const char *s_, struct ofpbuf 
*actions)
                 flags |= OVS_CT_F_COMMIT;
                 continue;
             }
+            if (ovs_scan(s, "zone=%"SCNu16"%n", &zone, &n)) {
+                continue;
+            }
 
             if (n < 0) {
                 return -EINVAL;
@@ -1062,6 +1069,9 @@ parse_conntrack_action(const char *s_, struct ofpbuf 
*actions)
         if (flags) {
             nl_msg_put_u32(actions, OVS_CT_ATTR_FLAGS, flags);
         }
+        if (zone) {
+            nl_msg_put_u16(actions, OVS_CT_ATTR_ZONE, zone);
+        }
         nl_msg_end_nested(actions, start);
     }
 
@@ -1302,6 +1312,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_DP_HASH: return 4;
     case OVS_KEY_ATTR_RECIRC_ID: return 4;
     case OVS_KEY_ATTR_CONN_STATE: return 1;
+    case OVS_KEY_ATTR_CONN_ZONE: return 2;
     case OVS_KEY_ATTR_TUNNEL: return -2;
     case OVS_KEY_ATTR_IN_PORT: return 4;
     case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
@@ -1932,6 +1943,10 @@ format_odp_key_attr(const struct nlattr *a, const struct 
nlattr *ma,
         }
         break;
 
+    case OVS_KEY_ATTR_CONN_ZONE:
+        ds_put_format(ds, "%"PRIx16, nl_attr_get_u16(a));
+        break;
+
     case OVS_KEY_ATTR_TUNNEL: {
         struct flow_tnl key, mask_;
         struct flow_tnl *mask = ma ? &mask_ : NULL;
@@ -2432,6 +2447,26 @@ scan_u8(const char *s, uint8_t *key, uint8_t *mask)
 }
 
 static int
+scan_u16(const char *s, uint16_t *key, uint16_t *mask)
+{
+    int n;
+
+    if (ovs_scan(s, "%"SCNi16"%n", key, &n)) {
+        int len = n;
+
+        if (mask) {
+            if (ovs_scan(s + len, "/%"SCNi16"%n", mask, &n)) {
+                len += n;
+            } else {
+                *mask = UINT16_MAX;
+            }
+        }
+        return len;
+    }
+    return 0;
+}
+
+static int
 scan_u32(const char *s, uint32_t *key, uint32_t *mask)
 {
     int n;
@@ -2858,6 +2893,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap 
*port_names,
     SCAN_SINGLE("dp_hash(", uint32_t, u32, OVS_KEY_ATTR_DP_HASH);
 
     SCAN_SINGLE("conn_state(", uint8_t, conn_state, OVS_KEY_ATTR_CONN_STATE);
+    SCAN_SINGLE("conn_zone(", uint16_t, u16, OVS_KEY_ATTR_CONN_ZONE);
 
     SCAN_BEGIN("tunnel(", struct flow_tnl) {
         SCAN_FIELD("tun_id=", be64, tun_id);
@@ -3094,6 +3130,9 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct 
flow *flow,
     if (data->conn_state) {
         nl_msg_put_u8(buf, OVS_KEY_ATTR_CONN_STATE, data->conn_state);
     }
+    if (data->conn_zone) {
+        nl_msg_put_u16(buf, OVS_KEY_ATTR_CONN_ZONE, data->conn_zone);
+    }
     if (recirc) {
         nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id);
         nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash);
@@ -3294,6 +3333,9 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const 
struct pkt_metadata *md)
     if (md->conn_state) {
         nl_msg_put_u8(buf, OVS_KEY_ATTR_CONN_STATE, md->conn_state);
     }
+    if (md->conn_zone) {
+        nl_msg_put_u16(buf, OVS_KEY_ATTR_CONN_ZONE, md->conn_zone);
+    }
 
     /* Add an ingress port attribute if 'odp_in_port' is not the magical
      * value "ODPP_NONE". */
@@ -3345,6 +3387,10 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t 
key_len,
             md->conn_state = nl_attr_get_u8(nla);
             wanted_attrs &= ~(1u << OVS_KEY_ATTR_CONN_STATE);
             break;
+        case OVS_KEY_ATTR_CONN_ZONE:
+            md->conn_zone = nl_attr_get_u16(nla);
+            wanted_attrs &= ~(1u << OVS_KEY_ATTR_CONN_ZONE);
+            break;
         case OVS_KEY_ATTR_TUNNEL: {
             enum odp_key_fitness res;
 
@@ -3901,6 +3947,11 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t 
key_len,
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CONN_STATE;
     }
 
+    if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CONN_ZONE)) {
+        flow->conn_zone = nl_attr_get_u16(attrs[OVS_KEY_ATTR_CONN_ZONE]);
+        expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CONN_ZONE;
+    }
+
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
         enum odp_key_fitness res;
 
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 37735ba..a7d93d0 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -121,6 +121,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
  *  OVS_KEY_ATTR_DP_HASH                 4    --     4      8
  *  OVS_KEY_ATTR_RECIRC_ID               4    --     4      8
  *  OVS_KEY_ATTR_CONN_STATE              1     3     4      8
+ *  OVS_KEY_ATTR_CONN_ZONE               2     2     4      8
  *  OVS_KEY_ATTR_ETHERNET               12    --     4     16
  *  OVS_KEY_ATTR_ETHERTYPE               2     2     4      8  (outer VLAN 
ethertype)
  *  OVS_KEY_ATTR_VLAN                    2     2     4      8
@@ -130,7 +131,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
  *  OVS_KEY_ATTR_ICMPV6                  2     2     4      8
  *  OVS_KEY_ATTR_ND                     28    --     4     32
  *  ----------------------------------------------------------
- *  total                                                 496
+ *  total                                                 504
  *
  * We include some slack space in case the calculation isn't quite right or we
  * add another field and forget to adjust this value.
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 3e3884c..e2b1f16 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -161,6 +161,9 @@ ofp_print_packet_in(struct ds *string, const struct 
ofp_header *oh,
     if (pin.fmd.conn_state != 0) {
         ds_put_format(string, " conn_state=0x%"PRIx8, pin.fmd.conn_state);
     }
+    if (pin.fmd.conn_zone != 0) {
+        ds_put_format(string, " conn_zone=0x%"PRIx16, pin.fmd.conn_zone);
+    }
 
     ds_put_format(string, " (via %s)",
                   ofputil_packet_in_reason_to_string(pin.reason, reasonbuf,
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 6d30f95..f89775d 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -3312,6 +3312,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in 
*pin,
     memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs);
     pin->fmd.pkt_mark = match->flow.pkt_mark;
     pin->fmd.conn_state = match->flow.conn_state;
+    pin->fmd.conn_zone = match->flow.conn_zone;
 }
 
 enum ofperr
@@ -3457,6 +3458,9 @@ ofputil_packet_in_to_match(const struct ofputil_packet_in 
*pin,
     if (pin->fmd.conn_state != 0) {
         match_set_conn_state(match, pin->fmd.conn_state);
     }
+    if (pin->fmd.conn_zone != 0) {
+        match_set_conn_zone(match, pin->fmd.conn_zone);
+    }
 
     match_set_in_port(match, pin->fmd.in_port);
 }
diff --git a/lib/packets.h b/lib/packets.h
index af2c767..418f674 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -66,6 +66,7 @@ struct pkt_metadata {
     uint32_t pkt_mark;          /* Packet mark. */
     union flow_in_port in_port; /* Input port. */
     uint8_t conn_state;         /* Connection state. */
+    uint16_t conn_zone;         /* Connection zone. */
 };
 
 #define PKT_METADATA_INITIALIZER(PORT) \
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index edefd05..c32d713 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2724,6 +2724,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
     ovs_be16 flow_vlan_tci;
     uint32_t flow_pkt_mark;
     uint8_t flow_conn_state;
+    uint16_t flow_conn_zone;
     uint8_t flow_nw_tos;
     odp_port_t out_port, odp_port;
     bool tnl_push_pop_send = false;
@@ -2868,6 +2869,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
     flow_vlan_tci = flow->vlan_tci;
     flow_pkt_mark = flow->pkt_mark;
     flow_conn_state = flow->conn_state;
+    flow_conn_zone = flow->conn_zone;
     flow_nw_tos = flow->nw_tos;
 
     if (count_skb_priorities(xport)) {
@@ -2988,6 +2990,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
     flow->vlan_tci = flow_vlan_tci;
     flow->pkt_mark = flow_pkt_mark;
     flow->conn_state = flow_conn_state;
+    flow->conn_zone = flow_conn_zone;
     flow->nw_tos = flow_nw_tos;
 }
 
@@ -4085,6 +4088,7 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct 
ofpact_conntrack *ofc)
 
     ct_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_CT);
     nl_msg_put_u32(odp_actions, OVS_CT_ATTR_FLAGS, flags);
+    nl_msg_put_u16(odp_actions, OVS_CT_ATTR_ZONE, ofc->zone);
     nl_msg_end_nested(odp_actions, ct_offset);
 
     if (ofc->flags & NX_CT_F_RECIRC) {
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 72f219f..ddb40ce 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -1226,6 +1226,7 @@ check_##FIELD(struct dpif_backer *backer)                 
                  \
 }
 
 CHECK_FEATURE(conn_state)
+CHECK_FEATURE(conn_zone)
 
 #undef CHECK_FEATURE
 
@@ -1241,6 +1242,7 @@ check_support(struct dpif_backer *backer)
     backer->support.ufid = check_ufid(backer);
     backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif);
     backer->support.conn_state = check_conn_state(backer);
+    backer->support.conn_zone = check_conn_zone(backer);
 }
 
 static int
@@ -3928,7 +3930,8 @@ rule_check(struct rule *rule)
 
     minimatch_expand(&rule->cr.match, &match);
 
-    if (match.wc.masks.conn_state && !ofproto->backer->support.conn_state) {
+    if ((match.wc.masks.conn_state && !ofproto->backer->support.conn_state)
+        || (match.wc.masks.conn_zone && !ofproto->backer->support.conn_zone)) {
         return OFPERR_OFPBMC_BAD_FIELD;
     }
     if (match.wc.masks.conn_state & CS_UNSUPPORTED_MASK) {
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index 2a85e47..74800c8 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -90,6 +90,7 @@ struct dpif_backer_support {
     bool tnl_push_pop;
     bool ufid;
     bool conn_state;
+    bool conn_zone;
 };
 
 size_t ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *);
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 6ab908c..5fc4132 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -82,7 +82,7 @@ AT_CHECK([cat ovs-vswitchd.log | grep -A 1 'miss upcall' | 
tail -n 1], [0], [dnl
 
skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)
 ])
 AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_XOUT], [0], [dnl
-pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0,
 actions: <del>
+pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,conn_zone=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0,
 actions: <del>
 
recirc_id=0,ip,in_port=1,vlan_tci=0x0000/0x1fff,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_frag=no,
 actions: <del>
 ])
 
diff --git a/tests/kmod-traffic.at b/tests/kmod-traffic.at
index 85b966c..0112086 100644
--- a/tests/kmod-traffic.at
+++ b/tests/kmod-traffic.at
@@ -226,3 +226,90 @@ AT_CHECK([ip netns exec at_ns2 wget 10.1.1.4 -t 3 -T 1 
--retry-connrefused -v -o
 
 OVS_KMOD_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([conntrack - zones])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_KMOD_VSWITCHD_START(
+   [set-fail-mode br0 standalone -- ])
+
+ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24")
+ADD_VETH(p3, at_ns3, br0, "10.1.1.4/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
+priority=10,icmp,action=normal
+in_port=1,tcp,action=ct(commit,zone=1),2
+in_port=2,conn_state=-trk,tcp,action=ct(recirc,zone=1)
+in_port=2,conn_state=+trk,conn_zone=1,tcp,action=1
+in_port=3,tcp,action=ct(commit,zone=2),4
+in_port=4,conn_state=-trk,tcp,action=ct(recirc,zone=2)
+in_port=4,conn_state=+trk,conn_zone=1,tcp,action=3
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl HTTP requests from p0->p1 should work fine.
+NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-conntrack.py]], 
[test-conntrack0.pid])
+AT_CHECK([ip netns exec at_ns0 wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v 
-o wget0.log])
+
+AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2)], [0], [dnl
+TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> 
src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 
zone=1 use=1
+])
+
+dnl HTTP requests from p2->p3 should fail due to network failure.
+dnl Try 5 times, in 1 second intervals.
+NETNS_DAEMONIZE([at_ns3], [[$PYTHON $srcdir/test-conntrack.py]], 
[test-conntrack1.pid])
+AT_CHECK([ip netns exec at_ns2 wget 10.1.1.4 -t 3 -T 1 -v -o wget1.log], [4])
+
+AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.4)], [0], [dnl
+SYN_RECV src=10.1.1.3 dst=10.1.1.4 sport=<cleared> dport=<cleared> 
src=10.1.1.4 dst=10.1.1.3 sport=<cleared> dport=<cleared> mark=0 zone=2 use=1
+])
+
+OVS_KMOD_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([conntrack - multiple zones])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+OVS_KMOD_VSWITCHD_START(
+   [set-fail-mode br0 standalone -- ])
+
+ADD_NAMESPACES(at_ns0, at_ns1, at_ns2, at_ns3)
+
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
+ADD_VETH(p2, at_ns2, br0, "10.1.1.3/24")
+ADD_VETH(p3, at_ns3, br0, "10.1.1.4/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
+priority=10,icmp,action=normal
+in_port=1,tcp,action=ct(commit,zone=1),ct(commit,zone=2),2
+in_port=2,conn_state=-trk,tcp,action=ct(recirc,zone=2)
+in_port=2,conn_state=+trk,conn_zone=2,tcp,action=1
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl HTTP requests from p0->p1 should work fine.
+NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-conntrack.py]], 
[test-conntrack0.pid])
+AT_CHECK([ip netns exec at_ns0 wget 10.1.1.2 -t 3 -T 1 --retry-connrefused -v 
-o wget0.log])
+
+dnl (again) HTTP requests from p0->p1 should work fine.
+dnl NETNS_DAEMONIZE([at_ns1], [[$PYTHON $srcdir/test-conntrack.py]], 
[test-conntrack0.pid])
+dnl AT_CHECK([ip netns exec at_ns0 wget 10.1.1.2 -t 3 -T 1 --retry-connrefused 
-v -o wget0.log])
+
+AT_CHECK([conntrack -L 2>&1 | FORMAT_CT(10.1.1.2)], [0], [dnl
+SYN_SENT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> 
[[UNREPLIED]] src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> mark=0 
zone=1 use=1
+TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> 
src=10.1.1.2 dst=10.1.1.1 sport=<cleared> dport=<cleared> [[ASSURED]] mark=0 
zone=2 use=1
+])
+
+OVS_KMOD_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 0ad80e9..f1e8105 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -6142,8 +6142,8 @@ for i in 1 2 3 4; do
 done
 sleep 1
 AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_INSTALL | 
STRIP_USED], [0], [dnl
-pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0,
 actions:2
-pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0,
 actions:drop
+pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,conn_zone=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0,
 actions:2
+pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,conn_zone=0,icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0,
 actions:drop
 ])
 AT_CHECK([cat ovs-vswitchd.log | STRIP_UFID | FILTER_FLOW_DUMP | grep 
'packets:3'], [0], [dnl
 
skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0),
 packets:3, bytes:180, used:0.0s, actions:2
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 205d80b..bc23aba 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1513,6 +1513,7 @@ OVS_VSWITCHD_START
       actset_output: exact match or wildcard
       pkt_mark: arbitrary mask
       conn_state: arbitrary mask
+      conn_zone: exact match or wildcard
       reg0: arbitrary mask
       reg1: arbitrary mask
       reg2: arbitrary mask
@@ -1582,7 +1583,7 @@ AT_CHECK(
 # Check that the configuration was updated.
 mv expout orig-expout
 sed 's/classifier/main/
-78s/1000000/1024/' < orig-expout > expout
+79s/1000000/1024/' < orig-expout > expout
 AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0 | sed '/^$/d
 /^OFPST_TABLE_FEATURES/d'], [0], [expout])
 OVS_VSWITCHD_STOP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index c677118..fadd407 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1218,6 +1218,11 @@ This is part of an already existing connection.
 This is a new connection that is related to an existing connection.
 .RE
 .
+.IP \fBconn_zone=\fIvalue
+Matches connection zone \fIvalue\fR exactly.  The zone is associated data with
+the connection tracker and can only be matched after running through the
+connection tracker through the \fBct\fR action.
+.
 .PP
 Defining IPv6 flows (those with \fBdl_type\fR equal to 0x86dd) requires
 support for NXM.  The following shorthand notations are available for
-- 
1.7.10.4

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

Reply via email to