LCOV - code coverage report
Current view: top level - plugins/linux-cp - lcp_interface_sync.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 33 180 18.3 %
Date: 2023-07-05 22:20:52 Functions: 14 19 73.7 %

          Line data    Source code
       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           0 : 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           0 :   int curr_ns_fd = -1;
      36           0 :   int vif_ns_fd = -1;
      37             :   u32 mtu;
      38             :   u32 netlink_mtu;
      39             : 
      40           0 :   if (!lcp_sync ())
      41           0 :     return;
      42             : 
      43             :   sw =
      44           0 :     vnet_get_sw_interface_or_null (vnet_get_main (), lip->lip_phy_sw_if_index);
      45           0 :   if (!sw)
      46           0 :     return;
      47             :   sup_sw =
      48           0 :     vnet_get_sw_interface_or_null (vnet_get_main (), sw->sup_sw_if_index);
      49           0 :   if (!sup_sw)
      50           0 :     return;
      51             : 
      52           0 :   if (lip->lip_namespace)
      53             :     {
      54           0 :       curr_ns_fd = clib_netns_open (NULL /* self */);
      55           0 :       vif_ns_fd = clib_netns_open (lip->lip_namespace);
      56           0 :       if (vif_ns_fd != -1)
      57           0 :         clib_setns (vif_ns_fd);
      58             :     }
      59             : 
      60           0 :   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           0 :   int state = sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP;
      68             : 
      69           0 :   if (state && !(sup_sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP))
      70             :     {
      71           0 :       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           0 :       state = 0;
      77             :     }
      78           0 :   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           0 :   mtu = sw->mtu[VNET_MTU_L3];
      86           0 :   if (mtu == 0)
      87           0 :     mtu = sup_sw->mtu[VNET_MTU_L3];
      88             : 
      89           0 :   if (sup_sw->mtu[VNET_MTU_L3] < sw->mtu[VNET_MTU_L3])
      90             :     {
      91           0 :       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           0 :       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           0 :   vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_phy_sw_if_index, mtu);
     102           0 :   vnet_sw_interface_set_mtu (vnet_get_main (), lip->lip_host_sw_if_index, mtu);
     103           0 :   if (NULL == vnet_netlink_get_link_mtu (lip->lip_vif_index, &netlink_mtu))
     104             :     {
     105           0 :       if (netlink_mtu != mtu)
     106           0 :         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           0 :   lcp_itf_set_interface_addr (lip);
     113             : 
     114           0 :   if (vif_ns_fd != -1)
     115           0 :     close (vif_ns_fd);
     116             : 
     117           0 :   if (curr_ns_fd != -1)
     118             :     {
     119           0 :       clib_setns (curr_ns_fd);
     120           0 :       close (curr_ns_fd);
     121             :     }
     122             : 
     123           0 :   return;
     124             : }
     125             : 
     126             : static walk_rc_t
     127           0 : lcp_itf_pair_walk_sync_state_all_cb (index_t lipi, void *ctx)
     128             : {
     129             :   lcp_itf_pair_t *lip;
     130           0 :   lip = lcp_itf_pair_get (lipi);
     131           0 :   if (!lip)
     132           0 :     return WALK_CONTINUE;
     133             : 
     134           0 :   lcp_itf_pair_sync_state (lip);
     135           0 :   return WALK_CONTINUE;
     136             : }
     137             : 
     138             : static walk_rc_t
     139           0 : 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           0 :   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
     145           0 :   if (!lip)
     146             :     {
     147           0 :       return WALK_CONTINUE;
     148             :     }
     149             : 
     150           0 :   lcp_itf_pair_sync_state (lip);
     151           0 :   return WALK_CONTINUE;
     152             : }
     153             : 
     154             : void
     155           0 : lcp_itf_pair_sync_state_all ()
     156             : {
     157           0 :   lcp_itf_pair_walk (lcp_itf_pair_walk_sync_state_all_cb, 0);
     158           0 : }
     159             : 
     160             : void
     161           0 : lcp_itf_pair_sync_state_hw (vnet_hw_interface_t *hi)
     162             : {
     163           0 :   if (!hi)
     164           0 :     return;
     165           0 :   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           0 :   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          37 : 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          37 :   if (!lcp_sync ())
     180          37 :     return 0;
     181             : 
     182           0 :   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           0 :   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
     187           0 :   if (!lip)
     188           0 :     return NULL;
     189           0 :   LCP_ITF_PAIR_INFO ("admin_state_change: %U flags %u", format_lcp_itf_pair,
     190             :                      lip, flags);
     191             : 
     192           0 :   if (vnet_sw_interface_is_sub (vnm, sw_if_index))
     193             :     {
     194           0 :       lcp_itf_pair_sync_state (lip);
     195           0 :       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           0 :   si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
     204           0 :   if (!si)
     205           0 :     return NULL;
     206             : 
     207           0 :   hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
     208           0 :   if (!hi)
     209           0 :     return NULL;
     210           0 :   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           0 :   lcp_itf_pair_sync_state_hw (hi);
     215             : 
     216           0 :   return NULL;
     217             : }
     218             : 
     219           6 : VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (lcp_itf_admin_state_change);
     220             : 
     221             : static clib_error_t *
     222          10 : 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          10 :   if (!lcp_sync ())
     227          10 :     return NULL;
     228             : 
     229           0 :   LCP_ITF_PAIR_DBG ("mtu_change: sw %U %u", format_vnet_sw_if_index_name, vnm,
     230             :                     sw_if_index, flags);
     231             : 
     232           0 :   if (vnet_sw_interface_is_sub (vnm, sw_if_index))
     233             :     {
     234             :       lcp_itf_pair_t *lip;
     235           0 :       lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
     236           0 :       if (lip)
     237           0 :         lcp_itf_pair_sync_state (lip);
     238           0 :       return NULL;
     239             :     }
     240             : 
     241             :   // When Linux changes link on a parent interface, all of its children also
     242             :   // change. If a parent interface changes MTU, all of its children are clamped
     243             :   // at that MTU by Linux. Neither holds true in VPP, so we are forced to undo
     244             :   // change by walking the sub-interfaces of a phy and syncing their state back
     245             :   // into Linux.
     246           0 :   si = vnet_get_sw_interface_or_null (vnm, sw_if_index);
     247           0 :   if (!si)
     248           0 :     return NULL;
     249             : 
     250           0 :   hi = vnet_get_hw_interface_or_null (vnm, si->hw_if_index);
     251           0 :   if (!hi)
     252           0 :     return NULL;
     253           0 :   LCP_ITF_PAIR_DBG ("mtu_change: si %U hi %U, syncing children",
     254             :                     format_vnet_sw_if_index_name, vnm, si->sw_if_index,
     255             :                     format_vnet_sw_if_index_name, vnm, hi->sw_if_index);
     256             : 
     257           0 :   lcp_itf_pair_sync_state_hw (hi);
     258             : 
     259           0 :   return NULL;
     260             : }
     261             : 
     262           4 : VNET_SW_INTERFACE_MTU_CHANGE_FUNCTION (lcp_itf_mtu_change);
     263             : 
     264             : static void
     265          16 : lcp_itf_ip4_add_del_interface_addr (ip4_main_t *im, uword opaque,
     266             :                                     u32 sw_if_index, ip4_address_t *address,
     267             :                                     u32 address_length, u32 if_address_index,
     268             :                                     u32 is_del)
     269             : {
     270             :   const lcp_itf_pair_t *lip;
     271          16 :   int curr_ns_fd = -1;
     272          16 :   int vif_ns_fd = -1;
     273             : 
     274          16 :   if (!lcp_sync ())
     275          16 :     return;
     276             : 
     277           0 :   LCP_ITF_PAIR_DBG ("ip4_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
     278             :                     format_vnet_sw_if_index_name, vnet_get_main (),
     279             :                     sw_if_index, format_ip4_address, address, address_length);
     280             : 
     281           0 :   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
     282           0 :   if (!lip)
     283           0 :     return;
     284             : 
     285           0 :   if (lip->lip_namespace)
     286             :     {
     287           0 :       curr_ns_fd = clib_netns_open (NULL /* self */);
     288           0 :       vif_ns_fd = clib_netns_open (lip->lip_namespace);
     289           0 :       if (vif_ns_fd != -1)
     290           0 :         clib_setns (vif_ns_fd);
     291             :     }
     292             : 
     293           0 :   LCP_ITF_PAIR_DBG ("ip4_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
     294             :                     format_lcp_itf_pair, lip, format_ip4_address, address,
     295             :                     address_length);
     296             : 
     297           0 :   if (is_del)
     298           0 :     vnet_netlink_del_ip4_addr (lip->lip_vif_index, address, address_length);
     299             :   else
     300           0 :     vnet_netlink_add_ip4_addr (lip->lip_vif_index, address, address_length);
     301             : 
     302           0 :   if (vif_ns_fd != -1)
     303           0 :     close (vif_ns_fd);
     304             : 
     305           0 :   if (curr_ns_fd != -1)
     306             :     {
     307           0 :       clib_setns (curr_ns_fd);
     308           0 :       close (curr_ns_fd);
     309             :     }
     310           0 :   return;
     311             : }
     312             : 
     313             : static void
     314          12 : lcp_itf_ip6_add_del_interface_addr (ip6_main_t *im, uword opaque,
     315             :                                     u32 sw_if_index, ip6_address_t *address,
     316             :                                     u32 address_length, u32 if_address_index,
     317             :                                     u32 is_del)
     318             : {
     319             :   const lcp_itf_pair_t *lip;
     320          12 :   int curr_ns_fd = -1;
     321          12 :   int vif_ns_fd = -1;
     322             : 
     323          12 :   if (!lcp_sync ())
     324          12 :     return;
     325             : 
     326           0 :   LCP_ITF_PAIR_DBG ("ip6_addr_%s: si:%U %U/%u", is_del ? "del" : "add",
     327             :                     format_vnet_sw_if_index_name, vnet_get_main (),
     328             :                     sw_if_index, format_ip6_address, address, address_length);
     329             : 
     330           0 :   lip = lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw_if_index));
     331           0 :   if (!lip)
     332           0 :     return;
     333             : 
     334           0 :   if (lip->lip_namespace)
     335             :     {
     336           0 :       curr_ns_fd = clib_netns_open (NULL /* self */);
     337           0 :       vif_ns_fd = clib_netns_open (lip->lip_namespace);
     338           0 :       if (vif_ns_fd != -1)
     339           0 :         clib_setns (vif_ns_fd);
     340             :     }
     341           0 :   LCP_ITF_PAIR_DBG ("ip6_addr_%s: %U ip4 %U/%u", is_del ? "del" : "add",
     342             :                     format_lcp_itf_pair, lip, format_ip6_address, address,
     343             :                     address_length);
     344           0 :   if (is_del)
     345           0 :     vnet_netlink_del_ip6_addr (lip->lip_vif_index, address, address_length);
     346             :   else
     347           0 :     vnet_netlink_add_ip6_addr (lip->lip_vif_index, address, address_length);
     348             : 
     349           0 :   if (vif_ns_fd != -1)
     350           0 :     close (vif_ns_fd);
     351             : 
     352           0 :   if (curr_ns_fd != -1)
     353             :     {
     354           0 :       clib_setns (curr_ns_fd);
     355           0 :       close (curr_ns_fd);
     356             :     }
     357             : }
     358             : 
     359             : static clib_error_t *
     360          19 : lcp_itf_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_create)
     361             : {
     362             :   const vnet_sw_interface_t *sw;
     363             :   uword is_sub;
     364             : 
     365          19 :   if (!lcp_auto_subint ())
     366          19 :     return NULL;
     367             : 
     368           0 :   sw = vnet_get_sw_interface_or_null (vnm, sw_if_index);
     369           0 :   if (!sw)
     370           0 :     return NULL;
     371             : 
     372           0 :   is_sub = vnet_sw_interface_is_sub (vnm, sw_if_index);
     373           0 :   if (!is_sub)
     374           0 :     return NULL;
     375             : 
     376           0 :   LCP_ITF_PAIR_DBG ("interface_%s: sw %U parent %U", is_create ? "add" : "del",
     377             :                     format_vnet_sw_if_index_name, vnet_get_main (),
     378             :                     sw->sw_if_index, format_vnet_sw_if_index_name,
     379             :                     vnet_get_main (), sw->sup_sw_if_index);
     380             : 
     381           0 :   if (is_create)
     382             :     {
     383             :       const lcp_itf_pair_t *sup_lip;
     384           0 :       u8 *name = 0;
     385             : 
     386             :       // If the parent has a LIP auto-create a LIP for this interface
     387             :       sup_lip =
     388           0 :         lcp_itf_pair_get (lcp_itf_pair_find_by_phy (sw->sup_sw_if_index));
     389           0 :       if (!sup_lip)
     390           0 :         return NULL;
     391             : 
     392           0 :       name = format (name, "%s.%d%c", sup_lip->lip_host_name, sw->sub.id, 0);
     393             : 
     394           0 :       LCP_ITF_PAIR_INFO (
     395             :         "interface_%s: %U has parent %U, auto-creating LCP with host-if %s",
     396             :         is_create ? "add" : "del", format_vnet_sw_if_index_name,
     397             :         vnet_get_main (), sw->sw_if_index, format_lcp_itf_pair, sup_lip, name);
     398             : 
     399           0 :       lcp_itf_pair_create (sw->sw_if_index, name, LCP_ITF_HOST_TAP,
     400             :                            sup_lip->lip_namespace, NULL);
     401             : 
     402           0 :       vec_free (name);
     403             :     }
     404             :   else
     405             :     {
     406           0 :       lcp_itf_pair_delete (sw_if_index);
     407             :     }
     408             : 
     409           0 :   return NULL;
     410             : }
     411             : 
     412           6 : VNET_SW_INTERFACE_ADD_DEL_FUNCTION (lcp_itf_interface_add_del);
     413             : 
     414             : static clib_error_t *
     415           2 : lcp_itf_sync_init (vlib_main_t *vm)
     416             : {
     417           2 :   ip4_main_t *im4 = &ip4_main;
     418           2 :   ip6_main_t *im6 = &ip6_main;
     419             : 
     420             :   ip4_add_del_interface_address_callback_t cb4;
     421             :   ip6_add_del_interface_address_callback_t cb6;
     422             : 
     423           2 :   cb4.function = lcp_itf_ip4_add_del_interface_addr;
     424           2 :   cb4.function_opaque = 0;
     425           2 :   vec_add1 (im4->add_del_interface_address_callbacks, cb4);
     426             : 
     427           2 :   cb6.function = lcp_itf_ip6_add_del_interface_addr;
     428           2 :   cb6.function_opaque = 0;
     429           2 :   vec_add1 (im6->add_del_interface_address_callbacks, cb6);
     430             : 
     431           2 :   return NULL;
     432             : }
     433             : 
     434           4 : VLIB_INIT_FUNCTION (lcp_itf_sync_init) = {
     435             :   .runs_after = VLIB_INITS ("vnet_interface_init", "tcp_init", "udp_init"),
     436             : };
     437             : 
     438             : /*
     439             :  * fd.io coding-style-patch-verification: ON
     440             :  *
     441             :  * Local Variables:
     442             :  * eval: (c-set-style "gnu")
     443             :  * End:
     444             :  */

Generated by: LCOV version 1.14