geneve: support geneve interface acting as a bvi

create geneve tunnel local 10.10.10.10 remote 10.10.10.9 vni 48 decap-next node ethernet-input l3-mode
set interface ip address geneve_tunnel0 11.11.11.12/24

Type: feature
Change-Id: I579ce879553d72a2e8048e33d0c0122674996b81
Signed-off-by: Ole Troan <ot@cisco.com>
This commit is contained in:
Ole Troan
2020-06-17 22:57:13 +02:00
committed by Andrew Yourtchenko
parent 5a849e3b35
commit 7fc88cf3a1
5 changed files with 189 additions and 34 deletions

View File

@ -13,7 +13,7 @@
* limitations under the License.
*/
option version = "2.0.0";
option version = "2.1.0";
import "vnet/interface_types.api";
import "vnet/ethernet/ethernet_types.api";
@ -21,6 +21,7 @@ import "vnet/ip/ip_types.api";
define geneve_add_del_tunnel
{
option deprecated="20.06";
u32 client_index;
u32 context;
bool is_add;
@ -39,6 +40,27 @@ define geneve_add_del_tunnel_reply
vl_api_interface_index_t sw_if_index;
};
define geneve_add_del_tunnel2
{
u32 client_index;
u32 context;
bool is_add;
vl_api_address_t local_address;
vl_api_address_t remote_address;
vl_api_interface_index_t mcast_sw_if_index;
u32 encap_vrf_id;
u32 decap_next_index;
u32 vni;
bool l3_mode;
};
define geneve_add_del_tunnel2_reply
{
u32 context;
i32 retval;
vl_api_interface_index_t sw_if_index;
};
define geneve_tunnel_dump
{
u32 client_index;

View File

@ -81,6 +81,7 @@ format_geneve_tunnel (u8 * s, va_list * args)
s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
s = format (s, "l3-mode %u ", t->l3_mode);
if (PREDICT_FALSE (ip46_address_is_multicast (&t->remote)))
s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
@ -105,12 +106,21 @@ geneve_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
return /* no error */ 0;
}
static clib_error_t *
geneve_mac_change (vnet_hw_interface_t * hi,
const u8 * old_address, const u8 * mac_address)
{
l2input_interface_mac_change (hi->sw_if_index, old_address, mac_address);
return (NULL);
}
/* *INDENT-OFF* */
VNET_DEVICE_CLASS (geneve_device_class, static) = {
.name = "GENEVE",
.format_device_name = format_geneve_name,
.format_tx_trace = format_geneve_encap_trace,
.admin_up_down_function = geneve_interface_admin_up_down,
.mac_addr_change_function = geneve_mac_change,
};
/* *INDENT-ON* */
@ -206,8 +216,9 @@ _(vni) \
_(mcast_sw_if_index) \
_(encap_fib_index) \
_(decap_next_index) \
_(local) \
_(remote)
_(local) \
_(remote) \
_(l3_mode)
static int
geneve_rewrite (geneve_tunnel_t * t, bool is_ip6)
@ -400,39 +411,30 @@ int vnet_geneve_add_del_tunnel
hash_set (vxm->geneve4_tunnel_by_key, key4.as_u64, t - vxm->tunnels);
vnet_hw_interface_t *hi;
if (vec_len (vxm->free_geneve_tunnel_hw_if_indices) > 0)
if (a->l3_mode)
{
vnet_interface_main_t *im = &vnm->interface_main;
hw_if_index = vxm->free_geneve_tunnel_hw_if_indices
[vec_len (vxm->free_geneve_tunnel_hw_if_indices) - 1];
_vec_len (vxm->free_geneve_tunnel_hw_if_indices) -= 1;
hi = vnet_get_hw_interface (vnm, hw_if_index);
hi->dev_instance = t - vxm->tunnels;
hi->hw_instance = hi->dev_instance;
/* clear old stats of freed tunnel before reuse */
sw_if_index = hi->sw_if_index;
vnet_interface_counter_lock (im);
vlib_zero_combined_counter
(&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
sw_if_index);
vlib_zero_combined_counter (&im->combined_sw_if_counters
[VNET_INTERFACE_COUNTER_RX],
sw_if_index);
vlib_zero_simple_counter (&im->sw_if_counters
[VNET_INTERFACE_COUNTER_DROP],
sw_if_index);
vnet_interface_counter_unlock (im);
u32 t_idx = t - vxm->tunnels;
u8 address[6] =
{ 0xd0, 0x0b, 0xee, 0xd0, (u8) (t_idx >> 8), (u8) t_idx };
clib_error_t *error =
ethernet_register_interface (vnm, geneve_device_class.index,
t_idx,
address, &hw_if_index, 0);
if (error)
{
clib_error_report (error);
return VNET_API_ERROR_INVALID_REGISTRATION;
}
}
else
{
hw_if_index = vnet_register_interface
(vnm, geneve_device_class.index, t - vxm->tunnels,
geneve_hw_class.index, t - vxm->tunnels);
hi = vnet_get_hw_interface (vnm, hw_if_index);
}
hi = vnet_get_hw_interface (vnm, hw_if_index);
/* Set geneve tunnel output node */
u32 encap_index = !is_ip6 ?
geneve4_encap_node.index : geneve6_encap_node.index;
@ -564,7 +566,11 @@ int vnet_geneve_add_del_tunnel
/* make sure tunnel is removed from l2 bd or xconnect */
set_int_l2_mode (vxm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0,
L2_BD_PORT_TYPE_NORMAL, 0, 0);
vec_add1 (vxm->free_geneve_tunnel_hw_if_indices, t->hw_if_index);
if (t->l3_mode)
ethernet_delete_interface (vnm, t->hw_if_index);
else
vnet_delete_hw_interface (vnm, t->hw_if_index);
vxm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
@ -651,6 +657,7 @@ geneve_add_del_tunnel_command_fn (vlib_main_t * vm,
u8 grp_set = 0;
u8 ipv4_set = 0;
u8 ipv6_set = 0;
u8 l3_mode = 0;
u32 encap_fib_index = 0;
u32 mcast_sw_if_index = ~0;
u32 decap_next_index = GENEVE_INPUT_NEXT_L2_INPUT;
@ -736,6 +743,10 @@ geneve_add_del_tunnel_command_fn (vlib_main_t * vm,
goto done;
}
}
else if (unformat (line_input, "l3-mode"))
{
l3_mode = 1;
}
else
{
error = clib_error_return (0, "parse error: '%U'",
@ -864,7 +875,7 @@ VLIB_CLI_COMMAND (create_geneve_tunnel_command, static) = {
.short_help =
"create geneve tunnel local <local-vtep-addr>"
" {remote <remote-vtep-addr>|group <mcast-vtep-addr> <intf-name>} vni <nn>"
" [encap-vrf-id <nn>] [decap-next [l2|node <name>]] [del]",
" [encap-vrf-id <nn>] [decap-next [l2|node <name>]] [l3-mode] [del]",
.function = geneve_add_del_tunnel_command_fn,
};
/* *INDENT-ON* */

View File

@ -135,6 +135,8 @@ typedef struct
* The tunnels sibling index on the FIB entry's dependency list.
*/
u32 sibling_index;
u8 l3_mode;
} geneve_tunnel_t;
#define foreach_geneve_input_next \
@ -173,9 +175,6 @@ typedef struct
/* mcast shared info */
uword *mcast_shared; /* keyed on mcast ip46 addr */
/* Free vlib hw_if_indices */
u32 *free_geneve_tunnel_hw_if_indices;
/* Mapping from sw_if_index to tunnel index */
u32 *tunnel_index_by_sw_if_index;
@ -205,6 +204,7 @@ typedef struct
u32 encap_fib_index;
u32 decap_next_index;
u32 vni;
u8 l3_mode;
} vnet_geneve_add_del_tunnel_args_t;
int vnet_geneve_add_del_tunnel

