From 7fc88cf3a1236ebf9f21a2054eee7d586e031e5f Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Wed, 17 Jun 2020 22:57:13 +0200 Subject: [PATCH] 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 --- src/vnet/geneve/geneve.api | 24 ++++++++++++++- src/vnet/geneve/geneve.c | 67 +++++++++++++++++++++++----------------- src/vnet/geneve/geneve.h | 6 ++-- src/vnet/geneve/geneve_api.c | 53 ++++++++++++++++++++++++++++++++ test/test_geneve.py | 73 ++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 189 insertions(+), 34 deletions(-) diff --git a/src/vnet/geneve/geneve.api b/src/vnet/geneve/geneve.api index 5fad670b9db..4502d890797 100644 --- a/src/vnet/geneve/geneve.api +++ b/src/vnet/geneve/geneve.api @@ -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; diff --git a/src/vnet/geneve/geneve.c b/src/vnet/geneve/geneve.c index 1e04c662431..e16616b69ea 100644 --- a/src/vnet/geneve/geneve.c +++ b/src/vnet/geneve/geneve.c @@ -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 " " {remote |group } vni " - " [encap-vrf-id ] [decap-next [l2|node ]] [del]", + " [encap-vrf-id ] [decap-next [l2|node ]] [l3-mode] [del]", .function = geneve_add_del_tunnel_command_fn, }; /* *INDENT-ON* */ diff --git a/src/vnet/geneve/geneve.h b/src/vnet/geneve/geneve.h index 491ae23242f..0b962e79883 100644 --- a/src/vnet/geneve/geneve.h +++ b/src/vnet/geneve/geneve.h @@ -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 diff --git a/src/vnet/geneve/geneve_api.c b/src/vnet/geneve/geneve_api.c index 996a7a029e0..10dafd2d63b 100644 --- a/src/vnet/geneve/geneve_api.c +++ b/src/vnet/geneve/geneve_api.c @@ -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) { diff --git a/test/test_geneve.py b/test/test_geneve.py index 16cb6c26642..11d69358b96 100644 --- a/test/test_geneve.py +++ b/test/test_geneve.py @@ -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) -- 2.16.6