Initial checkin - renamed the files to avoid clashing with 'lcp' and 'linux-cp' plugin
This commit is contained in:
		
							
								
								
									
										61
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | # Copyright (c) 2020 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. | ||||||
|  |  | ||||||
|  | vpp_find_path(LIBNL3_INCLUDE_DIR NAMES libnl3/netlink/route/link/vlan.h) | ||||||
|  |  | ||||||
|  | if (NOT LIBNL3_INCLUDE_DIR) | ||||||
|  |   message(WARNING "-- libnl3 headers not found - lcpng plugin disabled") | ||||||
|  |   return() | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | vpp_plugin_find_library(lcpng LIBNL3_LIB libnl-3.so) | ||||||
|  | vpp_plugin_find_library(lcpng LIBNL3_ROUTE_LIB libnl-route-3.so.200) | ||||||
|  |  | ||||||
|  | include_directories(${LIBNL3_INCLUDE_DIR}/libnl3) | ||||||
|  | include_directories(${LIBMNL_INCLUDE_DIR}) | ||||||
|  |  | ||||||
|  | add_vpp_library(lcpng | ||||||
|  |   SOURCES | ||||||
|  |   lcpng_interface.c | ||||||
|  |   lcpng_adj.c | ||||||
|  |   lcpng.c | ||||||
|  |  | ||||||
|  |   LINK_LIBRARIES | ||||||
|  |   ${LIBNL3_LIB} | ||||||
|  |   ${LIBNL3_ROUTE_LIB} | ||||||
|  |  | ||||||
|  |   INSTALL_HEADERS | ||||||
|  |   lcpng_interface.h | ||||||
|  |   lcpng.h | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_vpp_plugin(lcpng_if | ||||||
|  |   SOURCES | ||||||
|  |   lcpng_if_api.c | ||||||
|  |   lcpng_if_cli.c | ||||||
|  |   lcpng_if_node.c | ||||||
|  |  | ||||||
|  |   API_FILES | ||||||
|  |   lcpng_if.api | ||||||
|  |  | ||||||
|  |   LINK_LIBRARIES | ||||||
|  |   lcpng | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_vpp_plugin(lcpng_unittest | ||||||
|  |   SOURCES | ||||||
|  |   test/lcpng_unittest.c | ||||||
|  |  | ||||||
|  |   LINK_LIBRARIES | ||||||
|  |   lcpng | ||||||
|  | ) | ||||||
							
								
								
									
										25
									
								
								FEATURE.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								FEATURE.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | --- | ||||||
|  | name: Linux Control Plane (integration) | ||||||
|  | maintainer: Neale Ranns <neale@grahpiant.com> | ||||||
|  |  | ||||||
|  | description: |- | ||||||
|  |         This plugin provides the beginnings of an integration with the | ||||||
|  |         Linux network stack. | ||||||
|  |         The plugin provides the capability to 'mirror' VPP interfaces in | ||||||
|  |         the Linux kernel. This means that for any interface in VPP the user | ||||||
|  |         can create a corresponding TAP or TUN device in the Linux kernel | ||||||
|  |         and have VPP plumb them together. | ||||||
|  |         The plumbing mechanics is different in each direction. | ||||||
|  |         In the RX direction, all packets received on a given VPP interface | ||||||
|  |         that are punted (i.e. are not dropped or forwarded) are transmitted | ||||||
|  |         on its mirror interface (this includes for example ARP, ND etc, | ||||||
|  |         so the recommendation is to disable ARP, ND, ping plugin). | ||||||
|  |         In the TX direction, packets received by VPP an the mirror Tap/Tun | ||||||
|  |         are cross-connected to the VPP interfaces. For IP packets, IP output | ||||||
|  |         features are applied. | ||||||
|  |         This is the beginnings of integration, because there needs to be | ||||||
|  |         an external agent that will configure (and synchronize) the IP | ||||||
|  |         configuration of the paired interfaces. | ||||||
|  |  | ||||||
|  | state: experimental | ||||||
|  | properties: [API, CLI, MULTITHREAD] | ||||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | This code was taken from VPP's src/plugins/linux-cp/ directory, originally by: | ||||||
|  | Signed-off-by: Neale Ranns <nranns@cisco.com> | ||||||
|  | Signed-off-by: Matthew Smith <mgsmith@netgate.com> | ||||||
|  | Signed-off-by: Jon Loeliger <jdl@netgate.com> | ||||||
|  | Signed-off-by: Pim van Pelt <pim@ipng.nl> | ||||||
|  | Signed-off-by: Neale Ranns <neale@graphiant.com> | ||||||
|  |  | ||||||
|  | See previous work: | ||||||
|  | https://gerrit.fd.io/r/c/vpp/+/30759 (interface mirroring) | ||||||
|  | https://gerrit.fd.io/r/c/vpp/+/31122 (netlink listener) | ||||||
|  |  | ||||||
|  | It's intended to be re-submitted for review as a cleanup/rewrite of the existing | ||||||
|  | Linux CP interface mirror and netlink syncer.  | ||||||
							
								
								
									
										85
									
								
								lcpng.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								lcpng.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 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 <sched.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <net/if.h> | ||||||
|  |  | ||||||
|  | #include <plugins/lcpng/lcpng.h> | ||||||
|  |  | ||||||
|  | lcp_main_t lcp_main; | ||||||
|  |  | ||||||
|  | u8 * | ||||||
|  | lcp_get_default_ns (void) | ||||||
|  | { | ||||||
|  |   lcp_main_t *lcpm = &lcp_main; | ||||||
|  |  | ||||||
|  |   if (lcpm->default_namespace[0] == 0) | ||||||
|  |     return 0; | ||||||
|  |   return lcpm->default_namespace; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int | ||||||
|  | lcp_get_default_ns_fd (void) | ||||||
|  | { | ||||||
|  |   lcp_main_t *lcpm = &lcp_main; | ||||||
|  |  | ||||||
|  |   return lcpm->default_ns_fd; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * ns is expected to be or look like a NUL-terminated C string. | ||||||
|  |  */ | ||||||
|  | int | ||||||
|  | lcp_set_default_ns (u8 *ns) | ||||||
|  | { | ||||||
|  |   lcp_main_t *lcpm = &lcp_main; | ||||||
|  |   char *p; | ||||||
|  |   int len; | ||||||
|  |   u8 *s; | ||||||
|  |  | ||||||
|  |   p = (char *) ns; | ||||||
|  |   len = clib_strnlen (p, LCP_NS_LEN); | ||||||
|  |   if (len >= LCP_NS_LEN) | ||||||
|  |     return -1; | ||||||
|  |  | ||||||
|  |   if (!p || *p == 0) | ||||||
|  |     { | ||||||
|  |       clib_memset (lcpm->default_namespace, 0, | ||||||
|  | 		   sizeof (lcpm->default_namespace)); | ||||||
|  |       if (lcpm->default_ns_fd > 0) | ||||||
|  | 	close (lcpm->default_ns_fd); | ||||||
|  |       lcpm->default_ns_fd = 0; | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   clib_strncpy ((char *) lcpm->default_namespace, p, LCP_NS_LEN - 1); | ||||||
|  |  | ||||||
|  |   s = format (0, "/var/run/netns/%s%c", (char *) lcpm->default_namespace, 0); | ||||||
|  |   lcpm->default_ns_fd = open ((char *) s, O_RDONLY); | ||||||
|  |   vec_free (s); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										48
									
								
								lcpng.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lcpng.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 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. | ||||||
|  |  */ | ||||||
|  | #ifndef __LCP_H__ | ||||||
|  | #define __LCP_H__ | ||||||
|  |  | ||||||
|  | #include <vlib/vlib.h> | ||||||
|  |  | ||||||
|  | #define LCP_NS_LEN 32 | ||||||
|  |  | ||||||
|  | typedef struct lcp_main_s | ||||||
|  | { | ||||||
|  |   u16 msg_id_base;		    /* API message ID base */ | ||||||
|  |   u8 default_namespace[LCP_NS_LEN]; /* default namespace if set */ | ||||||
|  |   int default_ns_fd; | ||||||
|  |   /* Set when Unit testing */ | ||||||
|  |   u8 test_mode; | ||||||
|  | } lcp_main_t; | ||||||
|  |  | ||||||
|  | extern lcp_main_t lcp_main; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Get/Set the default namespace for LCP host taps. | ||||||
|  |  */ | ||||||
|  | int lcp_set_default_ns (u8 *ns); | ||||||
|  | u8 *lcp_get_default_ns (void); /* Returns NULL or shared string */ | ||||||
|  | int lcp_get_default_ns_fd (void); | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										95
									
								
								lcpng.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								lcpng.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | .. _Linux_control_plane: | ||||||
|  |  | ||||||
|  | .. toctree:: | ||||||
|  |  | ||||||
|  | Linux Control Plane Integration | ||||||
|  | =============================== | ||||||
|  |  | ||||||
|  | Overview | ||||||
|  | ________ | ||||||
|  |  | ||||||
|  | This plugin allows VPP to integrate with the Linux Kernel. The | ||||||
|  | general model is that Linux is the network stack, i.e. it has the | ||||||
|  | control plane protocols, like ARP, IPv6 ND/MLD, ping, etc, and VPP | ||||||
|  | provides a SW based ASIC for forwarding. | ||||||
|  |  | ||||||
|  | Interfaces | ||||||
|  | __________ | ||||||
|  |  | ||||||
|  | VPP owns the interfaces in the system; physical (.e.g PCI), quasi | ||||||
|  | physical (e.g. vhost), or virtual (e.g. tunnel). However, | ||||||
|  | for the Linux networking stack to function it needs a representation | ||||||
|  | of these interfaces; it needs a mirror image in the kernel. For this | ||||||
|  | mirror we use a Tap interface, if the VPP interface is multi-point, a | ||||||
|  | Tun if it's point-to-point. A physical and its mirror form an | ||||||
|  | interface 'pair'. | ||||||
|  |  | ||||||
|  | The host interface has two identities; the sw_if_index of the Tap and | ||||||
|  | the virtual interface index in the kernel. It may be in a Linux namespace. | ||||||
|  |  | ||||||
|  | The creation of the interface pairs is required from the control | ||||||
|  | plane. It can be statically configured in the VPP startup | ||||||
|  | configuration file. The intent here was to make the pair creation | ||||||
|  | explicit, rather than have VPP guess which of the interfaces it owns | ||||||
|  | require a mirror. | ||||||
|  |  | ||||||
|  | Configuration | ||||||
|  | _____________ | ||||||
|  |  | ||||||
|  | Linux will send and receive packets on the mirrored tap/tun | ||||||
|  | interfaces. Any configuration that is made on these Linux interfaces, | ||||||
|  | also needs to be applied on the corresponding physical interface in | ||||||
|  | VPP. | ||||||
|  |  | ||||||
|  | This is functionality is not provided in this plugin, but it can be | ||||||
|  | achieved in various ways, for example by listening to the netlink | ||||||
|  | messages and applying the config. As a result all e.g. routes | ||||||
|  | programmed in Linux, will also be present in VPP's FIB. | ||||||
|  |  | ||||||
|  | Linux will own the [ARP/ND] nieghbor tables (which will be copied via | ||||||
|  | netlink to VPP also). This means that Linux will send packets with the | ||||||
|  | peer's MAC address in the rewrite to VPP. The receiving TAP interface | ||||||
|  | must therefore be in promiscuous mode. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Forwarding | ||||||
|  | __________ | ||||||
|  |  | ||||||
|  | The basic principle is to x-connect traffic from a Linux host interface | ||||||
|  | (received on the Tap/Tun) to its paired the physical, and vice-versa. | ||||||
|  |  | ||||||
|  | Host to Physical | ||||||
|  | ^^^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
|  | All packets sent by the host, and received by VPP on a Tap/Tun should | ||||||
|  | be sent to its paired physical interface. However, they should be sent | ||||||
|  | with the same consequences as if they had originated from VPP, | ||||||
|  | i.e. they should be subject to all output features on the physical | ||||||
|  | interface. To achieve this there is a per-IP-address-family (AF) node | ||||||
|  | inserted in the per-AF input feature arc. The node must be per-AF, | ||||||
|  | since it must be a sibling of a start node for the ipX-output feature | ||||||
|  | arc. This node uses the packet's L2 rewrite to search for the | ||||||
|  | adjacency that VPP would have used to send this packet; this adjacency | ||||||
|  | is stored in the buffer's meta data so that it is available to all | ||||||
|  | output features. Then the packet is sent through the physical | ||||||
|  | interface's IP output feature arc. | ||||||
|  | All ARP packets are x-connected from the tap to the physical. | ||||||
|  |  | ||||||
|  | Physical to Host | ||||||
|  | ^^^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
|  | All ARP packets received on the physical are sent to the paired | ||||||
|  | Tap. This allows the Linux network stack to build the nieghbour table. | ||||||
|  |  | ||||||
|  | IP packets that are punted are sent to the host. They are sent on the | ||||||
|  | tap that is paired with the physical on which they were originally | ||||||
|  | received. The packet is sent on the Tap/Tun 'exactly' as it was | ||||||
|  | received (i.e. with the L2 rewrite) but post any translations that | ||||||
|  | input features may have made. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Recommendations | ||||||
|  | ^^^^^^^^^^^^^^^ | ||||||
|  |  | ||||||
|  | When using this plugin disable the ARP, ND, IGMP plugins; this is the | ||||||
|  | task for Linux. Disable ping plugin, since Linux will now respond. | ||||||
							
								
								
									
										227
									
								
								lcpng_adj.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								lcpng_adj.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,227 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 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/adj/adj_delegate.h> | ||||||
|  |  | ||||||
|  | #include <lcpng/lcpng_interface.h> | ||||||
|  | #include <lcpng/lcpng_adj.h> | ||||||
|  |  | ||||||
|  | #include <vppinfra/bihash_32_8.h> | ||||||
|  | #include <vppinfra/bihash_template.c> | ||||||
|  |  | ||||||
|  | static adj_delegate_type_t adj_type; | ||||||
|  | static lcp_adj_key_t *adj_keys; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The table of adjacencies indexed by the rewrite string | ||||||
|  |  */ | ||||||
|  | BVT (clib_bihash) lcp_adj_tbl; | ||||||
|  |  | ||||||
|  | static_always_inline void | ||||||
|  | lcp_adj_mk_key_adj (const ip_adjacency_t *adj, lcp_adj_key_t *key) | ||||||
|  | { | ||||||
|  |   lcp_adj_mk_key (adj->rewrite_header.data, adj->rewrite_header.data_bytes, | ||||||
|  | 		  adj->rewrite_header.sw_if_index, key); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static u8 * | ||||||
|  | lcp_adj_delegate_format (const adj_delegate_t *aed, u8 *s) | ||||||
|  | { | ||||||
|  |   return (format (s, "lcp")); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | lcp_adj_delegate_adj_deleted (adj_delegate_t *aed) | ||||||
|  | { | ||||||
|  |   ip_adjacency_t *adj; | ||||||
|  |   lcp_adj_kv_t kv; | ||||||
|  |  | ||||||
|  |   adj = adj_get (aed->ad_adj_index); | ||||||
|  |  | ||||||
|  |   lcp_adj_mk_key_adj (adj, &kv.k); | ||||||
|  |  | ||||||
|  |   BV (clib_bihash_add_del) (&lcp_adj_tbl, &kv.kv, 0); | ||||||
|  |  | ||||||
|  |   if (aed->ad_index != INDEX_INVALID) | ||||||
|  |     pool_put_index (adj_keys, aed->ad_index); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* when an adj is modified: | ||||||
|  |  * | ||||||
|  |  * An existing hash entry may need to be deleted. This may occur when: | ||||||
|  |  * * The newly modified adj does not have IP_LOOKUP_NEXT_REWRITE as next idx | ||||||
|  |  * * The rewrite (== major component of hash key) changed | ||||||
|  |  * | ||||||
|  |  * A new hash entry may need to be added. This may occur when: | ||||||
|  |  * * The newly modified adj has IP_LOOKUP_NEXT_REWRITE as next idx | ||||||
|  |  * * The rewrite changed or there was no existing hash entry | ||||||
|  |  */ | ||||||
|  | static void | ||||||
|  | lcp_adj_delegate_adj_modified (adj_delegate_t *aed) | ||||||
|  | { | ||||||
|  |   ip_adjacency_t *adj; | ||||||
|  |   lcp_adj_kv_t kv; | ||||||
|  |   lcp_adj_key_t *adj_key = NULL; | ||||||
|  |   u8 save_adj, key_changed; | ||||||
|  |  | ||||||
|  |   key_changed = 0; | ||||||
|  |  | ||||||
|  |   adj = adj_get (aed->ad_adj_index); | ||||||
|  |   save_adj = (IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index); | ||||||
|  |  | ||||||
|  |   if (aed->ad_index != INDEX_INVALID) | ||||||
|  |     adj_key = pool_elt_at_index (adj_keys, aed->ad_index); | ||||||
|  |  | ||||||
|  |   /* return if there was no stored adj and we will not add one */ | ||||||
|  |   if (!adj_key && !save_adj) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   /* build kv if a new entry should be stored */ | ||||||
|  |   if (save_adj) | ||||||
|  |     { | ||||||
|  |       lcp_adj_mk_key_adj (adj, &kv.k); | ||||||
|  |       kv.v = aed->ad_adj_index; | ||||||
|  |       if (adj_key) | ||||||
|  | 	key_changed = (clib_memcmp (adj_key, &kv.k, sizeof (*adj_key)) != 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   /* delete old entry if needed */ | ||||||
|  |   if (adj_key && ((save_adj && key_changed) || (!save_adj))) | ||||||
|  |     { | ||||||
|  |       lcp_adj_kv_t old_kv; | ||||||
|  |  | ||||||
|  |       clib_memcpy_fast (&old_kv.k, adj_key, sizeof (*adj_key)); | ||||||
|  |       old_kv.v = 0; | ||||||
|  |  | ||||||
|  |       BV (clib_bihash_add_del) (&lcp_adj_tbl, &old_kv.kv, 0); | ||||||
|  |  | ||||||
|  |       if (!save_adj) | ||||||
|  | 	{ | ||||||
|  | 	  pool_put (adj_keys, adj_key); | ||||||
|  | 	  aed->ad_index = INDEX_INVALID; | ||||||
|  | 	} | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   /* add new entry if needed */ | ||||||
|  |   if (save_adj) | ||||||
|  |     { | ||||||
|  |       BV (clib_bihash_add_del) (&lcp_adj_tbl, &kv.kv, 1); | ||||||
|  |  | ||||||
|  |       if (!adj_key) | ||||||
|  | 	{ | ||||||
|  | 	  pool_get (adj_keys, adj_key); | ||||||
|  | 	  aed->ad_index = adj_key - adj_keys; | ||||||
|  | 	} | ||||||
|  |       clib_memcpy_fast (adj_key, &kv.k, sizeof (*adj_key)); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | lcp_adj_delegate_adj_created (adj_index_t ai) | ||||||
|  | { | ||||||
|  |   ip_adjacency_t *adj; | ||||||
|  |   lcp_adj_kv_t kv; | ||||||
|  |   index_t lai = INDEX_INVALID; | ||||||
|  |   lcp_adj_key_t *adj_key; | ||||||
|  |   index_t lipi; | ||||||
|  |   lcp_itf_pair_t *lip; | ||||||
|  |  | ||||||
|  |   adj = adj_get (ai); | ||||||
|  |  | ||||||
|  |   lipi = lcp_itf_pair_find_by_phy (adj->rewrite_header.sw_if_index); | ||||||
|  |   if (lipi == INDEX_INVALID) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   lip = lcp_itf_pair_get (lipi); | ||||||
|  |   if (lip->lip_host_type == LCP_ITF_HOST_TUN) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   if (IP_LOOKUP_NEXT_REWRITE == adj->lookup_next_index) | ||||||
|  |     { | ||||||
|  |       lcp_adj_mk_key_adj (adj, &kv.k); | ||||||
|  |       pool_get (adj_keys, adj_key); | ||||||
|  |       clib_memcpy_fast (adj_key, &kv.k, sizeof (*adj_key)); | ||||||
|  |       kv.v = ai; | ||||||
|  |  | ||||||
|  |       BV (clib_bihash_add_del) (&lcp_adj_tbl, &kv.kv, 1); | ||||||
|  |       lai = adj_key - adj_keys; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   adj_delegate_add (adj, adj_type, lai); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | u8 * | ||||||
|  | format_lcp_adj_kvp (u8 *s, va_list *args) | ||||||
|  | { | ||||||
|  |   BVT (clib_bihash_kv) *kv = va_arg (*args, BVT (clib_bihash_kv) *); | ||||||
|  |   CLIB_UNUSED (int verbose) = va_arg (*args, int); | ||||||
|  |   lcp_adj_kv_t *akv = (lcp_adj_kv_t *) kv; | ||||||
|  |  | ||||||
|  |   s = format (s, "  %U:%U\n    %U", format_vnet_sw_if_index_name, | ||||||
|  | 	      vnet_get_main (), akv->k.sw_if_index, format_hex_bytes, | ||||||
|  | 	      akv->k.rewrite, 18, format_adj_nbr, akv->v, 4); | ||||||
|  |  | ||||||
|  |   return (s); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_adj_show_cmd (vlib_main_t *vm, unformat_input_t *input, | ||||||
|  | 		  vlib_cli_command_t *cmd) | ||||||
|  | { | ||||||
|  |   u8 verbose = 0; | ||||||
|  |  | ||||||
|  |   if (unformat (input, "verbose")) | ||||||
|  |     verbose = 1; | ||||||
|  |  | ||||||
|  |   vlib_cli_output (vm, "lcpng Adjs:\n%U", BV (format_bihash), &lcp_adj_tbl, | ||||||
|  | 		   verbose); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_CLI_COMMAND (lcp_itf_pair_show_cmd_node, static) = { | ||||||
|  |   .path = "show lcp adj", | ||||||
|  |   .function = lcp_adj_show_cmd, | ||||||
|  |   .short_help = "show lcp adj", | ||||||
|  |   .is_mp_safe = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const adj_delegate_vft_t lcp_adj_vft = { | ||||||
|  |   .adv_format = lcp_adj_delegate_format, | ||||||
|  |   .adv_adj_deleted = lcp_adj_delegate_adj_deleted, | ||||||
|  |   .adv_adj_modified = lcp_adj_delegate_adj_modified, | ||||||
|  |   .adv_adj_created = lcp_adj_delegate_adj_created, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_adj_init (vlib_main_t *vm) | ||||||
|  | { | ||||||
|  |   adj_type = adj_delegate_register_new_type (&lcp_adj_vft); | ||||||
|  |  | ||||||
|  |   BV (clib_bihash_init) (&lcp_adj_tbl, "lcpng ADJ table", 1024, 1 << 24); | ||||||
|  |   BV (clib_bihash_set_kvp_format_fn) (&lcp_adj_tbl, format_lcp_adj_kvp); | ||||||
|  |  | ||||||
|  |   return (NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_INIT_FUNCTION (lcp_adj_init); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										84
									
								
								lcpng_adj.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								lcpng_adj.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef __LCP_ADJ_DELEGATE_H__ | ||||||
|  | #define __LCP_ADJ_DELEGATE_H__ | ||||||
|  |  | ||||||
|  | #include <vppinfra/bihash_32_8.h> | ||||||
|  |  | ||||||
|  | typedef struct lcp_adj_key_t_ | ||||||
|  | { | ||||||
|  |   u32 sw_if_index; | ||||||
|  |   u8 rewrite[28]; | ||||||
|  | } lcp_adj_key_t; | ||||||
|  |  | ||||||
|  | STATIC_ASSERT (sizeof (lcp_adj_key_t) == 32, "LCP ADJ Key size changed"); | ||||||
|  |  | ||||||
|  | typedef struct lcp_adj_kv_t_ | ||||||
|  | { | ||||||
|  |   union | ||||||
|  |   { | ||||||
|  |     BVT (clib_bihash_kv) kv; | ||||||
|  |     struct | ||||||
|  |     { | ||||||
|  |       lcp_adj_key_t k; | ||||||
|  |       u64 v; | ||||||
|  |     }; | ||||||
|  |   }; | ||||||
|  | } lcp_adj_kv_t; | ||||||
|  |  | ||||||
|  | STATIC_ASSERT (sizeof (lcp_adj_kv_t) == sizeof (BVT (clib_bihash_kv)), | ||||||
|  | 	       "LCP ADJ Key size changed"); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * The table of adjacencies indexed by the rewrite string | ||||||
|  |  */ | ||||||
|  | extern BVT (clib_bihash) lcp_adj_tbl; | ||||||
|  |  | ||||||
|  | static_always_inline void | ||||||
|  | lcp_adj_mk_key (const u8 *rewrite, u8 len, u32 sw_if_index, lcp_adj_key_t *key) | ||||||
|  | { | ||||||
|  |   /* | ||||||
|  |    * Construct the key from the provided rewrite, then pad with zeros | ||||||
|  |    * to ensure the key does not have garbage bytes | ||||||
|  |    */ | ||||||
|  |   ASSERT (len <= sizeof (key->rewrite)); | ||||||
|  |   clib_memcpy_fast (key->rewrite, rewrite, len); | ||||||
|  |   clib_memset (key->rewrite + len, 0, sizeof (key->rewrite) - len); | ||||||
|  |   key->sw_if_index = sw_if_index; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static_always_inline adj_index_t | ||||||
|  | lcp_adj_lkup (const u8 *rewrite, u8 len, u32 sw_if_index) | ||||||
|  | { | ||||||
|  |   lcp_adj_kv_t kv; | ||||||
|  |  | ||||||
|  |   lcp_adj_mk_key (rewrite, len, sw_if_index, &kv.k); | ||||||
|  |  | ||||||
|  |   if (!BV (clib_bihash_search_inline) (&lcp_adj_tbl, &kv.kv)) | ||||||
|  |     return (kv.v); | ||||||
|  |  | ||||||
|  |   return (ADJ_INDEX_INVALID); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										182
									
								
								lcpng_if.api
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								lcpng_if.api
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,182 @@ | |||||||
|  | /* Hey Emacs use -*- mode: C -*- */ | ||||||
|  | /* | ||||||
|  |  * Linux Control Plane API | ||||||
|  |  * | ||||||
|  |  * Copyright 2020 Rubicon Communications, LLC. | ||||||
|  |  * | ||||||
|  |  * 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. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | option version = "1.0.0"; | ||||||
|  |  | ||||||
|  | import "vnet/interface_types.api"; | ||||||
|  |  | ||||||
|  | /** \brief Set the default Linux Control Plane namespace | ||||||
|  |     @param client_index - opaque cookie to identify the sender | ||||||
|  |     @param context - sender context, to match reply w/ request | ||||||
|  |     @param namespace - the new default namespace; namespace[0] == 0 iff none | ||||||
|  | */ | ||||||
|  | autoreply define lcp_default_ns_set | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  |   string namespace[32];		/* LCP_NS_LEN */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** \brief get the default Linux Control Plane namespace | ||||||
|  |     @param client_index - opaque cookie to identify the sender | ||||||
|  |     @param context - sender context, to match reply w/ request | ||||||
|  | */ | ||||||
|  | define lcp_default_ns_get | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** \brief get the default Linux Control Plane namespace | ||||||
|  |     @param client_index - opaque cookie to identify the sender | ||||||
|  |     @param context - sender context, to match reply w/ request | ||||||
|  |     @param namespace - the default namespace; namespace[0] == 0 iff none | ||||||
|  | */ | ||||||
|  | define lcp_default_ns_get_reply | ||||||
|  | { | ||||||
|  |   u32 context; | ||||||
|  |   string namespace[32];		/* LCP_NS_LEN */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum lcp_itf_host_type : u8 | ||||||
|  | { | ||||||
|  |   LCP_API_ITF_HOST_TAP = 0, | ||||||
|  |   LCP_API_ITF_HOST_TUN = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** \brief Add or delete a Linux Conrol Plane interface pair | ||||||
|  |     @param client_index - opaque cookie to identify the sender | ||||||
|  |     @param context - sender context, to match reply w/ request | ||||||
|  |     @param is_add - 0 if deleting, != 0 if adding | ||||||
|  |     @param sw_if_index - index of VPP PHY SW interface | ||||||
|  |     @param host_if_name - host tap interface name | ||||||
|  |     @param host_if_type - the type of host interface to create (tun, tap) | ||||||
|  |     @param namespace - optional tap namespace; namespace[0] == 0 iff none | ||||||
|  | */ | ||||||
|  | autoreply autoendian define lcp_itf_pair_add_del | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  |   bool is_add; | ||||||
|  |   vl_api_interface_index_t sw_if_index; | ||||||
|  |   string host_if_name[16];		/* IFNAMSIZ */ | ||||||
|  |   vl_api_lcp_itf_host_type_t host_if_type; | ||||||
|  |   string namespace[32];			/* LCP_NS_LEN */ | ||||||
|  | }; | ||||||
|  | autoendian define lcp_itf_pair_add_del_v2 | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  |   bool is_add; | ||||||
|  |   vl_api_interface_index_t sw_if_index; | ||||||
|  |   string host_if_name[16];		/* IFNAMSIZ */ | ||||||
|  |   vl_api_lcp_itf_host_type_t host_if_type; | ||||||
|  |   string namespace[32];			/* LCP_NS_LEN */ | ||||||
|  | }; | ||||||
|  | define lcp_itf_pair_add_del_v2_reply | ||||||
|  | { | ||||||
|  |   u32 context; | ||||||
|  |   i32 retval; | ||||||
|  |   vl_api_interface_index_t host_sw_if_index; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** \brief Dump Linux Control Plane interface pair data | ||||||
|  |     @param client_index - opaque cookie to identify the sender | ||||||
|  |     @param context - sender context, to match reply w/ request | ||||||
|  |     @param sw_if_index - interface to use as filter (~0 == "all") | ||||||
|  | */ | ||||||
|  | define lcp_itf_pair_get | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  |   u32 cursor; | ||||||
|  | }; | ||||||
|  | define lcp_itf_pair_get_reply | ||||||
|  | { | ||||||
|  |   u32 context; | ||||||
|  |   i32 retval; | ||||||
|  |   u32 cursor; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** \brief Linux Control Plane interface pair dump response | ||||||
|  |     @param context - sender context which was passed in the request | ||||||
|  |     @param phy_sw_if_index - VPP's sw_if_index for the PHY | ||||||
|  |     @param host_sw_if_index - VPP's sw_if_index for the host tap | ||||||
|  |     @param vif_index - tap linux index | ||||||
|  |     @param host_if_name - host interface name | ||||||
|  |     @param host_if_type - host interface type (tun, tap) | ||||||
|  |     @param namespace - host interface namespace | ||||||
|  | */ | ||||||
|  | autoendian define lcp_itf_pair_details | ||||||
|  | { | ||||||
|  |   u32 context; | ||||||
|  |   vl_api_interface_index_t phy_sw_if_index; | ||||||
|  |   vl_api_interface_index_t host_sw_if_index; | ||||||
|  |   u32 vif_index; | ||||||
|  |   string host_if_name[16];	/* IFNAMSIZ */ | ||||||
|  |   vl_api_lcp_itf_host_type_t host_if_type; | ||||||
|  |   string namespace[32];		/* LCP_NS_LEN */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | service { | ||||||
|  |   rpc lcp_itf_pair_get returns lcp_itf_pair_get_reply | ||||||
|  |     stream lcp_itf_pair_details; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** \brief Replace end/begin | ||||||
|  |  */ | ||||||
|  | autoreply define lcp_itf_pair_replace_begin | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  | }; | ||||||
|  | autoreply define lcp_itf_pair_replace_end | ||||||
|  | { | ||||||
|  |   u32 client_index; | ||||||
|  |   u32 context; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Linux-CP Error counters/messages | ||||||
|  |  */ | ||||||
|  | counters linuxcp { | ||||||
|  |   packets { | ||||||
|  |     severity info; | ||||||
|  |     type counter64; | ||||||
|  |     units "packets"; | ||||||
|  |     description "ARP packets processed"; | ||||||
|  |   }; | ||||||
|  |   copies { | ||||||
|  |     severity info; | ||||||
|  |     type counter64; | ||||||
|  |     units "packets"; | ||||||
|  |     description "ARP replies copied to host"; | ||||||
|  |   }; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | paths { | ||||||
|  |   "/err/lcpng-arp-phy" "lcpng"; | ||||||
|  |   "/err/lcpng-arp-host" "lcpng"; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										260
									
								
								lcpng_if_api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								lcpng_if_api.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,260 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2020 Rubicon Communications, LLC. | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <linux/if.h> | ||||||
|  |  | ||||||
|  | #include <vnet/vnet.h> | ||||||
|  | #include <vnet/plugin/plugin.h> | ||||||
|  |  | ||||||
|  | #include <vlibapi/api.h> | ||||||
|  | #include <vlibmemory/api.h> | ||||||
|  | #include <vpp/app/version.h> | ||||||
|  | #include <vnet/format_fns.h> | ||||||
|  |  | ||||||
|  | #include <lcpng/lcpng_interface.h> | ||||||
|  | #include <lcpng/lcpng_if.api_enum.h> | ||||||
|  | #include <lcpng/lcpng_if.api_types.h> | ||||||
|  |  | ||||||
|  | static u16 lcp_msg_id_base; | ||||||
|  | #define REPLY_MSG_ID_BASE lcp_msg_id_base | ||||||
|  | #include <vlibapi/api_helper_macros.h> | ||||||
|  |  | ||||||
|  | static lip_host_type_t | ||||||
|  | api_decode_host_type (vl_api_lcp_itf_host_type_t type) | ||||||
|  | { | ||||||
|  |   if (type == LCP_API_ITF_HOST_TUN) | ||||||
|  |     return LCP_ITF_HOST_TUN; | ||||||
|  |  | ||||||
|  |   return LCP_ITF_HOST_TAP; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static vl_api_lcp_itf_host_type_t | ||||||
|  | api_encode_host_type (lip_host_type_t type) | ||||||
|  | { | ||||||
|  |   if (type == LCP_ITF_HOST_TUN) | ||||||
|  |     return LCP_API_ITF_HOST_TUN; | ||||||
|  |  | ||||||
|  |   return LCP_API_ITF_HOST_TAP; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int | ||||||
|  | vl_api_lcp_itf_pair_add (u32 phy_sw_if_index, lip_host_type_t lip_host_type, | ||||||
|  | 			 u8 *mp_host_if_name, size_t sizeof_host_if_name, | ||||||
|  | 			 u8 *mp_namespace, size_t sizeof_mp_namespace, | ||||||
|  | 			 u32 *host_sw_if_index_p) | ||||||
|  | { | ||||||
|  |   u8 *host_if_name, *netns; | ||||||
|  |   int host_len, netns_len, rv; | ||||||
|  |  | ||||||
|  |   host_if_name = netns = 0; | ||||||
|  |  | ||||||
|  |   /* lcp_itf_pair_create expects vec of u8 */ | ||||||
|  |   host_len = clib_strnlen ((char *) mp_host_if_name, sizeof_host_if_name - 1); | ||||||
|  |   vec_add (host_if_name, mp_host_if_name, host_len); | ||||||
|  |   vec_add1 (host_if_name, 0); | ||||||
|  |  | ||||||
|  |   netns_len = clib_strnlen ((char *) mp_namespace, sizeof_mp_namespace - 1); | ||||||
|  |   vec_add (netns, mp_namespace, netns_len); | ||||||
|  |   vec_add1 (netns, 0); | ||||||
|  |  | ||||||
|  |   rv = lcp_itf_pair_create (phy_sw_if_index, host_if_name, lip_host_type, | ||||||
|  | 			    netns, host_sw_if_index_p); | ||||||
|  |  | ||||||
|  |   vec_free (host_if_name); | ||||||
|  |   vec_free (netns); | ||||||
|  |  | ||||||
|  |   return rv; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_itf_pair_add_del_t_handler (vl_api_lcp_itf_pair_add_del_t *mp) | ||||||
|  | { | ||||||
|  |   u32 phy_sw_if_index; | ||||||
|  |   vl_api_lcp_itf_pair_add_del_reply_t *rmp; | ||||||
|  |   lip_host_type_t lip_host_type; | ||||||
|  |   int rv; | ||||||
|  |  | ||||||
|  |   if (!vnet_sw_if_index_is_api_valid (mp->sw_if_index)) | ||||||
|  |     { | ||||||
|  |       rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; | ||||||
|  |       goto bad_sw_if_index; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   phy_sw_if_index = mp->sw_if_index; | ||||||
|  |   lip_host_type = api_decode_host_type (mp->host_if_type); | ||||||
|  |   if (mp->is_add) | ||||||
|  |     { | ||||||
|  |       rv = | ||||||
|  | 	vl_api_lcp_itf_pair_add (phy_sw_if_index, lip_host_type, | ||||||
|  | 				 mp->host_if_name, sizeof (mp->host_if_name), | ||||||
|  | 				 mp->namespace, sizeof (mp->namespace), NULL); | ||||||
|  |     } | ||||||
|  |   else | ||||||
|  |     { | ||||||
|  |       rv = lcp_itf_pair_delete (phy_sw_if_index); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   BAD_SW_IF_INDEX_LABEL; | ||||||
|  |   REPLY_MACRO (VL_API_LCP_ITF_PAIR_ADD_DEL_REPLY); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_itf_pair_add_del_v2_t_handler (vl_api_lcp_itf_pair_add_del_v2_t *mp) | ||||||
|  | { | ||||||
|  |   u32 phy_sw_if_index, host_sw_if_index = ~0; | ||||||
|  |   vl_api_lcp_itf_pair_add_del_v2_reply_t *rmp; | ||||||
|  |   lip_host_type_t lip_host_type; | ||||||
|  |   int rv; | ||||||
|  |  | ||||||
|  |   if (!vnet_sw_if_index_is_api_valid (mp->sw_if_index)) | ||||||
|  |     { | ||||||
|  |       rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; | ||||||
|  |       goto bad_sw_if_index; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   phy_sw_if_index = mp->sw_if_index; | ||||||
|  |   lip_host_type = api_decode_host_type (mp->host_if_type); | ||||||
|  |   if (mp->is_add) | ||||||
|  |     { | ||||||
|  |       rv = vl_api_lcp_itf_pair_add (phy_sw_if_index, lip_host_type, | ||||||
|  | 				    mp->host_if_name, | ||||||
|  | 				    sizeof (mp->host_if_name), mp->namespace, | ||||||
|  | 				    sizeof (mp->namespace), &host_sw_if_index); | ||||||
|  |     } | ||||||
|  |   else | ||||||
|  |     { | ||||||
|  |       rv = lcp_itf_pair_delete (phy_sw_if_index); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   BAD_SW_IF_INDEX_LABEL; | ||||||
|  |   REPLY_MACRO2 (VL_API_LCP_ITF_PAIR_ADD_DEL_V2_REPLY, | ||||||
|  | 		{ rmp->host_sw_if_index = ntohl (host_sw_if_index); }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | send_lcp_itf_pair_details (index_t lipi, vl_api_registration_t *rp, | ||||||
|  | 			   u32 context) | ||||||
|  | { | ||||||
|  |   vl_api_lcp_itf_pair_details_t *rmp; | ||||||
|  |   lcp_itf_pair_t *lcp_pair = lcp_itf_pair_get (lipi); | ||||||
|  |  | ||||||
|  |   REPLY_MACRO_DETAILS4 ( | ||||||
|  |     VL_API_LCP_ITF_PAIR_DETAILS, rp, context, ({ | ||||||
|  |       rmp->phy_sw_if_index = lcp_pair->lip_phy_sw_if_index; | ||||||
|  |       rmp->host_sw_if_index = lcp_pair->lip_host_sw_if_index; | ||||||
|  |       rmp->vif_index = lcp_pair->lip_vif_index; | ||||||
|  |       rmp->host_if_type = api_encode_host_type (lcp_pair->lip_host_type); | ||||||
|  |  | ||||||
|  |       memcpy_s (rmp->host_if_name, sizeof (rmp->host_if_name), | ||||||
|  | 		lcp_pair->lip_host_name, vec_len (lcp_pair->lip_host_name)); | ||||||
|  |  | ||||||
|  |       clib_strncpy ((char *) rmp->namespace, (char *) lcp_pair->lip_namespace, | ||||||
|  | 		    vec_len (lcp_pair->lip_namespace)); | ||||||
|  |     })); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_itf_pair_get_t_handler (vl_api_lcp_itf_pair_get_t *mp) | ||||||
|  | { | ||||||
|  |   vl_api_lcp_itf_pair_get_reply_t *rmp; | ||||||
|  |   i32 rv = 0; | ||||||
|  |  | ||||||
|  |   REPLY_AND_DETAILS_MACRO ( | ||||||
|  |     VL_API_LCP_ITF_PAIR_GET_REPLY, lcp_itf_pair_pool, | ||||||
|  |     ({ send_lcp_itf_pair_details (cursor, rp, mp->context); })); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_default_ns_set_t_handler (vl_api_lcp_default_ns_set_t *mp) | ||||||
|  | { | ||||||
|  |   vl_api_lcp_default_ns_set_reply_t *rmp; | ||||||
|  |   int rv; | ||||||
|  |  | ||||||
|  |   mp->namespace[LCP_NS_LEN - 1] = 0; | ||||||
|  |   rv = lcp_set_default_ns (mp->namespace); | ||||||
|  |  | ||||||
|  |   REPLY_MACRO (VL_API_LCP_DEFAULT_NS_SET_REPLY); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_default_ns_get_t_handler (vl_api_lcp_default_ns_get_t *mp) | ||||||
|  | { | ||||||
|  |   lcp_main_t *lcpm = &lcp_main; | ||||||
|  |   vl_api_lcp_default_ns_get_reply_t *rmp; | ||||||
|  |   vl_api_registration_t *reg; | ||||||
|  |   char *ns; | ||||||
|  |  | ||||||
|  |   reg = vl_api_client_index_to_registration (mp->client_index); | ||||||
|  |   if (!reg) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   rmp = vl_msg_api_alloc (sizeof (*rmp)); | ||||||
|  |   clib_memset (rmp, 0, sizeof (*rmp)); | ||||||
|  |   rmp->_vl_msg_id = (VL_API_LCP_DEFAULT_NS_GET_REPLY + lcpm->msg_id_base); | ||||||
|  |   rmp->context = mp->context; | ||||||
|  |  | ||||||
|  |   ns = (char *) lcp_get_default_ns (); | ||||||
|  |   if (ns) | ||||||
|  |     clib_strncpy ((char *) rmp->namespace, ns, LCP_NS_LEN - 1); | ||||||
|  |  | ||||||
|  |   vl_api_send_msg (reg, (u8 *) rmp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_itf_pair_replace_begin_t_handler ( | ||||||
|  |   vl_api_lcp_itf_pair_replace_begin_t *mp) | ||||||
|  | { | ||||||
|  |   vl_api_lcp_itf_pair_replace_begin_reply_t *rmp; | ||||||
|  |   int rv; | ||||||
|  |  | ||||||
|  |   rv = lcp_itf_pair_replace_begin (); | ||||||
|  |  | ||||||
|  |   REPLY_MACRO (VL_API_LCP_ITF_PAIR_REPLACE_BEGIN_REPLY); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | vl_api_lcp_itf_pair_replace_end_t_handler ( | ||||||
|  |   vl_api_lcp_itf_pair_replace_end_t *mp) | ||||||
|  | { | ||||||
|  |   vl_api_lcp_itf_pair_replace_end_reply_t *rmp; | ||||||
|  |   int rv = 0; | ||||||
|  |  | ||||||
|  |   rv = lcp_itf_pair_replace_end (); | ||||||
|  |  | ||||||
|  |   REPLY_MACRO (VL_API_LCP_ITF_PAIR_REPLACE_END_REPLY); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Set up the API message handling tables | ||||||
|  |  */ | ||||||
|  | #include <lcpng/lcpng_if.api.c> | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_plugin_api_hookup (vlib_main_t *vm) | ||||||
|  | { | ||||||
|  |   /* Ask for a correctly-sized block of API message decode slots */ | ||||||
|  |   lcp_msg_id_base = setup_message_id_table (); | ||||||
|  |  | ||||||
|  |   return (NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_INIT_FUNCTION (lcp_plugin_api_hookup); | ||||||
|  |  | ||||||
|  | #include <vpp/app/version.h> | ||||||
|  | VLIB_PLUGIN_REGISTER () = { | ||||||
|  |   .version = VPP_BUILD_VER, | ||||||
|  |   .description = "Linux Control Plane - Interface Mirror", | ||||||
|  |   .default_disabled = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										242
									
								
								lcpng_if_cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								lcpng_if_cli.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,242 @@ | |||||||
|  | /* Hey Emacs use -*- mode: C -*- */ | ||||||
|  | /* | ||||||
|  |  * Copyright 2020 Rubicon Communications, LLC. | ||||||
|  |  * | ||||||
|  |  * 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 <sys/socket.h> | ||||||
|  | #include <linux/if.h> | ||||||
|  |  | ||||||
|  | #include <vnet/vnet.h> | ||||||
|  | #include <vnet/plugin/plugin.h> | ||||||
|  |  | ||||||
|  | #include <vlibapi/api.h> | ||||||
|  | #include <vlibmemory/api.h> | ||||||
|  | #include <vpp/app/version.h> | ||||||
|  | #include <vnet/format_fns.h> | ||||||
|  |  | ||||||
|  | #include <plugins/lcpng/lcpng_interface.h> | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_itf_pair_create_command_fn (vlib_main_t *vm, unformat_input_t *input, | ||||||
|  | 				vlib_cli_command_t *cmd) | ||||||
|  | { | ||||||
|  |   unformat_input_t _line_input, *line_input = &_line_input; | ||||||
|  |   vnet_main_t *vnm = vnet_get_main (); | ||||||
|  |   u32 sw_if_index; | ||||||
|  |   u8 *host_if_name; | ||||||
|  |   lip_host_type_t host_if_type; | ||||||
|  |   u8 *ns; | ||||||
|  |   int r; | ||||||
|  |  | ||||||
|  |   if (!unformat_user (input, unformat_line_input, line_input)) | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  |   sw_if_index = ~0; | ||||||
|  |   host_if_name = ns = NULL; | ||||||
|  |   host_if_type = LCP_ITF_HOST_TAP; | ||||||
|  |  | ||||||
|  |   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) | ||||||
|  |     { | ||||||
|  |       if (unformat (line_input, "%d", &sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (line_input, "%U", unformat_vnet_sw_interface, vnm, | ||||||
|  | 			 &sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (line_input, "host-if %s", &host_if_name)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (line_input, "netns %s", &ns)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (line_input, "tun")) | ||||||
|  | 	host_if_type = LCP_ITF_HOST_TUN; | ||||||
|  |       else | ||||||
|  | 	{ | ||||||
|  | 	  unformat_free (line_input); | ||||||
|  | 	  vec_free (host_if_name); | ||||||
|  | 	  vec_free (ns); | ||||||
|  | 	  return clib_error_return (0, "unknown input `%U'", | ||||||
|  | 				    format_unformat_error, input); | ||||||
|  | 	} | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   unformat_free (line_input); | ||||||
|  |  | ||||||
|  |   if (!host_if_name) | ||||||
|  |     { | ||||||
|  |       vec_free (ns); | ||||||
|  |       return clib_error_return (0, "host interface name required"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   if (sw_if_index == ~0) | ||||||
|  |     { | ||||||
|  |       vec_free (host_if_name); | ||||||
|  |       vec_free (ns); | ||||||
|  |       return clib_error_return (0, "interface name or sw_if_index required"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   if (vec_len (ns) >= LCP_NS_LEN) | ||||||
|  |     { | ||||||
|  |       vec_free (host_if_name); | ||||||
|  |       vec_free (ns); | ||||||
|  |       return clib_error_return ( | ||||||
|  | 	0, "Namespace name should be fewer than %d characters", LCP_NS_LEN); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   r = lcp_itf_pair_create (sw_if_index, host_if_name, host_if_type, ns, NULL); | ||||||
|  |  | ||||||
|  |   vec_free (host_if_name); | ||||||
|  |   vec_free (ns); | ||||||
|  |  | ||||||
|  |   if (r) | ||||||
|  |     return clib_error_return (0, "lcpng pair creation failed (%d)", r); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_CLI_COMMAND (lcp_itf_pair_create_command, static) = { | ||||||
|  |   .path = "lcp create", | ||||||
|  |   .short_help = "lcp create <sw_if_index>|<if-name> host-if <host-if-name> " | ||||||
|  | 		"netns <namespace> [tun]", | ||||||
|  |   .function = lcp_itf_pair_create_command_fn, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_default_netns_command_fn (vlib_main_t *vm, unformat_input_t *input, | ||||||
|  | 			      vlib_cli_command_t *cmd) | ||||||
|  | { | ||||||
|  |   unformat_input_t _line_input, *line_input = &_line_input; | ||||||
|  |   u8 *ns; | ||||||
|  |   int r; | ||||||
|  |  | ||||||
|  |   if (!unformat_user (input, unformat_line_input, line_input)) | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  |   ns = 0; | ||||||
|  |  | ||||||
|  |   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) | ||||||
|  |     { | ||||||
|  |       if (unformat (line_input, "netns %s", &ns)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (line_input, "clear netns")) | ||||||
|  | 	; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   unformat_free (line_input); | ||||||
|  |  | ||||||
|  |   vlib_cli_output (vm, "lcp set default netns '%s'\n", (char *) ns); | ||||||
|  |  | ||||||
|  |   r = lcp_set_default_ns (ns); | ||||||
|  |  | ||||||
|  |   if (r) | ||||||
|  |     return clib_error_return (0, "lcnpg set default netns failed (%d)", r); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_CLI_COMMAND (lcp_default_netns_command, static) = { | ||||||
|  |   .path = "lcp default", | ||||||
|  |   .short_help = "lcp default netns [<namespace>]", | ||||||
|  |   .function = lcp_default_netns_command_fn, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_itf_pair_delete_command_fn (vlib_main_t *vm, unformat_input_t *input, | ||||||
|  | 				vlib_cli_command_t *cmd) | ||||||
|  | { | ||||||
|  |   vnet_main_t *vnm = vnet_get_main (); | ||||||
|  |   unformat_input_t _line_input, *line_input = &_line_input; | ||||||
|  |   u32 sw_if_index; | ||||||
|  |   int r; | ||||||
|  |  | ||||||
|  |   if (!unformat_user (input, unformat_line_input, line_input)) | ||||||
|  |     return 0; | ||||||
|  |  | ||||||
|  |   sw_if_index = ~0; | ||||||
|  |  | ||||||
|  |   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) | ||||||
|  |     { | ||||||
|  |       if (unformat (line_input, "%d", &sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (line_input, "%U", unformat_vnet_sw_interface, vnm, | ||||||
|  | 			 &sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else | ||||||
|  | 	return clib_error_return (0, "unknown input `%U'", | ||||||
|  | 				  format_unformat_error, input); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   unformat_free (line_input); | ||||||
|  |  | ||||||
|  |   if (sw_if_index == ~0) | ||||||
|  |     return clib_error_return (0, "interface name or sw_if_index required"); | ||||||
|  |  | ||||||
|  |   r = lcp_itf_pair_delete (sw_if_index); | ||||||
|  |  | ||||||
|  |   if (r) | ||||||
|  |     return clib_error_return (0, "lcpng pair deletion failed (%d)", r); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_CLI_COMMAND (lcp_itf_pair_delete_command, static) = { | ||||||
|  |   .path = "lcp delete", | ||||||
|  |   .short_help = "lcp delete <sw_if_index>|<if-name>", | ||||||
|  |   .function = lcp_itf_pair_delete_command_fn, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_itf_pair_show_cmd (vlib_main_t *vm, unformat_input_t *input, | ||||||
|  | 		       vlib_cli_command_t *cmd) | ||||||
|  | { | ||||||
|  |   vnet_main_t *vnm = vnet_get_main (); | ||||||
|  |   u32 phy_sw_if_index; | ||||||
|  |  | ||||||
|  |   phy_sw_if_index = ~0; | ||||||
|  |  | ||||||
|  |   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) | ||||||
|  |     { | ||||||
|  |       if (unformat (input, "phy %U", unformat_vnet_sw_interface, vnm, | ||||||
|  | 		    &phy_sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else | ||||||
|  | 	return clib_error_return (0, "unknown input '%U'", | ||||||
|  | 				  format_unformat_error, input); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   lcp_itf_pair_show (phy_sw_if_index); | ||||||
|  |  | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_CLI_COMMAND (lcp_itf_pair_show_cmd_node, static) = { | ||||||
|  |   .path = "show lcp", | ||||||
|  |   .function = lcp_itf_pair_show_cmd, | ||||||
|  |   .short_help = "show lcp [phy <interface>]", | ||||||
|  |   .is_mp_safe = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | clib_error_t * | ||||||
|  | lcp_cli_init (vlib_main_t *vm) | ||||||
|  | { | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_INIT_FUNCTION (lcp_cli_init); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										914
									
								
								lcpng_if_node.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										914
									
								
								lcpng_if_node.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,914 @@ | |||||||
|  | /* | ||||||
|  |  * lcp_enthernet_node.c : linux control plane ethernet node | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2021 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 <sys/socket.h> | ||||||
|  | #include <linux/if.h> | ||||||
|  |  | ||||||
|  | #include <plugins/lcpng/lcpng_interface.h> | ||||||
|  | #include <plugins/lcpng/lcpng_adj.h> | ||||||
|  | #include <lcpng/lcpng_if.api_enum.h> | ||||||
|  |  | ||||||
|  | #include <vnet/feature/feature.h> | ||||||
|  | #include <vnet/ip/ip4_packet.h> | ||||||
|  | #include <vnet/ethernet/arp_packet.h> | ||||||
|  | #include <vnet/ethernet/ethernet.h> | ||||||
|  | #include <vnet/ip/ip_types.h> | ||||||
|  | #include <vnet/ip/lookup.h> | ||||||
|  | #include <vnet/ip/ip4.h> | ||||||
|  | #include <vnet/ip/ip6.h> | ||||||
|  | #include <vnet/l2/l2_input.h> | ||||||
|  |  | ||||||
|  | #define foreach_lip_punt                                                      \ | ||||||
|  |   _ (IO, "punt to host")                                                      \ | ||||||
|  |   _ (DROP, "unknown input interface") | ||||||
|  |  | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  | #define _(sym, str) LIP_PUNT_NEXT_##sym, | ||||||
|  |   foreach_lip_punt | ||||||
|  | #undef _ | ||||||
|  |     LIP_PUNT_N_NEXT, | ||||||
|  | } lip_punt_next_t; | ||||||
|  |  | ||||||
|  | typedef struct lip_punt_trace_t_ | ||||||
|  | { | ||||||
|  |   u32 phy_sw_if_index; | ||||||
|  |   u32 host_sw_if_index; | ||||||
|  | } lip_punt_trace_t; | ||||||
|  |  | ||||||
|  | /* packet trace format function */ | ||||||
|  | static u8 * | ||||||
|  | format_lip_punt_trace (u8 *s, va_list *args) | ||||||
|  | { | ||||||
|  |   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); | ||||||
|  |   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); | ||||||
|  |   lip_punt_trace_t *t = va_arg (*args, lip_punt_trace_t *); | ||||||
|  |  | ||||||
|  |   s = | ||||||
|  |     format (s, "lip-punt: %u -> %u", t->phy_sw_if_index, t->host_sw_if_index); | ||||||
|  |  | ||||||
|  |   return s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Pass punted packets from the PHY to the HOST. | ||||||
|  |  */ | ||||||
|  | VLIB_NODE_FN (lip_punt_node) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   u32 n_left_from, *from, *to_next, n_left_to_next; | ||||||
|  |   lip_punt_next_t next_index; | ||||||
|  |  | ||||||
|  |   next_index = node->cached_next_index; | ||||||
|  |   n_left_from = frame->n_vectors; | ||||||
|  |   from = vlib_frame_vector_args (frame); | ||||||
|  |  | ||||||
|  |   while (n_left_from > 0) | ||||||
|  |     { | ||||||
|  |       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); | ||||||
|  |  | ||||||
|  |       while (n_left_from > 0 && n_left_to_next > 0) | ||||||
|  | 	{ | ||||||
|  | 	  vlib_buffer_t *b0; | ||||||
|  | 	  const lcp_itf_pair_t *lip0 = NULL; | ||||||
|  | 	  u32 next0 = ~0; | ||||||
|  | 	  u32 bi0, lipi0; | ||||||
|  | 	  u32 sw_if_index0; | ||||||
|  | 	  u8 len0; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  |  | ||||||
|  | 	  from += 1; | ||||||
|  | 	  to_next += 1; | ||||||
|  | 	  n_left_from -= 1; | ||||||
|  | 	  n_left_to_next -= 1; | ||||||
|  | 	  next0 = LIP_PUNT_NEXT_DROP; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  |  | ||||||
|  | 	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; | ||||||
|  | 	  lipi0 = lcp_itf_pair_find_by_phy (sw_if_index0); | ||||||
|  | 	  if (PREDICT_FALSE (lipi0 == INDEX_INVALID)) | ||||||
|  | 	    goto trace0; | ||||||
|  |  | ||||||
|  | 	  lip0 = lcp_itf_pair_get (lipi0); | ||||||
|  | 	  next0 = LIP_PUNT_NEXT_IO; | ||||||
|  | 	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip0->lip_host_sw_if_index; | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_TRUE (lip0->lip_host_type == LCP_ITF_HOST_TAP)) | ||||||
|  | 	    { | ||||||
|  | 	      /* | ||||||
|  | 	       * rewind to ethernet header | ||||||
|  | 	       */ | ||||||
|  | 	      len0 = ((u8 *) vlib_buffer_get_current (b0) - | ||||||
|  | 		      (u8 *) ethernet_buffer_get_header (b0)); | ||||||
|  | 	      vlib_buffer_advance (b0, -len0); | ||||||
|  | 	    } | ||||||
|  | 	  /* Tun packets don't need any special treatment, just need to | ||||||
|  | 	   * be escorted past the TTL decrement. If we still want to use | ||||||
|  | 	   * ip[46]-punt-redirect with these, we could just set the | ||||||
|  | 	   * VNET_BUFFER_F_LOCALLY_ORIGINATED in an 'else {}' here and | ||||||
|  | 	   * then pass to the next node on the ip[46]-punt feature arc | ||||||
|  | 	   */ | ||||||
|  |  | ||||||
|  | 	trace0: | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lip_punt_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->phy_sw_if_index = sw_if_index0; | ||||||
|  | 	      t->host_sw_if_index = | ||||||
|  | 		(lipi0 == INDEX_INVALID) ? ~0 : lip0->lip_host_sw_if_index; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, next0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       vlib_put_next_frame (vm, node, next_index, n_left_to_next); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   return frame->n_vectors; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lip_punt_node) = { | ||||||
|  |   .name = "lcpng-punt", | ||||||
|  |   .vector_size = sizeof (u32), | ||||||
|  |   .format_trace = format_lip_punt_trace, | ||||||
|  |   .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  |  | ||||||
|  |   .n_next_nodes = LIP_PUNT_N_NEXT, | ||||||
|  |   .next_nodes = { | ||||||
|  |     [LIP_PUNT_NEXT_DROP] = "error-drop", | ||||||
|  |     [LIP_PUNT_NEXT_IO] = "interface-output", | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define foreach_lcp_punt_l3 _ (DROP, "unknown error") | ||||||
|  |  | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  | #define _(sym, str) LCP_LOCAL_NEXT_##sym, | ||||||
|  |   foreach_lcp_punt_l3 | ||||||
|  | #undef _ | ||||||
|  |     LCP_LOCAL_N_NEXT, | ||||||
|  | } lcp_punt_l3_next_t; | ||||||
|  |  | ||||||
|  | typedef struct lcp_punt_l3_trace_t_ | ||||||
|  | { | ||||||
|  |   u32 phy_sw_if_index; | ||||||
|  | } lcp_punt_l3_trace_t; | ||||||
|  |  | ||||||
|  | /* packet trace format function */ | ||||||
|  | static u8 * | ||||||
|  | format_lcp_punt_l3_trace (u8 *s, va_list *args) | ||||||
|  | { | ||||||
|  |   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); | ||||||
|  |   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); | ||||||
|  |   lcp_punt_l3_trace_t *t = va_arg (*args, lcp_punt_l3_trace_t *); | ||||||
|  |  | ||||||
|  |   s = format (s, "lcpng-punt-l3: %u", t->phy_sw_if_index); | ||||||
|  |  | ||||||
|  |   return s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_NODE_FN (lcp_punt_l3_node) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   u32 n_left_from, *from, *to_next, n_left_to_next; | ||||||
|  |   lip_punt_next_t next_index; | ||||||
|  |  | ||||||
|  |   next_index = node->cached_next_index; | ||||||
|  |   n_left_from = frame->n_vectors; | ||||||
|  |   from = vlib_frame_vector_args (frame); | ||||||
|  |  | ||||||
|  |   while (n_left_from > 0) | ||||||
|  |     { | ||||||
|  |       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); | ||||||
|  |  | ||||||
|  |       while (n_left_from > 0 && n_left_to_next > 0) | ||||||
|  | 	{ | ||||||
|  | 	  vlib_buffer_t *b0; | ||||||
|  | 	  u32 next0 = LCP_LOCAL_NEXT_DROP; | ||||||
|  | 	  u32 bi0; | ||||||
|  | 	  index_t lipi0; | ||||||
|  | 	  lcp_itf_pair_t *lip0; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  |  | ||||||
|  | 	  from += 1; | ||||||
|  | 	  to_next += 1; | ||||||
|  | 	  n_left_from -= 1; | ||||||
|  | 	  n_left_to_next -= 1; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  | 	  vnet_feature_next (&next0, b0); | ||||||
|  |  | ||||||
|  | 	  lipi0 = | ||||||
|  | 	    lcp_itf_pair_find_by_phy (vnet_buffer (b0)->sw_if_index[VLIB_RX]); | ||||||
|  | 	  if (lipi0 != INDEX_INVALID) | ||||||
|  | 	    { | ||||||
|  | 	      /* | ||||||
|  | 	       * Avoid TTL check for packets which arrived on a tunnel and | ||||||
|  | 	       * are being punted to the local host. | ||||||
|  | 	       */ | ||||||
|  | 	      lip0 = lcp_itf_pair_get (lipi0); | ||||||
|  | 	      if (lip0->lip_host_type == LCP_ITF_HOST_TUN) | ||||||
|  | 		b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_punt_l3_trace_t *t = | ||||||
|  | 		vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->phy_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, next0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       vlib_put_next_frame (vm, node, next_index, n_left_to_next); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   return frame->n_vectors; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_punt_l3_node) = { | ||||||
|  |   .name = "lcpng-punt-l3", | ||||||
|  |   .vector_size = sizeof (u32), | ||||||
|  |   .format_trace = format_lcp_punt_l3_trace, | ||||||
|  |   .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  |  | ||||||
|  |   .n_next_nodes = 1, | ||||||
|  |   .next_nodes = { | ||||||
|  |     [LCP_LOCAL_NEXT_DROP] = "error-drop", | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_punt_l3_ip4, static) = { | ||||||
|  |   .arc_name = "ip4-punt", | ||||||
|  |   .node_name = "lcpng-punt-l3", | ||||||
|  |   .runs_before = VNET_FEATURES ("ip4-punt-redirect"), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lip_punt_l3_ip6, static) = { | ||||||
|  |   .arc_name = "ip6-punt", | ||||||
|  |   .node_name = "lcpng-punt-l3", | ||||||
|  |   .runs_before = VNET_FEATURES ("ip6-punt-redirect"), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define foreach_lcp_xc                                                        \ | ||||||
|  |   _ (DROP, "drop")                                                            \ | ||||||
|  |   _ (XC_IP4, "x-connnect-ip4")                                                \ | ||||||
|  |   _ (XC_IP6, "x-connnect-ip6") | ||||||
|  |  | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  | #define _(sym, str) LCP_XC_NEXT_##sym, | ||||||
|  |   foreach_lcp_xc | ||||||
|  | #undef _ | ||||||
|  |     LCP_XC_N_NEXT, | ||||||
|  | } lcp_xc_next_t; | ||||||
|  |  | ||||||
|  | typedef struct lcp_xc_trace_t_ | ||||||
|  | { | ||||||
|  |   u32 phy_sw_if_index; | ||||||
|  |   adj_index_t adj_index; | ||||||
|  | } lcp_xc_trace_t; | ||||||
|  |  | ||||||
|  | /* packet trace format function */ | ||||||
|  | static u8 * | ||||||
|  | format_lcp_xc_trace (u8 *s, va_list *args) | ||||||
|  | { | ||||||
|  |   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); | ||||||
|  |   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); | ||||||
|  |   lcp_xc_trace_t *t = va_arg (*args, lcp_xc_trace_t *); | ||||||
|  |  | ||||||
|  |   s = format (s, "lcp-xc: itf:%d adj:%d", t->phy_sw_if_index, t->adj_index); | ||||||
|  |  | ||||||
|  |   return s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * X-connect all packets from the HOST to the PHY. | ||||||
|  |  * | ||||||
|  |  * This runs in either the IP4 or IP6 path. The MAC rewrite on the received | ||||||
|  |  * packet from the host is used as a key to find the adjacency used on the phy. | ||||||
|  |  * This allows this code to start the feature arc on that adjacency. | ||||||
|  |  * Consequently, all packet sent from the host are also subject to output | ||||||
|  |  * features, which is symmetric w.r.t. to input features. | ||||||
|  |  */ | ||||||
|  | static_always_inline u32 | ||||||
|  | lcp_xc_inline (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame, | ||||||
|  | 	       ip_address_family_t af) | ||||||
|  | { | ||||||
|  |   u32 n_left_from, *from, *to_next, n_left_to_next; | ||||||
|  |   lcp_xc_next_t next_index; | ||||||
|  |   ip_lookup_main_t *lm; | ||||||
|  |  | ||||||
|  |   next_index = 0; | ||||||
|  |   n_left_from = frame->n_vectors; | ||||||
|  |   from = vlib_frame_vector_args (frame); | ||||||
|  |  | ||||||
|  |   if (AF_IP4 == af) | ||||||
|  |     lm = &ip4_main.lookup_main; | ||||||
|  |   else | ||||||
|  |     lm = &ip6_main.lookup_main; | ||||||
|  |  | ||||||
|  |   while (n_left_from > 0) | ||||||
|  |     { | ||||||
|  |       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); | ||||||
|  |  | ||||||
|  |       while (n_left_from > 0 && n_left_to_next > 0) | ||||||
|  | 	{ | ||||||
|  | 	  const ethernet_header_t *eth; | ||||||
|  | 	  const lcp_itf_pair_t *lip; | ||||||
|  | 	  u32 next0, bi0, lipi, ai; | ||||||
|  | 	  vlib_buffer_t *b0; | ||||||
|  | 	  const ip_adjacency_t *adj; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  |  | ||||||
|  | 	  from += 1; | ||||||
|  | 	  to_next += 1; | ||||||
|  | 	  n_left_from -= 1; | ||||||
|  | 	  n_left_to_next -= 1; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  |  | ||||||
|  | 	  lipi = | ||||||
|  | 	    lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]); | ||||||
|  | 	  lip = lcp_itf_pair_get (lipi); | ||||||
|  |  | ||||||
|  | 	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index; | ||||||
|  | 	  vlib_buffer_advance (b0, -lip->lip_rewrite_len); | ||||||
|  | 	  eth = vlib_buffer_get_current (b0); | ||||||
|  |  | ||||||
|  | 	  ai = ADJ_INDEX_INVALID; | ||||||
|  | 	  if (!ethernet_address_cast (eth->dst_address)) | ||||||
|  | 	    ai = lcp_adj_lkup ((u8 *) eth, lip->lip_rewrite_len, | ||||||
|  | 			       vnet_buffer (b0)->sw_if_index[VLIB_TX]); | ||||||
|  | 	  if (ai == ADJ_INDEX_INVALID) | ||||||
|  | 	    ai = lip->lip_phy_adjs.adj_index[af]; | ||||||
|  |  | ||||||
|  | 	  adj = adj_get (ai); | ||||||
|  | 	  vnet_buffer (b0)->ip.adj_index[VLIB_TX] = ai; | ||||||
|  | 	  next0 = adj->rewrite_header.next_index; | ||||||
|  | 	  vnet_buffer (b0)->ip.save_rewrite_length = lip->lip_rewrite_len; | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE (adj->rewrite_header.flags & | ||||||
|  | 			     VNET_REWRITE_HAS_FEATURES)) | ||||||
|  | 	    vnet_feature_arc_start_w_cfg_index ( | ||||||
|  | 	      lm->output_feature_arc_index, | ||||||
|  | 	      vnet_buffer (b0)->sw_if_index[VLIB_TX], &next0, b0, | ||||||
|  | 	      adj->ia_cfg_index); | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->phy_sw_if_index = lip->lip_phy_sw_if_index; | ||||||
|  | 	      t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, next0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       vlib_put_next_frame (vm, node, next_index, n_left_to_next); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   return frame->n_vectors; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_NODE_FN (lcp_xc_ip4) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   return (lcp_xc_inline (vm, node, frame, AF_IP4)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_NODE_FN (lcp_xc_ip6) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   return (lcp_xc_inline (vm, node, frame, AF_IP6)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_xc_ip4) = { .name = "lcpng-xc-ip4", | ||||||
|  | 				    .vector_size = sizeof (u32), | ||||||
|  | 				    .format_trace = format_lcp_xc_trace, | ||||||
|  | 				    .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  | 				    .sibling_of = "ip4-rewrite" }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_ip4_ucast_node, static) = { | ||||||
|  |   .arc_name = "ip4-unicast", | ||||||
|  |   .node_name = "lcpng-xc-ip4", | ||||||
|  | }; | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_ip4_mcast_node, static) = { | ||||||
|  |   .arc_name = "ip4-multicast", | ||||||
|  |   .node_name = "lcpng-xc-ip4", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_xc_ip6) = { .name = "lcpng-xc-ip6", | ||||||
|  | 				    .vector_size = sizeof (u32), | ||||||
|  | 				    .format_trace = format_lcp_xc_trace, | ||||||
|  | 				    .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  | 				    .sibling_of = "ip6-rewrite" }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_ip6_ucast_node, static) = { | ||||||
|  |   .arc_name = "ip6-unicast", | ||||||
|  |   .node_name = "lcpng-xc-ip6", | ||||||
|  | }; | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_ip6_mcast_node, static) = { | ||||||
|  |   .arc_name = "ip6-multicast", | ||||||
|  |   .node_name = "lcpng-xc-ip6", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |   LCP_XC_L3_NEXT_XC, | ||||||
|  |   LCP_XC_L3_N_NEXT, | ||||||
|  | } lcp_xc_l3_next_t; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * X-connect all packets from the HOST to the PHY on L3 interfaces | ||||||
|  |  * | ||||||
|  |  * There's only one adjacency that can be used on thises links. | ||||||
|  |  */ | ||||||
|  | static_always_inline u32 | ||||||
|  | lcp_xc_l3_inline (vlib_main_t *vm, vlib_node_runtime_t *node, | ||||||
|  | 		  vlib_frame_t *frame, ip_address_family_t af) | ||||||
|  | { | ||||||
|  |   u32 n_left_from, *from, *to_next, n_left_to_next; | ||||||
|  |   lcp_xc_next_t next_index; | ||||||
|  |  | ||||||
|  |   next_index = 0; | ||||||
|  |   n_left_from = frame->n_vectors; | ||||||
|  |   from = vlib_frame_vector_args (frame); | ||||||
|  |  | ||||||
|  |   while (n_left_from > 0) | ||||||
|  |     { | ||||||
|  |       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); | ||||||
|  |  | ||||||
|  |       while (n_left_from > 0 && n_left_to_next > 0) | ||||||
|  | 	{ | ||||||
|  | 	  vlib_buffer_t *b0; | ||||||
|  | 	  const lcp_itf_pair_t *lip; | ||||||
|  | 	  u32 next0 = ~0; | ||||||
|  | 	  u32 bi0, lipi; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  |  | ||||||
|  | 	  from += 1; | ||||||
|  | 	  to_next += 1; | ||||||
|  | 	  n_left_from -= 1; | ||||||
|  | 	  n_left_to_next -= 1; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  |  | ||||||
|  | 	  /* Flag buffers as locally originated. Otherwise their TTL will | ||||||
|  | 	   * be checked & decremented. That would break services like BGP | ||||||
|  | 	   * which set a TTL of 1 by default. | ||||||
|  | 	   */ | ||||||
|  | 	  b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED; | ||||||
|  |  | ||||||
|  | 	  lipi = | ||||||
|  | 	    lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]); | ||||||
|  | 	  lip = lcp_itf_pair_get (lipi); | ||||||
|  |  | ||||||
|  | 	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip->lip_phy_sw_if_index; | ||||||
|  | 	  next0 = LCP_XC_L3_NEXT_XC; | ||||||
|  | 	  vnet_buffer (b0)->ip.adj_index[VLIB_TX] = | ||||||
|  | 	    lip->lip_phy_adjs.adj_index[af]; | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_xc_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->phy_sw_if_index = lip->lip_phy_sw_if_index; | ||||||
|  | 	      t->adj_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, next0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       vlib_put_next_frame (vm, node, next_index, n_left_to_next); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   return frame->n_vectors; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * X-connect all packets from the HOST to the PHY. | ||||||
|  |  */ | ||||||
|  | VLIB_NODE_FN (lcp_xc_l3_ip4_node) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   return (lcp_xc_l3_inline (vm, node, frame, AF_IP4)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_NODE_FN (lcp_xc_l3_ip6_node) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   return (lcp_xc_l3_inline (vm, node, frame, AF_IP6)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_xc_l3_ip4_node) = { | ||||||
|  |   .name = "lcpng-xc-l3-ip4", | ||||||
|  |   .vector_size = sizeof (u32), | ||||||
|  |   .format_trace = format_lcp_xc_trace, | ||||||
|  |   .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  |  | ||||||
|  |   .n_next_nodes = LCP_XC_L3_N_NEXT, | ||||||
|  |   .next_nodes = { | ||||||
|  |     [LCP_XC_L3_NEXT_XC] = "ip4-midchain", | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_unicast, static) = { | ||||||
|  |   .arc_name = "ip4-unicast", | ||||||
|  |   .node_name = "lcpng-xc-l3-ip4", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_node_l3_ip4_multicaast, static) = { | ||||||
|  |   .arc_name = "ip4-multicast", | ||||||
|  |   .node_name = "lcpng-xc-l3-ip4", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_xc_l3_ip6_node) = { | ||||||
|  |   .name = "lcpng-xc-l3-ip6", | ||||||
|  |   .vector_size = sizeof (u32), | ||||||
|  |   .format_trace = format_lcp_xc_trace, | ||||||
|  |   .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  |  | ||||||
|  |   .n_next_nodes = LCP_XC_L3_N_NEXT, | ||||||
|  |   .next_nodes = { | ||||||
|  |     [LCP_XC_L3_NEXT_XC] = "ip6-midchain", | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_unicast, static) = { | ||||||
|  |   .arc_name = "ip6-unicast", | ||||||
|  |   .node_name = "lcpng-xc-l3-ip6", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_xc_node_l3_ip6_multicast, static) = { | ||||||
|  |   .arc_name = "ip6-multicast", | ||||||
|  |   .node_name = "lcpng-xc-l3-ip6", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define foreach_lcp_arp                                                       \ | ||||||
|  |   _ (DROP, "error-drop")                                                      \ | ||||||
|  |   _ (IO, "interface-output") | ||||||
|  |  | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  | #define _(sym, str) LCP_ARP_NEXT_##sym, | ||||||
|  |   foreach_lcp_arp | ||||||
|  | #undef _ | ||||||
|  |     LCP_ARP_N_NEXT, | ||||||
|  | } lcp_arp_next_t; | ||||||
|  |  | ||||||
|  | typedef struct lcp_arp_trace_t_ | ||||||
|  | { | ||||||
|  |   u32 rx_sw_if_index; | ||||||
|  |   u16 arp_opcode; | ||||||
|  | } lcp_arp_trace_t; | ||||||
|  |  | ||||||
|  | /* packet trace format function */ | ||||||
|  | static u8 * | ||||||
|  | format_lcp_arp_trace (u8 *s, va_list *args) | ||||||
|  | { | ||||||
|  |   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); | ||||||
|  |   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); | ||||||
|  |   lcp_arp_trace_t *t = va_arg (*args, lcp_arp_trace_t *); | ||||||
|  |  | ||||||
|  |   s = format (s, "rx-sw-if-index: %u opcode: %u", t->rx_sw_if_index, | ||||||
|  | 	      t->arp_opcode); | ||||||
|  |  | ||||||
|  |   return s; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * punt ARP replies to the host | ||||||
|  |  */ | ||||||
|  | VLIB_NODE_FN (lcp_arp_phy_node) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   u32 n_left_from, *from, *to_next, n_left_to_next; | ||||||
|  |   lcp_arp_next_t next_index; | ||||||
|  |   u32 reply_copies[VLIB_FRAME_SIZE]; | ||||||
|  |   u32 n_copies = 0; | ||||||
|  |  | ||||||
|  |   next_index = node->cached_next_index; | ||||||
|  |   n_left_from = frame->n_vectors; | ||||||
|  |   from = vlib_frame_vector_args (frame); | ||||||
|  |  | ||||||
|  |   while (n_left_from > 0) | ||||||
|  |     { | ||||||
|  |       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); | ||||||
|  |  | ||||||
|  |       while (n_left_from >= 2 && n_left_to_next >= 2) | ||||||
|  | 	{ | ||||||
|  | 	  u32 next0, next1, bi0, bi1; | ||||||
|  | 	  vlib_buffer_t *b0, *b1; | ||||||
|  | 	  ethernet_arp_header_t *arp0, *arp1; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  | 	  bi1 = to_next[1] = from[1]; | ||||||
|  |  | ||||||
|  | 	  from += 2; | ||||||
|  | 	  n_left_from -= 2; | ||||||
|  | 	  to_next += 2; | ||||||
|  | 	  n_left_to_next -= 2; | ||||||
|  |  | ||||||
|  | 	  next0 = next1 = LCP_ARP_NEXT_DROP; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  | 	  b1 = vlib_get_buffer (vm, bi1); | ||||||
|  |  | ||||||
|  | 	  arp0 = vlib_buffer_get_current (b0); | ||||||
|  | 	  arp1 = vlib_buffer_get_current (b1); | ||||||
|  |  | ||||||
|  | 	  vnet_feature_next (&next0, b0); | ||||||
|  | 	  vnet_feature_next (&next1, b1); | ||||||
|  |  | ||||||
|  | 	  /* | ||||||
|  | 	   * Replies might need to be received by the host, so we | ||||||
|  | 	   * make a copy of them. | ||||||
|  | 	   */ | ||||||
|  | 	  if (arp0->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_itf_pair_t *lip0 = 0; | ||||||
|  | 	      u32 lipi0; | ||||||
|  | 	      vlib_buffer_t *c0; | ||||||
|  | 	      u8 len0; | ||||||
|  |  | ||||||
|  | 	      lipi0 = lcp_itf_pair_find_by_phy ( | ||||||
|  | 		vnet_buffer (b0)->sw_if_index[VLIB_RX]); | ||||||
|  | 	      lip0 = lcp_itf_pair_get (lipi0); | ||||||
|  |  | ||||||
|  | 	      if (lip0) | ||||||
|  | 		{ | ||||||
|  | 		  /* | ||||||
|  | 		   * rewind to eth header, copy, advance back to current | ||||||
|  | 		   */ | ||||||
|  | 		  len0 = ((u8 *) vlib_buffer_get_current (b0) - | ||||||
|  | 			  (u8 *) ethernet_buffer_get_header (b0)); | ||||||
|  | 		  vlib_buffer_advance (b0, -len0); | ||||||
|  | 		  c0 = vlib_buffer_copy (vm, b0); | ||||||
|  | 		  vlib_buffer_advance (b0, len0); | ||||||
|  |  | ||||||
|  | 		  /* Send to the host */ | ||||||
|  | 		  vnet_buffer (c0)->sw_if_index[VLIB_TX] = | ||||||
|  | 		    lip0->lip_host_sw_if_index; | ||||||
|  | 		  reply_copies[n_copies++] = vlib_get_buffer_index (vm, c0); | ||||||
|  | 		} | ||||||
|  | 	    } | ||||||
|  | 	  if (arp1->opcode == clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_reply)) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_itf_pair_t *lip1 = 0; | ||||||
|  | 	      u32 lipi1; | ||||||
|  | 	      vlib_buffer_t *c1; | ||||||
|  | 	      u8 len1; | ||||||
|  |  | ||||||
|  | 	      lipi1 = lcp_itf_pair_find_by_phy ( | ||||||
|  | 		vnet_buffer (b1)->sw_if_index[VLIB_RX]); | ||||||
|  | 	      lip1 = lcp_itf_pair_get (lipi1); | ||||||
|  |  | ||||||
|  | 	      if (lip1) | ||||||
|  | 		{ | ||||||
|  | 		  /* | ||||||
|  | 		   * rewind to reveal the ethernet header | ||||||
|  | 		   */ | ||||||
|  | 		  len1 = ((u8 *) vlib_buffer_get_current (b1) - | ||||||
|  | 			  (u8 *) ethernet_buffer_get_header (b1)); | ||||||
|  | 		  vlib_buffer_advance (b1, -len1); | ||||||
|  | 		  c1 = vlib_buffer_copy (vm, b1); | ||||||
|  | 		  vlib_buffer_advance (b1, len1); | ||||||
|  |  | ||||||
|  | 		  /* Send to the host */ | ||||||
|  | 		  vnet_buffer (c1)->sw_if_index[VLIB_TX] = | ||||||
|  | 		    lip1->lip_host_sw_if_index; | ||||||
|  | 		  reply_copies[n_copies++] = vlib_get_buffer_index (vm, c1); | ||||||
|  | 		} | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; | ||||||
|  | 	    } | ||||||
|  | 	  if (PREDICT_FALSE ((b1->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_arp_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t)); | ||||||
|  | 	      t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, bi1, next0, | ||||||
|  | 					   next1); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       while (n_left_from > 0 && n_left_to_next > 0) | ||||||
|  | 	{ | ||||||
|  | 	  u32 next0, bi0; | ||||||
|  | 	  vlib_buffer_t *b0; | ||||||
|  | 	  ethernet_arp_header_t *arp0; | ||||||
|  | 	  u16 arp_opcode; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  |  | ||||||
|  | 	  from += 1; | ||||||
|  | 	  n_left_from -= 1; | ||||||
|  | 	  to_next += 1; | ||||||
|  | 	  n_left_to_next -= 1; | ||||||
|  | 	  next0 = LCP_ARP_NEXT_DROP; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  | 	  arp0 = vlib_buffer_get_current (b0); | ||||||
|  |  | ||||||
|  | 	  vnet_feature_next (&next0, b0); | ||||||
|  |  | ||||||
|  | 	  /* | ||||||
|  | 	   * Replies might need to be received by the host, so we | ||||||
|  | 	   * make a copy of them. | ||||||
|  | 	   */ | ||||||
|  | 	  arp_opcode = clib_host_to_net_u16 (arp0->opcode); | ||||||
|  |  | ||||||
|  | 	  if (arp_opcode == ETHERNET_ARP_OPCODE_reply) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_itf_pair_t *lip0 = 0; | ||||||
|  | 	      vlib_buffer_t *c0; | ||||||
|  | 	      u32 lipi0; | ||||||
|  | 	      u8 len0; | ||||||
|  |  | ||||||
|  | 	      lipi0 = lcp_itf_pair_find_by_phy ( | ||||||
|  | 		vnet_buffer (b0)->sw_if_index[VLIB_RX]); | ||||||
|  | 	      lip0 = lcp_itf_pair_get (lipi0); | ||||||
|  |  | ||||||
|  | 	      if (lip0) | ||||||
|  | 		{ | ||||||
|  |  | ||||||
|  | 		  /* | ||||||
|  | 		   * rewind to reveal the ethernet header | ||||||
|  | 		   */ | ||||||
|  | 		  len0 = ((u8 *) vlib_buffer_get_current (b0) - | ||||||
|  | 			  (u8 *) ethernet_buffer_get_header (b0)); | ||||||
|  | 		  vlib_buffer_advance (b0, -len0); | ||||||
|  | 		  c0 = vlib_buffer_copy (vm, b0); | ||||||
|  | 		  vlib_buffer_advance (b0, len0); | ||||||
|  |  | ||||||
|  | 		  /* Send to the host */ | ||||||
|  | 		  vnet_buffer (c0)->sw_if_index[VLIB_TX] = | ||||||
|  | 		    lip0->lip_host_sw_if_index; | ||||||
|  | 		  reply_copies[n_copies++] = vlib_get_buffer_index (vm, c0); | ||||||
|  | 		} | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; | ||||||
|  | 	      t->arp_opcode = arp_opcode; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, next0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       vlib_put_next_frame (vm, node, next_index, n_left_to_next); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   if (n_copies) | ||||||
|  |     vlib_buffer_enqueue_to_single_next (vm, node, reply_copies, | ||||||
|  | 					LCP_ARP_NEXT_IO, n_copies); | ||||||
|  |  | ||||||
|  |   return frame->n_vectors; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_arp_phy_node) = { | ||||||
|  |   .name = "lcpng-arp-phy", | ||||||
|  |   .vector_size = sizeof (u32), | ||||||
|  |   .format_trace = format_lcp_arp_trace, | ||||||
|  |   .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  |  | ||||||
|  |   .n_errors = LINUXCP_N_ERROR, | ||||||
|  |   .error_counters = linuxcp_error_counters, | ||||||
|  |  | ||||||
|  |   .n_next_nodes = LCP_ARP_N_NEXT, | ||||||
|  |   .next_nodes = { | ||||||
|  |     [LCP_ARP_NEXT_DROP] = "error-drop", | ||||||
|  |     [LCP_ARP_NEXT_IO] = "interface-output", | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_arp_phy_arp_feat, static) = { | ||||||
|  |   .arc_name = "arp", | ||||||
|  |   .node_name = "lcpng-arp-phy", | ||||||
|  |   .runs_before = VNET_FEATURES ("arp-reply"), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * x-connect ARP packets from the host to the phy | ||||||
|  |  */ | ||||||
|  | VLIB_NODE_FN (lcp_arp_host_node) | ||||||
|  | (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) | ||||||
|  | { | ||||||
|  |   u32 n_left_from, *from, *to_next, n_left_to_next; | ||||||
|  |   lcp_arp_next_t next_index; | ||||||
|  |  | ||||||
|  |   next_index = node->cached_next_index; | ||||||
|  |   n_left_from = frame->n_vectors; | ||||||
|  |   from = vlib_frame_vector_args (frame); | ||||||
|  |  | ||||||
|  |   while (n_left_from > 0) | ||||||
|  |     { | ||||||
|  |       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); | ||||||
|  |  | ||||||
|  |       while (n_left_from > 0 && n_left_to_next > 0) | ||||||
|  | 	{ | ||||||
|  | 	  const lcp_itf_pair_t *lip0; | ||||||
|  | 	  lcp_arp_next_t next0; | ||||||
|  | 	  vlib_buffer_t *b0; | ||||||
|  | 	  u32 bi0, lipi0; | ||||||
|  | 	  u8 len0; | ||||||
|  |  | ||||||
|  | 	  bi0 = to_next[0] = from[0]; | ||||||
|  |  | ||||||
|  | 	  from += 1; | ||||||
|  | 	  n_left_from -= 1; | ||||||
|  | 	  to_next += 1; | ||||||
|  | 	  n_left_to_next -= 1; | ||||||
|  | 	  next0 = LCP_ARP_NEXT_IO; | ||||||
|  |  | ||||||
|  | 	  b0 = vlib_get_buffer (vm, bi0); | ||||||
|  |  | ||||||
|  | 	  lipi0 = | ||||||
|  | 	    lcp_itf_pair_find_by_host (vnet_buffer (b0)->sw_if_index[VLIB_RX]); | ||||||
|  | 	  lip0 = lcp_itf_pair_get (lipi0); | ||||||
|  |  | ||||||
|  | 	  /* Send to the phy */ | ||||||
|  | 	  vnet_buffer (b0)->sw_if_index[VLIB_TX] = lip0->lip_phy_sw_if_index; | ||||||
|  |  | ||||||
|  | 	  len0 = ((u8 *) vlib_buffer_get_current (b0) - | ||||||
|  | 		  (u8 *) ethernet_buffer_get_header (b0)); | ||||||
|  | 	  vlib_buffer_advance (b0, -len0); | ||||||
|  |  | ||||||
|  | 	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED))) | ||||||
|  | 	    { | ||||||
|  | 	      lcp_arp_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); | ||||||
|  | 	      t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; | ||||||
|  | 	    } | ||||||
|  |  | ||||||
|  | 	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, | ||||||
|  | 					   n_left_to_next, bi0, next0); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |       vlib_put_next_frame (vm, node, next_index, n_left_to_next); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   return frame->n_vectors; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_REGISTER_NODE (lcp_arp_host_node) = { | ||||||
|  |   .name = "lcpng-arp-host", | ||||||
|  |   .vector_size = sizeof (u32), | ||||||
|  |   .format_trace = format_lcp_arp_trace, | ||||||
|  |   .type = VLIB_NODE_TYPE_INTERNAL, | ||||||
|  |  | ||||||
|  |   .n_errors = LINUXCP_N_ERROR, | ||||||
|  |   .error_counters = linuxcp_error_counters, | ||||||
|  |  | ||||||
|  |   .n_next_nodes = LCP_ARP_N_NEXT, | ||||||
|  |   .next_nodes = { | ||||||
|  |     [LCP_ARP_NEXT_DROP] = "error-drop", | ||||||
|  |     [LCP_ARP_NEXT_IO] = "interface-output", | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | VNET_FEATURE_INIT (lcp_arp_host_arp_feat, static) = { | ||||||
|  |   .arc_name = "arp", | ||||||
|  |   .node_name = "lcpng-arp-host", | ||||||
|  |   .runs_before = VNET_FEATURES ("arp-reply"), | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
							
								
								
									
										1022
									
								
								lcpng_interface.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1022
									
								
								lcpng_interface.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										165
									
								
								lcpng_interface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								lcpng_interface.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2020 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. | ||||||
|  |  */ | ||||||
|  | #ifndef __LCP_ITF_PAIR_H__ | ||||||
|  | #define __LCP_ITF_PAIR_H__ | ||||||
|  |  | ||||||
|  | #include <vnet/dpo/dpo.h> | ||||||
|  | #include <vnet/adj/adj.h> | ||||||
|  | #include <vnet/ip/ip_types.h> | ||||||
|  |  | ||||||
|  | #include <plugins/lcpng/lcpng.h> | ||||||
|  |  | ||||||
|  | #define foreach_lcp_itf_pair_flag _ (STALE, 0, "stale") | ||||||
|  |  | ||||||
|  | typedef enum lip_flag_t_ | ||||||
|  | { | ||||||
|  | #define _(a, b, c) LIP_FLAG_##a = (1 << b), | ||||||
|  |   foreach_lcp_itf_pair_flag | ||||||
|  | #undef _ | ||||||
|  | } lip_flag_t; | ||||||
|  |  | ||||||
|  | typedef enum | ||||||
|  | { | ||||||
|  |   LCP_ITF_HOST_TAP = 0, | ||||||
|  |   LCP_ITF_HOST_TUN = 1, | ||||||
|  | } lip_host_type_t; | ||||||
|  |  | ||||||
|  | #define N_LCP_ITF_HOST (LCP_ITF_HOST_TUN + 1) | ||||||
|  |  | ||||||
|  | typedef struct lcp_itf_phy_adj | ||||||
|  | { | ||||||
|  |   adj_index_t adj_index[N_AF]; | ||||||
|  | } lcp_itf_phy_adj_t; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * A pair of interfaces | ||||||
|  |  */ | ||||||
|  | typedef struct lcp_itf_pair_t_ | ||||||
|  | { | ||||||
|  |   u32 lip_host_sw_if_index;	  /* VPP's sw_if_index for the host tap */ | ||||||
|  |   u32 lip_phy_sw_if_index;	  /* VPP's sw_if_index for the phy */ | ||||||
|  |   u8 *lip_host_name;		  /* linux's name for the tap */ | ||||||
|  |   u32 lip_vif_index;		  /* linux's index for the tap */ | ||||||
|  |   u8 *lip_namespace;		  /* namespace in which the tap lives */ | ||||||
|  |   lip_host_type_t lip_host_type;  /* type of host interface */ | ||||||
|  |   lcp_itf_phy_adj_t lip_phy_adjs; /* adjacencies for phy l3 interface */ | ||||||
|  |   lip_flag_t lip_flags;		  /* Flags */ | ||||||
|  |   u8 lip_rewrite_len;		  /* The length of an L2 MAC rewrite */ | ||||||
|  |   f64 lip_create_ts;		  /* Timestamp of creation */ | ||||||
|  | } lcp_itf_pair_t; | ||||||
|  | extern lcp_itf_pair_t *lcp_itf_pair_pool; | ||||||
|  |  | ||||||
|  | extern vlib_node_registration_t lcp_ethernet_node; | ||||||
|  |  | ||||||
|  | u8 *format_lcp_itf_pair (u8 *s, va_list *args); | ||||||
|  | void lcp_itf_pair_show (u32 phy_sw_if_index); | ||||||
|  | u32 lcp_itf_num_pairs (void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Get an interface-pair object from its VPP index | ||||||
|  |  */ | ||||||
|  | extern lcp_itf_pair_t *lcp_itf_pair_get (index_t index); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Find a interface-pair object from the host interface | ||||||
|  |  * | ||||||
|  |  * @param host_sw_if_index host interface | ||||||
|  |  * @return VPP's object index | ||||||
|  |  */ | ||||||
|  | extern index_t lcp_itf_pair_find_by_vif (u32 vif_index); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Create an interface-pair | ||||||
|  |  * | ||||||
|  |  * @return error code | ||||||
|  |  */ | ||||||
|  | extern int lcp_itf_pair_add (u32 host_sw_if_index, u32 phy_sw_if_index, | ||||||
|  | 			     u8 *host_name, u32 host_index, | ||||||
|  | 			     lip_host_type_t host_type, u8 *ns); | ||||||
|  | extern int lcp_itf_pair_add_sub (u32 vif, u8 *host_name, u32 sub_sw_if_index, | ||||||
|  | 				 u32 phy_sw_if_index, u8 *ns); | ||||||
|  | extern int lcp_itf_pair_del (u32 phy_sw_if_index); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Create an interface-pair from PHY sw_if_index and tap name. | ||||||
|  |  * | ||||||
|  |  * @return error code | ||||||
|  |  */ | ||||||
|  | extern int lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name, | ||||||
|  | 				lip_host_type_t host_if_type, u8 *ns, | ||||||
|  | 				u32 *host_sw_if_indexp); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Delete a LCP_ITF_PAIR | ||||||
|  |  */ | ||||||
|  | extern int lcp_itf_pair_delete (u32 phy_sw_if_index); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Callback function invoked during a walk of all interface-pairs | ||||||
|  |  */ | ||||||
|  | typedef walk_rc_t (*lcp_itf_pair_walk_cb_t) (index_t index, void *ctx); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Walk/visit each of the interface pairs | ||||||
|  |  */ | ||||||
|  | extern void lcp_itf_pair_walk (lcp_itf_pair_walk_cb_t cb, void *ctx); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Begin and End the replace process | ||||||
|  |  */ | ||||||
|  | extern int lcp_itf_pair_replace_begin (void); | ||||||
|  | extern int lcp_itf_pair_replace_end (void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Retreive the pair in the DP | ||||||
|  |  */ | ||||||
|  | extern index_t *lip_db_by_phy; | ||||||
|  | extern u32 *lip_db_by_host; | ||||||
|  |  | ||||||
|  | always_inline index_t | ||||||
|  | lcp_itf_pair_find_by_phy (u32 phy_sw_if_index) | ||||||
|  | { | ||||||
|  |   if (phy_sw_if_index >= vec_len (lip_db_by_phy)) | ||||||
|  |     return INDEX_INVALID; | ||||||
|  |   return (lip_db_by_phy[phy_sw_if_index]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | always_inline index_t | ||||||
|  | lcp_itf_pair_find_by_host (u32 host_sw_if_index) | ||||||
|  | { | ||||||
|  |   if (host_sw_if_index >= vec_len (lip_db_by_host)) | ||||||
|  |     return INDEX_INVALID; | ||||||
|  |   return (lip_db_by_host[host_sw_if_index]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | typedef void (*lcp_itf_pair_add_cb_t) (lcp_itf_pair_t *); | ||||||
|  | typedef void (*lcp_itf_pair_del_cb_t) (lcp_itf_pair_t *); | ||||||
|  |  | ||||||
|  | typedef struct lcp_itf_pair_vft | ||||||
|  | { | ||||||
|  |   lcp_itf_pair_add_cb_t pair_add_fn; | ||||||
|  |   lcp_itf_pair_del_cb_t pair_del_fn; | ||||||
|  | } lcp_itf_pair_vft_t; | ||||||
|  |  | ||||||
|  | void lcp_itf_pair_register_vft (lcp_itf_pair_vft_t *lcp_itf_vft); | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										101
									
								
								test/lcpng_unittest.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								test/lcpng_unittest.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (c) 2021 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 <vlib/vlib.h> | ||||||
|  |  | ||||||
|  | #include <plugins/lcpng/lcpng_interface.h> | ||||||
|  |  | ||||||
|  | static u32 host_vif; | ||||||
|  | const static char *host_template = "tap%d"; | ||||||
|  |  | ||||||
|  | static clib_error_t * | ||||||
|  | lcp_add_pair_command_fn (vlib_main_t *vm, unformat_input_t *input, | ||||||
|  | 			 vlib_cli_command_t *cmd) | ||||||
|  | { | ||||||
|  |   u32 phy_sw_if_index, host_sw_if_index; | ||||||
|  |   u8 is_add, *host_name; | ||||||
|  |   vnet_main_t *vnm = vnet_get_main (); | ||||||
|  |  | ||||||
|  |   ++host_vif; | ||||||
|  |   host_name = format (NULL, host_template, host_vif); | ||||||
|  |   phy_sw_if_index = host_sw_if_index = ~0; | ||||||
|  |   is_add = 1; | ||||||
|  |   lcp_main.test_mode = 1; | ||||||
|  |  | ||||||
|  |   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) | ||||||
|  |     { | ||||||
|  |       if (unformat (input, "add")) | ||||||
|  | 	is_add = 1; | ||||||
|  |       else if (unformat (input, "del")) | ||||||
|  | 	is_add = 0; | ||||||
|  |       else if (unformat (input, "phy %U", unformat_vnet_sw_interface, vnm, | ||||||
|  | 			 &phy_sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else if (unformat (input, "host %U", unformat_vnet_sw_interface, vnm, | ||||||
|  | 			 &host_sw_if_index)) | ||||||
|  | 	; | ||||||
|  |       else | ||||||
|  | 	return clib_error_return (0, "unknown input:%U", format_unformat_error, | ||||||
|  | 				  input); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |   if (phy_sw_if_index == ~0) | ||||||
|  |     return clib_error_return (0, "ERROR; no phy:%U", format_unformat_error, | ||||||
|  | 			      input); | ||||||
|  |  | ||||||
|  |   lip_host_type_t host_type = | ||||||
|  |     (vnet_sw_interface_is_p2p (vnm, phy_sw_if_index) ? LCP_ITF_HOST_TUN : | ||||||
|  | 						       LCP_ITF_HOST_TAP); | ||||||
|  |  | ||||||
|  |   int rv; | ||||||
|  |  | ||||||
|  |   if (is_add) | ||||||
|  |     { | ||||||
|  |       if (host_sw_if_index == ~0) | ||||||
|  | 	return clib_error_return (0, "ERROR no-host:%U", format_unformat_error, | ||||||
|  | 				  input); | ||||||
|  |  | ||||||
|  |       rv = lcp_itf_pair_add (host_sw_if_index, phy_sw_if_index, host_name, | ||||||
|  | 			     host_vif, host_type, NULL); | ||||||
|  |     } | ||||||
|  |   else | ||||||
|  |     rv = lcp_itf_pair_del (phy_sw_if_index); | ||||||
|  |  | ||||||
|  |   if (rv) | ||||||
|  |     return clib_error_return (0, "ERROR rv:%d", rv); | ||||||
|  |  | ||||||
|  |   return (NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VLIB_CLI_COMMAND (test_time_range_command, static) = { | ||||||
|  |   .path = "test lcp", | ||||||
|  |   .short_help = "lcp [add|del] phy <SW_IF_INDEX> host <SW_IF_INDEX>", | ||||||
|  |   .function = lcp_add_pair_command_fn, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #include <vnet/plugin/plugin.h> | ||||||
|  | #include <vpp/app/version.h> | ||||||
|  | VLIB_PLUGIN_REGISTER () = { | ||||||
|  |   .version = VPP_BUILD_VER, | ||||||
|  |   .description = "Linux Control Plane - Unit Test", | ||||||
|  |   .default_disabled = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * fd.io coding-style-patch-verification: ON | ||||||
|  |  * | ||||||
|  |  * Local Variables: | ||||||
|  |  * eval: (c-set-style "gnu") | ||||||
|  |  * End: | ||||||
|  |  */ | ||||||
		Reference in New Issue
	
	Block a user