diff options
author | Adrian Pistol <vifino@posteo.net> | 2023-06-07 15:28:17 +0200 |
---|---|---|
committer | Matthew Smith <mgsmith@netgate.com> | 2023-07-11 17:09:25 +0000 |
commit | 44b3abe86ab49a35403ba8d3938955549e392d22 (patch) | |
tree | f8dda9648b065b20c8226a557dd2745861d2c095 /src/plugins/linux-cp/lcp_mpls_sync.c | |
parent | ff27c9f8ecb08ff0f5b98332975733e8bab125f1 (diff) |
linux-cp: Basic MPLS support.
1) Imports ENCAP_MPLS labels from IPv4/IPv6 routes.
Note that this requires libnl 3.6.0 or newer.
In previous patches, the fib_path_ext_t had a path ID of -1.
After a long investigation, it turned out to be caused by route weight
being set to 0. There is a comment explaining more details.
2) Handles MPLS routes.
MPLS routes were wrongly added as IPv4 routes before.
POP and SWAP are now both supported.
All the routes are installed as NON-EOS and EOS routes,
as the Linux kernel does not differentiate.
EOS POP used in PHP uses the next-hop address family
to determine the resulting address family.
This patch is sufficient for P setups.
PE setups with implicit null should also function okay, as long as a
seperate label gets programmed per address family.
PE setups with explicit null will also forward packets,
but punting is a bit odd and needs MPLS input enabled on the LCP host
device.
3) Propagate MPLS input state to LCP Pair and Linux.
Since the Linux kernel uses the MPLS routes itself,
the LCP pair tap needs MPLS enabled to allow host originated packets.
This also syncs the Linux `net.mpls.conf.<host_if>.input` sysctl to
allow punted packets to have MPLS labels, mostly explicit nulls.
In addition, a special feature is enabled to cross connect MPLS packets
coming from Linux directly to interface-output untouched.
Make sure to enable MPLS/add a table in VPP first and load the
MPLS kernel modules!!
Type: feature
Change-Id: Ie4184bb4cc96905bf8b483a27e7ca6d251697374
Signed-off-by: Adrian Pistol <vifino@posteo.net>
Signed-off-by: Pim van Pelt <pim@ipng.nl>
Diffstat (limited to 'src/plugins/linux-cp/lcp_mpls_sync.c')
-rw-r--r-- | src/plugins/linux-cp/lcp_mpls_sync.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/plugins/linux-cp/lcp_mpls_sync.c b/src/plugins/linux-cp/lcp_mpls_sync.c new file mode 100644 index 00000000000..d4b0d13b907 --- /dev/null +++ b/src/plugins/linux-cp/lcp_mpls_sync.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2023 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. + */ + +#define _GNU_SOURCE + +#include <linux-cp/lcp_interface.h> + +#include <vnet/plugin/plugin.h> +#include <vnet/mpls/mpls.h> +#include <vppinfra/linux/netns.h> + +#include <fcntl.h> + +vlib_log_class_t lcp_mpls_sync_logger; + +#define LCP_MPLS_SYNC_DBG(...) \ + vlib_log_debug (lcp_mpls_sync_logger, __VA_ARGS__); + +void +lcp_mpls_sync_pair_add_cb (lcp_itf_pair_t *lip) +{ + u8 phy_is_enabled = mpls_sw_interface_is_enabled (lip->lip_phy_sw_if_index); + LCP_MPLS_SYNC_DBG ("pair_add_cb: mpls enabled %u, parent %U", phy_is_enabled, + format_lcp_itf_pair, lip); + if (phy_is_enabled) + mpls_sw_interface_enable_disable (&mpls_main, lip->lip_host_sw_if_index, + 1); +} + +void +lcp_mpls_sync_state_cb (struct mpls_main_t *mm, uword opaque, u32 sw_if_index, + u32 is_enable) +{ + lcp_itf_pair_t *lip; + index_t lipi; + int curr_ns_fd = -1; + int vif_ns_fd = -1; + int ctl_fd = -1; + u8 *ctl_path = NULL; + + LCP_MPLS_SYNC_DBG ("sync_state_cb: called for sw_if_index %u", sw_if_index); + + // If device is LCP PHY, sync state to host tap. + lipi = lcp_itf_pair_find_by_phy (sw_if_index); + if (INDEX_INVALID != lipi) + { + lip = lcp_itf_pair_get (lipi); + LCP_MPLS_SYNC_DBG ("sync_state_cb: mpls enabled %u parent %U", is_enable, + format_lcp_itf_pair, lip); + mpls_sw_interface_enable_disable (&mpls_main, lip->lip_host_sw_if_index, + is_enable); + return; + } + + // If device is LCP host, toggle MPLS XC feature. + lipi = lcp_itf_pair_find_by_host (sw_if_index); + if (INDEX_INVALID == lipi) + return; + lip = lcp_itf_pair_get (lipi); + + vnet_feature_enable_disable ("mpls-input", "linux-cp-xc-mpls", sw_if_index, + is_enable, NULL, 0); + + LCP_MPLS_SYNC_DBG ("sync_state_cb: mpls xc state %u parent %U", is_enable, + format_lcp_itf_pair, lip); + + // If syncing is enabled, sync Linux state as well. + if (!lcp_sync ()) + return; + + if (lip->lip_namespace) + { + curr_ns_fd = clib_netns_open (NULL /* self */); + vif_ns_fd = clib_netns_open (lip->lip_namespace); + if (vif_ns_fd != -1) + clib_setns (vif_ns_fd); + } + + ctl_path = format (NULL, "/proc/sys/net/mpls/conf/%s/input%c", + lip->lip_host_name, NULL); + if (NULL == ctl_path) + { + LCP_MPLS_SYNC_DBG ("sync_state_cb: failed to format sysctl"); + goto SYNC_CLEANUP; + } + + ctl_fd = open ((char *) ctl_path, O_WRONLY); + if (ctl_fd < 0) + { + LCP_MPLS_SYNC_DBG ("sync_state_cb: failed to open %s for writing", + ctl_path); + goto SYNC_CLEANUP; + } + + if (fdformat (ctl_fd, "%u", is_enable) < 1) + { + LCP_MPLS_SYNC_DBG ("sync_state_cb: failed to write to %s", ctl_path); + goto SYNC_CLEANUP; + } + + LCP_MPLS_SYNC_DBG ("sync_state_cb: set mpls input for %s", + lip->lip_host_name); + +SYNC_CLEANUP: + if (ctl_fd > -1) + close (ctl_fd); + + if (NULL != ctl_path) + vec_free (ctl_path); + + if (vif_ns_fd != -1) + close (vif_ns_fd); + + if (curr_ns_fd != -1) + { + clib_setns (curr_ns_fd); + close (curr_ns_fd); + } +} + +static clib_error_t * +lcp_mpls_sync_init (vlib_main_t *vm) +{ + lcp_itf_pair_vft_t mpls_sync_itf_pair_vft = { + .pair_add_fn = lcp_mpls_sync_pair_add_cb, + }; + lcp_itf_pair_register_vft (&mpls_sync_itf_pair_vft); + + mpls_interface_state_change_add_callback (lcp_mpls_sync_state_cb, 0); + + lcp_mpls_sync_logger = vlib_log_register_class ("linux-cp", "mpls-sync"); + + return NULL; +} + +VLIB_INIT_FUNCTION (lcp_mpls_sync_init) = { + .runs_after = VLIB_INITS ("lcp_interface_init", "mpls_init"), +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |