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 <jrajaha...@nicira.com> --- v4: Fix struct flow padding for 32-bit builds. v3: Rebase, move OVS_KEY_ATTR_TCP_FLAGS out of the temporary area.Summary: 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.Summary: datapath/flow.c | 2 ++ datapath/flow.h | 2 ++ datapath/flow_netlink.c | 29 ++++++++++++++-- include/linux/openvswitch.h | 1 + include/openflow/nicira-ext.h | 12 +++++++ lib/flow.c | 3 +- lib/flow.h | 8 +++-- 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 | 73 +++++++++++++++++++++++++++++++++++++++++ utilities/ovs-ofctl.8.in | 34 +++++++++++++++++++ 19 files changed, 275 insertions(+), 13 deletions(-) diff --git a/datapath/flow.c b/datapath/flow.c index 2709cdc..921c158 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -428,6 +428,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)) { @@ -496,6 +497,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)) { diff --git a/datapath/flow.h b/datapath/flow.h index bf34487..8b3173c 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -95,6 +95,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. */ @@ -111,6 +112,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/datapath/flow_netlink.c b/datapath/flow_netlink.c index 515a9f6..fc6f42e 100644 --- a/datapath/flow_netlink.c +++ b/datapath/flow_netlink.c @@ -114,6 +114,7 @@ static bool 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) @@ -154,8 +155,10 @@ static bool 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) { @@ -186,8 +189,10 @@ static bool 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) { @@ -235,6 +240,7 @@ static 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), @@ -634,6 +640,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; @@ -1004,9 +1023,15 @@ int ovs_nla_put_flow(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/include/linux/openvswitch.h b/include/linux/openvswitch.h index 09c26b5..f7cca2d 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -283,6 +283,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */ + OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */ #ifdef __KERNEL__ OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index ca272fd..d88b758 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -1784,6 +1784,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 0678c6f..1d6b580 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -255,6 +255,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; } } @@ -513,7 +514,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 == 21); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22); 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 4bd1504..f1d3aad 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -37,7 +37,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 21 +#define FLOW_WC_SEQ 22 #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,14 +112,15 @@ 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]; }; BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); #define FLOW_U32S (sizeof(struct flow) / 4) /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ -BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 152 && - FLOW_WC_SEQ == 21); +BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 && + FLOW_WC_SEQ == 22); /* Represents the metadata fields of struct flow. */ struct flow_metadata { diff --git a/lib/match.c b/lib/match.c index 93f61f9..adc53c8 100644 --- a/lib/match.c +++ b/lib/match.c @@ -118,6 +118,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); @@ -538,6 +542,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; @@ -831,7 +848,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22); if (priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%u,", priority); @@ -1057,6 +1074,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 48c8aa2..b3a2873 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 8444ab7..e7aadc3 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 == 21); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22); /* 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 5c7ccfb..1831237 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -109,6 +109,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"; @@ -747,6 +748,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); @@ -1242,6 +1244,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); @@ -2059,6 +2068,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; @@ -2570,6 +2600,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; @@ -2955,6 +2989,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 173b534..a5864d5 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 == 21); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 8d05917..eb023b4 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -1548,7 +1548,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 == 21); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22); 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..7ab3ad0 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,62 @@ 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 + 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 c43b48c..60cd362 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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev