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;
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);
50 if (lip->lip_namespace)
52 curr_ns_fd = clib_netns_open (NULL /* self */);
53 vif_ns_fd = clib_netns_open (lip->lip_namespace);
55 clib_setns (vif_ns_fd);
58 LCP_ITF_PAIR_INFO ("sync_state: %U flags %u sup-flags %u mtu %u sup-mtu %u",
59 format_lcp_itf_pair, lip, sw->flags, sup_sw->flags,
60 sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
62 /* Linux will not allow children to be admin-up if their parent is
63 * admin-down. If child is up but parent is not, force it down.
65 int state = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
67 if (state && !(sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
70 "sync_state: %U flags %u sup-flags %u mtu %u sup-mtu %u: "
71 "forcing state to sup-flags to satisfy netlink",
72 format_lcp_itf_pair, lip, sw->flags, sup_sw->flags,
73 sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
76 lcp_itf_set_link_state (lip, state);
78 /* Linux will clamp MTU of children when the parent is lower. VPP is fine
79 * with differing MTUs. VPP assumes that if a subint has MTU of 0, that it
80 * inherits from its parent. Linux likes to be more explicit, so we
81 * reconcile any differences.
83 mtu = sw->mtu[VNET_MTU_L3];
85 mtu = sup_sw->mtu[VNET_MTU_L3];
87 if (sup_sw->mtu[VNET_MTU_L3] < sw->mtu[VNET_MTU_L3])
89 LCP_ITF_PAIR_WARN ("sync_state: %U flags %u mtu %u sup-mtu %u: "
90 "clamping to sup-mtu to satisfy netlink",
91 format_lcp_itf_pair, lip, sw->flags,
92 sw->mtu[VNET_MTU_L3], sup_sw->mtu[VNET_MTU_L3]);
93 mtu = sup_sw->mtu[VNET_MTU_L3];
96 /* Set MTU on all of {sw, tap, netlink}. Only send a netlink message if we
97 * really do want to change the MTU.
99 vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_phy_sw_if_index, mtu);
100 vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_host_sw_if_index, mtu);
101 if (NULL == vnet_netlink_get_link_mtu (lip->lip_vif_index, &netlink_mtu))
103 if (netlink_mtu != mtu)
104 vnet_netlink_set_link_mtu (lip->lip_vif_index, mtu);
107 /* Linux will remove IPv6 addresses on children when the parent state
108 * goes down, so we ensure all IPv4/IPv6 addresses are synced.
110 lcp_itf_set_interface_addr (lip);
115 if (curr_ns_fd != -1)
117 clib_setns (curr_ns_fd);
125 lcp_itf_pair_walk_sync_state_all_cb (index_t lipi, void *ctx)
128 lip = lcp_itf_pair_get (lipi);
130 return WALK_CONTINUE;
132 lcp_itf_pair_sync_state (lip);
133 return WALK_CONTINUE;
137 lcp_itf_pair_walk_sync_state_hw_cb (vnet_main_t *vnm, u32 sw_if_index,
142 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
145 return WALK_CONTINUE;
148 lcp_itf_pair_sync_state (lip);
149 return WALK_CONTINUE;
153 lcp_itf_pair_sync_state_all ()
155 lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_all_cb, 0);
159 lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi)
163 LCP_ITF_PAIR_DBG ("sync_state_hw: hi %U", format_vnet_sw_if_index_name,
164 vnet_get_main (), hi->hw_if_index);
166 vnet_hw_interface_walk_sw (vnet_get_main (), hi->hw_if_index,
167 lcp_itf_pair_walk_sync_state_hw_cb, NULL);
170 static clib_error_t *
171 lcp_itf_admin_state_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
174 vnet_hw_interface_t *hi;
175 vnet_sw_interface_t *si;
180 LCP_ITF_PAIR_DBG ("admin_state_change: sw %U %u",
181 format_vnet_sw_if_index_name, vnm, sw_if_index, flags);
183 // Sync interface state changes into host
184 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
187 LCP_ITF_PAIR_INFO ("admin_state_change: %U flags %u", format_lcp_itf_pair,
190 if (vnet_sw_interface_is_sub (vnm, sw_if_index))
192 lcp_itf_pair_sync_state (lip);
196 // When Linux changes link on a parent interface, all of its children also
197 // change. If a parent interface changes MTU, all of its children are clamped
198 // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
199 // change by walking the sub-interfaces of a phy and syncing their state back
201 si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
205 hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
208 LCP_ITF_PAIR_DBG ("admin_state_change: si %U hi %U, syncing children",
209 format_vnet_sw_if_index_name, vnm, si->sw_if_index,
210 format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
212 lcp_itf_pair_sync_state_hw (hi);
217 VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lcp_itf_admin_state_change);
219 static clib_error_t *
220 lcp_itf_mtu_change (vnet_main_t *vnm, u32 sw_if_index, u32 flags)
222 vnet_sw_interface_t *si;
223 vnet_hw_interface_t *hi;
227 LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm,
230 if (vnet_sw_interface_is_sub (vnm, sw_if_index))
233 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
235 lcp_itf_pair_sync_state (lip);
239 // When Linux changes link on a parent interface, all of its children also
240 // change. If a parent interface changes MTU, all of its children are clamped
241 // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
242 // change by walking the sub-interfaces of a phy and syncing their state back
244 si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
248 hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
251 LCP_ITF_PAIR_DBG ("mtu_change: si %U hi %U, syncing children",
252 format_vnet_sw_if_index_name, vnm, si->sw_if_index,
253 format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
255 lcp_itf_pair_sync_state_hw (hi);
260 VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION (lcp_itf_mtu_change);
263 lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque,
264 u32 sw_if_index, ip4_address_t *address,
265 u32 address_length, u32 if_address_index,
268 const lcp_itf_pair_t *lip;
275 LCP_ITF_PAIR_DBG ("ip4_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
276 format_vnet_sw_if_index_name, vnet_get_main (),
277 sw_if_index, format_ip4_address, address, address_length);
279 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
283 if (lip->lip_namespace)
285 curr_ns_fd = clib_netns_open (NULL /* self */);
286 vif_ns_fd = clib_netns_open (lip->lip_namespace);
288 clib_setns (vif_ns_fd);
291 LCP_ITF_PAIR_DBG ("ip4_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
292 format_lcp_itf_pair, lip, format_ip4_address, address,
296 vnet_netlink_del_ip4_addr (lip->lip_vif_index, address, address_length);
298 vnet_netlink_add_ip4_addr (lip->lip_vif_index, address, address_length);
303 if (curr_ns_fd != -1)
305 clib_setns (curr_ns_fd);
312 lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque,
313 u32 sw_if_index, ip6_address_t *address,
314 u32 address_length, u32 if_address_index,
317 const lcp_itf_pair_t *lip;
324 LCP_ITF_PAIR_DBG ("ip6_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
325 format_vnet_sw_if_index_name, vnet_get_main (),
326 sw_if_index, format_ip6_address, address, address_length);
328 lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
332 if (lip->lip_namespace)
334 curr_ns_fd = clib_netns_open (NULL /* self */);
335 vif_ns_fd = clib_netns_open (lip->lip_namespace);
337 clib_setns (vif_ns_fd);
339 LCP_ITF_PAIR_DBG ("ip6_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
340 format_lcp_itf_pair, lip, format_ip6_address, address,
343 vnet_netlink_del_ip6_addr (lip->lip_vif_index, address, address_length);
345 vnet_netlink_add_ip6_addr (lip->lip_vif_index, address, address_length);
350 if (curr_ns_fd != -1)
352 clib_setns (curr_ns_fd);
357 static clib_error_t *
358 lcp_itf_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_create)
360 const vnet_sw_interface_t *sw;
363 if (!lcp_auto_subint ())
366 sw = vnet_get_sw_interface_or_null (vnm, sw_if_index);
370 is_sub = vnet_sw_interface_is_sub (vnm, sw_if_index);
374 LCP_ITF_PAIR_DBG ("interface_%s: sw %U parent %U", is_create ? "add" : "del",
375 format_vnet_sw_if_index_name, vnet_get_main (),
376 sw->sw_if_index, format_vnet_sw_if_index_name,
377 vnet_get_main (), sw->sup_sw_if_index);
381 const lcp_itf_pair_t *sup_lip;
384 // If the parent has a LIP auto-create a LIP for this interface
386 lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
390 name = format (name, "%s.%d", sup_lip->lip_host_name, sw->sub.id);
393 "interface_%s: %U has parent %U, auto-creating LCP with host-if %s",
394 is_create ? "add" : "del", format_vnet_sw_if_index_name,
395 vnet_get_main (), sw->sw_if_index, format_lcp_itf_pair, sup_lip, name);
397 lcp_itf_pair_create (sw->sw_if_index, name, LCP_ITF_HOST_TAP,
398 sup_lip->lip_namespace, NULL);
404 lcp_itf_pair_delete (sw_if_index);
410 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_interface_add_del);
412 static clib_error_t *
413 lcp_itf_sync_init (vlib_main_t *vm)
415 ip4_main_t *im4 = &ip4_main;
416 ip6_main_t *im6 = &ip6_main;
418 ip4_add_del_interface_address_callback_t cb4;
419 ip6_add_del_interface_address_callback_t cb6;
421 cb4.function = lcp_itf_ip4_add_del_interface_addr;
422 cb4.function_opaque = 0;
423 vec_add1 (im4->add_del_interface_address_callbacks, cb4);
425 cb6.function = lcp_itf_ip6_add_del_interface_addr;
426 cb6.function_opaque = 0;
427 vec_add1 (im6->add_del_interface_address_callbacks, cb6);
432 VLIB_INIT_FUNCTION (lcp_itf_sync_init) = {
433 .runs_after = VLIB_INITS ("vnet_interface_init", "tcp_init", "udp_init"),
437 * fd.io coding-style-patch-verification: ON
440 * eval: (c-set-style "gnu")