[ovs-dev] [PATCH v2 2/2] TCP flags matching support.
Jarno Rajahalme
jrajahalme at nicira.com
Wed Sep 25 16:42:29 UTC 2013
tcp_flags=flags/mask
Bitwise match on TCP flags. The flags and mask are 16-bit num‐
bers written in decimal or in hexadecimal prefixed by 0x. Each
1-bit in mask requires that the corresponding bit in port must
match. Each 0-bit in mask causes the corresponding bit to be
ignored.
TCP protocol currently defines 9 flag bits, and additional 3
bits are reserved (must be transmitted as zero), see RFCs 793,
3168, and 3540. The flag bits are, numbering from the least
significant bit:
0: FIN No more data from sender.
1: SYN Synchronize sequence numbers.
2: RST Reset the connection.
3: PSH Push function.
4: ACK Acknowledgement field significant.
5: URG Urgent pointer field significant.
6: ECE ECN Echo.
7: CWR Congestion Windows Reduced.
8: NS Nonce Sum.
9-11: Reserved.
12-15: Not matchable, must be zero.
Signed-off-by: Jarno Rajahalme <jrajahalme at nicira.com>
---
v2:
- datapath/flow.c: make netlink OVS_KEY_ATTR_TCP_FLAGS attribute optional
to be (more) compatible with older userspace implementations.
- lib/match.c: make full mask for tcp_flags to be 0xffff
- Use is_ip_any() instead of open coding it.
- lib/meta-flow.c: Do not allow tcp_flags on OF 1.0 or OF 1.1, as they
do not support it.
- lib/odp-util.c: Fix duplicate "0x" in printing OVS_KEY_ATTR_TCP_FLAGS.
- ovs-ofctl.8: Start TCP flag bit numbering from 0 instead on 1, do not
require non-flags bits in the mask to be zero.
datapath/flow.c | 31 +++++++++++++++--
datapath/flow.h | 2 ++
include/linux/openvswitch.h | 2 ++
include/openflow/nicira-ext.h | 12 +++++++
lib/flow.c | 3 +-
lib/flow.h | 7 ++--
lib/match.c | 27 ++++++++++++++-
lib/match.h | 2 ++
lib/meta-flow.c | 40 ++++++++++++++++++++++
lib/meta-flow.h | 2 ++
lib/nx-match.c | 4 ++-
lib/odp-execute.c | 1 +
lib/odp-util.c | 38 +++++++++++++++++++++
lib/ofp-util.c | 2 +-
ofproto/ofproto-dpif-xlate.c | 2 +-
tests/ofp-print.at | 6 ++--
tests/ovs-ofctl.at | 74 +++++++++++++++++++++++++++++++++++++++++
utilities/ovs-ofctl.8.in | 34 +++++++++++++++++++
18 files changed, 276 insertions(+), 13 deletions(-)
diff --git a/datapath/flow.c b/datapath/flow.c
index bb6ceed..1641ee3 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -135,6 +135,7 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
mask_allowed &= ~((1ULL << OVS_KEY_ATTR_IPV4)
| (1ULL << OVS_KEY_ATTR_IPV6)
| (1ULL << OVS_KEY_ATTR_TCP)
+ | (1ULL << OVS_KEY_ATTR_TCP_FLAGS)
| (1ULL << OVS_KEY_ATTR_UDP)
| (1ULL << OVS_KEY_ATTR_SCTP)
| (1ULL << OVS_KEY_ATTR_ICMP)
@@ -175,8 +176,10 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
if (match->key->ip.proto == IPPROTO_TCP) {
key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
- if (match->mask && (match->mask->key.ip.proto == 0xff))
+ if (match->mask && (match->mask->key.ip.proto == 0xff)) {
mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP;
+ mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS;
+ }
}
if (match->key->ip.proto == IPPROTO_ICMP) {
@@ -207,8 +210,10 @@ static bool ovs_match_validate(const struct sw_flow_match *match,
if (match->key->ip.proto == IPPROTO_TCP) {
key_expected |= 1ULL << OVS_KEY_ATTR_TCP;
- if (match->mask && (match->mask->key.ip.proto == 0xff))
+ if (match->mask && (match->mask->key.ip.proto == 0xff)) {
mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP;
+ mask_allowed |= 1ULL << OVS_KEY_ATTR_TCP_FLAGS;
+ }
}
if (match->key->ip.proto == IPPROTO_ICMPV6) {
@@ -916,6 +921,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
struct tcphdr *tcp = tcp_hdr(skb);
key->ipv4.tp.src = tcp->source;
key->ipv4.tp.dst = tcp->dest;
+ key->ipv4.tp.flags = TCP_FLAGS_BE16(tcp);
}
} else if (key->ip.proto == IPPROTO_UDP) {
if (udphdr_ok(skb)) {
@@ -984,6 +990,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key)
struct tcphdr *tcp = tcp_hdr(skb);
key->ipv6.tp.src = tcp->source;
key->ipv6.tp.dst = tcp->dest;
+ key->ipv6.tp.flags = TCP_FLAGS_BE16(tcp);
}
} else if (key->ip.proto == NEXTHDR_UDP) {
if (udphdr_ok(skb)) {
@@ -1145,6 +1152,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4),
[OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6),
[OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp),
+ [OVS_KEY_ATTR_TCP_FLAGS] = sizeof(__be16),
[OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp),
[OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp),
[OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp),
@@ -1544,6 +1552,19 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
attrs &= ~(1ULL << OVS_KEY_ATTR_TCP);
}
+ if (attrs & (1ULL << OVS_KEY_ATTR_TCP_FLAGS)) {
+ if (orig_attrs & (1ULL << OVS_KEY_ATTR_IPV4)) {
+ SW_FLOW_KEY_PUT(match, ipv4.tp.flags,
+ nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+ is_mask);
+ } else {
+ SW_FLOW_KEY_PUT(match, ipv6.tp.flags,
+ nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+ is_mask);
+ }
+ attrs &= ~(1ULL << OVS_KEY_ATTR_TCP_FLAGS);
+ }
+
if (attrs & (1ULL << OVS_KEY_ATTR_UDP)) {
const struct ovs_key_udp *udp_key;
@@ -1905,9 +1926,15 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey,
if (swkey->eth.type == htons(ETH_P_IP)) {
tcp_key->tcp_src = output->ipv4.tp.src;
tcp_key->tcp_dst = output->ipv4.tp.dst;
+ if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+ output->ipv4.tp.flags))
+ goto nla_put_failure;
} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
tcp_key->tcp_src = output->ipv6.tp.src;
tcp_key->tcp_dst = output->ipv6.tp.dst;
+ if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS,
+ output->ipv6.tp.flags))
+ goto nla_put_failure;
}
} else if (swkey->ip.proto == IPPROTO_UDP) {
struct ovs_key_udp *udp_key;
diff --git a/datapath/flow.h b/datapath/flow.h
index 5626b25..a200a79 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -103,6 +103,7 @@ struct sw_flow_key {
struct {
__be16 src; /* TCP/UDP/SCTP source port. */
__be16 dst; /* TCP/UDP/SCTP destination port. */
+ __be16 flags; /* TCP flags. */
} tp;
struct {
u8 sha[ETH_ALEN]; /* ARP source hardware address. */
@@ -119,6 +120,7 @@ struct sw_flow_key {
struct {
__be16 src; /* TCP/UDP/SCTP source port. */
__be16 dst; /* TCP/UDP/SCTP destination port. */
+ __be16 flags; /* TCP flags. */
} tp;
struct {
struct in6_addr target; /* ND target address. */
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 09c26b5..2642ca5 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -291,6 +291,8 @@ enum ovs_key_attr {
OVS_KEY_ATTR_MPLS = 62, /* array of struct ovs_key_mpls.
* The implementation may restrict
* the accepted length of the array. */
+ OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */
+
__OVS_KEY_ATTR_MAX
};
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index de5ff6a..b6384ef 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1783,6 +1783,18 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
#define NXM_NX_PKT_MARK NXM_HEADER (0x0001, 33, 4)
#define NXM_NX_PKT_MARK_W NXM_HEADER_W(0x0001, 33, 4)
+/* The flags in the TCP header.
+*
+* Prereqs:
+* NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd.
+* NXM_OF_IP_PROTO must match 6 exactly.
+*
+* Format: 16-bit integer with 4 most-significant bits forced to 0.
+*
+* Masking: Bits 0-11 fully maskable. */
+#define NXM_NX_TCP_FLAGS NXM_HEADER (0x0001, 34, 2)
+#define NXM_NX_TCP_FLAGS_W NXM_HEADER_W(0x0001, 34, 2)
+
/* ## --------------------- ## */
/* ## Requests and replies. ## */
/* ## --------------------- ## */
diff --git a/lib/flow.c b/lib/flow.c
index 9ab1961..bbec7b8 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -256,6 +256,7 @@ parse_tcp(struct ofpbuf *packet, struct ofpbuf *b, struct flow *flow)
if (tcp) {
flow->tp_src = tcp->tcp_src;
flow->tp_dst = tcp->tcp_dst;
+ flow->tcp_flags = tcp->tcp_ctl & htons(0x0fff);
packet->l7 = b->data;
}
}
@@ -514,7 +515,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
fmd->tun_id = flow->tunnel.tun_id;
fmd->tun_src = flow->tunnel.ip_src;
diff --git a/lib/flow.h b/lib/flow.h
index 75d95e8..0ce85b3 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -36,7 +36,7 @@ struct ofpbuf;
/* 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 20
+#define FLOW_WC_SEQ 21
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -103,6 +103,7 @@ struct flow {
ovs_be16 dl_type; /* Ethernet frame type. */
ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */
ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port. */
+ ovs_be16 tcp_flags; /* TCP flags. */
uint8_t dl_src[6]; /* Ethernet source address. */
uint8_t dl_dst[6]; /* Ethernet destination address. */
uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */
@@ -111,7 +112,7 @@ struct flow {
uint8_t arp_tha[6]; /* ARP/ND target hardware address. */
uint8_t nw_ttl; /* IP TTL/Hop Limit. */
uint8_t nw_frag; /* FLOW_FRAG_* flags. */
- uint8_t zeros[6];
+ uint8_t zeros[4];
};
BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
@@ -119,7 +120,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 &&
- FLOW_WC_SEQ == 20);
+ FLOW_WC_SEQ == 21);
/* Represents the metadata fields of struct flow. */
struct flow_metadata {
diff --git a/lib/match.c b/lib/match.c
index 03413fa..87c2ab3 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -122,6 +122,10 @@ match_wc_init(struct match *match, const struct flow *flow)
memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
}
+ if (flow->nw_proto == IPPROTO_TCP &&
+ flow->tcp_flags != 0) { /* XXX: How about matching zero flags? */
+ memset(&wc->masks.tcp_flags, 0xff, sizeof wc->masks.tcp_flags);
+ }
if (flow->nw_proto == IPPROTO_ICMPV6) {
memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
@@ -542,6 +546,19 @@ match_set_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
}
void
+match_set_tcp_flags(struct match *match, ovs_be16 flags)
+{
+ match_set_tcp_flags_masked(match, flags, OVS_BE16_MAX);
+}
+
+void
+match_set_tcp_flags_masked(struct match *match, ovs_be16 flags, ovs_be16 mask)
+{
+ match->flow.tcp_flags = flags & mask;
+ match->wc.masks.tcp_flags = mask;
+}
+
+void
match_set_nw_proto(struct match *match, uint8_t nw_proto)
{
match->flow.nw_proto = nw_proto;
@@ -835,7 +852,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%u,", priority);
@@ -1061,6 +1078,14 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
}
+ if (is_ip_any(f) && f->nw_proto == IPPROTO_TCP && wc->masks.tcp_flags) {
+ if (wc->masks.tcp_flags == htons(UINT16_MAX)) {
+ ds_put_format(s, "tcp_flags=0x%03"PRIx16",", ntohs(f->tcp_flags));
+ } else {
+ ds_put_format(s, "tcp_flags=0x%03"PRIx16"/0x%03"PRIx16",",
+ ntohs(f->tcp_flags), ntohs(wc->masks.tcp_flags));
+ }
+ }
if (s->length > start_len && ds_last(s) == ',') {
s->length--;
diff --git a/lib/match.h b/lib/match.h
index 7b104ee..07c9895 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -89,6 +89,8 @@ void match_set_tp_src(struct match *, ovs_be16);
void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
void match_set_tp_dst(struct match *, ovs_be16);
void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask);
+void match_set_tcp_flags(struct match *, ovs_be16);
+void match_set_tcp_flags_masked(struct match *, ovs_be16 flags, ovs_be16 mask);
void match_set_nw_proto(struct match *, uint8_t);
void match_set_nw_src(struct match *, ovs_be32);
void match_set_nw_src_masked(struct match *, ovs_be32 ip, ovs_be32 mask);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 3a31c29..272ebc8 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -559,6 +559,17 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
OXM_OF_TCP_DST, "OXM_OF_TCP_DST",
OFPUTIL_P_ANY,
OFPUTIL_P_NXM_OXM_ANY,
+ }, {
+ MFF_TCP_FLAGS, "tcp_flags", NULL,
+ 2, 12,
+ MFM_FULLY,
+ MFS_HEXADECIMAL,
+ MFP_TCP,
+ false,
+ NXM_NX_TCP_FLAGS, "NXM_NX_TCP_FLAGS",
+ NXM_NX_TCP_FLAGS, "NXM_NX_TCP_FLAGS",
+ OFPUTIL_P_NXM_OXM_ANY,
+ OFPUTIL_P_NXM_OXM_ANY,
},
{
@@ -915,6 +926,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_ICMPV4_CODE:
case MFF_ICMPV6_CODE:
return !wc->masks.tp_dst;
+ case MFF_TCP_FLAGS:
+ return !wc->masks.tcp_flags;
case MFF_N_IDS:
default:
@@ -1084,6 +1097,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
return !(value->u8 & ~IP_ECN_MASK);
case MFF_IP_FRAG:
return !(value->u8 & ~FLOW_NW_FRAG_MASK);
+ case MFF_TCP_FLAGS:
+ return !(value->be16 & ~htons(0x0fff));
case MFF_ARP_OP:
return !(value->be16 & htons(0xff00));
@@ -1282,6 +1297,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
value->be16 = flow->tp_dst;
break;
+ case MFF_TCP_FLAGS:
+ value->be16 = flow->tcp_flags;
+ break;
+
case MFF_ICMPV4_TYPE:
case MFF_ICMPV6_TYPE:
value->u8 = ntohs(flow->tp_src);
@@ -1474,6 +1493,10 @@ mf_set_value(const struct mf_field *mf,
match_set_tp_dst(match, value->be16);
break;
+ case MFF_TCP_FLAGS:
+ match_set_tcp_flags(match, value->be16);
+ break;
+
case MFF_ICMPV4_TYPE:
case MFF_ICMPV6_TYPE:
match_set_icmp_type(match, value->u8);
@@ -1668,6 +1691,10 @@ mf_set_flow_value(const struct mf_field *mf,
flow->tp_dst = value->be16;
break;
+ case MFF_TCP_FLAGS:
+ flow->tcp_flags = value->be16;
+ break;
+
case MFF_ICMPV4_TYPE:
case MFF_ICMPV6_TYPE:
flow->tp_src = htons(value->u8);
@@ -1879,6 +1906,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
match->flow.tp_dst = htons(0);
break;
+ case MFF_TCP_FLAGS:
+ match->wc.masks.tcp_flags = htons(0);
+ match->flow.tcp_flags = htons(0);
+ break;
+
case MFF_ND_TARGET:
memset(&match->wc.masks.nd_target, 0,
sizeof match->wc.masks.nd_target);
@@ -2049,6 +2081,10 @@ mf_set(const struct mf_field *mf,
match_set_tp_dst_masked(match, value->be16, mask->be16);
break;
+ case MFF_TCP_FLAGS:
+ match_set_tcp_flags_masked(match, value->be16, mask->be16);
+ break;
+
case MFF_N_IDS:
default:
NOT_REACHED();
@@ -2170,6 +2206,10 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
case MFF_ND_TLL:
break;
+ case MFF_TCP_FLAGS:
+ value->be16 &= ~htons(0x0fff);
+ break;
+
case MFF_IN_PORT_OXM:
value->be32 = ofputil_port_to_ofp11(u16_to_ofp(ntohs(value->be16)));
break;
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index dd8b95d..2f855b0 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -118,6 +118,8 @@ enum mf_field_id {
/* L4. */
MFF_TCP_SRC, /* be16 (used for IPv4 or IPv6) */
MFF_TCP_DST, /* be16 (used for IPv4 or IPv6) */
+ MFF_TCP_FLAGS, /* be16, 12 bits (4 MSB zeroed)
+ * used for IPv4 or IPv6) */
MFF_UDP_SRC, /* be16 (used for IPv4 or IPv6) */
MFF_UDP_DST, /* be16 (used for IPv4 or IPv6) */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 2d7ee34..ab45be8 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -530,6 +530,8 @@ nxm_put_ip(struct ofpbuf *b, const struct match *match,
flow->tp_src, match->wc.masks.tp_src);
nxm_put_16m(b, oxm ? OXM_OF_TCP_DST : NXM_OF_TCP_DST,
flow->tp_dst, match->wc.masks.tp_dst);
+ nxm_put_16m(b, NXM_NX_TCP_FLAGS,
+ flow->tcp_flags, match->wc.masks.tcp_flags);
} else if (flow->nw_proto == IPPROTO_UDP) {
nxm_put_16m(b, oxm ? OXM_OF_UDP_SRC : NXM_OF_UDP_SRC,
flow->tp_src, match->wc.masks.tp_src);
@@ -570,7 +572,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
/* Metadata. */
if (match->wc.masks.in_port.ofp_port) {
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index c91cc4a..a221870 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -115,6 +115,7 @@ odp_execute_set_action(struct ofpbuf *packet, const struct nlattr *a,
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_ARP:
case OVS_KEY_ATTR_ND:
+ case OVS_KEY_ATTR_TCP_FLAGS:
case __OVS_KEY_ATTR_MAX:
default:
NOT_REACHED();
diff --git a/lib/odp-util.c b/lib/odp-util.c
index aec4196..f8ead9b 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -108,6 +108,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
case OVS_KEY_ATTR_IPV4: return "ipv4";
case OVS_KEY_ATTR_IPV6: return "ipv6";
case OVS_KEY_ATTR_TCP: return "tcp";
+ case OVS_KEY_ATTR_TCP_FLAGS: return "tcp_flags";
case OVS_KEY_ATTR_UDP: return "udp";
case OVS_KEY_ATTR_SCTP: return "sctp";
case OVS_KEY_ATTR_ICMP: return "icmp";
@@ -746,6 +747,7 @@ odp_flow_key_attr_len(uint16_t type)
case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
+ case OVS_KEY_ATTR_TCP_FLAGS: return 2;
case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp);
case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp);
case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp);
@@ -1191,6 +1193,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
}
break;
+ case OVS_KEY_ATTR_TCP_FLAGS:
+ ds_put_format(ds, "0x%03"PRIx16, ntohs(nl_attr_get_be16(a)));
+ if (!is_exact) {
+ ds_put_format(ds, "/0x%03"PRIx16, ntohs(nl_attr_get_be16(ma)));
+ }
+ break;
+
case OVS_KEY_ATTR_UDP:
if (!is_exact) {
const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
@@ -2008,6 +2017,27 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
}
{
+ uint16_t tcp_flags, tcp_flags_mask;
+ int n = -1;
+
+ if (mask && sscanf(s, "tcp_flags(%"SCNi16"/%"SCNi16")%n",
+ &tcp_flags, &tcp_flags_mask, &n) > 0 && n > 0) {
+ if (tcp_flags != 0) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
+ }
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags_mask));
+ return n;
+ } else if (sscanf(s, "tcp_flags(%"SCNi16")%n", &tcp_flags, &n) > 0 && n > 0) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
+ if (mask) {
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS,
+ htons(UINT16_MAX));
+ }
+ return n;
+ }
+ }
+
+ {
int udp_src;
int udp_dst;
int udp_src_mask;
@@ -2521,6 +2551,10 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
sizeof *tcp_key);
tcp_key->tcp_src = data->tp_src;
tcp_key->tcp_dst = data->tp_dst;
+
+ if (data->tcp_flags) {
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_TCP_FLAGS, data->tcp_flags);
+ }
} else if (flow->nw_proto == IPPROTO_UDP) {
struct ovs_key_udp *udp_key;
@@ -2911,6 +2945,10 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
flow->tp_dst = tcp_key->tcp_dst;
expected_bit = OVS_KEY_ATTR_TCP;
}
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS;
+ flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]);
+ }
} else if (src_flow->nw_proto == IPPROTO_UDP
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
src_flow->dl_type == htons(ETH_TYPE_IPV6))
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 6a2bf5b..85c486c 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -84,7 +84,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 == 20);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 8b47ef7..93b38ba 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1534,7 +1534,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
if (!xport) {
xlate_report(ctx, "Nonexistent output port");
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index 52b84ef..cbc61a5 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -631,7 +631,7 @@ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
00 00 00 00 \
"], [0], [dnl
OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=0x014 tcp_csum:6d75
])
AT_CLEANUP
@@ -646,7 +646,7 @@ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
00 00 00 00 \
" 3], [0], [dnl
OFPT_PACKET_OUT (xid=0x0): in_port=1 actions=output:3 data_len=60
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=0x014 tcp_csum:6d75
00000000 50 54 00 00 00 05 50 54-00 00 00 06 08 00 45 00
00000010 00 28 00 00 40 00 40 06-b9 7c c0 a8 00 02 c0 a8
00000020 00 01 00 00 2b 60 00 00-00 00 6a 4f 2b 58 50 14
@@ -677,7 +677,7 @@ b9 7c c0 a8 00 02 c0 a8 00 01 00 00 2b 60 00 00 \
00 00 00 00 \
"], [0], [dnl
OFPT_PACKET_OUT (OF1.2) (xid=0x8858dfc5): in_port=LOCAL actions=FLOOD data_len=60
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104 tcp_csum:6d75
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=50:54:00:00:00:06,dl_dst=50:54:00:00:00:05,nw_src=192.168.0.2,nw_dst=192.168.0.1,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=0,tp_dst=11104,tcp_flags=0x014 tcp_csum:6d75
])
AT_CLEANUP
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index cbd6aec..37334bb 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -496,6 +496,13 @@ NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/ffff)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_OF_TCP_DST_W(FDE0/0000)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_OF_TCP_DST(4231)
+# TCP flags
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS(0131)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(00F0/0FF0)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(01E2/ffff)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(06) NXM_NX_TCP_FLAGS_W(00E1/0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(07) NXM_NX_TCP_FLAGS(4321)
+
# UDP source port
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC(8732)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(11) NXM_OF_UDP_SRC_W(0132/01FF)
@@ -783,6 +790,13 @@ NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_OF_TCP_DST(fde0)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
nx_pull_match() returned error OFPBMC_BAD_PREREQ
+# TCP flags
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(0131)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS_W(00f0/0ff0)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06), NXM_NX_TCP_FLAGS(01e2)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(06)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
# UDP source port
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC(8732)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(11), NXM_OF_UDP_SRC_W(0132/01ff)
@@ -2337,3 +2351,63 @@ OFPT_HELLO (xid=0x1):
00000000 01 00 00 0b 00 00 00 01-41 42 43 |........ABC |
])
AT_CLEANUP
+
+AT_SETUP([tcp flags - filtering])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
+AT_DATA([flows.txt], [dnl
+ in_port=1,tcp,tp_dst=80,tcp_flags=0x02/0x17,action=2 # Allow outbound web traffic bare-SYN
+ in_port=1,tcp,tp_dst=80,tcp_flags=0x10/0x10,action=2 # Allow outbound web traffic with ACK bit
+ in_port=1,tcp,tp_dst=80,tcp_flags=0x04/0x04,action=2 # Allow outbound web traffic with RST bit
+ in_port=2,tcp,tp_src=80,tcp_flags=0x10/0x10,action=1 # Allow inbound web traffic with ACK bit
+ in_port=2,tcp,tp_src=80,tcp_flags=0x04/0x04,action=1 # Allow inbound web traffic with RST bit
+ priority=0,in_port=1,action=drop # Default drop outbound
+ priority=0,in_port=2,action=drop # Default drop inbound
+])
+
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +5], [0], [dnl
+ br0 65534/100: (dummy)
+ p1 1/1: (dummy)
+ p2 2/2: (dummy)
+])
+
+dnl Outbound web traffic with base-SYN
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x002)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+
+dnl Outbopund web traffic with ACK bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x110)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+
+dnl Outbound web traffic with RST bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=80),tcp_flags(0x104)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 2
+])
+
+dnl Inbound web traffic with ACK bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0x010)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 1
+])
+
+dnl Inbound web traffic with RST bit
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0x014)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 1
+])
+
+dnl Inbound web traffic with SYN bit without ACK or RST bits
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=6,tos=0,ttl=64,frag=no),tcp(src=80,dst=8),tcp_flags(0xfeb)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: drop
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 526e12c..845e017 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -716,6 +716,40 @@ Like the exact-match forms of \fBtp_src\fR and \fBtp_dst\fR described
above, the bitwise match forms apply only when \fBdl_type\fR and
\fBnw_proto\fR specify TCP or UDP or SCTP.
.
+.IP \fBtcp_flags=\fIflags\fB/\fImask\fR
+Bitwise match on TCP flags. The \fIflags\fR and \fImask\fR are 16-bit
+numbers written in decimal or in hexadecimal prefixed by \fB0x\fR.
+Each 1-bit in \fImask\fR requires that the corresponding bit in
+\fIflags\fR must match. Each 0-bit in \fImask\fR causes the corresponding
+bit to be ignored.
+.IP
+TCP protocol currently defines 9 flag bits, and additional 3 bits are
+reserved (must be transmitted as zero), see RFCs 793, 3168, and 3540.
+The flag bits are, numbering from the least significant bit:
+.RS
+.IP "\fB0: FIN\fR"
+No more data from sender.
+.IP "\fB1: SYN\fR"
+Synchronize sequence numbers.
+.IP "\fB2: RST\fR"
+Reset the connection.
+.IP "\fB3: PSH\fR"
+Push function.
+.IP "\fB4: ACK\fR"
+Acknowledgement field significant.
+.IP "\fB5: URG\fR"
+Urgent pointer field significant.
+.IP "\fB6: ECE\fR"
+ECN Echo.
+.IP "\fB7: CWR\fR"
+Congestion Windows Reduced.
+.IP "\fB8: NS\fR"
+Nonce Sum.
+.IP "\fB9-11:\fR"
+Reserved.
+.IP "\fB12-15:\fR"
+Not matchable, must be zero.
+.RE
.IP \fBicmp_type=\fItype\fR
.IQ \fBicmp_code=\fIcode\fR
When \fBdl_type\fR and \fBnw_proto\fR specify ICMP or ICMPv6, \fItype\fR
--
1.7.10.4
More information about the dev
mailing list