Skip to content

Commit

Permalink
Merged PR #650 (v2015.11 release) onto master
Browse files Browse the repository at this point in the history
  • Loading branch information
eugeneia committed Nov 5, 2015
2 parents 20532ef + 8b2a016 commit e285507
Show file tree
Hide file tree
Showing 91 changed files with 3,127 additions and 1,679 deletions.
5 changes: 4 additions & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -209,5 +209,8 @@ mrproper: clean
$(E) "RM $(RMOBJS)"
$(Q)-rm -rf $(RMOBJS)

.PHONY: clean $(TESTMODS) $(TESTSCRIPTS)
benchmarks:
$(Q) (scripts/bench.sh)

.PHONY: clean $(TESTMODS) $(TESTSCRIPTS) benchmarks

Binary file modified src/apps/bridge/.images/bridge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 26 additions & 19 deletions src/apps/bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,45 @@ The flooding `bridge` app ignores the *config* key of its configuration.

# Learning bridge (apps.bridge.learning)

The learning `bridge` app implements a *learning bridge* using a [Bloom
filter](https://en.wikipedia.org/wiki/Bloom_filter) to store the set of
MAC source addresses of packets arriving on each input port. When a
packet is received it is forwarded to all output ports whose
corresponding input ports match the packet's destination MAC address.
When no input port matches, the packet is flooded to all output ports.
Multicast MAC addresses are always flooded to all output ports associated
with the input port. The scoping rules according to the split-horizon
topology apply unchanged.
The learning `bridge` app implements a *learning bridge* using a
custom hash table to store the set of MAC source addresses of packets
arriving on each input port. When a packet is received it is forwarded
to all output ports whose corresponding input ports match the packet's
destination MAC address. When no input port matches, the packet is
flooded to all output ports. Multicast MAC addresses are always
flooded to all output ports associated with the input port. The
scoping rules according to the split-horizon topology apply unchanged.

## Configuration

The learning `bridge` app accepts a table as the value of the *config*
key of its configuration. The following keys are defined:

— Key **mac_table_size**
— Key **mac_table**

**Optional**. Expected maximum number of MAC addresses to store in each
per-port Bloom filter. Default is 1000.
*Optional*. This is a table that defines the characteristics of the
MAC table. The following keys are defined

— Key **fp_rate**
— Key **size**

**Optional**. Maximum rate of false-positives for look-ups in the Bloom
filters, provided the number of distinct objects stored in the filter
does not exceed *mac_table_size*. Default is 0.001.
*Optional*. The number of MAC addresses to be stored in the
table. Default is 256. The size of the table is increased
automatically if this limit is reached or if an overflow in one of
the hash buckets occurs. This value is capped by **resize_max**.

— Key **timeout**

**Optional**. Timeout for learned MAC addresses in seconds. Default is
*Optional*. Timeout for learned MAC addresses in seconds. Default is
60.

— Key **verbose**

**Optional**. A boolean value. If true, enables the printing of debugging
output. Default is `false`.
*Optional*. A boolean value. If true, statistics about table usage is logged during each timeout interval. Default is `false`.

— Key **copy_on_resize**

*Optional*. A boolean value. If true, the contents of the table is copied to the newly allocated table after a resize operation. Default is `true`.

— Key **resize_max**

*Optional*. An upper bound for the size of the table. Default is 65536.
45 changes: 26 additions & 19 deletions src/apps/bridge/README.md.src
Original file line number Diff line number Diff line change
Expand Up @@ -54,38 +54,45 @@ The flooding `bridge` app ignores the *config* key of its configuration.

# Learning bridge (apps.bridge.learning)

The learning `bridge` app implements a *learning bridge* using a [Bloom
filter](https://en.wikipedia.org/wiki/Bloom_filter) to store the set of
MAC source addresses of packets arriving on each input port. When a
packet is received it is forwarded to all output ports whose
corresponding input ports match the packet's destination MAC address.
When no input port matches, the packet is flooded to all output ports.
Multicast MAC addresses are always flooded to all output ports associated
with the input port. The scoping rules according to the split-horizon
topology apply unchanged.
The learning `bridge` app implements a *learning bridge* using a
custom hash table to store the set of MAC source addresses of packets
arriving on each input port. When a packet is received it is forwarded
to all output ports whose corresponding input ports match the packet's
destination MAC address. When no input port matches, the packet is
flooded to all output ports. Multicast MAC addresses are always
flooded to all output ports associated with the input port. The
scoping rules according to the split-horizon topology apply unchanged.

## Configuration

The learning `bridge` app accepts a table as the value of the *config*
key of its configuration. The following keys are defined:

— Key **mac_table_size**
— Key **mac_table**

**Optional**. Expected maximum number of MAC addresses to store in each
per-port Bloom filter. Default is 1000.
*Optional*. This is a table that defines the characteristics of the
MAC table. The following keys are defined

— Key **fp_rate**
— Key **size**

**Optional**. Maximum rate of false-positives for look-ups in the Bloom
filters, provided the number of distinct objects stored in the filter
does not exceed *mac_table_size*. Default is 0.001.
*Optional*. The number of MAC addresses to be stored in the
table. Default is 256. The size of the table is increased
automatically if this limit is reached or if an overflow in one of
the hash buckets occurs. This value is capped by **resize_max**.

— Key **timeout**

**Optional**. Timeout for learned MAC addresses in seconds. Default is
*Optional*. Timeout for learned MAC addresses in seconds. Default is
60.

— Key **verbose**

**Optional**. A boolean value. If true, enables the printing of debugging
output. Default is `false`.
*Optional*. A boolean value. If true, statistics about table usage is logged during each timeout interval. Default is `false`.

— Key **copy_on_resize**

*Optional*. A boolean value. If true, the contents of the table is copied to the newly allocated table after a resize operation. Default is `true`.

— Key **resize_max**

*Optional*. An upper bound for the size of the table. Default is 65536.
136 changes: 117 additions & 19 deletions src/apps/bridge/base.lua
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,46 @@
-- ...},
-- config = { <bridge-specific-config> } }
--
-- Port names have to be unique by themselves, irrespective of whether
-- they are free ports or belong to a split-horizon group.
--
-- The "config" table contains configuration options specific to a
-- derived class. It is ignored by the base class.
-- derived class. It is ignored by the base class. A derived class
-- can access the configuration via self._conf.config. If config is
-- not set, it is initialiezed to an empty table.
--
-- Note that it is necessary to call the method post_config() after
-- the app has been configured with engine.configure() to complete the
-- initialization. This step will add the ringbuffers associated with
-- the ports to an internal data structure to save a lookup in the
-- input and output tables during packet processing.
--
-- To make processing in the fast path easier, each port and group is
-- assigned a unique integer greater than zero to serve as a "handle".
-- The group handle 0 is assigned to all free ports.
--
-- The base constructor creates the following arrays as private
-- instance variables for efficient access in the push() method (which
-- must be provided by any derived class).
--
-- The base constructor checks the configuration and creates the
-- following arrays as private instance variables for efficient access
-- in the push() method (which must be provided by any derived class).
-- self._ports
--
-- self._src_ports
-- Each port is assigned a table containing the following information
--
-- This array contains the names of all ports connected to the
-- bridge.
-- { name = <port-name>,
-- group = <group-handle>,
-- handle = <handle> }
--
-- The tables of all ports is stored in the self._ports table,
-- which can be indexed by both, the port name as well as the port
-- handle to access the information for a particular port.
--
-- self._dst_ports
--
-- This table is keyed by the name of an input port and associates
-- it with an array of output ports according to the split-horizon
-- topology.
-- This is an array which stores an array of egress port handles
-- for every ingress port handle. According to the split-horizon
-- semantics, this includes all port handles except the ingress
-- handle and all handles that belong to the same group.
--
-- The push() method of a derived class should iterate over all source
-- ports and forward the incoming packets to the associated output
Expand All @@ -54,36 +77,111 @@ function bridge:new (arg)
local conf = arg and config.parse_app_arg(arg) or {}
assert(conf.ports, self._name..": invalid configuration")
o._conf = conf
if not o._conf.config then
o._conf.config = {}
end

