Add the ability to match and set the "conn_mark" field that associates the mark with a connection tracking entry. This is only available on on systems that support connection tracking.
OpenFlow rules which have a "set_field:X->conn_mark" action must have a prior ct() action in the action list. The connection tracking entry in the zone specified by the prior ct() action is the entry which the mark is associated with. For example, "action=ct(zone=1),ct(zone=2),set_field:1->conn_mark" will apply the conn_mark to the connection in zone 2, but not the entry in zone 1. For the conn_mark to persist, the connection must be committed. Here's a simple example flow table to allow outbound TCP traffic from port 1; Associate mark "1" with all connections, and match on that mark: ovs-ofctl del-flows br0 ovs-ofctl add-flow br0 in_port=1,tcp,action=ct(commit),set_field:1->conn_mark,2 ovs-ofctl add-flow br0 in_port=2,conn_state=-trk,tcp,action=ct(recirc) ovs-ofctl add-flow br0 in_port=2,conn_state=+trk,conn_mark=1,tcp,action=1 Signed-off-by: Joe Stringer <joestrin...@nicira.com> --- NEWS | 3 ++ datapath/flow_netlink.c | 2 +- datapath/linux/compat/include/linux/openvswitch.h | 1 + lib/dpif-netdev.c | 2 +- lib/flow.c | 10 ++++- lib/flow.h | 10 +++-- lib/match.c | 18 ++++++++ lib/match.h | 3 ++ lib/meta-flow.c | 25 +++++++++++ lib/meta-flow.h | 17 ++++++++ lib/nx-match.c | 6 ++- lib/odp-execute.c | 14 +++++- lib/odp-util.c | 39 ++++++++++++++++- lib/odp-util.h | 3 +- lib/ofp-actions.c | 4 +- lib/ofp-print.c | 4 ++ lib/ofp-util.c | 5 +++ lib/packets.h | 1 + ofproto/ofproto-dpif-xlate.c | 4 +- ofproto/ofproto-dpif.c | 5 ++- ofproto/ofproto-dpif.h | 1 + ofproto/ofproto-unixctl.man | 2 + tests/dpif-netdev.at | 2 +- tests/kmod-traffic.at | 47 +++++++++++++++++++++ tests/ofproto-dpif.at | 4 +- tests/ofproto.at | 5 ++- utilities/ovs-ofctl.8.in | 6 +++ 27 files changed, 223 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 18f7ae4..435389f 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,9 @@ Post-v2.3.0 - Add support for connection tracking through the new "conntrack" action and "conn_state" match field. Only available on Linux kernels with the connection tracking module loaded. + - Add support for setting and matching the "conn_mark" field that + associates the mark with a connection tracking module. Only + available on systems that support connection tracking. - The "learn" action supports a new flag "delete_learned" that causes the learned flows to be deleted when the flow with the "learn" action is deleted. diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c index 1246370..93422fe 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 != 24); + BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 25); 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 d4b77b9..d915333 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -345,6 +345,7 @@ enum ovs_key_attr { * 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. */ + OVS_KEY_ATTR_CONN_MARK, /* u32 connection tracking mark */ #ifdef __KERNEL__ /* Only used within kernel data path. */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 5995aa4..6e809d5 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 || flow->conn_zone) { + if (flow->conn_state || flow->conn_zone || flow->conn_mark) { return EINVAL; } diff --git a/lib/flow.c b/lib/flow.c index 03ef433..75d82e6 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_zone || md->conn_state) { + if (md->conn_mark || md->conn_zone || md->conn_state) { + miniflow_push_uint32(mf, conn_mark, md->conn_mark); miniflow_push_uint16(mf, conn_zone, md->conn_zone); miniflow_push_uint8(mf, conn_state, md->conn_state); miniflow_pad_to_64(mf, pad1); @@ -803,6 +804,7 @@ flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) fmd->pkt_mark = flow->pkt_mark; fmd->conn_state = flow->conn_state; fmd->conn_zone = flow->conn_zone; + fmd->conn_mark = flow->conn_mark; fmd->in_port = flow->in_port.ofp_port; } @@ -914,6 +916,9 @@ flow_format(struct ds *ds, const struct flow *flow) if (!flow->conn_zone) { WC_UNMASK_FIELD(wc, conn_zone); } + if (!flow->conn_mark) { + WC_UNMASK_FIELD(wc, conn_mark); + } for (int i = 0; i < FLOW_N_REGS; i++) { if (!flow->regs[i]) { WC_UNMASK_FIELD(wc, regs[i]); @@ -978,6 +983,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, WC_MASK_FIELD(wc, pkt_mark); WC_MASK_FIELD(wc, conn_state); WC_MASK_FIELD(wc, conn_zone); + WC_MASK_FIELD(wc, conn_mark); WC_MASK_FIELD(wc, recirc_id); WC_MASK_FIELD(wc, dp_hash); WC_MASK_FIELD(wc, in_port); @@ -1061,7 +1067,7 @@ 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(in_port) + | MINIFLOW_MAP(in_port) | MINIFLOW_MAP(conn_mark) | 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 cb2bcfa..97aa3ca 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -106,10 +106,12 @@ struct flow { union flow_in_port in_port; /* Input port.*/ uint32_t recirc_id; /* Must be exact match. */ uint32_t conj_id; /* Conjunction ID. */ + uint32_t conn_mark; /* Connection mark. With L3 to avoid L4 match.*/ uint16_t conn_zone; /* Connection Zone. */ uint8_t conn_state; /* Connection state. */ - uint8_t pad1[3]; /* Pad to 32 bits. */ + uint8_t pad1[1]; /* Pad to 64 bits. */ ofp_port_t actset_output; /* Output port in action set. */ + uint8_t pad2[6]; /* Pad to 64 bits. */ /* L2, Order the same as in the Ethernet header! (64-bit aligned) */ uint8_t dl_dst[ETH_ADDR_LEN]; /* Ethernet destination address. */ @@ -132,7 +134,7 @@ struct flow { uint8_t arp_sha[ETH_ADDR_LEN]; /* ARP/ND source hardware address. */ uint8_t arp_tha[ETH_ADDR_LEN]; /* ARP/ND target hardware address. */ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */ - ovs_be16 pad2; /* Pad to 64 bits. */ + ovs_be16 pad3; /* Pad to 64 bits. */ /* L4 (64-bit aligned) */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */ @@ -157,7 +159,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0); /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) - == sizeof(struct flow_tnl) + 192 + == sizeof(struct flow_tnl) + 200 && FLOW_WC_SEQ == 31); /* Incremental points at which flow classification may be performed in @@ -194,6 +196,7 @@ struct flow_metadata { uint32_t regs[FLOW_N_REGS]; /* Registers. */ uint32_t pkt_mark; /* Packet mark. */ ofp_port_t in_port; /* OpenFlow port or zero. */ + uint32_t conn_mark; /* Connection mark. */ uint16_t conn_zone; /* Connection zone. */ uint8_t conn_state; /* Connection state. */ }; @@ -759,6 +762,7 @@ pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow) md->in_port = flow->in_port; md->conn_state = flow->conn_state; md->conn_zone = flow->conn_zone; + md->conn_mark = flow->conn_mark; } static inline bool is_ip_any(const struct flow *flow) diff --git a/lib/match.c b/lib/match.c index c9e2676..3e8f928 100644 --- a/lib/match.c +++ b/lib/match.c @@ -299,6 +299,20 @@ match_set_conn_zone(struct match *match, uint16_t conn_zone) } void +match_set_conn_mark(struct match *match, uint32_t conn_mark) +{ + match_set_conn_mark_masked(match, conn_mark, UINT32_MAX); +} + +void +match_set_conn_mark_masked(struct match *match, uint32_t conn_mark, + uint32_t mask) +{ + match->flow.conn_mark = conn_mark & mask; + match->wc.masks.conn_mark = mask; +} + +void match_set_dl_type(struct match *match, ovs_be16 dl_type) { match->wc.masks.dl_type = OVS_BE16_MAX; @@ -1000,6 +1014,10 @@ match_format(const struct match *match, struct ds *s, int priority) format_uint16_masked(s, "conn_zone", f->conn_zone, wc->masks.conn_zone); } + if (wc->masks.conn_mark) { + format_uint32_masked(s, "conn_mark", f->conn_mark, wc->masks.conn_mark); + } + 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 38c4768..b512200 100644 --- a/lib/match.h +++ b/lib/match.h @@ -82,6 +82,9 @@ 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_conn_mark(struct match *, uint32_t conn_mark); +void match_set_conn_mark_masked(struct match *, uint32_t conn_mark, + uint32_t mask); 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 c30d7ef..4fbe18d 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -136,6 +136,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) return !wc->masks.conn_state; case MFF_CONN_ZONE: return !wc->masks.conn_zone; + case MFF_CONN_MARK: + return !wc->masks.conn_mark; CASE_MFF_REGS: return !wc->masks.regs[mf->id - MFF_REG0]; CASE_MFF_XREGS: @@ -423,6 +425,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_PKT_MARK: case MFF_CONN_STATE: case MFF_CONN_ZONE: + case MFF_CONN_MARK: CASE_MFF_REGS: CASE_MFF_XREGS: case MFF_ETH_SRC: @@ -572,6 +575,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, value->be16 = htons(flow->conn_zone); break; + case MFF_CONN_MARK: + value->be32 = htonl(flow->conn_mark); + break; + CASE_MFF_REGS: value->be32 = htonl(flow->regs[mf->id - MFF_REG0]); break; @@ -801,6 +808,10 @@ mf_set_value(const struct mf_field *mf, match_set_conn_zone(match, ntohs(value->be16)); break; + case MFF_CONN_MARK: + match_set_conn_mark(match, ntohl(value->be32)); + break; + CASE_MFF_REGS: match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32)); break; @@ -1041,6 +1052,10 @@ mf_set_flow_value(const struct mf_field *mf, flow->conn_zone = ntohs(value->be16); break; + case MFF_CONN_MARK: + flow->conn_mark = ntohl(value->be32); + break; + CASE_MFF_REGS: flow->regs[mf->id - MFF_REG0] = ntohl(value->be32); break; @@ -1316,6 +1331,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match) match->wc.masks.conn_zone = 0; break; + case MFF_CONN_MARK: + match->flow.conn_mark = 0; + match->wc.masks.conn_mark = 0; + break; + CASE_MFF_REGS: match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0); break; @@ -1570,6 +1590,11 @@ mf_set(const struct mf_field *mf, match_set_conn_state_masked(match, value->u8, mask->u8); break; + case MFF_CONN_MARK: + match_set_conn_mark_masked(match, ntohl(value->be32), + ntohl(mask->be32)); + break; + case MFF_ETH_DST: match_set_dl_dst_masked(match, value->mac, mask->mac); break; diff --git a/lib/meta-flow.h b/lib/meta-flow.h index 37e23ac..c70cfaa 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -582,6 +582,23 @@ enum OVS_PACKED_ENUM mf_field_id { */ MFF_CONN_ZONE, + /* "conn_mark". + * + * Connection tracking mark. The mark is carried with the + * connection tracking state. On Linux this corresponds to the + * nf_conn's "mark" member but the exact implementation is + * platform-dependent. + * + * Type: be32. + * Maskable: bitwise. + * Formatting: hexadecimal. + * Prerequisites: none. + * Access: read/write. + * NXM: NXM_NX_CONN_MARK(41) since v2.4. + * OXM: none. + */ + MFF_CONN_MARK, + #if FLOW_N_REGS == 8 /* "reg<N>". * diff --git a/lib/nx-match.c b/lib/nx-match.c index 470ee92..2a13c35 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -993,7 +993,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, } } - /* Mark. */ + /* Packet mark. */ nxm_put_32m(b, MFF_PKT_MARK, oxm, htonl(flow->pkt_mark), htonl(match->wc.masks.pkt_mark)); @@ -1006,6 +1006,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, nxm_put_16m(b, MFF_CONN_ZONE, oxm, htons(flow->conn_zone), htons(match->wc.masks.conn_zone)); } + if (match->wc.masks.conn_mark) { + nxm_put_32m(b, MFF_CONN_MARK, oxm, htonl(flow->conn_mark), + htonl(match->wc.masks.conn_mark)); + } /* OpenFlow 1.1+ Metadata. */ nxm_put_64m(b, MFF_METADATA, oxm, diff --git a/lib/odp-execute.c b/lib/odp-execute.c index c56841c..a1c5e07 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -328,6 +328,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a) case OVS_KEY_ATTR_TCP_FLAGS: case OVS_KEY_ATTR_CONN_STATE: case OVS_KEY_ATTR_CONN_ZONE: + case OVS_KEY_ATTR_CONN_MARK: case __OVS_KEY_ATTR_MAX: default: OVS_NOT_REACHED(); @@ -418,6 +419,7 @@ odp_execute_masked_set_action(struct dp_packet *packet, case OVS_KEY_ATTR_UNSPEC: case OVS_KEY_ATTR_CONN_STATE: case OVS_KEY_ATTR_CONN_ZONE: + case OVS_KEY_ATTR_CONN_MARK: case OVS_KEY_ATTR_ENCAP: case OVS_KEY_ATTR_ETHERTYPE: case OVS_KEY_ATTR_IN_PORT: @@ -484,7 +486,17 @@ requires_datapath_assistance(const struct nlattr *a) return true; case OVS_ACTION_ATTR_SET: - case OVS_ACTION_ATTR_SET_MASKED: + case OVS_ACTION_ATTR_SET_MASKED: { + const struct nlattr *set = nl_attr_get(a); + enum ovs_key_attr set_type = nl_attr_type(set); + + /* Conntrack set_field() actions need to be executed in datapath. */ + if (set_type == OVS_KEY_ATTR_CONN_MARK) { + return true; + } + return false; + } + case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_POP_VLAN: case OVS_ACTION_ATTR_SAMPLE: diff --git a/lib/odp-util.c b/lib/odp-util.c index fa6f0a3..d24f687 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -114,6 +114,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize) 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_CONN_MARK: return "conn_mark"; case OVS_KEY_ATTR_TUNNEL: return "tunnel"; case OVS_KEY_ATTR_IN_PORT: return "in_port"; case OVS_KEY_ATTR_ETHERNET: return "eth"; @@ -1313,6 +1314,7 @@ odp_flow_key_attr_len(uint16_t type) 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_CONN_MARK: return 4; 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); @@ -1926,6 +1928,7 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma, case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_DP_HASH: case OVS_KEY_ATTR_RECIRC_ID: + case OVS_KEY_ATTR_CONN_MARK: ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a)); if (!is_exact) { ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma)); @@ -2894,6 +2897,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, 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_SINGLE("conn_mark(", uint32_t, u32, OVS_KEY_ATTR_CONN_MARK); SCAN_BEGIN("tunnel(", struct flow_tnl) { SCAN_FIELD("tun_id=", be64, tun_id); @@ -3133,6 +3137,9 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow, if (data->conn_zone) { nl_msg_put_u16(buf, OVS_KEY_ATTR_CONN_ZONE, data->conn_zone); } + if (data->conn_mark) { + nl_msg_put_u32(buf, OVS_KEY_ATTR_CONN_MARK, data->conn_mark); + } 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); @@ -3336,6 +3343,9 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md) if (md->conn_zone) { nl_msg_put_u16(buf, OVS_KEY_ATTR_CONN_ZONE, md->conn_zone); } + if (md->conn_mark) { + nl_msg_put_u32(buf, OVS_KEY_ATTR_CONN_MARK, md->conn_mark); + } /* Add an ingress port attribute if 'odp_in_port' is not the magical * value "ODPP_NONE". */ @@ -3391,6 +3401,10 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, md->conn_zone = nl_attr_get_u16(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_CONN_ZONE); break; + case OVS_KEY_ATTR_CONN_MARK: + md->conn_mark = nl_attr_get_u32(nla); + wanted_attrs &= ~(1u << OVS_KEY_ATTR_CONN_MARK); + break; case OVS_KEY_ATTR_TUNNEL: { enum odp_key_fitness res; @@ -3946,11 +3960,14 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, flow->conn_state = nl_attr_get_u8(attrs[OVS_KEY_ATTR_CONN_STATE]); 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_CONN_MARK)) { + flow->conn_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_CONN_MARK]); + expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CONN_MARK; + } if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) { enum odp_key_fitness res; @@ -4632,6 +4649,25 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow, } } +static void +commit_set_conn_mark_action(const struct flow *flow, struct flow *base_flow, + struct ofpbuf *odp_actions, + struct flow_wildcards *wc, + bool use_masked) +{ + uint32_t key, mask, base; + + key = flow->conn_mark; + base = base_flow->conn_mark; + mask = wc->masks.conn_mark; + + if (commit(OVS_KEY_ATTR_CONN_MARK, use_masked, &key, &base, &mask, + sizeof key, odp_actions)) { + base_flow->conn_mark = base; + wc->masks.conn_mark = mask; + } +} + /* If any of the flow key data that ODP actions can modify are different in * 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow * key from 'base' into 'flow', and then changes 'base' the same way. Does not @@ -4655,6 +4691,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, commit_vlan_action(flow->vlan_tci, base, odp_actions, wc); commit_set_priority_action(flow, base, odp_actions, wc, use_masked); commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked); + commit_set_conn_mark_action(flow, base, odp_actions, wc, use_masked); return slow; } diff --git a/lib/odp-util.h b/lib/odp-util.h index a7d93d0..da70fc4 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -122,6 +122,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); * 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_CONN_MARK 4 -- 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 @@ -131,7 +132,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 504 + * total 512 * * 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-actions.c b/lib/ofp-actions.c index f546992..2d2d7e6 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -4374,8 +4374,8 @@ format_SAMPLE(const struct ofpact_sample *a, struct ds *s) * * Pass traffic to the connection tracker. If 'flags' is * NX_CT_F_RECIRC, traffic is recirculated back to flow table - * with the NXM_NX_CONN_STATE and NXM_NX_CONN_STATE_W matches set. A - * standard "resubmit" action is not sufficient, since connection + * with the NXM_NX_CONN_STATE[_W] and NXM_NX_CONN_MARK[_W] matches set. + * A standard "resubmit" action is not sufficient, since connection * tracking occurs outside of the classifier. The 'zone' argument * specifies a context within which the tracking is done. */ struct nx_action_conntrack { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index e2b1f16..88be31c 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -165,6 +165,10 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, ds_put_format(string, " conn_zone=0x%"PRIx16, pin.fmd.conn_zone); } + if (pin.fmd.conn_mark != 0) { + ds_put_format(string, " conn_mark=0x%"PRIx32, pin.fmd.conn_mark); + } + ds_put_format(string, " (via %s)", ofputil_packet_in_reason_to_string(pin.reason, reasonbuf, sizeof reasonbuf)); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index f89775d..f1b134f 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -3313,6 +3313,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin, pin->fmd.pkt_mark = match->flow.pkt_mark; pin->fmd.conn_state = match->flow.conn_state; pin->fmd.conn_zone = match->flow.conn_zone; + pin->fmd.conn_mark = match->flow.conn_mark; } enum ofperr @@ -3462,6 +3463,10 @@ ofputil_packet_in_to_match(const struct ofputil_packet_in *pin, match_set_conn_zone(match, pin->fmd.conn_zone); } + if (pin->fmd.conn_mark != 0) { + match_set_conn_mark(match, pin->fmd.conn_mark); + } + match_set_in_port(match, pin->fmd.in_port); } diff --git a/lib/packets.h b/lib/packets.h index 418f674..c769643 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -67,6 +67,7 @@ struct pkt_metadata { union flow_in_port in_port; /* Input port. */ uint8_t conn_state; /* Connection state. */ uint16_t conn_zone; /* Connection zone. */ + uint32_t conn_mark; /* Connection mark. */ }; #define PKT_METADATA_INITIALIZER(PORT) \ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index c32d713..b514b89 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -2722,7 +2722,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, struct flow *flow = &ctx->xin->flow; struct flow_tnl flow_tnl; ovs_be16 flow_vlan_tci; - uint32_t flow_pkt_mark; + uint32_t flow_pkt_mark, flow_conn_mark; uint8_t flow_conn_state; uint16_t flow_conn_zone; uint8_t flow_nw_tos; @@ -2871,6 +2871,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, flow_conn_state = flow->conn_state; flow_conn_zone = flow->conn_zone; flow_nw_tos = flow->nw_tos; + flow_conn_mark = flow->conn_mark; if (count_skb_priorities(xport)) { memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority); @@ -2991,6 +2992,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, flow->pkt_mark = flow_pkt_mark; flow->conn_state = flow_conn_state; flow->conn_zone = flow_conn_zone; + flow->conn_mark = flow_conn_mark; flow->nw_tos = flow_nw_tos; } diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index ddb40ce..718ddfa 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1227,6 +1227,7 @@ check_##FIELD(struct dpif_backer *backer) \ CHECK_FEATURE(conn_state) CHECK_FEATURE(conn_zone) +CHECK_FEATURE(conn_mark) #undef CHECK_FEATURE @@ -1243,6 +1244,7 @@ check_support(struct dpif_backer *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); + backer->support.conn_mark = check_conn_mark(backer); } static int @@ -3931,7 +3933,8 @@ rule_check(struct rule *rule) minimatch_expand(&rule->cr.match, &match); if ((match.wc.masks.conn_state && !ofproto->backer->support.conn_state) - || (match.wc.masks.conn_zone && !ofproto->backer->support.conn_zone)) { + || (match.wc.masks.conn_zone && !ofproto->backer->support.conn_zone) + || (match.wc.masks.conn_mark && !ofproto->backer->support.conn_mark)) { 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 74800c8..5738d79 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -91,6 +91,7 @@ struct dpif_backer_support { bool ufid; bool conn_state; bool conn_zone; + bool conn_mark; }; size_t ofproto_dpif_get_max_mpls_depth(const struct ofproto_dpif *); diff --git a/ofproto/ofproto-unixctl.man b/ofproto/ofproto-unixctl.man index 83820ee..98c0ac2 100644 --- a/ofproto/ofproto-unixctl.man +++ b/ofproto/ofproto-unixctl.man @@ -105,6 +105,8 @@ Packet QoS priority. Mark of the packet. .IP \fIconn_state\fR Connection state of the packet. +.IP \fIconn_mark\fR +Connection mark of the packet. .IP \fItun_id\fR The tunnel ID on which the packet arrived. .IP \fIin_port\fR diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at index 5fc4132..65086db 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,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> +pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,conn_zone=0,conn_mark=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 0112086..bff40c9 100644 --- a/tests/kmod-traffic.at +++ b/tests/kmod-traffic.at @@ -313,3 +313,50 @@ TIME_WAIT src=10.1.1.1 dst=10.1.1.2 sport=<cleared> dport=<cleared> src=10.1.1.2 OVS_KMOD_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([conntrack - conn_mark]) +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),set_field:1->conn_mark,2 +in_port=2,conn_state=-trk,tcp,action=ct(recirc) +in_port=2,conn_state=+trk,conn_mark=1,tcp,action=1 +in_port=3,tcp,action=ct(commit),set_field:2->conn_mark,4 +in_port=4,conn_state=-trk,tcp,action=ct(recirc) +in_port=4,conn_state=+trk,conn_mark=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) | grep TIME], [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=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=2 use=1 +]) + +OVS_KMOD_VSWITCHD_STOP +AT_CLEANUP diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index f1e8105..112ed25 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,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 +pkt_mark=0,recirc_id=0,dp_hash=0,skb_priority=0,conn_state=0,conn_zone=0,conn_mark=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,conn_mark=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 bc23aba..7ddb4f4 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1497,7 +1497,7 @@ OVS_VSWITCHD_START instructions: meter,apply_actions,clear_actions,write_actions,write_metadata$goto Write-Actions and Apply-Actions features: actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue - supported on Set-Field: tun_id tun_src tun_dst tun_gbp_id tun_gbp_flags metadata in_port in_port_oxm pkt_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll + supported on Set-Field: tun_id tun_src tun_dst tun_gbp_id tun_gbp_flags metadata in_port in_port_oxm pkt_mark conn_mark reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 xreg0 xreg1 xreg2 xreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst nd_target nd_sll nd_tll matching: dp_hash: arbitrary mask recirc_id: exact match or wildcard @@ -1514,6 +1514,7 @@ OVS_VSWITCHD_START pkt_mark: arbitrary mask conn_state: arbitrary mask conn_zone: exact match or wildcard + conn_mark: arbitrary mask reg0: arbitrary mask reg1: arbitrary mask reg2: arbitrary mask @@ -1583,7 +1584,7 @@ AT_CHECK( # Check that the configuration was updated. mv expout orig-expout sed 's/classifier/main/ -79s/1000000/1024/' < orig-expout > expout +80s/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 fadd407..2fc2532 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1223,6 +1223,12 @@ 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. . +.IP \fBconn_mark=\fIvalue\fR[\fB/\fImask\fR] +Matches connection mark \fIvalue\fR either exactly or with optional +\fImask\fR. The mark is associated data with the connection tracker and +can only be matched or set 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