2009-07-08 13:19:16 -07:00
|
|
|
/*
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
* Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
|
2009-07-08 13:19:16 -07:00
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at:
|
2009-07-08 13:19:16 -07:00
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2009-07-08 13:19:16 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include "mac-learning.h"
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2009-11-06 17:13:51 -08:00
|
|
|
#include "bitmap.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
#include "coverage.h"
|
|
|
|
#include "hash.h"
|
2016-03-25 14:10:21 -07:00
|
|
|
#include "openvswitch/list.h"
|
2017-11-03 13:53:53 +08:00
|
|
|
#include "openvswitch/poll-loop.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
#include "timeval.h"
|
2012-04-09 14:33:56 -07:00
|
|
|
#include "unaligned.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
#include "util.h"
|
2011-04-08 13:19:33 -07:00
|
|
|
#include "vlan-bitmap.h"
|
2010-07-16 11:02:49 -07:00
|
|
|
|
coverage: Make the coverage counters catalog program-specific.
Until now, the collection of coverage counters supported by a given OVS
program was not specific to that program. That means that, for example,
even though ovs-dpctl does not have anything to do with mac_learning, it
still has a coverage counter for it. This is confusing, at best.
This commit fixes the problem on some systems, in particular on ones that
use GCC and the GNU linker. It uses the feature of the GNU linker
described in its manual as:
If an orphaned section's name is representable as a C identifier then
the linker will automatically see PROVIDE two symbols: __start_SECNAME
and __end_SECNAME, where SECNAME is the name of the section. These
indicate the start address and end address of the orphaned section
respectively.
Systems that don't support these features retain the earlier behavior.
This commit also fixes the annoyance that files that include coverage
counters must be listed on COVERAGE_FILES in lib/automake.mk.
This commit also fixes the annoyance that modifying any source file that
includes a coverage counter caused all programs that link against
libopenvswitch.a to relink, even programs that the source file was not
linked into. For example, modifying ofproto/ofproto.c (which includes
coverage counters) caused tests/test-aes128 to relink, even though
test-aes128 does not link again ofproto.o.
2010-11-01 14:14:27 -07:00
|
|
|
COVERAGE_DEFINE(mac_learning_learned);
|
|
|
|
COVERAGE_DEFINE(mac_learning_expired);
|
2018-06-25 12:57:26 +02:00
|
|
|
COVERAGE_DEFINE(mac_learning_evicted);
|
|
|
|
COVERAGE_DEFINE(mac_learning_moved);
|
coverage: Make the coverage counters catalog program-specific.
Until now, the collection of coverage counters supported by a given OVS
program was not specific to that program. That means that, for example,
even though ovs-dpctl does not have anything to do with mac_learning, it
still has a coverage counter for it. This is confusing, at best.
This commit fixes the problem on some systems, in particular on ones that
use GCC and the GNU linker. It uses the feature of the GNU linker
described in its manual as:
If an orphaned section's name is representable as a C identifier then
the linker will automatically see PROVIDE two symbols: __start_SECNAME
and __end_SECNAME, where SECNAME is the name of the section. These
indicate the start address and end address of the orphaned section
respectively.
Systems that don't support these features retain the earlier behavior.
This commit also fixes the annoyance that files that include coverage
counters must be listed on COVERAGE_FILES in lib/automake.mk.
This commit also fixes the annoyance that modifying any source file that
includes a coverage counter caused all programs that link against
libopenvswitch.a to relink, even programs that the source file was not
linked into. For example, modifying ofproto/ofproto.c (which includes
coverage counters) caused tests/test-aes128 to relink, even though
test-aes128 does not link again ofproto.o.
2010-11-01 14:14:27 -07:00
|
|
|
|
2012-02-01 15:04:51 -08:00
|
|
|
/* Returns the number of seconds since 'e' (within 'ml') was last learned. */
|
2009-07-15 11:02:24 -07:00
|
|
|
int
|
2012-02-01 15:04:51 -08:00
|
|
|
mac_entry_age(const struct mac_learning *ml, const struct mac_entry *e)
|
2009-07-15 11:02:24 -07:00
|
|
|
{
|
|
|
|
time_t remaining = e->expires - time_now();
|
2012-02-01 15:04:51 -08:00
|
|
|
return ml->idle_time - remaining;
|
2009-07-15 11:02:24 -07:00
|
|
|
}
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
static uint32_t
|
2015-08-28 14:55:11 -07:00
|
|
|
mac_table_hash(const struct mac_learning *ml, const struct eth_addr mac,
|
2011-07-22 11:50:26 -07:00
|
|
|
uint16_t vlan)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
2014-03-27 20:22:37 -07:00
|
|
|
return hash_mac(mac, vlan, ml->secret);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct mac_entry *
|
2014-12-15 14:10:38 +01:00
|
|
|
mac_entry_from_lru_node(struct ovs_list *list)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
|
|
|
return CONTAINER_OF(list, struct mac_entry, lru_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct mac_entry *
|
2011-07-21 17:41:58 -07:00
|
|
|
mac_entry_lookup(const struct mac_learning *ml,
|
2015-08-28 14:55:11 -07:00
|
|
|
const struct eth_addr mac, uint16_t vlan)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
|
|
|
struct mac_entry *e;
|
2011-07-21 17:41:58 -07:00
|
|
|
|
2011-07-22 11:50:26 -07:00
|
|
|
HMAP_FOR_EACH_WITH_HASH (e, hmap_node, mac_table_hash(ml, mac, vlan),
|
2011-07-21 17:41:58 -07:00
|
|
|
&ml->table) {
|
|
|
|
if (e->vlan == vlan && eth_addr_equals(e->mac, mac)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
return e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
static struct mac_learning_port *
|
|
|
|
mac_learning_port_lookup(struct mac_learning *ml, void *port)
|
|
|
|
{
|
|
|
|
struct mac_learning_port *mlport;
|
|
|
|
|
|
|
|
HMAP_FOR_EACH_IN_BUCKET (mlport, hmap_node, hash_pointer(port, ml->secret),
|
|
|
|
&ml->ports_by_ptr) {
|
|
|
|
if (mlport->port == port) {
|
|
|
|
return mlport;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Changes the client-owned pointer for entry 'e' in 'ml' to 'port'. The
|
|
|
|
* pointer can be retrieved with mac_entry_get_port().
|
|
|
|
*
|
|
|
|
* The MAC-learning implementation treats the data that 'port' points to as
|
|
|
|
* opaque and never tries to dereference it. However, when a MAC learning
|
|
|
|
* table becomes overfull, so that eviction is required, the implementation
|
|
|
|
* does first evict MAC entries for the most common 'port's values in 'ml', so
|
|
|
|
* that there is a degree of fairness, that is, each port is entitled to its
|
|
|
|
* fair share of MAC entries. */
|
|
|
|
void
|
|
|
|
mac_entry_set_port(struct mac_learning *ml, struct mac_entry *e, void *port)
|
|
|
|
OVS_REQ_WRLOCK(ml->rwlock)
|
|
|
|
{
|
|
|
|
if (mac_entry_get_port(ml, e) != port) {
|
|
|
|
ml->need_revalidate = true;
|
|
|
|
|
|
|
|
if (e->mlport) {
|
|
|
|
struct mac_learning_port *mlport = e->mlport;
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&e->port_lru_node);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
|
2016-03-25 14:10:22 -07:00
|
|
|
if (ovs_list_is_empty(&mlport->port_lrus)) {
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
ovs_assert(mlport->heap_node.priority == 1);
|
|
|
|
hmap_remove(&ml->ports_by_ptr, &mlport->hmap_node);
|
|
|
|
heap_remove(&ml->ports_by_usage, &mlport->heap_node);
|
|
|
|
free(mlport);
|
|
|
|
} else {
|
|
|
|
ovs_assert(mlport->heap_node.priority > 1);
|
|
|
|
heap_change(&ml->ports_by_usage, &mlport->heap_node,
|
|
|
|
mlport->heap_node.priority - 1);
|
|
|
|
}
|
|
|
|
e->mlport = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port) {
|
|
|
|
struct mac_learning_port *mlport;
|
|
|
|
|
|
|
|
mlport = mac_learning_port_lookup(ml, port);
|
|
|
|
if (!mlport) {
|
|
|
|
mlport = xzalloc(sizeof *mlport);
|
|
|
|
hmap_insert(&ml->ports_by_ptr, &mlport->hmap_node,
|
|
|
|
hash_pointer(port, ml->secret));
|
|
|
|
heap_insert(&ml->ports_by_usage, &mlport->heap_node, 1);
|
|
|
|
mlport->port = port;
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_init(&mlport->port_lrus);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
} else {
|
|
|
|
heap_change(&ml->ports_by_usage, &mlport->heap_node,
|
|
|
|
mlport->heap_node.priority + 1);
|
|
|
|
}
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_push_back(&mlport->port_lrus, &e->port_lru_node);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
e->mlport = mlport;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finds one of the ports with the most MAC entries and evicts its least
|
|
|
|
* recently used entry. */
|
|
|
|
static void
|
|
|
|
evict_mac_entry_fairly(struct mac_learning *ml)
|
|
|
|
OVS_REQ_WRLOCK(ml->rwlock)
|
|
|
|
{
|
|
|
|
struct mac_learning_port *mlport;
|
|
|
|
struct mac_entry *e;
|
|
|
|
|
|
|
|
mlport = CONTAINER_OF(heap_max(&ml->ports_by_usage),
|
|
|
|
struct mac_learning_port, heap_node);
|
2016-03-25 14:10:22 -07:00
|
|
|
e = CONTAINER_OF(ovs_list_front(&mlport->port_lrus),
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
struct mac_entry, port_lru_node);
|
2018-06-25 12:57:26 +02:00
|
|
|
COVERAGE_INC(mac_learning_evicted);
|
2018-06-25 12:57:40 +02:00
|
|
|
ml->total_evicted++;
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
mac_learning_expire(ml, e);
|
|
|
|
}
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
/* If the LRU list is not empty, stores the least-recently-used entry in '*e'
|
|
|
|
* and returns true. Otherwise, if the LRU list is empty, stores NULL in '*e'
|
|
|
|
* and return false. */
|
|
|
|
static bool
|
|
|
|
get_lru(struct mac_learning *ml, struct mac_entry **e)
|
2013-07-22 11:11:54 -07:00
|
|
|
OVS_REQ_RDLOCK(ml->rwlock)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
2016-03-25 14:10:22 -07:00
|
|
|
if (!ovs_list_is_empty(&ml->lrus)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
*e = mac_entry_from_lru_node(ml->lrus.next);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
*e = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 15:04:51 -08:00
|
|
|
static unsigned int
|
|
|
|
normalize_idle_time(unsigned int idle_time)
|
|
|
|
{
|
|
|
|
return (idle_time < 15 ? 15
|
|
|
|
: idle_time > 3600 ? 3600
|
|
|
|
: idle_time);
|
|
|
|
}
|
|
|
|
|
2018-06-25 12:57:40 +02:00
|
|
|
/* Clear all the mac_learning statistics */
|
2018-06-25 12:58:05 +02:00
|
|
|
void
|
2018-06-25 12:57:40 +02:00
|
|
|
mac_learning_clear_statistics(struct mac_learning *ml)
|
|
|
|
{
|
|
|
|
if (ml != NULL) {
|
|
|
|
ml->total_learned = 0;
|
|
|
|
ml->total_expired = 0;
|
|
|
|
ml->total_evicted = 0;
|
|
|
|
ml->total_moved = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-01 15:04:51 -08:00
|
|
|
/* Creates and returns a new MAC learning table with an initial MAC aging
|
2012-12-08 12:32:33 -08:00
|
|
|
* timeout of 'idle_time' seconds and an initial maximum of MAC_DEFAULT_MAX
|
|
|
|
* entries. */
|
2009-07-08 13:19:16 -07:00
|
|
|
struct mac_learning *
|
2012-02-01 15:04:51 -08:00
|
|
|
mac_learning_create(unsigned int idle_time)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
|
|
|
struct mac_learning *ml;
|
|
|
|
|
|
|
|
ml = xmalloc(sizeof *ml);
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_init(&ml->lrus);
|
2011-07-21 17:41:58 -07:00
|
|
|
hmap_init(&ml->table);
|
2009-07-08 13:19:16 -07:00
|
|
|
ml->secret = random_uint32();
|
2010-01-19 10:41:46 -08:00
|
|
|
ml->flood_vlans = NULL;
|
2012-02-01 15:04:51 -08:00
|
|
|
ml->idle_time = normalize_idle_time(idle_time);
|
2012-12-08 12:32:33 -08:00
|
|
|
ml->max_entries = MAC_DEFAULT_MAX;
|
2013-08-01 18:04:07 -07:00
|
|
|
ml->need_revalidate = false;
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
hmap_init(&ml->ports_by_ptr);
|
|
|
|
heap_init(&ml->ports_by_usage);
|
2013-12-27 19:39:24 -08:00
|
|
|
ovs_refcount_init(&ml->ref_cnt);
|
2013-07-22 11:11:54 -07:00
|
|
|
ovs_rwlock_init(&ml->rwlock);
|
2018-06-25 12:57:40 +02:00
|
|
|
mac_learning_clear_statistics(ml);
|
2009-07-08 13:19:16 -07:00
|
|
|
return ml;
|
|
|
|
}
|
|
|
|
|
2013-06-18 19:41:51 -07:00
|
|
|
struct mac_learning *
|
|
|
|
mac_learning_ref(const struct mac_learning *ml_)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
2013-06-18 19:41:51 -07:00
|
|
|
struct mac_learning *ml = CONST_CAST(struct mac_learning *, ml_);
|
2009-11-06 17:13:51 -08:00
|
|
|
if (ml) {
|
2013-12-27 19:39:24 -08:00
|
|
|
ovs_refcount_ref(&ml->ref_cnt);
|
2013-06-18 19:41:51 -07:00
|
|
|
}
|
|
|
|
return ml;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unreferences (and possibly destroys) MAC learning table 'ml'. */
|
|
|
|
void
|
|
|
|
mac_learning_unref(struct mac_learning *ml)
|
|
|
|
{
|
2013-12-27 19:39:24 -08:00
|
|
|
if (ml && ovs_refcount_unref(&ml->ref_cnt) == 1) {
|
2011-07-22 12:00:46 -07:00
|
|
|
struct mac_entry *e, *next;
|
|
|
|
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
ovs_rwlock_wrlock(&ml->rwlock);
|
2011-07-22 12:00:46 -07:00
|
|
|
HMAP_FOR_EACH_SAFE (e, next, hmap_node, &ml->table) {
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
mac_learning_expire(ml, e);
|
2011-07-22 12:00:46 -07:00
|
|
|
}
|
2011-07-21 17:41:58 -07:00
|
|
|
hmap_destroy(&ml->table);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
hmap_destroy(&ml->ports_by_ptr);
|
|
|
|
heap_destroy(&ml->ports_by_usage);
|
2011-07-22 12:00:46 -07:00
|
|
|
|
2010-01-19 10:41:46 -08:00
|
|
|
bitmap_free(ml->flood_vlans);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
ovs_rwlock_unlock(&ml->rwlock);
|
2013-07-22 11:11:54 -07:00
|
|
|
ovs_rwlock_destroy(&ml->rwlock);
|
2011-04-08 13:19:56 -07:00
|
|
|
free(ml);
|
2009-11-06 17:13:51 -08:00
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
|
2010-01-19 10:41:46 -08:00
|
|
|
/* Provides a bitmap of VLANs which have learning disabled, that is, VLANs on
|
2011-04-06 15:31:22 -07:00
|
|
|
* which all packets are flooded. Returns true if the set has changed from the
|
|
|
|
* previous value. */
|
2009-11-06 17:13:51 -08:00
|
|
|
bool
|
2011-04-06 15:31:22 -07:00
|
|
|
mac_learning_set_flood_vlans(struct mac_learning *ml,
|
|
|
|
const unsigned long *bitmap)
|
2009-11-06 17:13:51 -08:00
|
|
|
{
|
2011-04-06 15:31:22 -07:00
|
|
|
if (vlan_bitmap_equal(ml->flood_vlans, bitmap)) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
bitmap_free(ml->flood_vlans);
|
|
|
|
ml->flood_vlans = vlan_bitmap_clone(bitmap);
|
|
|
|
return true;
|
|
|
|
}
|
2009-11-06 17:13:51 -08:00
|
|
|
}
|
|
|
|
|
2012-02-01 15:04:51 -08:00
|
|
|
/* Changes the MAC aging timeout of 'ml' to 'idle_time' seconds. */
|
|
|
|
void
|
|
|
|
mac_learning_set_idle_time(struct mac_learning *ml, unsigned int idle_time)
|
|
|
|
{
|
|
|
|
idle_time = normalize_idle_time(idle_time);
|
|
|
|
if (idle_time != ml->idle_time) {
|
|
|
|
struct mac_entry *e;
|
|
|
|
int delta;
|
|
|
|
|
|
|
|
delta = (int) idle_time - (int) ml->idle_time;
|
|
|
|
LIST_FOR_EACH (e, lru_node, &ml->lrus) {
|
|
|
|
e->expires += delta;
|
|
|
|
}
|
|
|
|
ml->idle_time = idle_time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-08 12:32:33 -08:00
|
|
|
/* Sets the maximum number of entries in 'ml' to 'max_entries', adjusting it
|
|
|
|
* to be within a reasonable range. */
|
|
|
|
void
|
|
|
|
mac_learning_set_max_entries(struct mac_learning *ml, size_t max_entries)
|
|
|
|
{
|
|
|
|
ml->max_entries = (max_entries < 10 ? 10
|
|
|
|
: max_entries > 1000 * 1000 ? 1000 * 1000
|
|
|
|
: max_entries);
|
|
|
|
}
|
|
|
|
|
2009-11-06 17:13:51 -08:00
|
|
|
static bool
|
|
|
|
is_learning_vlan(const struct mac_learning *ml, uint16_t vlan)
|
|
|
|
{
|
2011-07-21 17:03:03 -07:00
|
|
|
return !ml->flood_vlans || !bitmap_is_set(ml->flood_vlans, vlan);
|
2009-11-06 17:13:51 -08:00
|
|
|
}
|
|
|
|
|
2011-03-22 09:47:02 -07:00
|
|
|
/* Returns true if 'src_mac' may be learned on 'vlan' for 'ml'.
|
|
|
|
* Returns false if 'ml' is NULL, if src_mac is not valid for learning, or if
|
|
|
|
* 'vlan' is configured on 'ml' to flood all packets. */
|
|
|
|
bool
|
|
|
|
mac_learning_may_learn(const struct mac_learning *ml,
|
2015-08-28 14:55:11 -07:00
|
|
|
const struct eth_addr src_mac, uint16_t vlan)
|
2011-03-22 09:47:02 -07:00
|
|
|
{
|
|
|
|
return ml && is_learning_vlan(ml, vlan) && !eth_addr_is_multicast(src_mac);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Searches 'ml' for and returns a MAC learning entry for 'src_mac' in 'vlan',
|
|
|
|
* inserting a new entry if necessary. The caller must have already verified,
|
|
|
|
* by calling mac_learning_may_learn(), that 'src_mac' and 'vlan' are
|
|
|
|
* learnable.
|
2010-06-02 16:26:46 -07:00
|
|
|
*
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
* If the returned MAC entry is new (that is, if it has a NULL client-provided
|
|
|
|
* port, as returned by mac_entry_get_port()), then the caller must initialize
|
|
|
|
* the new entry's port to a nonnull value with mac_entry_set_port(). */
|
2011-03-22 09:47:02 -07:00
|
|
|
struct mac_entry *
|
|
|
|
mac_learning_insert(struct mac_learning *ml,
|
2015-08-28 14:55:11 -07:00
|
|
|
const struct eth_addr src_mac, uint16_t vlan)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
|
|
|
struct mac_entry *e;
|
|
|
|
|
2011-07-21 17:41:58 -07:00
|
|
|
e = mac_entry_lookup(ml, src_mac, vlan);
|
2009-07-08 13:19:16 -07:00
|
|
|
if (!e) {
|
2011-07-22 11:50:26 -07:00
|
|
|
uint32_t hash = mac_table_hash(ml, src_mac, vlan);
|
|
|
|
|
2012-12-08 12:32:33 -08:00
|
|
|
if (hmap_count(&ml->table) >= ml->max_entries) {
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
evict_mac_entry_fairly(ml);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
2011-07-22 11:50:26 -07:00
|
|
|
|
2011-07-22 12:00:46 -07:00
|
|
|
e = xmalloc(sizeof *e);
|
2011-07-22 11:50:26 -07:00
|
|
|
hmap_insert(&ml->table, &e->hmap_node, hash);
|
2015-08-28 14:55:11 -07:00
|
|
|
e->mac = src_mac;
|
2009-07-08 13:19:16 -07:00
|
|
|
e->vlan = vlan;
|
2010-06-02 16:26:46 -07:00
|
|
|
e->grat_arp_lock = TIME_MIN;
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
e->mlport = NULL;
|
|
|
|
COVERAGE_INC(mac_learning_learned);
|
2018-06-25 12:57:40 +02:00
|
|
|
ml->total_learned++;
|
2011-07-22 12:00:46 -07:00
|
|
|
} else {
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&e->lru_node);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
|
2011-03-22 09:47:02 -07:00
|
|
|
/* Mark 'e' as recently used. */
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_push_back(&ml->lrus, &e->lru_node);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
if (e->mlport) {
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&e->port_lru_node);
|
|
|
|
ovs_list_push_back(&e->mlport->port_lrus, &e->port_lru_node);
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
}
|
2012-02-01 15:04:51 -08:00
|
|
|
e->expires = time_now() + ml->idle_time;
|
2010-06-02 16:26:46 -07:00
|
|
|
|
2011-03-22 09:47:02 -07:00
|
|
|
return e;
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
|
2016-09-14 16:51:26 -07:00
|
|
|
/* Checks whether a MAC learning update is necessary for MAC learning table
|
|
|
|
* 'ml' given that a packet matching 'src' was received on 'in_port' in 'vlan',
|
|
|
|
* and given that the packet was gratuitous ARP if 'is_gratuitous_arp' is
|
|
|
|
* 'true' and 'in_port' is a bond port if 'is_bond' is 'true'.
|
|
|
|
*
|
|
|
|
* Most packets processed through the MAC learning table do not actually
|
|
|
|
* change it in any way. This function requires only a read lock on the MAC
|
|
|
|
* learning table, so it is much cheaper in this common case.
|
|
|
|
*
|
|
|
|
* Keep the code here synchronized with that in update_learning_table__()
|
|
|
|
* below. */
|
|
|
|
static bool
|
|
|
|
is_mac_learning_update_needed(const struct mac_learning *ml,
|
|
|
|
struct eth_addr src, int vlan,
|
|
|
|
bool is_gratuitous_arp, bool is_bond,
|
|
|
|
void *in_port)
|
|
|
|
OVS_REQ_RDLOCK(ml->rwlock)
|
|
|
|
{
|
|
|
|
struct mac_entry *mac;
|
|
|
|
|
|
|
|
if (!mac_learning_may_learn(ml, src, vlan)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac = mac_learning_lookup(ml, src, vlan);
|
|
|
|
if (!mac || mac_entry_age(ml, mac)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_gratuitous_arp) {
|
|
|
|
/* We don't want to learn from gratuitous ARP packets that are
|
2020-06-17 14:16:08 -07:00
|
|
|
* reflected back over bond members so we lock the learning table. For
|
2016-09-14 16:51:26 -07:00
|
|
|
* more detail, see the bigger comment in update_learning_table__(). */
|
|
|
|
if (!is_bond) {
|
|
|
|
return true; /* Need to set the gratuitous ARP lock. */
|
|
|
|
} else if (mac_entry_is_grat_arp_locked(mac)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mac_entry_get_port(ml, mac) != in_port /* ofbundle */;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Updates MAC learning table 'ml' given that a packet matching 'src' was
|
|
|
|
* received on 'in_port' in 'vlan', and given that the packet was gratuitous
|
|
|
|
* ARP if 'is_gratuitous_arp' is 'true' and 'in_port' is a bond port if
|
|
|
|
* 'is_bond' is 'true'.
|
|
|
|
*
|
|
|
|
* This code repeats all the checks in is_mac_learning_update_needed() because
|
|
|
|
* the lock was released between there and here and thus the MAC learning state
|
|
|
|
* could have changed.
|
|
|
|
*
|
|
|
|
* Returns 'true' if 'ml' was updated, 'false' otherwise.
|
|
|
|
*
|
|
|
|
* Keep the code here synchronized with that in is_mac_learning_update_needed()
|
|
|
|
* above. */
|
|
|
|
static bool
|
|
|
|
update_learning_table__(struct mac_learning *ml, struct eth_addr src,
|
|
|
|
int vlan, bool is_gratuitous_arp, bool is_bond,
|
|
|
|
void *in_port)
|
|
|
|
OVS_REQ_WRLOCK(ml->rwlock)
|
|
|
|
{
|
|
|
|
struct mac_entry *mac;
|
|
|
|
|
|
|
|
if (!mac_learning_may_learn(ml, src, vlan)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mac = mac_learning_insert(ml, src, vlan);
|
|
|
|
if (is_gratuitous_arp) {
|
|
|
|
/* Gratuitous ARP packets received over non-bond interfaces could be
|
2020-06-17 14:16:08 -07:00
|
|
|
* reflected back over bond members. We don't want to learn from these
|
2016-09-14 16:51:26 -07:00
|
|
|
* reflected packets, so we lock each entry for which a gratuitous ARP
|
|
|
|
* packet was received over a non-bond interface and refrain from
|
|
|
|
* learning from gratuitous ARP packets that arrive over bond
|
2016-12-08 12:55:26 +00:00
|
|
|
* interfaces for this entry while the lock is in effect. Refer to the
|
|
|
|
* 'ovs-vswitch Internals' document for more in-depth discussion on
|
|
|
|
* this topic. */
|
2016-09-14 16:51:26 -07:00
|
|
|
if (!is_bond) {
|
|
|
|
mac_entry_set_grat_arp_lock(mac);
|
|
|
|
} else if (mac_entry_is_grat_arp_locked(mac)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mac_entry_get_port(ml, mac) != in_port) {
|
2018-06-25 12:57:26 +02:00
|
|
|
if (mac_entry_get_port(ml, mac) != NULL) {
|
|
|
|
COVERAGE_INC(mac_learning_moved);
|
2018-06-25 12:57:40 +02:00
|
|
|
ml->total_moved++;
|
2018-06-25 12:57:26 +02:00
|
|
|
}
|
2016-09-14 16:51:26 -07:00
|
|
|
mac_entry_set_port(ml, mac, in_port);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Updates MAC learning table 'ml' given that a packet matching 'src' was
|
|
|
|
* received on 'in_port' in 'vlan', and given that the packet was gratuitous
|
|
|
|
* ARP if 'is_gratuitous_arp' is 'true' and 'in_port' is a bond port if
|
|
|
|
* 'is_bond' is 'true'.
|
|
|
|
*
|
|
|
|
* Returns 'true' if 'ml' was updated, 'false' otherwise. */
|
|
|
|
bool
|
|
|
|
mac_learning_update(struct mac_learning *ml, struct eth_addr src,
|
|
|
|
int vlan, bool is_gratuitous_arp, bool is_bond,
|
|
|
|
void *in_port)
|
|
|
|
OVS_EXCLUDED(ml->rwlock)
|
|
|
|
{
|
|
|
|
bool need_update;
|
|
|
|
bool updated = false;
|
|
|
|
|
|
|
|
/* Don't learn the OFPP_NONE port. */
|
|
|
|
if (in_port != NULL) {
|
|
|
|
/* First try the common case: no change to MAC learning table. */
|
|
|
|
ovs_rwlock_rdlock(&ml->rwlock);
|
|
|
|
need_update = is_mac_learning_update_needed(ml, src, vlan,
|
|
|
|
is_gratuitous_arp, is_bond,
|
|
|
|
in_port);
|
|
|
|
ovs_rwlock_unlock(&ml->rwlock);
|
|
|
|
|
|
|
|
if (need_update) {
|
|
|
|
/* Slow path: MAC learning table might need an update. */
|
|
|
|
ovs_rwlock_wrlock(&ml->rwlock);
|
|
|
|
updated = update_learning_table__(ml, src, vlan, is_gratuitous_arp,
|
|
|
|
is_bond, in_port);
|
|
|
|
ovs_rwlock_unlock(&ml->rwlock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return updated;
|
|
|
|
}
|
|
|
|
|
2011-03-22 09:47:02 -07:00
|
|
|
/* Looks up MAC 'dst' for VLAN 'vlan' in 'ml' and returns the associated MAC
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
* learning entry, if any. */
|
2011-03-22 09:47:02 -07:00
|
|
|
struct mac_entry *
|
|
|
|
mac_learning_lookup(const struct mac_learning *ml,
|
2015-08-28 14:55:11 -07:00
|
|
|
const struct eth_addr dst, uint16_t vlan)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
2011-03-22 09:47:02 -07:00
|
|
|
if (eth_addr_is_multicast(dst)) {
|
|
|
|
return NULL;
|
|
|
|
} else if (!is_learning_vlan(ml, vlan)) {
|
|
|
|
return NULL;
|
2009-07-08 13:19:16 -07:00
|
|
|
} else {
|
2011-07-21 17:41:58 -07:00
|
|
|
struct mac_entry *e = mac_entry_lookup(ml, dst, vlan);
|
|
|
|
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
ovs_assert(e == NULL || mac_entry_get_port(ml, e) != NULL);
|
2011-03-22 09:47:02 -07:00
|
|
|
return e;
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-22 12:00:46 -07:00
|
|
|
/* Expires 'e' from the 'ml' hash table. */
|
2011-03-18 15:28:21 -07:00
|
|
|
void
|
|
|
|
mac_learning_expire(struct mac_learning *ml, struct mac_entry *e)
|
|
|
|
{
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
ml->need_revalidate = true;
|
|
|
|
mac_entry_set_port(ml, e, NULL);
|
2011-07-21 17:41:58 -07:00
|
|
|
hmap_remove(&ml->table, &e->hmap_node);
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_remove(&e->lru_node);
|
2011-07-22 12:00:46 -07:00
|
|
|
free(e);
|
2011-03-18 15:28:21 -07:00
|
|
|
}
|
|
|
|
|
mac-learning: Implement per-port MAC learning fairness.
In "MAC flooding", an attacker transmits an overwhelming number of frames
with unique Ethernet source address on a switch port. The goal is to
force the switch to evict all useful MAC learning table entries, so that
its behavior degenerates to that of a hub, flooding all traffic. In turn,
that allows an attacker to eavesdrop on the traffic of other hosts attached
to the switch, with all the risks that that entails.
Before this commit, the Open vSwitch "normal" action that implements its
standalone switch behavior (and that can be used by OpenFlow controllers
as well) was vulnerable to MAC flooding attacks. This commit fixes the
problem by implementing per-port fairness for MAC table entries: when
the MAC table is at its maximum size, MAC table eviction always deletes an
entry from the port with the most entries. Thus, MAC entries will never
be evicted from ports with only a few entries if a port with a huge number
of entries exists.
Controllers could introduce their own MAC flooding vulnerabilities into
OVS. For a controller that adds destination MAC based flows to an OpenFlow
flow table as a reaction to "packet-in" events, such a bug, if it exists,
would be in the controller code itself and would need to be fixed in the
controller. For a controller that relies on the Open vSwitch "learn"
action to add destination MAC based flows, Open vSwitch has existing
support for eviction policy similar to that implemented in this commit
through the "groups" column in the Flow_Table table documented in
ovs-vswitchd.conf.db(5); we recommend that users of "learn" not already
familiar with eviction groups to read that documentation.
In addition to implementation of per-port MAC learning fairness,
this commit includes some closely related changes:
- Access to client-provided "port" data in struct mac_entry
is now abstracted through helper functions, which makes it
easier to ensure that the per-port data structures are maintained
consistently.
- The mac_learning_changed() function, which had become trivial,
vestigial, and confusing, was removed. Its functionality was folded
into the new function mac_entry_set_port().
- Many comments were added and improved; there had been a lot of
comment rot in previous versions.
CERT: VU#784996
Reported-by: "Ronny L. Bull - bullrl" <bullrl@clarkson.edu>
Reported-at: http://www.irongeek.com/i.php?page=videos/derbycon4/t314-exploring-layer-2-network-security-in-virtualized-environments-ronny-l-bull-dr-jeanna-n-matthews
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Ethan Jackson <ethan@nicira.com>
2015-02-11 23:34:50 -08:00
|
|
|
/* Expires all the mac-learning entries in 'ml'. */
|
2009-07-08 13:19:16 -07:00
|
|
|
void
|
2013-08-01 18:04:07 -07:00
|
|
|
mac_learning_flush(struct mac_learning *ml)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
|
|
|
struct mac_entry *e;
|
|
|
|
while (get_lru(ml, &e)){
|
2011-03-18 15:28:21 -07:00
|
|
|
mac_learning_expire(ml, e);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
2011-07-22 12:00:46 -07:00
|
|
|
hmap_shrink(&ml->table);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
|
2013-08-01 18:04:07 -07:00
|
|
|
/* Does periodic work required by 'ml'. Returns true if something changed that
|
|
|
|
* may require flow revalidation. */
|
|
|
|
bool
|
|
|
|
mac_learning_run(struct mac_learning *ml)
|
2009-07-08 13:19:16 -07:00
|
|
|
{
|
2013-08-01 18:04:07 -07:00
|
|
|
bool need_revalidate;
|
2009-07-08 13:19:16 -07:00
|
|
|
struct mac_entry *e;
|
2013-06-12 13:58:16 -07:00
|
|
|
|
2012-12-08 12:32:33 -08:00
|
|
|
while (get_lru(ml, &e)
|
|
|
|
&& (hmap_count(&ml->table) > ml->max_entries
|
|
|
|
|| time_now() >= e->expires)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
COVERAGE_INC(mac_learning_expired);
|
2018-06-25 12:57:40 +02:00
|
|
|
ml->total_expired++;
|
2011-03-18 15:28:21 -07:00
|
|
|
mac_learning_expire(ml, e);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
2013-08-01 18:04:07 -07:00
|
|
|
|
|
|
|
need_revalidate = ml->need_revalidate;
|
|
|
|
ml->need_revalidate = false;
|
|
|
|
return need_revalidate;
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mac_learning_wait(struct mac_learning *ml)
|
|
|
|
{
|
2013-06-12 13:58:16 -07:00
|
|
|
if (hmap_count(&ml->table) > ml->max_entries
|
2013-08-01 18:04:07 -07:00
|
|
|
|| ml->need_revalidate) {
|
2012-12-08 12:32:33 -08:00
|
|
|
poll_immediate_wake();
|
2016-03-25 14:10:22 -07:00
|
|
|
} else if (!ovs_list_is_empty(&ml->lrus)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
struct mac_entry *e = mac_entry_from_lru_node(ml->lrus.next);
|
2010-05-12 12:53:07 -07:00
|
|
|
poll_timer_wait_until(e->expires * 1000LL);
|
2009-07-08 13:19:16 -07:00
|
|
|
}
|
|
|
|
}
|