-- Create a list of forwarding ports for all ports connected to the
-- bridge, taking split horizon groups into account
local ports = {}
local ports, groups = {}, {}
local function add_port(port, group)
assert(not ports[port],
self:name()..": duplicate definition of port "..port)
ports[port] = group
local group_handle = 0
if group then
local desc = groups[group]
if not desc then
desc = { name = group, ports = {} }
groups[group] = desc
table.insert(groups, desc)
desc.handle = #groups
end
group_handle = desc.handle
end
local desc = { name = port,
group = group_handle }
ports[port] = desc
table.insert(ports, desc)
desc.handle = #ports
if group_handle ~= 0 then
table.insert(groups[group_handle].ports, desc.handle)
end
end

-- Add free ports
for _, port in ipairs(conf.ports) do
add_port(port, '')
add_port(port)
end

-- Add split horizon groups
if conf.split_horizon_groups then
for group, ports in pairs(conf.split_horizon_groups) do
for _, port in ipairs(ports) do
add_port(port, group)
end
end
end
local src_ports, dst_ports = {}, {}
for sport, sgroup in pairs(ports) do
table.insert(src_ports, sport)

-- Create list of egress ports for each ingress port, containing
-- all free ports as well as all ports from different split-horizon
-- groups
local dst_ports = {}
for sport, sdesc in ipairs(ports) do
dst_ports[sport] = {}
for dport, dgroup in pairs(ports) do
if not (sport == dport or (sgroup ~= '' and sgroup == dgroup)) then
for dport, ddesc in ipairs(ports) do
if not (sport == dport or (sdesc.group ~= 0 and
sdesc.group == ddesc.group)) then
table.insert(dst_ports[sport], dport)
end
end
end
o._src_ports = src_ports
o._groups = groups
o._ports = ports
o._dst_ports = dst_ports
return o
end

