For most of the history of Open vSwitch, one could assume that a
given datapath flow key would consistently translate into the same
userspace struct flow representation. However, with the switch to
flow based tunneling, we now have a situation where a database
configuration change can cause a datapath flow key's in_port to
correspond to a completely different OpenFlow in_port possibly on a
completely different bridge. This can cause all sorts of problems,
including traffic black holes due to confused facet revalidations.
To solve the problem, this patch verifies that each facet's
subfacets still result in the appropriate struct flow. If a facet
fails this test, it is simply removed.
Bug #15213.
Signed-off-by: Ethan Jackson <ethan@nicira.com>
backer->need_revalidate = 0;
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
backer->need_revalidate = 0;
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct facet *facet, *next;
if (ofproto->backer != backer) {
continue;
}
if (ofproto->backer != backer) {
continue;
}
- HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
if (need_revalidate
|| tag_set_intersects(&revalidate_set, facet->tags)) {
facet_revalidate(facet);
if (need_revalidate
|| tag_set_intersects(&revalidate_set, facet->tags)) {
facet_revalidate(facet);
|| tag_set_intersects(&ofproto->backer->revalidate_set,
facet->tags))) {
facet_revalidate(facet);
|| tag_set_intersects(&ofproto->backer->revalidate_set,
facet->tags))) {
facet_revalidate(facet);
+
+ /* facet_revalidate() may have destroyed 'facet'. */
+ facet = facet_find(ofproto, flow, hash);
* 'facet' to the new rule and recompiles its actions.
*
* - If the rule found is the same as 'facet''s current rule, leaves 'facet'
* 'facet' to the new rule and recompiles its actions.
*
* - If the rule found is the same as 'facet''s current rule, leaves 'facet'
- * where it is and recompiles its actions anyway. */
+ * where it is and recompiles its actions anyway.
+ *
+ * - If any of 'facet''s subfacets correspond to a new flow according to
+ * ofproto_receive(), 'facet' is removed. */
static void
facet_revalidate(struct facet *facet)
{
static void
facet_revalidate(struct facet *facet)
{
COVERAGE_INC(facet_revalidate);
COVERAGE_INC(facet_revalidate);
+ /* Check that child subfacets still correspond to this facet. Tunnel
+ * configuration changes could cause a subfacet's OpenFlow in_port to
+ * change. */
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ struct ofproto_dpif *recv_ofproto;
+ struct flow recv_flow;
+ int error;
+
+ error = ofproto_receive(ofproto->backer, NULL, subfacet->key,
+ subfacet->key_len, &recv_flow, NULL,
+ &recv_ofproto, NULL, NULL);
+ if (error
+ || recv_ofproto != ofproto
+ || memcmp(&recv_flow, &facet->flow, sizeof recv_flow)) {
+ facet_remove(facet);
+ return;
+ }
+ }
+
new_rule = rule_dpif_lookup(ofproto, &facet->flow);
/* Calculate new datapath actions.
new_rule = rule_dpif_lookup(ofproto, &facet->flow);
/* Calculate new datapath actions.