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