summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Pfaff <blp@nicira.com>2014-02-11 08:24:16 -0800
committerBen Pfaff <blp@nicira.com>2014-02-11 09:43:26 -0800
commitd2bf1bf62e81862fe02795277b9427916f2d9425 (patch)
treec514369147ab8141f1b382954c560f1ca8229f4c
parent89ffab9d002bb74c829a45c499386769749f741c (diff)
downloadopenvswitch-d2bf1bf62e81862fe02795277b9427916f2d9425.tar.gz
ofproto-dpif-xlate: Make flows that match ICMP fields revalidate correctly.
ICMPv4 and ICMPv6 have 8-bit "type" and "code" fields. struct flow uses the low 8 bits of the 16-bit tp_src and tp_dst members to represent these fields. The datapath interface, on the other hand, represents them with just 8 bits each. This means that if the high 8 bits of the masks for these fields somehow become set (meaning to match on the nonexistent "high bits" of these fields) during translation, then they will get chopped off by a round trip through the datapath, and revalidation will spot that as an inconsistency and delete the flow. This commit avoids the problem by making sure that only the low 8 bits of either field can be unwildcarded for ICMP. This seems like the minimal fix for this problem, appropriate for backporting to earlier branches. The root of the issue is that these high bits can get set in the match at all. I have some leads on that, but they require more invasive changes elsewhere. Bug #23320. Reported-by: Krishna Miriyala <miriyalak@vmware.com> Signed-off-by: Ben Pfaff <blp@nicira.com> Acked-by: Andy Zhou <azhou@nicira.com>
-rw-r--r--lib/meta-flow.c14
-rw-r--r--lib/packets.h14
-rw-r--r--ofproto/ofproto-dpif-xlate.c19
3 files changed, 31 insertions, 16 deletions
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 51de89b91..b2c6cddc4 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -957,20 +957,6 @@ mf_is_mask_valid(const struct mf_field *mf, const union mf_value *mask)
NOT_REACHED();
}
-static bool
-is_icmpv4(const struct flow *flow)
-{
- return (flow->dl_type == htons(ETH_TYPE_IP)
- && flow->nw_proto == IPPROTO_ICMP);
-}
-
-static bool
-is_icmpv6(const struct flow *flow)
-{
- return (flow->dl_type == htons(ETH_TYPE_IPV6)
- && flow->nw_proto == IPPROTO_ICMPV6);
-}
-
/* Returns true if 'flow' meets the prerequisites for 'mf', false otherwise. */
bool
mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
diff --git a/lib/packets.h b/lib/packets.h
index b77691465..9ad1a9fea 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -618,6 +618,18 @@ static inline bool is_ip_any(const struct flow *flow)
return dl_type_is_ip_any(flow->dl_type);
}
+static inline bool is_icmpv4(const struct flow *flow)
+{
+ return (flow->dl_type == htons(ETH_TYPE_IP)
+ && flow->nw_proto == IPPROTO_ICMP);
+}
+
+static inline bool is_icmpv6(const struct flow *flow)
+{
+ return (flow->dl_type == htons(ETH_TYPE_IPV6)
+ && flow->nw_proto == IPPROTO_ICMPV6);
+}
+
void format_ipv6_addr(char *addr_str, const struct in6_addr *addr);
void print_ipv6_addr(struct ds *string, const struct in6_addr *addr);
void print_ipv6_masked(struct ds *string, const struct in6_addr *addr,
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 5b34c2e33..89131ebba 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -2531,6 +2531,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
struct xlate_ctx ctx;
size_t ofpacts_len;
bool tnl_may_send;
+ bool is_icmp;
COVERAGE_INC(xlate_actions);
@@ -2585,6 +2586,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
memset(&wc->masks.skb_priority, 0xff, sizeof wc->masks.skb_priority);
memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type);
wc->masks.nw_frag |= FLOW_NW_FRAG_MASK;
+ is_icmp = is_icmpv4(flow) || is_icmpv6(flow);
tnl_may_send = tnl_xlate_init(&ctx.base_flow, flow, wc);
if (ctx.xbridge->has_netflow) {
@@ -2694,6 +2696,21 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
* use non-header fields as part of the cache. */
flow_wildcards_clear_non_packet_fields(wc);
+ /* ICMPv4 and ICMPv6 have 8-bit "type" and "code" fields. struct flow uses
+ * the low 8 bits of the 16-bit tp_src and tp_dst members to represent
+ * these fields. The datapath interface, on the other hand, represents
+ * them with just 8 bits each. This means that if the high 8 bits of the
+ * masks for these fields somehow become set, then they will get chopped
+ * off by a round trip through the datapath, and revalidation will spot
+ * that as an inconsistency and delete the flow. Avoid the problem here by
+ * making sure that only the low 8 bits of either field can be unwildcarded
+ * for ICMP.
+ */
+ if (is_icmp) {
+ wc->masks.tp_src &= htons(UINT8_MAX);
+ wc->masks.tp_dst &= htons(UINT8_MAX);
+ }
+
out:
ovs_rwlock_unlock(&xlate_rwlock);