BVI Interface

a new dedicated BVI interface as opposed to [re]using a loopback.

benefits:
 - removes ambiguity over the purpose of a loopback interface
 - TX node dedicated to BVI only functions.

Change-Id: I749d6b38440d450ac5b909a28053c75ec9df946a
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2019-03-15 02:16:20 -07:00
committed by John Lo
parent 1ea74b5df5
commit 192b13f96d
12 changed files with 578 additions and 35 deletions

View File

@ -161,6 +161,7 @@ list(APPEND VNET_SOURCES
l2/l2_api.c
l2/l2_bd.c
l2/l2_bvi.c
l2/l2_bvi_node.c
l2/l2_input_classify.c
l2/l2_output_classify.c
l2/l2_efp_filter.c
@ -181,6 +182,7 @@ list(APPEND VNET_SOURCES
)
list(APPEND VNET_MULTIARCH_SOURCES
l2/l2_bvi_node.c
l2/l2_fwd.c
l2/l2_learn.c
l2/l2_output.c

View File

@ -314,7 +314,7 @@ ethernet_interface_t *ethernet_get_interface (ethernet_main_t * em,
clib_error_t *ethernet_register_interface (vnet_main_t * vnm,
u32 dev_class_index,
u32 dev_instance,
u8 * address,
const u8 * address,
u32 * hw_if_index_return,
ethernet_flag_change_function_t
flag_change);

View File

@ -278,7 +278,7 @@ clib_error_t *
ethernet_register_interface (vnet_main_t * vnm,
u32 dev_class_index,
u32 dev_instance,
u8 * address,
const u8 * address,
u32 * hw_if_index_return,
ethernet_flag_change_function_t flag_change)
{

View File

@ -553,6 +553,44 @@ autoreply define sw_interface_set_vpath
u8 enable;
};
/** \brief Create BVI interface instance request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param mac_address - mac addr to assign to the interface if none-zero
@param user_instance - requested instance, ~0 => dynamically allocate
*/
define bvi_create
{
u32 client_index;
u32 context;
vl_api_mac_address_t mac;
u32 user_instance;
};
/** \brief Create BVI interface instance response
@param context - sender context, to match reply w/ request
@param sw_if_index - sw index of the interface that was created
@param retval - return code for the request
*/
define bvi_create_reply
{
u32 context;
i32 retval;
u32 sw_if_index;
};
/** \brief Delete BVI interface request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sw_if_index - sw index of the interface that was created
*/
autoreply define bvi_delete
{
u32 client_index;
u32 context;
u32 sw_if_index;
};
/*
* Local Variables:
* eval: (c-set-style "gnu")

View File

@ -27,6 +27,7 @@
#include <vnet/l2/l2_vtr.h>
#include <vnet/l2/l2_learn.h>
#include <vnet/l2/l2_bd.h>
#include <vnet/l2/l2_bvi.h>
#include <vnet/ip/ip_types_api.h>
#include <vnet/ethernet/ethernet_types_api.h>
@ -74,7 +75,9 @@ _(BRIDGE_FLAGS, bridge_flags) \
_(L2_INTERFACE_VLAN_TAG_REWRITE, l2_interface_vlan_tag_rewrite) \
_(L2_INTERFACE_PBB_TAG_REWRITE, l2_interface_pbb_tag_rewrite) \
_(BRIDGE_DOMAIN_SET_MAC_AGE, bridge_domain_set_mac_age) \
_(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath)
_(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) \
_(BVI_CREATE, bvi_create) \
_(BVI_DELETE, bvi_delete)
static void
send_l2_xconnect_details (vl_api_registration_t * reg, u32 context,
@ -993,6 +996,37 @@ vl_api_sw_interface_set_vpath_t_handler (vl_api_sw_interface_set_vpath_t * mp)
REPLY_MACRO (VL_API_SW_INTERFACE_SET_VPATH_REPLY);
}
static void
vl_api_bvi_create_t_handler (vl_api_bvi_create_t * mp)
{
vl_api_bvi_create_reply_t *rmp;
mac_address_t mac;
u32 sw_if_index;
int rv;
mac_address_decode (mp->mac, &mac);
rv = l2_bvi_create (ntohl (mp->user_instance), &mac, &sw_if_index);
/* *INDENT-OFF* */
REPLY_MACRO2(VL_API_BVI_CREATE_REPLY,
({
rmp->sw_if_index = ntohl (sw_if_index);
}));
/* *INDENT-ON* */
}
static void
vl_api_bvi_delete_t_handler (vl_api_bvi_delete_t * mp)
{
vl_api_bvi_delete_reply_t *rmp;
int rv;
rv = l2_bvi_delete (ntohl (mp->sw_if_index));
REPLY_MACRO (VL_API_BVI_DELETE_REPLY);
}
/*
* l2_api_hookup
* Add vpe's API message handlers to the table.

File diff suppressed because it is too large Load Diff

View File

@ -97,6 +97,11 @@ l2_to_bvi (vlib_main_t * vlib_main,
void
l2bvi_register_input_type (vlib_main_t * vm,
ethernet_type_t type, u32 node_index);
extern int l2_bvi_create (u32 instance, const mac_address_t * mac,
u32 * sw_if_index);
extern int l2_bvi_delete (u32 sw_if_index);
#endif
/*

116
src/vnet/l2/l2_bvi_node.c Normal file
View File

@ -0,0 +1,116 @@
/*
* l2_bvi.c : layer 2 Bridged Virtual Interface
*
* Copyright (c) 2013 Cisco and/or its affiliates.
* 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:
*
* 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.
*/
#include <vnet/l2/l2_bvi.h>
/**
* send packets to l2-input.
*/
VNET_DEVICE_CLASS_TX_FN (bvi_device_class) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index;
vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
u16 nexts[VLIB_FRAME_SIZE];
u32 n_left, *from;
n_left = frame->n_vectors;
from = vlib_frame_vector_args (frame);
vlib_get_buffers (vm, from, bufs, n_left);
b = bufs;
sw_if_index = sw_if_indices;
/* It's all going to l2-input */
clib_memset_u16 (nexts, 0, VLIB_FRAME_SIZE);
/*
* For each packet:
* - fixup the L2 length of the packet
* - set the RX interface (which the bridge will use) to the
* TX interface (which routing has chosen)
* - Set the TX interface to the special ID so the DP knows this is a BVI
* Don't counts packets and bytes, that's done in the bviX-output node
*/
while (n_left >= 4)
{
/* Prefetch next iteration. */
if (PREDICT_TRUE (n_left >= 8))
{
/* LOAD pre-fetch since meta and packet data is read */
vlib_prefetch_buffer_header (b[4], LOAD);
vlib_prefetch_buffer_header (b[5], LOAD);
vlib_prefetch_buffer_header (b[6], LOAD);
vlib_prefetch_buffer_header (b[7], LOAD);
vlib_prefetch_buffer_data (b[4], LOAD);
vlib_prefetch_buffer_data (b[5], LOAD);
vlib_prefetch_buffer_data (b[6], LOAD);
vlib_prefetch_buffer_data (b[7], LOAD);
}
vnet_update_l2_len (b[0]);
vnet_update_l2_len (b[1]);
vnet_update_l2_len (b[2]);
vnet_update_l2_len (b[3]);
sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
vnet_buffer (b[0])->sw_if_index[VLIB_TX] = L2INPUT_BVI;
vnet_buffer (b[1])->sw_if_index[VLIB_TX] = L2INPUT_BVI;
vnet_buffer (b[2])->sw_if_index[VLIB_TX] = L2INPUT_BVI;
vnet_buffer (b[3])->sw_if_index[VLIB_TX] = L2INPUT_BVI;
vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index[0];
vnet_buffer (b[1])->sw_if_index[VLIB_RX] = sw_if_index[1];
vnet_buffer (b[2])->sw_if_index[VLIB_RX] = sw_if_index[2];
vnet_buffer (b[3])->sw_if_index[VLIB_RX] = sw_if_index[3];
b += 4;
n_left -= 4;
sw_if_index += 4;
}
while (n_left)
{
sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
vnet_buffer (b[0])->sw_if_index[VLIB_TX] = L2INPUT_BVI;
vnet_buffer (b[0])->sw_if_index[VLIB_RX] = sw_if_index[0];
vnet_update_l2_len (b[0]);
b += 1;
n_left -= 1;
sw_if_index += 1;
}
vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
return frame->n_vectors;
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -25,6 +25,7 @@ from hook import StepHook, PollHook, VppDiedError
from vpp_pg_interface import VppPGInterface
from vpp_sub_interface import VppSubInterface
from vpp_lo_interface import VppLoInterface
from vpp_bvi_interface import VppBviInterface
from vpp_papi_provider import VppPapiProvider
from vpp_papi.vpp_stats import VPPStats
from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
@ -690,6 +691,20 @@ class VppTestCase(unittest.TestCase):
cls.lo_interfaces = result
return result
@classmethod
def create_bvi_interfaces(cls, count):
"""
Create BVI interfaces.
:param count: number of interfaces created.
:returns: List of created interfaces.
"""
result = [VppBviInterface(cls) for i in range(count)]
for intf in result:
setattr(cls, intf.name, intf)
cls.bvi_interfaces = result
return result
@staticmethod
def extend_packet(packet, size, padding=' '):
"""

View File

@ -42,10 +42,10 @@ class TestIpIrb(VppTestCase):
def setUpClass(cls):
"""
#. Create BD with MAC learning enabled and put interfaces to this BD.
#. Configure IPv4 addresses on loopback interface and routed interface.
#. Configure MAC address binding to IPv4 neighbors on loop0.
#. Configure IPv4 addresses on BVI interface and routed interface.
#. Configure MAC address binding to IPv4 neighbors on bvi0.
#. Configure MAC address on pg2.
#. Loopback BVI interface has remote hosts, one half of hosts are
#. BVI interface has remote hosts, one half of hosts are
behind pg0 second behind pg1.
"""
super(TestIpIrb, cls).setUpClass()
@ -54,40 +54,40 @@ class TestIpIrb(VppTestCase):
cls.bd_id = 10
cls.remote_hosts_count = 250
# create 3 pg interfaces, 1 loopback interface
# create 3 pg interfaces, 1 BVI interface
cls.create_pg_interfaces(range(3))
cls.create_loopback_interfaces(1)
cls.create_bvi_interfaces(1)
cls.interfaces = list(cls.pg_interfaces)
cls.interfaces.extend(cls.lo_interfaces)
cls.interfaces.extend(cls.bvi_interfaces)
for i in cls.interfaces:
i.admin_up()
# Create BD with MAC learning enabled and put interfaces to this BD
cls.vapi.sw_interface_set_l2_bridge(
rx_sw_if_index=cls.loop0.sw_if_index, bd_id=cls.bd_id,
rx_sw_if_index=cls.bvi0.sw_if_index, bd_id=cls.bd_id,
port_type=L2_PORT_TYPE.BVI)
cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=cls.pg0.sw_if_index,
bd_id=cls.bd_id)
cls.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=cls.pg1.sw_if_index,
bd_id=cls.bd_id)
# Configure IPv4 addresses on loopback interface and routed interface
cls.loop0.config_ip4()
# Configure IPv4 addresses on BVI interface and routed interface
cls.bvi0.config_ip4()
cls.pg2.config_ip4()
# Configure MAC address binding to IPv4 neighbors on loop0
cls.loop0.generate_remote_hosts(cls.remote_hosts_count)
cls.loop0.configure_ipv4_neighbors()
# Configure MAC address binding to IPv4 neighbors on bvi0
cls.bvi0.generate_remote_hosts(cls.remote_hosts_count)
cls.bvi0.configure_ipv4_neighbors()
# configure MAC address on pg2
cls.pg2.resolve_arp()
# Loopback BVI interface has remote hosts, one half of hosts are behind
# BVI interface has remote hosts, one half of hosts are behind
# pg0 second behind pg1
half = cls.remote_hosts_count // 2
cls.pg0.remote_hosts = cls.loop0.remote_hosts[:half]
cls.pg1.remote_hosts = cls.loop0.remote_hosts[half:]
cls.pg0.remote_hosts = cls.bvi0.remote_hosts[:half]
cls.pg1.remote_hosts = cls.bvi0.remote_hosts[half:]
def tearDown(self):
"""Run standard test teardown and log ``show l2patch``,
@ -220,32 +220,32 @@ class TestIpIrb(VppTestCase):
Test scenario:
- ip traffic from pg2 interface must ends in both pg0 and pg1
- arp entry present in loop0 interface for destination IP
- no l2 entree configured, pg0 and pg1 are same
- arp entry present in bvi0 interface for destination IP
- no l2 entry configured, pg0 and pg1 are same
"""
stream = self.create_stream(
self.pg2, self.loop0, self.pg_if_packet_sizes)
self.pg2, self.bvi0, self.pg_if_packet_sizes)
self.pg2.add_stream(stream)
self.pg_enable_capture(self.pg_interfaces)
self.pg_start()
packet_count = self.get_packet_count_for_if_idx(self.loop0.sw_if_index)
packet_count = self.get_packet_count_for_if_idx(self.bvi0.sw_if_index)
rcvd1 = self.pg0.get_capture(packet_count)
rcvd2 = self.pg1.get_capture(packet_count)
self.verify_capture(self.loop0, self.pg2, rcvd1)
self.verify_capture(self.loop0, self.pg2, rcvd2)
self.verify_capture(self.bvi0, self.pg2, rcvd1)
self.verify_capture(self.bvi0, self.pg2, rcvd2)
self.assertListEqual(rcvd1.res, rcvd2.res)
def send_and_verify_l2_to_ip(self):
stream1 = self.create_stream_l2_to_ip(
self.pg0, self.loop0, self.pg2, self.pg_if_packet_sizes)
self.pg0, self.bvi0, self.pg2, self.pg_if_packet_sizes)
stream2 = self.create_stream_l2_to_ip(
self.pg1, self.loop0, self.pg2, self.pg_if_packet_sizes)
self.pg1, self.bvi0, self.pg2, self.pg_if_packet_sizes)
self.vapi.cli("clear trace")
self.pg0.add_stream(stream1)
self.pg1.add_stream(stream2)
@ -254,7 +254,7 @@ class TestIpIrb(VppTestCase):
self.pg_start()
rcvd = self.pg2.get_capture(514)
self.verify_capture_l2_to_ip(self.pg2, self.loop0, rcvd)
self.verify_capture_l2_to_ip(self.pg2, self.bvi0, rcvd)
def test_ip4_irb_2(self):
""" IPv4 IRB test 2
@ -265,7 +265,7 @@ class TestIpIrb(VppTestCase):
self.send_and_verify_l2_to_ip()
# change the BVI's mac and resed traffic
self.loop0.set_mac(MACAddress("00:00:00:11:11:33"))
self.bvi0.set_mac(MACAddress("00:00:00:11:11:33"))
self.send_and_verify_l2_to_ip()
# check it wasn't flooded

View File

@ -20,24 +20,24 @@ class TestL2Flood(VppTestCase):
# 12 l2 interface and one l3
self.create_pg_interfaces(range(13))
self.create_loopback_interfaces(1)
self.create_bvi_interfaces(1)
for i in self.pg_interfaces:
i.admin_up()
for i in self.lo_interfaces:
for i in self.bvi_interfaces:
i.admin_up()
self.pg12.config_ip4()
self.pg12.resolve_arp()
self.loop0.config_ip4()
self.bvi0.config_ip4()
def tearDown(self):
self.pg12.unconfig_ip4()
self.loop0.unconfig_ip4()
self.bvi0.unconfig_ip4()
for i in self.pg_interfaces:
i.admin_down()
for i in self.lo_interfaces:
for i in self.bvi_interfaces:
i.admin_down()
super(TestL2Flood, self).tearDown()
@ -61,7 +61,7 @@ class TestL2Flood(VppTestCase):
for i in self.pg_interfaces[8:12]:
self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
bd_id=1, shg=2)
for i in self.lo_interfaces:
for i in self.bvi_interfaces:
self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
bd_id=1, shg=2,
port_type=L2_PORT_TYPE.BVI)
@ -142,7 +142,7 @@ class TestL2Flood(VppTestCase):
for i in self.pg_interfaces[:12]:
self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
bd_id=1, enable=0)
for i in self.lo_interfaces:
for i in self.bvi_interfaces:
self.vapi.sw_interface_set_l2_bridge(rx_sw_if_index=i.sw_if_index,
bd_id=1, shg=2,
port_type=L2_PORT_TYPE.BVI,

22
test/vpp_bvi_interface.py Normal file
View File

@ -0,0 +1,22 @@
from vpp_object import VppObject
from vpp_interface import VppInterface
class VppBviInterface(VppInterface, VppObject):
"""VPP bvi interface."""
def __init__(self, test):
""" Create VPP BVI interface """
super(VppBviInterface, self).__init__(test)
self.add_vpp_config()
def add_vpp_config(self):
r = self.test.vapi.bvi_create(user_instance=0xffffffff,
mac="00:00:00:00:00:00")
self.set_sw_if_index(r.sw_if_index)
def remove_vpp_config(self):
self.test.vapi.bvi_delete(sw_if_index=self.sw_if_index)
def object_id(self):
return "bvi-%d" % self._sw_if_index