-- API
--
-- Add the ingress and egress links to the port descriptor tables,
-- accessible via the keys l_in and l_out, respectively. This helps
-- to speed up packet forwarding by eliminating a lookup in the input
-- and output tables.
function bridge:post_config ()
assert(self.input and self.output)
for _, port in ipairs(self._ports) do
port.l_in = self.input[port.name]
port.l_out = self.output[port.name]
end
end

-- API
--
-- Print the port configuration and forwarding tables of the bridge.
-- This is primarily intended for debugging.
function bridge:info ()
local ports, groups = self._ports, self._groups
local function nh (n, h)
return n.."("..h..")"
end
print("Free ports:")
for p, desc in ipairs(ports) do
if desc.group == 0 then
print("\t"..nh(desc.name, p))
end
end
print("Split-horizon groups:")
for g, desc in ipairs(groups) do
print("\t"..nh(desc.name, g)..", members:")
for _, p in ipairs(desc.ports) do
print("\t\t"..nh(ports[p].name, p))
end
end
print("Forwarding tables:")
for p, dst in ipairs(self._dst_ports) do
print("\t"..nh(ports[p].name, p))
for _, d in ipairs(dst) do
print("\t\t"..nh(ports[d].name, d))
end
end
end
68 changes: 68 additions & 0 deletions src/apps/bridge/learning.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Type for port and split-horizon group handles */
typedef uint16_t handle_t;

/* List of egress port handles. This fixed-sized declaration is used
in mac_table.c. The actual port lists are of variable size and
allocated by learning.lua. */
typedef struct {
uint16_t length;
handle_t ports[1];
} port_list_t;

/* Packets are queued in various packet forwarding tables during
processing in learning:push(). Each entry points to a list of port
handles to which the packet will be sent. */
typedef struct {
struct packet *p;
port_list_t *plist;
} pft_entry_t;

typedef struct pft {
uint16_t length;
pft_entry_t entries[1];
} pft_t;

/* Mapping of a destination MAC address to an egress port handle. The
split-horizon group to which it belongs is stored as well in order
to detect group collision during lookup. The address is stored as
a 64-bit integer in host-byte order to allow efficient access and
comparison. A bucket in the hash table is made up of an array of
BUCKET_SIZE entries of this type. */
typedef struct {
uint64_t mac;
handle_t port;
handle_t group;
} mac_entry_t;

/* The MAC addresses are stored in simple hash tables with fixed-sized
buckets. */
typedef struct {
uint32_t ubuckets; /* Number of buckets with at least one used slot */
uint32_t entries; /* Number of stored objects */
uint8_t overflow; /* Flag to indicate overflow in at least one bucket */
} hash_table_header_t;

/* This fixed-sized declaration is used in mac_table.c. The actual
tables are of variable size and allocated by mac_table.lua. The
size of a bucket must be known to access a row in the buckets
matrix. There should be no need to change BUCKET_SIZE for
perfomance reasons, see the comments in mac_table.lua. This
declaration must match the ctype for hash_table_t in
mac_table.lua. */
enum { BUCKET_SIZE = 6 };
typedef struct {
hash_table_header_t h;
mac_entry_t buckets[1][BUCKET_SIZE];
} hash_table_t;

typedef struct {
handle_t port;
handle_t group;
} lookup_result_t;

void mac_table_insert(uint64_t mac, handle_t port, handle_t group,
hash_table_t **tables, uint32_t index);
lookup_result_t *mac_table_lookup(uint64_t mac, mac_entry_t *bucket);
void mac_table_lookup_pft(uint64_t mac, mac_entry_t *bucket,
handle_t port, handle_t group, struct packet *p,
pft_t **pfts, port_list_t *flood_pl);
Loading

0 comments on commit e285507

Please sign in to comment.