dpdk-cryptodev: fix coverity issues
[vpp.git] / src / plugins / linux-cp / lcp_interface_sync.c
1 /* Hey Emacs use -*- mode: C -*- */
2 /*
3  * Copyright 2021 Cisco and/or its affiliates.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vnet/devices/netlink.h>
21 #include <vnet/ip/ip.h>
22 #include <vppinfra/linux/netns.h>
23 #include <plugins/linux-cp/lcp_interface.h>
24
25 /* helper function to copy forward all sw interface link state flags
26  * MTU, and IP addresses into their counterpart LIP interface.
27  *
28  * This is called upon MTU changes and state changes.
29  */
30 void
31 lcp_itf_pair_sync_state (lcp_itf_pair_t *lip)
32 {
33   vnet_sw_interface_t *sw;
34   vnet_sw_interface_t *sup_sw;
35   int curr_ns_fd = -1;
36   int vif_ns_fd = -1;
37   u32 mtu;
38   u32 netlink_mtu;
39
40   if (!lcp_sync () || lcp_get_netlink_processing_active ())
41     return;
42
43   sw =
44     vnet_get_sw_interface_or_null (vnet_get_main (), lip->lip_phy_sw_if_index);
45   if (!sw)
46     return;
47   sup_sw =
48     vnet_get_sw_interface_or_null (vnet_get_main (), sw->sup_sw_if_index);
49   if (!sup_sw)
50     return;
51
52   if (lip->lip_namespace)
53     {
54       curr_ns_fd = clib_netns_open (NULL /* self */);
55       vif_ns_fd = clib_netns_open (lip->lip_namespace);
56       if (vif_ns_fd != -1)
57         clib_setns (vif_ns_fd);
58     }
59
60   LCP_ITF_PAIR_INFO ("sync_state: %U flags %u sup-flags %u mtu %u sup-mtu %u",
61                      format_lcp_itf_pair, lip, sw->flags, sup_sw->flags,
62                      sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
63
64   /* Linux will not allow children to be admin-up if their parent is
65    * admin-down. If child is up but parent is not, force it down.
66    */
67   int state = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
68
69   if (state && !(sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
70     {
71       LCP_ITF_PAIR_WARN (
72         "sync_state: %U flags %u sup-flags %u mtu %u sup-mtu %u: "
73         "forcing state to sup-flags to satisfy netlink",
74         format_lcp_itf_pair, lip, sw->flags, sup_sw->flags,
75         sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
76       state = 0;
77     }
78   lcp_itf_set_link_state (lip, state);
79
80   /* Linux will clamp MTU of children when the parent is lower. VPP is fine
81    * with differing MTUs. VPP assumes that if a subint has MTU of 0, that it
82    * inherits from its parent. Linux likes to be more explicit, so we
83    * reconcile any differences.
84    */
85   mtu = sw->mtu[VNET_MTU_L3];
86   if (mtu == 0)
87     mtu = sup_sw->mtu[VNET_MTU_L3];
88
89   if (sup_sw->mtu[VNET_MTU_L3] < sw->mtu[VNET_MTU_L3])
90     {
91       LCP_ITF_PAIR_WARN ("sync_state: %U flags %u mtu %u sup-mtu %u: "
92                          "clamping to sup-mtu to satisfy netlink",
93                          format_lcp_itf_pair, lip, sw->flags,
94                          sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
95       mtu = sup_sw->mtu[VNET_MTU_L3];
96     }
97
98   /* Set MTU on all of {sw, tap, netlink}. Only send a netlink message if we
99    * really do want to change the MTU.
100    */
101   vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_phy_sw_if_index, mtu);
102   vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_host_sw_if_index, mtu);
103   if (NULL == vnet_netlink_get_link_mtu (lip->lip_vif_index, &netlink_mtu))
104     {
105       if (netlink_mtu != mtu)
106         vnet_netlink_set_link_mtu (lip->lip_vif_index, mtu);
107     }
108
109   /* Linux will remove IPv6 addresses on children when the parent state
110    * goes down, so we ensure all IPv4/IPv6 addresses are synced.
111    */
112   lcp_itf_set_interface_addr (lip);
113
114   if (vif_ns_fd != -1)
115     close (vif_ns_fd);
116
117   if (curr_ns_fd != -1)
118     {
119       clib_setns (curr_ns_fd);
120       close (curr_ns_fd);
121     }
122
123   return;
124 }
125
126 static walk_rc_t
127 lcp_itf_pair_walk_sync_state_all_cb (index_t lipi, void *ctx)
128 {
129   lcp_itf_pair_t *lip;
130   lip = lcp_itf_pair_get (lipi);
131   if (!lip)
132     return WALK_CONTINUE;
133
134   lcp_itf_pair_sync_state (lip);
135   return WALK_CONTINUE;
136 }
137
138 static walk_rc_t
139 lcp_itf_pair_walk_sync_state_hw_cb (vnet_main_t *vnm, u32 sw_if_index,
140                                     void *arg)
141 {
142   lcp_itf_pair_t *lip;
143
144   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
145   if (!lip)
146     {
147       return WALK_CONTINUE;
148     }
149
150   lcp_itf_pair_sync_state (lip);
151   return WALK_CONTINUE;
152 }
153
154 void
155 lcp_itf_pair_sync_state_all ()
156 {
157   lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_all_cb, 0);
158 }
159
160 void
161 lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi)
162 {
163   if (!hi)
164     return;
165   LCP_ITF_PAIR_DBG ("sync_state_hw: hi %U", format_vnet_sw_if_index_name,
166                     vnet_get_main (), hi->hw_if_index);
167
168   vnet_hw_interface_walk_sw (vnet_get_main (), hi->hw_if_index,
169                              lcp_itf_pair_walk_sync_state_hw_cb, NULL);
170 }
171
172 static clib_error_t *
173 lcp_itf_admin_state_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
174 {
175   lcp_itf_pair_t *lip;
176   vnet_hw_interface_t *hi;
177   vnet_sw_interface_t *si;
178
179   if (!lcp_sync () || lcp_get_netlink_processing_active ())
180     return 0;
181
182   LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u",
183                     format_vnet_sw_if_index_name, vnm, sw_if_index, flags);
184
185   // Sync interface state changes into host
186   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
187   if (!lip)
188     return NULL;
189   LCP_ITF_PAIR_INFO ("admin_state_change: %U flags %u", format_lcp_itf_pair,
190                      lip, flags);
191
192   if (vnet_sw_interface_is_sub (vnm, sw_if_index))
193     {
194       lcp_itf_pair_sync_state (lip);
195       return NULL;
196     }
197
198   // When Linux changes link on a parent interface, all of its children also
199   // change. If a parent interface changes MTU, all of its children are clamped
200   // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
201   // change by walking the sub-interfaces of a phy and syncing their state back
202   // into Linux.
203   si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
204   if (!si)
205     return NULL;
206
207   hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
208   if (!hi)
209     return NULL;
210   LCP_ITF_PAIR_DBG ("admin_state_change: si %U hi %U, syncing children",
211                     format_vnet_sw_if_index_name, vnm, si->sw_if_index,
212                     format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
213
214   lcp_itf_pair_sync_state_hw (hi);
215
216   return NULL;
217 }
218
219 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lcp_itf_admin_state_change);
220
221 static clib_error_t *
222 lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
223 {
224   vnet_sw_interface_t *si;
225   vnet_hw_interface_t *hi;
226
227   if (!lcp_sync () || lcp_get_netlink_processing_active ())
228     return NULL;
229
230   LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm,
231                     sw_if_index, flags);
232
233   if (vnet_sw_interface_is_sub (vnm, sw_if_index))
234     {
235       lcp_itf_pair_t *lip;
236       lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
237       if (lip)
238         lcp_itf_pair_sync_state (lip);
239       return NULL;
240     }
241
242   // When Linux changes link on a parent interface, all of its children also
243   // change. If a parent interface changes MTU, all of its children are clamped
244   // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
245   // change by walking the sub-interfaces of a phy and syncing their state back
246   // into Linux.
247   si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
248   if (!si)
249     return NULL;
250
251   hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
252   if (!hi)
253     return NULL;
254   LCP_ITF_PAIR_DBG ("mtu_change: si %U hi %U, syncing children",
255                     format_vnet_sw_if_index_name, vnm, si->sw_if_index,
256                     format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
257
258   lcp_itf_pair_sync_state_hw (hi);
259
260   return NULL;
261 }
262
263 VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION (lcp_itf_mtu_change);
264
265 static void
266 lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque,
267                                     u32 sw_if_index, ip4_address_t *address,
268                                     u32 address_length, u32 if_address_index,
269                                     u32 is_del)
270 {
271   const lcp_itf_pair_t *lip;
272   int curr_ns_fd = -1;
273   int vif_ns_fd = -1;
274
275   if (!lcp_sync () || lcp_get_netlink_processing_active ())
276     return;
277
278   LCP_ITF_PAIR_DBG ("ip4_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
279                     format_vnet_sw_if_index_name, vnet_get_main (),
280                     sw_if_index, format_ip4_address, address, address_length);
281
282   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
283   if (!lip)
284     return;
285
286   if (lip->lip_namespace)
287     {
288       curr_ns_fd = clib_netns_open (NULL /* self */);
289       vif_ns_fd = clib_netns_open (lip->lip_namespace);
290       if (vif_ns_fd != -1)
291         clib_setns (vif_ns_fd);
292     }
293
294   LCP_ITF_PAIR_DBG ("ip4_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
295                     format_lcp_itf_pair, lip, format_ip4_address, address,
296                     address_length);
297
298   if (is_del)
299     vnet_netlink_del_ip4_addr (lip->lip_vif_index, address, address_length);
300   else
301     vnet_netlink_add_ip4_addr (lip->lip_vif_index, address, address_length);
302
303   if (vif_ns_fd != -1)
304     close (vif_ns_fd);
305
306   if (curr_ns_fd != -1)
307     {
308       clib_setns (curr_ns_fd);
309       close (curr_ns_fd);
310     }
311   return;
312 }
313
314 static void
315 lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque,
316                                     u32 sw_if_index, ip6_address_t *address,
317                                     u32 address_length, u32 if_address_index,
318                                     u32 is_del)
319 {
320   const lcp_itf_pair_t *lip;
321   int curr_ns_fd = -1;
322   int vif_ns_fd = -1;
323
324   if (!lcp_sync () || lcp_get_netlink_processing_active ())
325     return;
326
327   LCP_ITF_PAIR_DBG ("ip6_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
328                     format_vnet_sw_if_index_name, vnet_get_main (),
329                     sw_if_index, format_ip6_address, address, address_length);
330
331   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
332   if (!lip)
333     return;
334
335   if (lip->lip_namespace)
336     {
337       curr_ns_fd = clib_netns_open (NULL /* self */);
338       vif_ns_fd = clib_netns_open (lip->lip_namespace);
339       if (vif_ns_fd != -1)
340         clib_setns (vif_ns_fd);
341     }
342   LCP_ITF_PAIR_DBG ("ip6_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
343                     format_lcp_itf_pair, lip, format_ip6_address, address,
344                     address_length);
345   if (is_del)
346     vnet_netlink_del_ip6_addr (lip->lip_vif_index, address, address_length);
347   else
348     vnet_netlink_add_ip6_addr (lip->lip_vif_index, address, address_length);
349
350   if (vif_ns_fd != -1)
351     close (vif_ns_fd);
352
353   if (curr_ns_fd != -1)
354     {
355       clib_setns (curr_ns_fd);
356       close (curr_ns_fd);
357     }
358 }
359
360 static clib_error_t *
361 lcp_itf_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_create)
362 {
363   const vnet_sw_interface_t *sw;
364   uword is_sub;
365
366   if (!lcp_auto_subint ())
367     return NULL;
368
369   sw = vnet_get_sw_interface_or_null (vnm, sw_if_index);
370   if (!sw)
371     return NULL;
372
373   is_sub = vnet_sw_interface_is_sub (vnm, sw_if_index);
374   if (!is_sub)
375     return NULL;
376
377   LCP_ITF_PAIR_DBG ("interface_%s: sw %U parent %U", is_create ? "add" : "del",
378                     format_vnet_sw_if_index_name, vnet_get_main (),
379                     sw->sw_if_index, format_vnet_sw_if_index_name,
380                     vnet_get_main (), sw->sup_sw_if_index);
381
382   if (is_create)
383     {
384       const lcp_itf_pair_t *sup_lip;
385       u8 *name = 0;
386
387       // If the parent has a LIP auto-create a LIP for this interface
388       sup_lip =
389         lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
390       if (!sup_lip)
391         return NULL;
392
393       name = format (name, "%s.%d%c", sup_lip->lip_host_name, sw->sub.id, 0);
394
395       LCP_ITF_PAIR_INFO (
396         "interface_%s: %U has parent %U, auto-creating LCP with host-if %s",
397         is_create ? "add" : "del", format_vnet_sw_if_index_name,
398         vnet_get_main (), sw->sw_if_index, format_lcp_itf_pair, sup_lip, name);
399
400       lcp_itf_pair_create (sw->sw_if_index, name, LCP_ITF_HOST_TAP,
401                            sup_lip->lip_namespace, NULL);
402
403       vec_free (name);
404     }
405   else
406     {
407       lcp_itf_pair_delete (sw_if_index);
408     }
409
410   return NULL;
411 }
412
413 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_interface_add_del);
414
415 static clib_error_t *
416 lcp_itf_sync_init (vlib_main_t *vm)
417 {
418   ip4_main_t *im4 = &ip4_main;
419   ip6_main_t *im6 = &ip6_main;
420
421   ip4_add_del_interface_address_callback_t cb4;
422   ip6_add_del_interface_address_callback_t cb6;
423
424   cb4.function = lcp_itf_ip4_add_del_interface_addr;
425   cb4.function_opaque = 0;
426   vec_add1 (im4->add_del_interface_address_callbacks, cb4);
427
428   cb6.function = lcp_itf_ip6_add_del_interface_addr;
429   cb6.function_opaque = 0;
430   vec_add1 (im6->add_del_interface_address_callbacks, cb6);
431
432   return NULL;
433 }
434
435 VLIB_INIT_FUNCTION (lcp_itf_sync_init) = {
436   .runs_after = VLIB_INITS ("vnet_interface_init", "tcp_init", "udp_init"),
437 };
438
439 /*
440  * fd.io coding-style-patch-verification: ON
441  *
442  * Local Variables:
443  * eval: (c-set-style "gnu")
444  * End:
445  */