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