linux-cp: Basic MPLS support.
[vpp.git] / src / plugins / linux-cp / lcp_mpls_sync.c
1 /*
2  * Copyright (c) 2023 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #define _GNU_SOURCE
17
18 #include <linux-cp/lcp_interface.h>
19
20 #include <vnet/plugin/plugin.h>
21 #include <vnet/mpls/mpls.h>
22 #include <vppinfra/linux/netns.h>
23
24 #include <fcntl.h>
25
26 vlib_log_class_t lcp_mpls_sync_logger;
27
28 #define LCP_MPLS_SYNC_DBG(...)                                                \
29   vlib_log_debug (lcp_mpls_sync_logger, __VA_ARGS__);
30
31 void
32 lcp_mpls_sync_pair_add_cb (lcp_itf_pair_t *lip)
33 {
34   u8 phy_is_enabled = mpls_sw_interface_is_enabled (lip->lip_phy_sw_if_index);
35   LCP_MPLS_SYNC_DBG ("pair_add_cb: mpls enabled %u, parent %U", phy_is_enabled,
36                      format_lcp_itf_pair, lip);
37   if (phy_is_enabled)
38     mpls_sw_interface_enable_disable (&mpls_main, lip->lip_host_sw_if_index,
39                                       1);
40 }
41
42 void
43 lcp_mpls_sync_state_cb (struct mpls_main_t *mm, uword opaque, u32 sw_if_index,
44                         u32 is_enable)
45 {
46   lcp_itf_pair_t *lip;
47   index_t lipi;
48   int curr_ns_fd = -1;
49   int vif_ns_fd = -1;
50   int ctl_fd = -1;
51   u8 *ctl_path = NULL;
52
53   LCP_MPLS_SYNC_DBG ("sync_state_cb: called for sw_if_index %u", sw_if_index);
54
55   // If device is LCP PHY, sync state to host tap.
56   lipi = lcp_itf_pair_find_by_phy (sw_if_index);
57   if (INDEX_INVALID != lipi)
58     {
59       lip = lcp_itf_pair_get (lipi);
60       LCP_MPLS_SYNC_DBG ("sync_state_cb: mpls enabled %u parent %U", is_enable,
61                          format_lcp_itf_pair, lip);
62       mpls_sw_interface_enable_disable (&mpls_main, lip->lip_host_sw_if_index,
63                                         is_enable);
64       return;
65     }
66
67   // If device is LCP host, toggle MPLS XC feature.
68   lipi = lcp_itf_pair_find_by_host (sw_if_index);
69   if (INDEX_INVALID == lipi)
70     return;
71   lip = lcp_itf_pair_get (lipi);
72
73   vnet_feature_enable_disable ("mpls-input", "linux-cp-xc-mpls", sw_if_index,
74                                is_enable, NULL, 0);
75
76   LCP_MPLS_SYNC_DBG ("sync_state_cb: mpls xc state %u parent %U", is_enable,
77                      format_lcp_itf_pair, lip);
78
79   // If syncing is enabled, sync Linux state as well.
80   if (!lcp_sync ())
81     return;
82
83   if (lip->lip_namespace)
84     {
85       curr_ns_fd = clib_netns_open (NULL /* self */);
86       vif_ns_fd = clib_netns_open (lip->lip_namespace);
87       if (vif_ns_fd != -1)
88         clib_setns (vif_ns_fd);
89     }
90
91   ctl_path = format (NULL, "/proc/sys/net/mpls/conf/%s/input%c",
92                      lip->lip_host_name, NULL);
93   if (NULL == ctl_path)
94     {
95       LCP_MPLS_SYNC_DBG ("sync_state_cb: failed to format sysctl");
96       goto SYNC_CLEANUP;
97     }
98
99   ctl_fd = open ((char *) ctl_path, O_WRONLY);
100   if (ctl_fd < 0)
101     {
102       LCP_MPLS_SYNC_DBG ("sync_state_cb: failed to open %s for writing",
103                          ctl_path);
104       goto SYNC_CLEANUP;
105     }
106
107   if (fdformat (ctl_fd, "%u", is_enable) < 1)
108     {
109       LCP_MPLS_SYNC_DBG ("sync_state_cb: failed to write to %s", ctl_path);
110       goto SYNC_CLEANUP;
111     }
112
113   LCP_MPLS_SYNC_DBG ("sync_state_cb: set mpls input for %s",
114                      lip->lip_host_name);
115
116 SYNC_CLEANUP:
117   if (ctl_fd > -1)
118     close (ctl_fd);
119
120   if (NULL != ctl_path)
121     vec_free (ctl_path);
122
123   if (vif_ns_fd != -1)
124     close (vif_ns_fd);
125
126   if (curr_ns_fd != -1)
127     {
128       clib_setns (curr_ns_fd);
129       close (curr_ns_fd);
130     }
131 }
132
133 static clib_error_t *
134 lcp_mpls_sync_init (vlib_main_t *vm)
135 {
136   lcp_itf_pair_vft_t mpls_sync_itf_pair_vft = {
137     .pair_add_fn = lcp_mpls_sync_pair_add_cb,
138   };
139   lcp_itf_pair_register_vft (&mpls_sync_itf_pair_vft);
140
141   mpls_interface_state_change_add_callback (lcp_mpls_sync_state_cb, 0);
142
143   lcp_mpls_sync_logger = vlib_log_register_class ("linux-cp", "mpls-sync");
144
145   return NULL;
146 }
147
148 VLIB_INIT_FUNCTION (lcp_mpls_sync_init) = {
149   .runs_after = VLIB_INITS ("lcp_interface_init", "mpls_init"),
150 };
151
152 /*
153  * fd.io coding-style-patch-verification: ON
154  *
155  * Local Variables:
156  * eval: (c-set-style "gnu")
157  * End:
158  */