View File

@ -45,6 +45,7 @@
#define foreach_vpe_api_msg \
_(SW_INTERFACE_SET_GENEVE_BYPASS, sw_interface_set_geneve_bypass) \
_(GENEVE_ADD_DEL_TUNNEL, geneve_add_del_tunnel) \
_(GENEVE_ADD_DEL_TUNNEL2, geneve_add_del_tunnel2) \
_(GENEVE_TUNNEL_DUMP, geneve_tunnel_dump)
static void
@ -114,6 +115,58 @@ out:
/* *INDENT-ON* */
}
static void vl_api_geneve_add_del_tunnel2_t_handler
(vl_api_geneve_add_del_tunnel2_t * mp)
{
vl_api_geneve_add_del_tunnel2_reply_t *rmp;
int rv = 0;
ip4_main_t *im = &ip4_main;
uword *p = hash_get (im->fib_index_by_table_id, ntohl (mp->encap_vrf_id));
if (!p)
{
rv = VNET_API_ERROR_NO_SUCH_FIB;
goto out;
}
vnet_geneve_add_del_tunnel_args_t a = {
.is_add = mp->is_add,
.is_ip6 = mp->remote_address.af,
.mcast_sw_if_index = ntohl (mp->mcast_sw_if_index),
.encap_fib_index = p[0],
.decap_next_index = ntohl (mp->decap_next_index),
.vni = ntohl (mp->vni),
.l3_mode = mp->l3_mode,
};
ip_address_decode (&mp->remote_address, &a.remote);
ip_address_decode (&mp->local_address, &a.local);
/* Check src & dst are different */
if (ip46_address_cmp (&a.remote, &a.local) == 0)
{
rv = VNET_API_ERROR_SAME_SRC_DST;
goto out;
}
if (ip46_address_is_multicast (&a.remote) &&
!vnet_sw_if_index_is_api_valid (a.mcast_sw_if_index))
{
rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
goto out;
}
u32 sw_if_index = ~0;
rv = vnet_geneve_add_del_tunnel (&a, &sw_if_index);
out:
/* *INDENT-OFF* */
REPLY_MACRO2(VL_API_GENEVE_ADD_DEL_TUNNEL2_REPLY,
({
rmp->sw_if_index = ntohl (sw_if_index);
}));
/* *INDENT-ON* */
}
static void send_geneve_tunnel_details
(geneve_tunnel_t * t, vl_api_registration_t * reg, u32 context)
{

View File

@ -6,8 +6,8 @@ import unittest
from framework import VppTestCase, VppTestRunner
from template_bd import BridgeDomain
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
from scapy.layers.l2 import Ether, ARP
from scapy.layers.inet import IP, UDP, ICMP
from scapy.contrib.geneve import GENEVE
import util
@ -234,5 +234,74 @@ class TestGeneve(BridgeDomain, VppTestCase):
self.logger.info(self.vapi.cli("show geneve tunnel"))
class TestGeneveL3(VppTestCase):
""" GENEVE L3 Test Case """
@classmethod
def setUpClass(cls):
super(TestGeneveL3, cls).setUpClass()
try:
cls.create_pg_interfaces(range(2))
cls.interfaces = list(cls.pg_interfaces)
for i in cls.interfaces:
i.admin_up()
i.config_ip4()
i.resolve_arp()
except Exception:
super(TestGeneveL3, cls).tearDownClass()
raise
@classmethod
def tearDownClass(cls):
super(TestGeneveL3, cls).tearDownClass()
def tearDown(self):
super(TestGeneveL3, self).tearDown()
def show_commands_at_teardown(self):
self.logger.info(self.vapi.cli("show geneve tunnel"))
self.logger.info(self.vapi.cli("show ip neighbor"))
def test_l3_packet(self):
vni = 1234
r = self.vapi.add_node_next(node_name="geneve4-input",
next_name="ethernet-input")
r = self.vapi.geneve_add_del_tunnel2(
is_add=1,
local_address=self.pg0.local_ip4,
remote_address=self.pg0.remote_ip4,
vni=vni,
l3_mode=1,
decap_next_index=r.node_index)
self.vapi.sw_interface_add_del_address(
sw_if_index=r.sw_if_index, prefix="10.0.0.1/24")
pkt = (Ether(src=self.pg0.remote_mac, dst="d0:0b:ee:d0:00:00") /
IP(src='10.0.0.2', dst='10.0.0.1') /
ICMP())
encap = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
UDP(sport=6081, dport=6081, chksum=0) /
GENEVE(vni=vni))
arp = (Ether(src=self.pg0.remote_mac, dst="d0:0b:ee:d0:00:00") /
ARP(op="is-at", hwsrc=self.pg0.remote_mac,
hwdst="d0:0b:ee:d0:00:00", psrc="10.0.0.2",
pdst="10.0.0.1"))
rx = self.send_and_expect(self.pg0, encap/pkt*1, self.pg0)
rx = self.send_and_assert_no_replies(self.pg0, encap/arp*1, self.pg0)
rx = self.send_and_expect(self.pg0, encap/pkt*1, self.pg0)
self.assertEqual(rx[0][ICMP].type, 0) # echo reply
r = self.vapi.geneve_add_del_tunnel2(
is_add=0,
local_address=self.pg0.local_ip4,
remote_address=self.pg0.remote_ip4,
vni=vni)
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)