An RFC to get back early feedback on exposing Linux's kernel connection tracker to OVS. The code has a few rough spots that will be addressed in the next version:
- Need better interface than setting individual flags for the state. - Need support for IP frags. - Need support for zones. - Should have ability to match on "invalid" connection states. - Should only allow conntrack() if conn_state is 0x00 to prevent loops. I'd be interested in hearing back suggestion on improvements other than those mentioned above. Here's a simple example flow table to allow outbound TCP traffic from port 1; drop traffic from port 2 that was not initiated by port 1: ovs-ofctl add-flow br0 \ "in_port=1,conn_state=0x00/0x80,tcp,action=conntrack(zone=0),normal" ovs-ofctl add-flow br0 \ "in_port=2,conn_state=0x00/0x80,tcp,action=conntrack(flags=1,zone=0)" ovs-ofctl add-flow br0 in_port=2,conn_state=0x82/0x83,tcp,action=1 ovs-ofctl add-flow br0 in_port=2,conn_state=0x81/0x83,tcp,action=drop ovs-ofctl add-flow br0 priority=10,action=normal --- datapath/actions.c | 49 +++++++++++ datapath/datapath.c | 9 ++ datapath/flow.c | 25 ++++++ datapath/flow.h | 3 + datapath/flow_netlink.c | 21 +++++- datapath/linux/compat/include/linux/openvswitch.h | 3 + include/openflow/nicira-ext.h | 22 +++++ lib/dpif-netdev.c | 1 + lib/dpif.c | 1 + lib/flow.c | 61 ++++++++++++-- lib/flow.h | 9 ++- lib/match.c | 40 +++++++++- lib/match.h | 3 + lib/meta-flow.c | 36 ++++++++ lib/meta-flow.h | 1 + lib/nx-match.c | 6 +- lib/odp-execute.c | 8 ++ lib/odp-util.c | 59 ++++++++++++++ lib/odp-util.h | 5 +- lib/ofp-actions.c | 90 +++++++++++++++++++++ lib/ofp-actions.h | 18 ++++ lib/ofp-print.c | 4 + lib/ofp-util.c | 7 ++- lib/packets.h | 1 + ofproto/ofproto-dpif-xlate.c | 19 ++++- ofproto/ofproto-unixctl.man | 2 + tests/dpif-netdev.at | 16 ++-- tests/odp.at | 20 +++--- tests/ofproto-dpif.at | 18 ++-- tests/ofproto.at | 5 +- utilities/ovs-ofctl.8.in | 20 +++++ 31 files changed, 535 insertions(+), 47 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index d70348e..908ed02 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -32,6 +32,7 @@ #include <net/ipv6.h> #include <net/checksum.h> #include <net/dsfield.h> +#include <net/netfilter/nf_conntrack_core.h> #include <net/sctp/checksum.h> #include "datapath.h" @@ -752,6 +753,46 @@ static void execute_hash(struct sk_buff *skb, const struct nlattr *attr) key->ovs_flow_hash = hash; } +static int conntrack(struct sk_buff *skb, uint16_t zone) +{ + struct sw_flow_key *key = OVS_CB(skb)->pkt_key; + int nh_ofs = skb_network_offset(skb); + struct vport *vport; + struct net *net; + + if (skb->nfct) { + pr_warn_once("Attempt to run through conntrack again\n"); + return 0; + } + +#ifdef CONFIG_NET_NS + vport = OVS_CB(skb)->input_vport; + if (!vport) + return EINVAL; + + net = vport->dp->net; +#else + net = &init_net; +#endif + + /* The conntrack module expects to be working at L3. */ + skb_pull(skb, nh_ofs); + + /* xxx What's the best return val? */ + if (nf_conntrack_in(net, PF_INET, NF_INET_PRE_ROUTING, skb) != NF_ACCEPT) + return EINVAL; + + if (nf_conntrack_confirm(skb) != NF_ACCEPT) + return EINVAL; + + /* Point back to L2, which OVS expects. */ + skb_push(skb, nh_ofs); + + key->phy.conn_state = ovs_map_nfctinfo(skb); + + return 0; +} + static int execute_set_action(struct sk_buff *skb, const struct nlattr *nested_attr) { @@ -897,6 +938,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_RECIRC: err = execute_recirc(dp, skb, a, rem); + /* xxx Work around crash when recirculating without forwarding. */ + /* xxx It's being fixed upstream. */ + if (last_action(a, rem)) + return err; break; case OVS_ACTION_ATTR_SET: @@ -906,6 +951,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_SAMPLE: err = sample(dp, skb, a); break; + + case OVS_ACTION_ATTR_CONNTRACK: + err = conntrack(skb, nla_get_u16(a)); + break; } if (unlikely(err)) { diff --git a/datapath/datapath.c b/datapath/datapath.c index 3ca9716..75a53fb 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -264,8 +264,17 @@ void ovs_dp_process_packet(struct sk_buff *skb, bool recirc) stats = this_cpu_ptr(dp->stats_percpu); /* Look up flow. */ + /* xxx Are we better off resetting the SKB hash, since we've changed + * xxx the value of a field? Will we always have collision for packets + * xxx that only vary based on the conn_state? */ +#if 0 flow = ovs_flow_tbl_lookup_stats(&dp->table, pkt_key, skb_get_hash(skb), &n_mask_hit); +#else + /* xxx Gross, clearing hash. */ + flow = ovs_flow_tbl_lookup_stats(&dp->table, pkt_key, 0, + &n_mask_hit); +#endif if (unlikely(!flow)) { struct dp_upcall_info upcall; diff --git a/datapath/flow.c b/datapath/flow.c index 12ebc5b..393c490 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -42,6 +42,7 @@ #include <net/ip.h> #include <net/ipv6.h> #include <net/ndisc.h> +#include <linux/netfilter/nf_conntrack_common.h> #include "datapath.h" #include "flow.h" @@ -678,6 +679,29 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key) return key_extract(skb, key); } +/* Map SKB connection state into the values used by flow definition. */ +u8 ovs_map_nfctinfo(struct sk_buff *skb) +{ + if (!skb->nfct) + return 0; + + /* xxx This should use #defines instead of numbers. */ + if (skb->nfctinfo == IP_CT_ESTABLISHED) + return 0x82; + else if (skb->nfctinfo == IP_CT_RELATED) + return 0x84; + else if (skb->nfctinfo == IP_CT_NEW) + return 0x81; + else if (skb->nfctinfo == IP_CT_ESTABLISHED_REPLY) + return 0xc2; + else if (skb->nfctinfo == IP_CT_RELATED_REPLY) + return 0xc4; + else if (skb->nfctinfo == IP_CT_NEW_REPLY) + return 0xc1; + else + return 0x80; +} + int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info, struct sk_buff *skb, struct sw_flow_key *key) @@ -704,6 +728,7 @@ int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info, key->phy.priority = skb->priority; key->phy.in_port = OVS_CB(skb)->input_vport->port_no; key->phy.skb_mark = skb->mark; + key->phy.conn_state = ovs_map_nfctinfo(skb); key->ovs_flow_hash = 0; key->recirc_id = 0; diff --git a/datapath/flow.h b/datapath/flow.h index 44ed10d..22f2c83 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -130,6 +130,7 @@ struct sw_flow_key { u32 priority; /* Packet QoS priority. */ u32 skb_mark; /* SKB mark. */ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ + u8 conn_state; /* Connection state. */ } __packed phy; /* Safe when right after 'tun_key'. */ u32 ovs_flow_hash; /* Datapath computed hash value. */ u32 recirc_id; /* Recirculation ID. */ @@ -252,6 +253,8 @@ void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *, void ovs_flow_stats_clear(struct sw_flow *); u64 ovs_flow_used_time(unsigned long flow_jiffies); +u8 ovs_map_nfctinfo(struct sk_buff *skb); + int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info, struct sk_buff *skb, struct sw_flow_key *key); /* Extract key from packet coming from userspace. */ diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c index 69d1919..caaaaaa 100644 --- a/datapath/flow_netlink.c +++ b/datapath/flow_netlink.c @@ -267,7 +267,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 != 22); + BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 23); return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ @@ -276,6 +276,7 @@ size_t ovs_key_attr_size(void) + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */ + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */ + + nla_total_size(1) /* OVS_KEY_ATTR_CONN_STATE */ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */ @@ -292,6 +293,7 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_PRIORITY] = sizeof(u32), [OVS_KEY_ATTR_IN_PORT] = sizeof(u32), [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32), + [OVS_KEY_ATTR_CONN_STATE] = sizeof(u8), [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet), [OVS_KEY_ATTR_VLAN] = sizeof(__be16), [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16), @@ -663,6 +665,13 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, return -EINVAL; *attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL); } + + if (*attrs & (1ULL << OVS_KEY_ATTR_CONN_STATE)) { + uint8_t conn_state = nla_get_u8(a[OVS_KEY_ATTR_CONN_STATE]); + + SW_FLOW_KEY_PUT(match, phy.conn_state, conn_state, is_mask); + *attrs &= ~(1ULL << OVS_KEY_ATTR_CONN_STATE); + } return 0; } @@ -1146,6 +1155,9 @@ int ovs_nla_put_flow(struct datapath *dp, const struct sw_flow_key *swkey, if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark)) goto nla_put_failure; + if (nla_put_u8(skb, OVS_KEY_ATTR_CONN_STATE, output->phy.conn_state)) + goto nla_put_failure; + nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); if (!nla) goto nla_put_failure; @@ -1616,6 +1628,7 @@ static int validate_set(const struct nlattr *a, case OVS_KEY_ATTR_PRIORITY: case OVS_KEY_ATTR_SKB_MARK: + case OVS_KEY_ATTR_CONN_STATE: case OVS_KEY_ATTR_ETHERNET: break; @@ -1750,7 +1763,8 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr, [OVS_ACTION_ATTR_POP_VLAN] = 0, [OVS_ACTION_ATTR_SET] = (u32)-1, [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, - [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash) + [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), + [OVS_ACTION_ATTR_CONNTRACK] = sizeof(u16) }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -1856,6 +1870,9 @@ static int __ovs_nla_copy_actions(const struct nlattr *attr, skip_copy = true; break; + case OVS_ACTION_ATTR_CONNTRACK: + break; + default: return -EINVAL; } diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 9c18b3b..50dfcac 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -329,6 +329,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_MPLS, /* array of struct ovs_key_mpls. * The implementation may restrict * the accepted length of the array. */ + OVS_KEY_ATTR_CONN_STATE,/* u8 conn state */ #ifdef __KERNEL__ /* Only used within kernel data path. */ @@ -603,6 +604,7 @@ struct ovs_action_hash { * indicate the new packet contents. This could potentially still be * %ETH_P_MPLS if the resulting MPLS label stack is not empty. If there * is no MPLS label stack, as determined by ethertype, no action is taken. + * @OVS_ACTION_ATTR_CONNTRACK: Track the connection. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -621,6 +623,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */ OVS_ACTION_ATTR_PUSH_MPLS, /* struct ovs_action_push_mpls. */ OVS_ACTION_ATTR_POP_MPLS, /* __be16 ethertype. */ + OVS_ACTION_ATTR_CONNTRACK, /* u16 zone. */ __OVS_ACTION_ATTR_MAX }; diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index bbf3388..57f0133 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -968,6 +968,28 @@ OFP_ASSERT(sizeof(struct nx_async_config) == 24); * Masking: not maskable. */ #define NXM_NX_RECIRC_ID NXM_HEADER (0x0001, 36, 4) +/* Connection tracking state. + * + * The connection tracking state is populated by the NXAST_CONNTRACK + * action. The following flags are defined: + * + * - CONN_STATE_TRACKED (0x80): Connection tracking has occurred. + * - CONN_STATE_REPLY (0x40): This flow did not initiate the connection. + * + * The following values describe the state of the connection: + * + * - New (0x01): This is the beginning of a new connection. + * - Established (0x02): This is part of an already existing connection. + * - Related (0x04): This is a new connection that is "expected". + * + * Prereqs: None. + * + * Format: 8-bit fully maskable + * + * Masking: Fully maskable. */ +#define NXM_NX_CONN_STATE NXM_HEADER (0x0001, 37, 1) +#define NXM_NX_CONN_STATE_W NXM_HEADER_W(0x0001, 37, 1) + /* ## --------------------- ## */ /* ## Requests and replies. ## */ /* ## --------------------- ## */ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 7401293..ef6b722 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -2241,6 +2241,7 @@ dp_execute_cb(void *aux_, struct dpif_packet **packets, int cnt, case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_UNSPEC: + case OVS_ACTION_ATTR_CONNTRACK: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/dpif.c b/lib/dpif.c index b2a1972..d4edbdb 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1043,6 +1043,7 @@ dpif_execute_helper_cb(void *aux_, struct dpif_packet **packets, int cnt, case OVS_ACTION_ATTR_SET: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_UNSPEC: + case OVS_ACTION_ATTR_CONNTRACK: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/flow.c b/lib/flow.c index 29b331e..a27b4f5 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -70,11 +70,12 @@ BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) + 3 offsetof(struct flow, nw_proto) / 4 == offsetof(struct flow, nw_tos) / 4); -/* TCP flags in the first half of a BE32, zeroes in the other half. */ +/* TCP flags in the first half of a BE32, 'conn_state' and pad in the + * other half. */ BUILD_ASSERT_DECL(offsetof(struct flow, tcp_flags) + 2 - == offsetof(struct flow, pad) && + == offsetof(struct flow, conn_state) && offsetof(struct flow, tcp_flags) / 4 - == offsetof(struct flow, pad) / 4); + == offsetof(struct flow, conn_state) / 4); #if WORDS_BIGENDIAN #define TCP_FLAGS_BE32(tcp_ctl) ((OVS_FORCE ovs_be32)TCP_FLAGS_BE16(tcp_ctl) \ << 16) @@ -138,6 +139,19 @@ struct mf_ctx { #define miniflow_push_be32_(MF, OFS, VALUE) \ miniflow_push_uint32_(MF, OFS, (OVS_FORCE uint32_t)(VALUE)) +/* xxx Possibly clean this up. Assert if another value has been pushed. */ +/* Caller must have previously called a miniflow_push_* macro for "OFS" + * with no other push calls in between. */ +#define miniflow_update_uint32_(MF, OFS, VALUE, MASK) \ +{ \ + MINIFLOW_ASSERT(MF.data < MF.end && (OFS) % 4 == 0); \ + *(MF.data-1) |= (VALUE & MASK); \ +} + +#define miniflow_update_be32_(MF, OFS, VALUE, MASK) \ + miniflow_update_uint32_(MF, OFS, (OVS_FORCE uint32_t)(VALUE), \ + (OVS_FORCE uint32_t)(MASK)) + #define miniflow_push_uint16_(MF, OFS, VALUE) \ { \ MINIFLOW_ASSERT(MF.data < MF.end && \ @@ -188,6 +202,12 @@ struct mf_ctx { } \ } +#define miniflow_update_uint32(MF, FIELD, VALUE, MASK) \ + miniflow_update_uint32_(MF, offsetof(struct flow, FIELD), VALUE, MASK) + +#define miniflow_update_be32(MF, FIELD, VALUE, MASK) \ + miniflow_update_be32_(MF, offsetof(struct flow, FIELD), VALUE, MASK) + #define miniflow_push_uint16(MF, FIELD, VALUE) \ miniflow_push_uint16_(MF, offsetof(struct flow, FIELD), VALUE) @@ -570,13 +590,28 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md, miniflow_push_be32(mf, nw_frag, BYTES_TO_BE32(nw_frag, nw_tos, nw_ttl, nw_proto)); + /* xxx This is hacky to get around ICMPv6 issues. */ + if ((nw_frag & FLOW_NW_FRAG_LATER) || (nw_proto != IPPROTO_ICMPV6)) { + if (md) { + /* xxx Can't be use _check() version, since state may be 0 */ + miniflow_push_be32(mf, tcp_flags, + BYTES_TO_BE32(0, 0, md->conn_state, 0)); + } else { + /* xxx Hack so tcp_flags always has pushed entry */ + miniflow_push_be32(mf, tcp_flags, + BYTES_TO_BE32(0, 0, 0, 0)); + } + } + if (OVS_LIKELY(!(nw_frag & FLOW_NW_FRAG_LATER))) { if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) { if (OVS_LIKELY(size >= TCP_HEADER_LEN)) { const struct tcp_header *tcp = data; - miniflow_push_be32(mf, tcp_flags, - TCP_FLAGS_BE32(tcp->tcp_ctl)); + miniflow_update_be32(mf, tcp_flags, + TCP_FLAGS_BE32(tcp->tcp_ctl), + htonl(0xffff0000)); + miniflow_push_words(mf, tp_src, &tcp->tcp_src, 1); } } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) { @@ -622,6 +657,17 @@ miniflow_extract(struct ofpbuf *packet, const struct pkt_metadata *md, miniflow_push_words(mf, nd_target, nd_target, sizeof *nd_target / 4); } + /* xxx This is gross. */ + if (md) { + /* xxx Can't be use _check() version, since + * xxx state may be 0 */ + miniflow_push_be32(mf, tcp_flags, + BYTES_TO_BE32(0, 0, md->conn_state, 0)); + } else { + /* xxx Hack so tcp_flags always has pushed entry */ + miniflow_push_be32(mf, tcp_flags, + BYTES_TO_BE32(0, 0, 0, 0)); + } miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type)); miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code)); } @@ -665,7 +711,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc) void flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); fmd->dp_hash = flow->dp_hash; fmd->recirc_id = flow->recirc_id; @@ -675,6 +721,7 @@ flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd) fmd->metadata = flow->metadata; memcpy(fmd->regs, flow->regs, sizeof fmd->regs); fmd->pkt_mark = flow->pkt_mark; + fmd->conn_state = flow->conn_state; fmd->in_port = flow->in_port.ofp_port; } @@ -1335,7 +1382,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label)); /* Clear all L3 and L4 fields. */ - BUILD_ASSERT(FLOW_WC_SEQ == 27); + BUILD_ASSERT(FLOW_WC_SEQ == 28); memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0, sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT); } diff --git a/lib/flow.h b/lib/flow.h index 3b8d24d..7405437 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -38,7 +38,7 @@ struct pkt_metadata; /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ -#define FLOW_WC_SEQ 27 +#define FLOW_WC_SEQ 28 /* Number of Open vSwitch extension 32-bit registers. */ #define FLOW_N_REGS 8 @@ -122,7 +122,8 @@ struct flow { uint8_t arp_tha[6]; /* ARP/ND target hardware address. */ struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */ - ovs_be16 pad; /* Padding. */ + uint8_t conn_state ; /* Connection state. */ + uint8_t pad; /* Padding. */ /* L4 */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */ @@ -139,7 +140,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ BUILD_ASSERT_DECL(offsetof(struct flow, dp_hash) + sizeof(uint32_t) == sizeof(struct flow_tnl) + 176 - && FLOW_WC_SEQ == 27); + && FLOW_WC_SEQ == 28); /* Incremental points at which flow classification may be performed in * segments. @@ -172,6 +173,7 @@ 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. */ }; @@ -657,6 +659,7 @@ pkt_metadata_from_flow(const struct flow *flow) md.skb_priority = flow->skb_priority; md.pkt_mark = flow->pkt_mark; md.in_port = flow->in_port; + md.conn_state = flow->conn_state; return md; } diff --git a/lib/match.c b/lib/match.c index 8364906..5a63628 100644 --- a/lib/match.c +++ b/lib/match.c @@ -60,6 +60,10 @@ match_wc_init(struct match *match, const struct flow *flow) memset(&wc->masks.pkt_mark, 0xff, sizeof wc->masks.pkt_mark); } + if (flow->conn_state) { + memset(&wc->masks.conn_state, 0xff, sizeof wc->masks.conn_state); + } + for (i = 0; i < FLOW_N_REGS; i++) { if (flow->regs[i]) { memset(&wc->masks.regs[i], 0xff, sizeof wc->masks.regs[i]); @@ -335,6 +339,20 @@ match_set_pkt_mark_masked(struct match *match, uint32_t pkt_mark, uint32_t mask) } void +match_set_conn_state(struct match *match, uint8_t conn_state) +{ + match_set_conn_state_masked(match, conn_state, UINT8_MAX); +} + +void +match_set_conn_state_masked(struct match *match, uint8_t conn_state, + uint8_t mask) +{ + match->flow.conn_state = conn_state & mask; + match->wc.masks.conn_state = mask; +} + +void match_set_dl_type(struct match *match, ovs_be16 dl_type) { match->wc.masks.dl_type = OVS_BE16_MAX; @@ -867,6 +885,19 @@ format_ipv6_netmask(struct ds *s, const char *name, } static void +format_uint8_masked(struct ds *s, const char *name, + uint8_t value, uint8_t mask) +{ + if (mask) { + ds_put_format(s, "%s=%#"PRIx8, name, value); + if (mask != UINT8_MAX) { + ds_put_format(s, "/%#"PRIx8, mask); + } + ds_put_char(s, ','); + } +} + +static void format_be16_masked(struct ds *s, const char *name, ovs_be16 value, ovs_be16 mask) { @@ -959,7 +990,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%u,", priority); @@ -981,6 +1012,13 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority); } + if (wc->masks.conn_state) { + /* xxx Spell out the flags? To be prettier? */ + /* xxx If pretty print, remove format_uint8_masked(). */ + format_uint8_masked(s, "conn_state", f->conn_state, + wc->masks.conn_state); + } + 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 ce9fb28..b8e3745 100644 --- a/lib/match.h +++ b/lib/match.h @@ -71,6 +71,9 @@ void match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t ma void match_set_in_port(struct match *, ofp_port_t ofp_port); void match_set_pkt_mark(struct match *, uint32_t pkt_mark); 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_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[6]); diff --git a/lib/meta-flow.c b/lib/meta-flow.c index b76c11d..a7d0b91 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -207,6 +207,18 @@ const struct mf_field mf_fields[MFF_N_IDS] = { OFPUTIL_P_NXM_OXM_ANY, OFPUTIL_P_NXM_OXM_ANY, -1, + }, { + MFF_CONN_STATE, "conn_state", NULL, + MF_FIELD_SIZES(u8), + MFM_FULLY, + MFS_HEXADECIMAL, + MFP_NONE, + true, + NXM_NX_CONN_STATE, "NXM_NX_CONN_STATE", + NXM_NX_CONN_STATE, "NXM_NX_CONN_STATE", 0, + OFPUTIL_P_NXM_OXM_ANY, + OFPUTIL_P_NXM_OXM_ANY, + -1, }, #define REGISTER(IDX) \ @@ -943,6 +955,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) return !wc->masks.skb_priority; case MFF_PKT_MARK: return !wc->masks.pkt_mark; + case MFF_CONN_STATE: + return !wc->masks.conn_state; CASE_MFF_REGS: return !wc->masks.regs[mf->id - MFF_REG0]; CASE_MFF_XREGS: @@ -1184,6 +1198,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_IN_PORT: case MFF_SKB_PRIORITY: case MFF_PKT_MARK: + case MFF_CONN_STATE: CASE_MFF_REGS: CASE_MFF_XREGS: case MFF_ETH_SRC: @@ -1312,6 +1327,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, value->be32 = htonl(flow->pkt_mark); break; + case MFF_CONN_STATE: + value->u8 = flow->conn_state; + break; + CASE_MFF_REGS: value->be32 = htonl(flow->regs[mf->id - MFF_REG0]); break; @@ -1518,6 +1537,10 @@ mf_set_value(const struct mf_field *mf, match_set_pkt_mark(match, ntohl(value->be32)); break; + case MFF_CONN_STATE: + match_set_conn_state(match, value->u8); + break; + CASE_MFF_REGS: match_set_reg(match, mf->id - MFF_REG0, ntohl(value->be32)); break; @@ -1741,6 +1764,10 @@ mf_set_flow_value(const struct mf_field *mf, flow->pkt_mark = ntohl(value->be32); break; + case MFF_CONN_STATE: + flow->conn_state = value->u8; + break; + CASE_MFF_REGS: flow->regs[mf->id - MFF_REG0] = ntohl(value->be32); break; @@ -1962,6 +1989,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match) match->wc.masks.pkt_mark = 0; break; + case MFF_CONN_STATE: + match->flow.conn_state = 0; + match->wc.masks.conn_state = 0; + break; + CASE_MFF_REGS: match_set_reg_masked(match, mf->id - MFF_REG0, 0, 0); break; @@ -2203,6 +2235,10 @@ mf_set(const struct mf_field *mf, ntohl(mask->be32)); break; + case MFF_CONN_STATE: + match_set_conn_state_masked(match, value->u8, mask->u8); + 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 c11f7ab..865ce59 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -46,6 +46,7 @@ enum OVS_PACKED_ENUM mf_field_id { MFF_IN_PORT_OXM, /* be32 */ MFF_SKB_PRIORITY, /* be32 */ MFF_PKT_MARK, /* be32 */ + MFF_CONN_STATE, /* u8 */ #if FLOW_N_REGS == 8 MFF_REG0, /* be32 */ diff --git a/lib/nx-match.c b/lib/nx-match.c index 1d3205f..dec38c7 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -616,7 +616,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); /* Metadata. */ if (match->wc.masks.dp_hash) { @@ -732,6 +732,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, nxm_put_32m(b, mf_oxm_header(MFF_PKT_MARK, oxm), htonl(flow->pkt_mark), htonl(match->wc.masks.pkt_mark)); + /* Connection state. */ + nxm_put_8m(b, NXM_NX_CONN_STATE, flow->conn_state, + match->wc.masks.conn_state); + /* OpenFlow 1.1+ Metadata. */ nxm_put_64m(b, mf_oxm_header(MFF_METADATA, oxm), flow->metadata, match->wc.masks.metadata); diff --git a/lib/odp-execute.c b/lib/odp-execute.c index e1e9b57..8681eda 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -88,6 +88,10 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a, md->pkt_mark = nl_attr_get_u32(a); break; + case OVS_KEY_ATTR_CONN_STATE: + md->conn_state = nl_attr_get_u8(a); + break; + case OVS_KEY_ATTR_ETHERNET: odp_eth_set_addrs(&packet->ofpbuf, nl_attr_get_unspec(a, sizeof(struct ovs_key_ethernet))); @@ -314,6 +318,10 @@ odp_execute_actions__(void *dp, struct dpif_packet **packets, int cnt, } break; + case OVS_ACTION_ATTR_CONNTRACK: + /* xxx I don't think there's anything we can do here. */ + break; + case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/odp-util.c b/lib/odp-util.c index ffc3673..1c02db7 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -83,6 +83,7 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_HASH: return sizeof(struct ovs_action_hash); case OVS_ACTION_ATTR_SET: return -2; case OVS_ACTION_ATTR_SAMPLE: return -2; + case OVS_ACTION_ATTR_CONNTRACK: return sizeof(uint16_t); case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: @@ -104,6 +105,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize) case OVS_KEY_ATTR_ENCAP: return "encap"; 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_TUNNEL: return "tunnel"; case OVS_KEY_ATTR_IN_PORT: return "in_port"; case OVS_KEY_ATTR_ETHERNET: return "eth"; @@ -477,6 +479,11 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_SAMPLE: format_odp_sample_action(ds, a); break; + case OVS_ACTION_ATTR_CONNTRACK: { + uint16_t zone = nl_attr_get_u16(a); + ds_put_format(ds, "conntrack(zone=%"PRIu16")", zone); + break; + } case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: @@ -741,6 +748,16 @@ parse_odp_action(const char *s, const struct simap *port_names, } } + { + int zone; + int n = -1; + + if (ovs_scan(s, "conntrack(zone=%i)%n", &zone, &n)) { + nl_msg_put_u16(actions, OVS_ACTION_ATTR_CONNTRACK, zone); + return n; + } + } + return -EINVAL; } @@ -795,6 +812,7 @@ odp_flow_key_attr_len(uint16_t type) case OVS_KEY_ATTR_SKB_MARK: return 4; 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_TUNNEL: return -2; case OVS_KEY_ATTR_IN_PORT: return 4; case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet); @@ -1163,6 +1181,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma, } break; + case OVS_KEY_ATTR_CONN_STATE: + ds_put_format(ds, "%#"PRIx8, nl_attr_get_u8(a)); + if (!is_exact) { + ds_put_format(ds, "/%#"PRIx8, nl_attr_get_u8(ma)); + } + break; + case OVS_KEY_ATTR_TUNNEL: memset(&tun_key, 0, sizeof tun_key); if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) { @@ -1780,6 +1805,25 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, } { + uint8_t conn_state; + uint8_t conn_state_mask; + int n = -1; + + if (mask && ovs_scan(s, "conn_state(%"SCNi8"/%"SCNi8")%n", + &conn_state, &conn_state_mask, &n)) { + nl_msg_put_u8(key, OVS_KEY_ATTR_CONN_STATE, conn_state); + nl_msg_put_u8(mask, OVS_KEY_ATTR_CONN_STATE, conn_state_mask); + return n; + } else if (ovs_scan(s, "conn_state(%"SCNi8")%n", &conn_state, &n)) { + nl_msg_put_u8(key, OVS_KEY_ATTR_CONN_STATE, conn_state); + if (mask) { + nl_msg_put_u8(mask, OVS_KEY_ATTR_CONN_STATE, UINT8_MAX); + } + return n; + } + } + + { uint64_t tun_id, tun_id_mask; struct flow_tnl tun_key, tun_key_mask; int n = -1; @@ -2594,6 +2638,7 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow, } nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark); + nl_msg_put_u8(buf, OVS_KEY_ATTR_CONN_STATE, data->conn_state); if (recirc) { nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id); @@ -2813,6 +2858,9 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md) } nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark); +#if 0 + nl_msg_put_u8(buf, OVS_KEY_ATTR_CONN_STATE, md->conn_state); +#endif /* Add an ingress port attribute if 'odp_in_port' is not the magical * value "ODPP_NONE". */ @@ -2860,6 +2908,12 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len, md->pkt_mark = nl_attr_get_u32(nla); wanted_attrs &= ~(1u << OVS_KEY_ATTR_SKB_MARK); break; +#if 0 + case OVS_KEY_ATTR_CONN_STATE: + md->conn_state = nl_attr_get_u8(nla); + wanted_attrs &= ~(1u << OVS_KEY_ATTR_CONN_STATE); + break; +#endif case OVS_KEY_ATTR_TUNNEL: { enum odp_key_fitness res; @@ -3434,6 +3488,11 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK; } + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CONN_STATE)) { + 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_TUNNEL)) { enum odp_key_fitness res; diff --git a/lib/odp-util.h b/lib/odp-util.h index b9b33e5..92c55fc 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -110,6 +110,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); * OVS_KEY_ATTR_SKB_MARK 4 -- 4 8 * 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_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype) * OVS_KEY_ATTR_VLAN 2 2 4 8 @@ -119,13 +120,13 @@ 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 488 + * total 496 * * We include some slack space in case the calculation isn't quite right or we * add another field and forget to adjust this value. */ #define ODPUTIL_FLOW_KEY_BYTES 512 -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow * key. An array of "struct nlattr" might not, in theory, be sufficiently diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 61ee5dc..eca3b50 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -267,6 +267,9 @@ enum ofp_raw_action_type { /* NX1.0+(29): struct nx_action_sample. */ NXAST_RAW_SAMPLE, + + /* NX1.0+(32): struct nx_action_conntrack. */ + NXAST_RAW_CONNTRACK, }; /* OpenFlow actions are always a multiple of 8 bytes in length. */ @@ -3924,6 +3927,86 @@ format_SAMPLE(const struct ofpact_sample *a, struct ds *s) a->obs_domain_id, a->obs_point_id); } +/* Action structure for NXAST_CONNTRACK. + * + * Pass traffic to the connection tracker. If 'flags' is + * NX_CONNTRACK_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 + * tracking occurs outside of the classifier. The 'zone' argument + * specifies a context within which the tracking is done. */ +struct nx_action_conntrack { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_CONNTRACK. */ + ovs_be16 flags; /* Either 0 or NX_CONNTRACK_F_RECIRC. */ + ovs_be16 zone; /* Connection tracking context. */ + uint8_t pad[2]; +}; +OFP_ASSERT(sizeof(struct nx_action_conntrack) == 16); + +static enum ofperr +decode_NXAST_RAW_CONNTRACK(const struct nx_action_conntrack *nac, + struct ofpbuf *out) +{ + struct ofpact_conntrack *conntrack; + + conntrack = ofpact_put_CONNTRACK(out); + conntrack->flags = ntohs(nac->flags); + conntrack->zone = ntohs(nac->zone); + + return 0; +} + +static void +encode_CONNTRACK(const struct ofpact_conntrack *conntrack, + enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out) +{ + struct nx_action_conntrack *nac; + + nac = put_NXAST_CONNTRACK(out); + nac->flags = htons(conntrack->flags); + nac->zone = htons(conntrack->zone); +} + +/* Parses 'arg' as the argument to a "conntrack" action, and appends such an + * action to 'ofpacts'. + * + * Returns NULL if successful, otherwise a malloc()'d string describing the + * error. The caller is responsible for freeing the returned string. */ +static char * WARN_UNUSED_RESULT +parse_CONNTRACK(char *arg, struct ofpbuf *ofpacts, + enum ofputil_protocol *usable_protocols OVS_UNUSED) +{ + struct ofpact_conntrack *oc = ofpact_put_CONNTRACK(ofpacts); + char *key, *value; + + while (ofputil_parse_key_value(&arg, &key, &value)) { + char *error = NULL; + + if (!strcmp(key, "flags")) { + error = str_to_u16(value, "flags", &oc->flags); + } else if (!strcmp(key, "zone")) { + error = str_to_u16(value, "zone", &oc->zone); + } else { + error = xasprintf("invalid key \"%s\" in \"conntrack\" argument", + key); + } + if (error) { + return error; + } + } + return NULL; +} + +static void +format_CONNTRACK(const struct ofpact_conntrack *a, struct ds *s) +{ + ds_put_format(s, "conntrack(flags=%"PRIu16",zone=%"PRIu16")", + a->flags, a->zone); +} + /* Meter instruction. */ static void @@ -4304,6 +4387,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a) return true; case OFPACT_BUNDLE: case OFPACT_CLEAR_ACTIONS: + case OFPACT_CONNTRACK: case OFPACT_CONTROLLER: case OFPACT_DEC_MPLS_TTL: case OFPACT_DEC_TTL: @@ -4376,6 +4460,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) * in the action set is undefined. */ case OFPACT_BUNDLE: case OFPACT_CONTROLLER: + case OFPACT_CONNTRACK: case OFPACT_ENQUEUE: case OFPACT_EXIT: case OFPACT_FIN_TIMEOUT: @@ -4599,6 +4684,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type) case OFPACT_NOTE: case OFPACT_EXIT: case OFPACT_SAMPLE: + case OFPACT_CONNTRACK: default: return OVSINST_OFPIT11_APPLY_ACTIONS; } @@ -5160,6 +5246,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a, case OFPACT_SAMPLE: return 0; + case OFPACT_CONNTRACK: + return 0; + case OFPACT_CLEAR_ACTIONS: return 0; @@ -5578,6 +5667,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port) case OFPACT_GOTO_TABLE: case OFPACT_METER: case OFPACT_GROUP: + case OFPACT_CONNTRACK: default: return false; } diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h index 5436f24..a3a4b41 100644 --- a/lib/ofp-actions.h +++ b/lib/ofp-actions.h @@ -105,6 +105,7 @@ OFPACT(NOTE, ofpact_note, data, "note") \ OFPACT(EXIT, ofpact_null, ofpact, "exit") \ OFPACT(SAMPLE, ofpact_sample, ofpact, "sample") \ + OFPACT(CONNTRACK, ofpact_conntrack, ofpact, "conntrack") \ \ /* Instructions. */ \ OFPACT(METER, ofpact_meter, ofpact, "meter") \ @@ -472,6 +473,23 @@ BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) % OFPACT_ALIGNTO == 0); BUILD_ASSERT_DECL(offsetof(struct ofpact_nest, actions) == sizeof(struct ofpact_nest)); +/* Bits for 'flags' in struct nx_action_conntrack. + * + * If NX_CONNTRACK_F_RECIRC is set, then the packet will be recirculated + * through the datapath after running through the connection tracker. */ +enum nx_conntrack_flags { + NX_CONNTRACK_F_RECIRC = 1 << 0 +}; + +/* OFPACT_CONNTRACK. + * + * Used for NXAST_CONNTRACK. */ +struct ofpact_conntrack { + struct ofpact ofpact; + uint16_t flags; + uint16_t zone; +}; + static inline size_t ofpact_nest_get_action_len(const struct ofpact_nest *on) { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 43bfa17..12e0f6c 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -141,6 +141,10 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, ds_put_format(string, " pkt_mark=0x%"PRIx32, pin.fmd.pkt_mark); } + if (pin.fmd.conn_state != 0) { + ds_put_format(string, " conn_state=0x%"PRIx8, pin.fmd.conn_state); + } + 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 c8d38e8..f352336 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -185,7 +185,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); @@ -3276,6 +3276,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin, pin->fmd.metadata = match->flow.metadata; 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; } enum ofperr @@ -3412,6 +3413,10 @@ ofputil_packet_in_to_match(const struct ofputil_packet_in *pin, match_set_pkt_mark(match, pin->fmd.pkt_mark); } + if (pin->fmd.conn_state != 0) { + match_set_conn_state(match, pin->fmd.conn_state); + } + match_set_in_port(match, pin->fmd.in_port); } diff --git a/lib/packets.h b/lib/packets.h index 0258745..c7bddf5 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -62,6 +62,7 @@ struct pkt_metadata { uint32_t skb_priority; /* Packet priority for QoS. */ uint32_t pkt_mark; /* Packet mark. */ union flow_in_port in_port; /* Input port. */ + uint8_t conn_state; /* Connection state. */ }; #define PKT_METADATA_INITIALIZER(PORT) \ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index d366500..5e49701 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -2401,13 +2401,14 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, struct flow *flow = &ctx->xin->flow; ovs_be16 flow_vlan_tci; uint32_t flow_pkt_mark; + uint8_t flow_conn_state; uint8_t flow_nw_tos; odp_port_t out_port, odp_port; uint8_t dscp; /* If 'struct flow' gets additional metadata, we'll need to zero it out * before traversing a patch port. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); if (!xport) { xlate_report(ctx, "Nonexistent output port"); @@ -2488,6 +2489,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_nw_tos = flow->nw_tos; if (dscp_from_skb_priority(xport, flow->skb_priority, &dscp)) { @@ -2574,6 +2576,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* Restore flow */ flow->vlan_tci = flow_vlan_tci; flow->pkt_mark = flow_pkt_mark; + flow->conn_state = flow_conn_state; flow->nw_tos = flow_nw_tos; } @@ -3480,6 +3483,7 @@ ofpact_needs_recirculation_after_mpls(const struct xlate_ctx *ctx, case OFPACT_WRITE_ACTIONS: case OFPACT_CLEAR_ACTIONS: case OFPACT_SAMPLE: + case OFPACT_CONNTRACK: return false; case OFPACT_SET_IPV4_SRC: @@ -3819,6 +3823,19 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SAMPLE: xlate_sample_action(ctx, ofpact_get_SAMPLE(a)); break; + + case OFPACT_CONNTRACK: { + struct ofpact_conntrack *ofc = ofpact_get_CONNTRACK(a); + + nl_msg_put_u16(ctx->xout->odp_actions, + OVS_ACTION_ATTR_CONNTRACK, ofc->zone); + /* xxx Need to put the recirc here. */ + if (ofc->flags & NX_CONNTRACK_F_RECIRC) { + nl_msg_put_u32(ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC, + 0); /* xxx Choose real recird id */ + } + break; + } } } } diff --git a/ofproto/ofproto-unixctl.man b/ofproto/ofproto-unixctl.man index 89013d9..83820ee 100644 --- a/ofproto/ofproto-unixctl.man +++ b/ofproto/ofproto-unixctl.man @@ -103,6 +103,8 @@ only metadata. The metadata can be: Packet QoS priority. .IP \fIpkt_mark\fR Mark of the packet. +.IP \fIconn_state\fR +Connection state 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 c50b1a8..c1b4ae4 100644 --- a/tests/dpif-netdev.at +++ b/tests/dpif-netdev.at @@ -65,7 +65,7 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00: sleep 1 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) +skb_priority(0),skb_mark(0),conn_state(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 recirc_id=0,skb_priority=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> @@ -78,10 +78,10 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00: sleep 1 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) +skb_priority(0),skb_mark(0),conn_state(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,skb_priority=0,icmp,tun_id=0,tun_src=0.0.0.0,tun_dst=0.0.0.0,tun_tos=0,tun_ttl=0,,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,mpls_label=0,mpls_tc=0,mpls_ttl=0,mpls_bos=0,mpls_lse1=0,mpls_lse2=0,icmp_type=8,icmp_code=0, actions: <del> +pkt_mark=0,recirc_id=0,skb_priority=0,conn_state=0,icmp,tun_id=0,tun_src=0.0.0.0,tun_dst=0.0.0.0,tun_tos=0,tun_ttl=0,,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,mpls_label=0,mpls_tc=0,mpls_ttl=0,mpls_bos=0,mpls_lse1=0,mpls_lse2=0,icmp_type=8,icmp_code=0, actions: <del> recirc_id=0,skb_priority=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> ]) @@ -100,10 +100,10 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00: sleep 1 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) +skb_priority(0),skb_mark(0),conn_state(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_DUMP | STRIP_XOUT], [0], [dnl -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/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/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del> +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/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/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del> ]) # Now, the same again without megaflows. @@ -113,11 +113,11 @@ AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00: sleep 1 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) +skb_priority(0),skb_mark(0),conn_state(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_DUMP | STRIP_XOUT], [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:0, bytes:0, used:never, actions: <del> -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/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/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del> +skb_priority(0),skb_mark(0),conn_state(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:0, bytes:0, used:never, actions: <del> +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/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/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions: <del> ]) OVS_VSWITCHD_STOP diff --git a/tests/odp.at b/tests/odp.at index 5c00764..f1ca721 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -33,52 +33,52 @@ in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpl ]) (echo '# Valid forms without tun_id or VLAN header.' - set 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt + set 's/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/' odp-base.txt set ' -s/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/ +s/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/ ' odp-base.txt echo echo '# Valid forms with tunnel header.' - sed 's/^/skb_priority(0),tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),skb_mark(0x1234),recirc_id(0),dp_hash(0),/' odp-base.txt + sed 's/^/skb_priority(0),tunnel(tun_id=0x7f10354,src=10.10.10.10,dst=20.20.20.20,tos=0x0,ttl=64,flags(csum,key)),skb_mark(0x1234),conn_state(0),recirc_id(0),dp_hash(0),/' odp-base.txt echo echo '# Valid forms with VLAN header.' - sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/ + sed 's/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with MPLS header.' - sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/ + sed 's/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/ s/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt echo echo '# Valid forms with MPLS multicast header.' - sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/ + sed 's/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/ s/\(eth([[^)]]*),?\)/\1,eth_type(0x8848),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt echo echo '# Valid forms with tunnel and VLAN headers.' - sed 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),skb_mark(0),recirc_id(0),dp_hash(0),/ + sed 's/^/skb_priority(0),tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with QOS priority, tunnel, and VLAN headers.' - sed 's/^/skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),skb_mark(0),recirc_id(0),dp_hash(0),/ + sed 's/^/skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/ s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ s/$/)/' odp-base.txt echo echo '# Valid forms with IP first fragment.' -sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt | sed -n 's/,frag=no),/,frag=first),/p' +sed 's/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/' odp-base.txt | sed -n 's/,frag=no),/,frag=first),/p' echo echo '# Valid forms with IP later fragment.' -sed 's/^/skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),/' odp-base.txt | sed -n 's/,frag=no),.*/,frag=later)/p' +sed 's/^/skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),/' odp-base.txt | sed -n 's/,frag=no),.*/,frag=later)/p' ) > odp-in.txt AT_CAPTURE_FILE([odp-in.txt]) diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 7e7d907..66f0416 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -4629,12 +4629,12 @@ skb_priority(0),recirc_id(0),in_port(3),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0 ]) AT_CHECK([ovs-appctl dpif/dump-flows -m br0 | sort | STRIP_USED], [0], [dnl -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/0),in_port(p1),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/0),in_port(p2),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=0/0,code=0/0), packets:0, bytes:0, used:never, actions:drop ]) AT_CHECK([ovs-appctl dpif/dump-flows -m br1 | sort | STRIP_USED], [0], [dnl -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/0),in_port(p3),eth(src=50:54:00:00:00:09/00:00:00:00:00:00,dst=50:54:00:00:00:0a/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=10.0.0.2/0.0.0.0,dst=10.0.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:0, bytes:0, used:never, actions:drop ]) OVS_VSWITCHD_STOP @@ -4768,10 +4768,10 @@ recirc_id=0,skb_priority=0,ip,in_port=101,nw_frag=no, actions:100,2,3 ]) AT_CHECK([cat ovs-vswitchd.log | grep -e 'in_port(100).*packets:9' | FILTER_FLOW_DUMP], [0], [dnl -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(100),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:9, bytes:540, used:0.0s, actions:101,3,2 +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/0),in_port(100),eth(src=50:54:00:00:00:05/00:00:00:00:00:00,dst=50:54:00:00:00:07/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.1/0.0.0.0,dst=192.168.0.2/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:9, bytes:540, used:0.0s, actions:101,3,2 ]) AT_CHECK([cat ovs-vswitchd.log | grep -e 'in_port(101).*packets:4' | FILTER_FLOW_DUMP], [0], [dnl -skb_priority(0),skb_mark(0/0),recirc_id(0),dp_hash(0/0),in_port(101),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:4, bytes:240, used:0.0s, actions:100,2,3 +skb_priority(0),skb_mark(0/0),conn_state(0/0),recirc_id(0),dp_hash(0/0),in_port(101),eth(src=50:54:00:00:00:07/00:00:00:00:00:00,dst=50:54:00:00:00:05/00:00:00:00:00:00),eth_type(0x0800),ipv4(src=192.168.0.2/0.0.0.0,dst=192.168.0.1/0.0.0.0,proto=1/0,tos=0/0,ttl=64/0,frag=no/0xff),icmp(type=8/0,code=0/0), packets:4, bytes:240, used:0.0s, actions:100,2,3 ]) AT_CHECK([ovs-ofctl dump-ports br0 pbr0], [0], [dnl @@ -5318,12 +5318,12 @@ for i in 1 2 3 4; do done sleep 1 AT_CHECK([cat ovs-vswitchd.log | FILTER_FLOW_INSTALL | STRIP_USED], [0], [dnl -pkt_mark=0,recirc_id=0,skb_priority=0,icmp,tun_id=0,tun_src=0.0.0.0,tun_dst=0.0.0.0,tun_tos=0,tun_ttl=0,,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,mpls_label=0,mpls_tc=0,mpls_ttl=0,mpls_bos=0,mpls_lse1=0,mpls_lse2=0,icmp_type=8,icmp_code=0, actions:2 -pkt_mark=0,recirc_id=0,skb_priority=0,icmp,tun_id=0,tun_src=0.0.0.0,tun_dst=0.0.0.0,tun_tos=0,tun_ttl=0,,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,mpls_label=0,mpls_tc=0,mpls_ttl=0,mpls_bos=0,mpls_lse1=0,mpls_lse2=0,icmp_type=8,icmp_code=0, actions:drop +pkt_mark=0,recirc_id=0,skb_priority=0,conn_state=0,icmp,tun_id=0,tun_src=0.0.0.0,tun_dst=0.0.0.0,tun_tos=0,tun_ttl=0,,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,mpls_label=0,mpls_tc=0,mpls_ttl=0,mpls_bos=0,mpls_lse1=0,mpls_lse2=0,icmp_type=8,icmp_code=0, actions:2 +pkt_mark=0,recirc_id=0,skb_priority=0,conn_state=0,icmp,tun_id=0,tun_src=0.0.0.0,tun_dst=0.0.0.0,tun_tos=0,tun_ttl=0,,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,mpls_label=0,mpls_tc=0,mpls_ttl=0,mpls_bos=0,mpls_lse1=0,mpls_lse2=0,icmp_type=8,icmp_code=0, actions:drop ]) AT_CHECK([cat ovs-vswitchd.log | 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 -skb_priority(0),skb_mark(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:drop +skb_priority(0),skb_mark(0),conn_state(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 +skb_priority(0),skb_mark(0),conn_state(0),recirc_id(0),dp_hash(0),in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:3, bytes:180, used:0.0s, actions:drop ]) OVS_VSWITCHD_STOP AT_CLEANUP diff --git a/tests/ofproto.at b/tests/ofproto.at index d2e7196..e51155d 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1167,7 +1167,7 @@ OVS_VSWITCHD_START instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table 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 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 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 + supported on Set-Field: tun_id tun_src tun_dst metadata in_port in_port_oxm pkt_mark conn_state 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 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 matching: dp_hash: arbitrary mask recirc_id: exact match or wildcard @@ -1178,6 +1178,7 @@ OVS_VSWITCHD_START in_port: exact match or wildcard in_port_oxm: exact match or wildcard pkt_mark: arbitrary mask + conn_state: arbitrary mask reg0: arbitrary mask reg1: arbitrary mask reg2: arbitrary mask @@ -1247,7 +1248,7 @@ AT_CHECK( # Check that the configuration was updated. mv expout orig-expout sed 's/classifier/main/ -73s/1000000/1024/' < orig-expout > expout +74s/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 be82ed9..03b6988 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1108,6 +1108,26 @@ system components in order to facilitate interaction between subsystems. On Linux this corresponds to the skb mark but the exact implementation is platform-dependent. . +.IP \fBconn_state=\fIvalue\fR[\fB/\fImask\fR] +Matches packet connection state \fIvalue\fR either exactly or with optional +\fImask\fR. The following flags are defined: +.RS +.IP \fB0x80\fR +Connection tracking has occurred. +.IP \fB0x40\fR +The flow did not initiate the connection. +.RE +.IP +The following values describe the state of the connection: +.RS +.IP \fB0x01\fR +This is the beginning of a new connection. +.IP \fB0x02\fR +This is part of an already existing connection. +.IP \fB0x04\fR +This is a new connection that is "expected". +.RE +. .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.5.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev