Initial checkin - renamed the files to avoid clashing with 'lcp' and 'linux-cp' plugin

This commit is contained in:
Pim van Pelt
2021-08-08 19:50:25 +02:00
commit 80934a5c46
15 changed files with 3524 additions and 0 deletions

61
CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

165
lcpng_interface.h Normal file
View 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
View 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:
*/