1 /* Hey Emacs use -*- mode: C -*- */
3 * Copyright 2021 Cisco and/or its affiliates.
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
25 /* helper function to copy forward all sw interface link state flags
26 * MTU, and IP addresses into their counterpart LIP interface.
28 * This is called upon MTU changes and state changes.
31 lcp_itf_pair_sync_state (lcp_itf_pair_t *lip)
33 vnet_sw_interface_t *sw;
34 vnet_sw_interface_t *sup_sw;
40 if (!lcp_sync () || lcp_get_netlink_processing_active ())
44 vnet_get_sw_interface_or_null (vnet_get_main (), lip->lip_phy_sw_if_index);
48 vnet_get_sw_interface_or_null (vnet_get_main (), sw->sup_sw_if_index);
52 if (lip->lip_namespace)
54 curr_ns_fd = clib_netns_open (NULL /* self */);
55 vif_ns_fd = clib_netns_open (lip->lip_namespace);
57 clib_setns (vif_ns_fd);
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]);
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.
67 int state = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
69 if (state && !(sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
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]);
78 lcp_itf_set_link_state (lip, state);
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.
85 mtu = sw->mtu[VNET_MTU_L3];
87 mtu = sup_sw->mtu[VNET_MTU_L3];
89 if (sup_sw->mtu[VNET_MTU_L3] < sw->mtu[VNET_MTU_L3])
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];
98 /* Set MTU on all of {sw, tap, netlink}. Only send a netlink message if we
99 * really do want to change the MTU.
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))
105 if (netlink_mtu != mtu)
106 vnet_netlink_set_link_mtu (lip->lip_vif_index, mtu);
109 /* Linux will remove IPv6 addresses on children when the parent state
110 * goes down, so we ensure all IPv4/IPv6 addresses are synced.
112 lcp_itf_set_interface_addr (lip);
117 if (curr_ns_fd != -1)
119 clib_setns (curr_ns_fd);
127 lcp_itf_pair_walk_sync_state_all_cb (index_t lipi, void *ctx)
130 lip = lcp_itf_pair_get (lipi);
132 return WALK_CONTINUE;
134 lcp_itf_pair_sync_state (lip);
135 return WALK_CONTINUE;
139 lcp_itf_pair_walk_sync_state_hw_cb (vnet_main_t *vnm, u32 sw_if_index,
144 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
147 return WALK_CONTINUE;
150 lcp_itf_pair_sync_state (lip);
151 return WALK_CONTINUE;
155 lcp_itf_pair_sync_state_all ()
157 lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_all_cb, 0);
161 lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi)
165 LCP_ITF_PAIR_DBG ("sync_state_hw: hi %U", format_vnet_sw_if_index_name,
166 vnet_get_main (), hi->hw_if_index);
168 vnet_hw_interface_walk_sw (vnet_get_main (), hi->hw_if_index,
169 lcp_itf_pair_walk_sync_state_hw_cb, NULL);
172 static clib_error_t *
173 lcp_itf_admin_state_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
176 vnet_hw_interface_t *hi;
177 vnet_sw_interface_t *si;
179 if (!lcp_sync () || lcp_get_netlink_processing_active ())
182 LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u",
183 format_vnet_sw_if_index_name, vnm, sw_if_index, flags);
185 // Sync interface state changes into host
186 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
189 LCP_ITF_PAIR_INFO ("admin_state_change: %U flags %u", format_lcp_itf_pair,
192 if (vnet_sw_interface_is_sub (vnm, sw_if_index))
194 lcp_itf_pair_sync_state (lip);
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
203 si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
207 hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
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);
214 lcp_itf_pair_sync_state_hw (hi);
219 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lcp_itf_admin_state_change);
221 static clib_error_t *
222 lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
224 vnet_sw_interface_t *si;
225 vnet_hw_interface_t *hi;
227 if (!lcp_sync () || lcp_get_netlink_processing_active ())
230 LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm,
233 if (vnet_sw_interface_is_sub (vnm, sw_if_index))
236 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
238 lcp_itf_pair_sync_state (lip);
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
247 si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
251 hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
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);
258 lcp_itf_pair_sync_state_hw (hi);
263 VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION (lcp_itf_mtu_change);
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,
271 const lcp_itf_pair_t *lip;
275 if (!lcp_sync () || lcp_get_netlink_processing_active ())
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);
282 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
286 if (lip->lip_namespace)
288 curr_ns_fd = clib_netns_open (NULL /* self */);
289 vif_ns_fd = clib_netns_open (lip->lip_namespace);
291 clib_setns (vif_ns_fd);
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,
299 vnet_netlink_del_ip4_addr (lip->lip_vif_index, address, address_length);
301 vnet_netlink_add_ip4_addr (lip->lip_vif_index, address, address_length);
306 if (curr_ns_fd != -1)
308 clib_setns (curr_ns_fd);
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,
320 const lcp_itf_pair_t *lip;
324 if (!lcp_sync () || lcp_get_netlink_processing_active ())
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);
331 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
335 if (lip->lip_namespace)
337 curr_ns_fd = clib_netns_open (NULL /* self */);
338 vif_ns_fd = clib_netns_open (lip->lip_namespace);
340 clib_setns (vif_ns_fd);
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,
346 vnet_netlink_del_ip6_addr (lip->lip_vif_index, address, address_length);
348 vnet_netlink_add_ip6_addr (lip->lip_vif_index, address, address_length);
353 if (curr_ns_fd != -1)
355 clib_setns (curr_ns_fd);
360 static clib_error_t *
361 lcp_itf_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_create)
363 const vnet_sw_interface_t *sw;
366 if (!lcp_auto_subint ())
369 sw = vnet_get_sw_interface_or_null (vnm, sw_if_index);
373 is_sub = vnet_sw_interface_is_sub (vnm, sw_if_index);
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);
384 const lcp_itf_pair_t *sup_lip;
387 // If the parent has a LIP auto-create a LIP for this interface
389 lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
393 name = format (name, "%s.%d%c", sup_lip->lip_host_name, sw->sub.id, 0);
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);
400 lcp_itf_pair_create (sw->sw_if_index, name, LCP_ITF_HOST_TAP,
401 sup_lip->lip_namespace, NULL);
407 lcp_itf_pair_delete (sw_if_index);
413 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_interface_add_del);
415 static clib_error_t *
416 lcp_itf_sync_init (vlib_main_t *vm)
418 ip4_main_t *im4 = &ip4_main;
419 ip6_main_t *im6 = &ip6_main;
421 ip4_add_del_interface_address_callback_t cb4;
422 ip6_add_del_interface_address_callback_t cb6;
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);
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);
435 VLIB_INIT_FUNCTION (lcp_itf_sync_init) = {
436 .runs_after = VLIB_INITS ("vnet_interface_init", "tcp_init", "udp_init"),
440 * fd.io coding-style-patch-verification: ON
443 * eval: (c-set-style "gnu")