back to topotato report
topotato coverage report
Current view: top level - zebra - zebra_netns_notify.c (source / functions) Hit Total Coverage
Test: test_ospf6_vlink.py::VirtualLinkBasic Lines: 3 205 1.5 %
Date: 2023-02-16 02:06:43 Functions: 3 13 23.1 %

          Line data    Source code
       1             : /*
       2             :  * Zebra NS collector and notifier for Network NameSpaces
       3             :  * Copyright (C) 2017 6WIND
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or modify it
       6             :  * under the terms of the GNU General Public License as published by the Free
       7             :  * Software Foundation; either version 2 of the License, or (at your option)
       8             :  * any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful, but WITHOUT
      11             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12             :  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      13             :  * more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License along
      16             :  * with this program; see the file COPYING; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      18             :  */
      19             : 
      20             : #include <zebra.h>
      21             : 
      22             : #ifdef HAVE_NETLINK
      23             : #ifdef HAVE_NETNS
      24             : #undef _GNU_SOURCE
      25             : #define _GNU_SOURCE
      26             : 
      27             : #include <sched.h>
      28             : #endif
      29             : #include <dirent.h>
      30             : #include <sys/inotify.h>
      31             : #include <sys/stat.h>
      32             : 
      33             : #include "thread.h"
      34             : #include "ns.h"
      35             : #include "command.h"
      36             : #include "memory.h"
      37             : #include "lib_errors.h"
      38             : 
      39             : #include "zebra_router.h"
      40             : #endif /* defined(HAVE_NETLINK) */
      41             : 
      42             : #include "zebra_netns_notify.h"
      43             : #include "zebra_netns_id.h"
      44             : #include "zebra_errors.h"
      45             : #include "interface.h"
      46             : 
      47             : #ifdef HAVE_NETLINK
      48             : 
      49             : /* upon creation of folder under /var/run/netns,
      50             :  * wait that netns context is bound to
      51             :  * that folder 10 seconds
      52             :  */
      53             : #define ZEBRA_NS_POLLING_INTERVAL_MSEC     1000
      54             : #define ZEBRA_NS_POLLING_MAX_RETRIES  200
      55             : 
      56          24 : DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo");
      57             : static struct thread *zebra_netns_notify_current;
      58             : 
      59             : struct zebra_netns_info {
      60             :         const char *netnspath;
      61             :         unsigned int retries;
      62             : };
      63             : 
      64             : static void zebra_ns_ready_read(struct thread *t);
      65             : static void zebra_ns_notify_create_context_from_entry_name(const char *name);
      66             : static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
      67             :                                   int stop_retry);
      68             : static void zebra_ns_notify_read(struct thread *t);
      69             : 
      70           0 : static struct vrf *vrf_handler_create(struct vty *vty, const char *vrfname)
      71             : {
      72           0 :         if (strlen(vrfname) > VRF_NAMSIZ) {
      73           0 :                 flog_warn(EC_LIB_VRF_LENGTH,
      74             :                           "%% VRF name %s invalid: length exceeds %d bytes",
      75             :                           vrfname, VRF_NAMSIZ);
      76           0 :                 return NULL;
      77             :         }
      78             : 
      79           0 :         return vrf_get(VRF_UNKNOWN, vrfname);
      80             : }
      81             : 
      82           0 : static void zebra_ns_notify_create_context_from_entry_name(const char *name)
      83             : {
      84           0 :         char *netnspath = ns_netns_pathname(NULL, name);
      85           0 :         struct vrf *vrf;
      86           0 :         int ret;
      87           0 :         ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN;
      88           0 :         struct ns *default_ns;
      89             : 
      90           0 :         if (netnspath == NULL)
      91             :                 return;
      92             : 
      93           0 :         frr_with_privs(&zserv_privs) {
      94           0 :                 ns_id = zebra_ns_id_get(netnspath, -1);
      95             :         }
      96           0 :         if (ns_id == NS_UNKNOWN)
      97             :                 return;
      98           0 :         ns_id_external = ns_map_nsid_with_external(ns_id, true);
      99             :         /* if VRF with NS ID already present */
     100           0 :         vrf = vrf_lookup_by_id((vrf_id_t)ns_id_external);
     101           0 :         if (vrf) {
     102           0 :                 zlog_debug(
     103             :                         "NS notify : same NSID used by VRF %s. Ignore NS %s creation",
     104             :                         vrf->name, netnspath);
     105           0 :                 return;
     106             :         }
     107           0 :         vrf = vrf_handler_create(NULL, name);
     108           0 :         if (!vrf) {
     109           0 :                 flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED,
     110             :                           "NS notify : failed to create VRF %s", name);
     111           0 :                 ns_map_nsid_with_external(ns_id, false);
     112           0 :                 return;
     113             :         }
     114             : 
     115           0 :         default_ns = ns_get_default();
     116             : 
     117             :         /* force kernel ns_id creation in that new vrf */
     118           0 :         frr_with_privs(&zserv_privs) {
     119           0 :                 ns_switch_to_netns(netnspath);
     120           0 :                 ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd);
     121           0 :                 ns_switchback_to_initial();
     122             :         }
     123             : 
     124           0 :         frr_with_privs(&zserv_privs) {
     125           0 :                 ret = zebra_vrf_netns_handler_create(NULL, vrf, netnspath,
     126             :                                                      ns_id_external, ns_id,
     127             :                                                      ns_id_relative);
     128             :         }
     129           0 :         if (ret != CMD_SUCCESS) {
     130           0 :                 flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED,
     131             :                           "NS notify : failed to create NS %s", netnspath);
     132           0 :                 ns_map_nsid_with_external(ns_id, false);
     133           0 :                 vrf_delete(vrf);
     134           0 :                 return;
     135             :         }
     136           0 :         zlog_info("NS notify : created VRF %s NS %s", name, netnspath);
     137             : }
     138             : 
     139           0 : static int zebra_ns_continue_read(struct zebra_netns_info *zns_info,
     140             :                                   int stop_retry)
     141             : {
     142           0 :         void *ns_path_ptr = (void *)zns_info->netnspath;
     143             : 
     144           0 :         if (stop_retry) {
     145           0 :                 XFREE(MTYPE_NETNS_MISC, ns_path_ptr);
     146           0 :                 XFREE(MTYPE_NETNS_MISC, zns_info);
     147           0 :                 return 0;
     148             :         }
     149           0 :         thread_add_timer_msec(zrouter.master, zebra_ns_ready_read,
     150             :                               (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC,
     151             :                               NULL);
     152           0 :         return 0;
     153             : }
     154             : 
     155           0 : static int zebra_ns_delete(char *name)
     156             : {
     157           0 :         struct vrf *vrf = vrf_lookup_by_name(name);
     158           0 :         struct interface *ifp, *tmp;
     159           0 :         struct ns *ns;
     160             : 
     161           0 :         if (!vrf) {
     162           0 :                 flog_warn(EC_ZEBRA_NS_DELETION_FAILED_NO_VRF,
     163             :                           "NS notify : no VRF found using NS %s", name);
     164           0 :                 return 0;
     165             :         }
     166             : 
     167             :         /*
     168             :          * We don't receive interface down/delete notifications from kernel
     169             :          * when a netns is deleted. Therefore we have to manually replicate
     170             :          * the necessary actions here.
     171             :          */
     172           0 :         RB_FOREACH_SAFE (ifp, if_name_head, &vrf->ifaces_by_name, tmp) {
     173           0 :                 if (!CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE))
     174           0 :                         continue;
     175             : 
     176           0 :                 if (if_is_no_ptm_operative(ifp)) {
     177           0 :                         UNSET_FLAG(ifp->flags, IFF_RUNNING);
     178           0 :                         if_down(ifp);
     179             :                 }
     180             : 
     181           0 :                 UNSET_FLAG(ifp->flags, IFF_UP);
     182           0 :                 if_delete_update(&ifp);
     183             :         }
     184             : 
     185           0 :         ns = (struct ns *)vrf->ns_ctxt;
     186             :         /* the deletion order is the same
     187             :          * as the one used when siging signal is received
     188             :          */
     189           0 :         vrf->ns_ctxt = NULL;
     190           0 :         vrf_delete(vrf);
     191           0 :         if (ns)
     192           0 :                 ns_delete(ns);
     193             : 
     194           0 :         zlog_info("NS notify : deleted VRF %s", name);
     195           0 :         return 0;
     196             : }
     197             : 
     198           0 : static int zebra_ns_notify_self_identify(struct stat *netst)
     199             : {
     200           0 :         char net_path[PATH_MAX];
     201           0 :         int netns;
     202             : 
     203           0 :         snprintf(net_path, sizeof(net_path), "/proc/self/ns/net");
     204           0 :         netns = open(net_path, O_RDONLY);
     205           0 :         if (netns < 0)
     206             :                 return -1;
     207           0 :         if (fstat(netns, netst) < 0) {
     208           0 :                 close(netns);
     209           0 :                 return -1;
     210             :         }
     211           0 :         close(netns);
     212           0 :         return 0;
     213             : }
     214             : 
     215           0 : static bool zebra_ns_notify_is_default_netns(const char *name)
     216             : {
     217           0 :         struct stat default_netns_stat;
     218           0 :         struct stat st;
     219           0 :         char netnspath[PATH_MAX];
     220             : 
     221           0 :         if (zebra_ns_notify_self_identify(&default_netns_stat))
     222             :                 return false;
     223             : 
     224           0 :         memset(&st, 0, sizeof(st));
     225           0 :         snprintf(netnspath, sizeof(netnspath), "%s/%s", NS_RUN_DIR, name);
     226             :         /* compare with local stat */
     227           0 :         if (stat(netnspath, &st) == 0 &&
     228           0 :             (st.st_dev == default_netns_stat.st_dev) &&
     229           0 :             (st.st_ino == default_netns_stat.st_ino))
     230             :                 return true;
     231             :         return false;
     232             : }
     233             : 
     234           0 : static void zebra_ns_ready_read(struct thread *t)
     235             : {
     236           0 :         struct zebra_netns_info *zns_info = THREAD_ARG(t);
     237           0 :         const char *netnspath;
     238           0 :         int err, stop_retry = 0;
     239             : 
     240           0 :         if (!zns_info)
     241             :                 return;
     242           0 :         if (!zns_info->netnspath) {
     243           0 :                 XFREE(MTYPE_NETNS_MISC, zns_info);
     244           0 :                 return;
     245             :         }
     246           0 :         netnspath = zns_info->netnspath;
     247           0 :         if (--zns_info->retries == 0)
     248           0 :                 stop_retry = 1;
     249           0 :         frr_with_privs(&zserv_privs) {
     250           0 :                 err = ns_switch_to_netns(netnspath);
     251             :         }
     252           0 :         if (err < 0) {
     253           0 :                 zebra_ns_continue_read(zns_info, stop_retry);
     254           0 :                 return;
     255             :         }
     256             : 
     257             :         /* go back to default ns */
     258           0 :         frr_with_privs(&zserv_privs) {
     259           0 :                 err = ns_switchback_to_initial();
     260             :         }
     261           0 :         if (err < 0) {
     262           0 :                 zebra_ns_continue_read(zns_info, stop_retry);
     263           0 :                 return;
     264             :         }
     265             : 
     266             :         /* check default name is not already set */
     267           0 :         if (strmatch(VRF_DEFAULT_NAME, basename(netnspath))) {
     268           0 :                 zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", basename(netnspath));
     269           0 :                 zebra_ns_continue_read(zns_info, 1);
     270           0 :                 return;
     271             :         }
     272           0 :         if (zebra_ns_notify_is_default_netns(basename(netnspath))) {
     273           0 :                 zlog_warn(
     274             :                         "NS notify : NS %s is default VRF. Ignore VRF creation",
     275             :                         basename(netnspath));
     276           0 :                 zebra_ns_continue_read(zns_info, 1);
     277           0 :                 return;
     278             :         }
     279             : 
     280             :         /* success : close fd and create zns context */
     281           0 :         zebra_ns_notify_create_context_from_entry_name(basename(netnspath));
     282           0 :         zebra_ns_continue_read(zns_info, 1);
     283             : }
     284             : 
     285           0 : static void zebra_ns_notify_read(struct thread *t)
     286             : {
     287           0 :         int fd_monitor = THREAD_FD(t);
     288           0 :         struct inotify_event *event;
     289           0 :         char buf[BUFSIZ];
     290           0 :         ssize_t len;
     291           0 :         char event_name[NAME_MAX + 1];
     292             : 
     293           0 :         thread_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor,
     294             :                         &zebra_netns_notify_current);
     295           0 :         len = read(fd_monitor, buf, sizeof(buf));
     296           0 :         if (len < 0) {
     297           0 :                 flog_err_sys(EC_ZEBRA_NS_NOTIFY_READ,
     298             :                              "NS notify read: failed to read (%s)",
     299             :                              safe_strerror(errno));
     300           0 :                 return;
     301             :         }
     302           0 :         for (event = (struct inotify_event *)buf; (char *)event < &buf[len];
     303           0 :              event = (struct inotify_event *)((char *)event + sizeof(*event)
     304           0 :                                               + event->len)) {
     305           0 :                 char *netnspath;
     306           0 :                 struct zebra_netns_info *netnsinfo;
     307             : 
     308           0 :                 if (!(event->mask & (IN_CREATE | IN_DELETE)))
     309           0 :                         continue;
     310             : 
     311           0 :                 if (offsetof(struct inotify_event, name) + event->len
     312             :                     >= sizeof(buf)) {
     313           0 :                         flog_err(EC_ZEBRA_NS_NOTIFY_READ,
     314             :                                  "NS notify read: buffer underflow");
     315           0 :                         break;
     316             :                 }
     317             : 
     318           0 :                 if (strnlen(event->name, event->len) == event->len) {
     319           0 :                         flog_err(EC_ZEBRA_NS_NOTIFY_READ,
     320             :                                  "NS notify error: bad event name");
     321           0 :                         break;
     322             :                 }
     323             : 
     324             :                 /*
     325             :                  * Coverity Scan extra steps to satisfy `STRING_NULL` warning:
     326             :                  * - Make sure event name is present by checking `len != 0`
     327             :                  * - Event name length must be at most `NAME_MAX + 1`
     328             :                  *   (null byte inclusive)
     329             :                  * - Copy event name to a stack buffer to make sure it
     330             :                  *   includes the null byte. `event->name` includes at least
     331             :                  *   one null byte and `event->len` accounts the null bytes,
     332             :                  *   so the operation after `memcpy` will look like a
     333             :                  *   truncation to satisfy Coverity Scan null byte ending.
     334             :                  *
     335             :                  *   Example:
     336             :                  *   if `event->name` is `abc\0` and `event->len` is 4,
     337             :                  *   `memcpy` will copy the 4 bytes and then we set the
     338             :                  *   null byte again at the position 4.
     339             :                  *
     340             :                  * For more information please read inotify(7) man page.
     341             :                  */
     342           0 :                 if (event->len == 0)
     343           0 :                         continue;
     344             : 
     345           0 :                 if (event->len > sizeof(event_name)) {
     346           0 :                         flog_err(EC_ZEBRA_NS_NOTIFY_READ,
     347             :                                  "NS notify error: unexpected big event name");
     348           0 :                         break;
     349             :                 }
     350             : 
     351           0 :                 memcpy(event_name, event->name, event->len);
     352           0 :                 event_name[event->len - 1] = 0;
     353             : 
     354           0 :                 if (event->mask & IN_DELETE) {
     355           0 :                         zebra_ns_delete(event_name);
     356           0 :                         continue;
     357             :                 }
     358           0 :                 netnspath = ns_netns_pathname(NULL, event_name);
     359           0 :                 if (!netnspath)
     360           0 :                         continue;
     361           0 :                 netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath);
     362           0 :                 netnsinfo = XCALLOC(MTYPE_NETNS_MISC,
     363             :                                     sizeof(struct zebra_netns_info));
     364           0 :                 netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES;
     365           0 :                 netnsinfo->netnspath = netnspath;
     366           0 :                 thread_add_timer_msec(zrouter.master, zebra_ns_ready_read,
     367             :                                       (void *)netnsinfo, 0, NULL);
     368             :         }
     369             : }
     370             : 
     371           0 : void zebra_ns_notify_parse(void)
     372             : {
     373           0 :         struct dirent *dent;
     374           0 :         DIR *srcdir = opendir(NS_RUN_DIR);
     375             : 
     376           0 :         if (srcdir == NULL) {
     377           0 :                 flog_err_sys(EC_LIB_SYSTEM_CALL,
     378             :                              "NS parsing init: failed to parse %s", NS_RUN_DIR);
     379           0 :                 return;
     380             :         }
     381           0 :         while ((dent = readdir(srcdir)) != NULL) {
     382           0 :                 struct stat st;
     383             : 
     384           0 :                 if (strcmp(dent->d_name, ".") == 0
     385           0 :                     || strcmp(dent->d_name, "..") == 0)
     386           0 :                         continue;
     387           0 :                 if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
     388           0 :                         flog_err_sys(
     389             :                                 EC_LIB_SYSTEM_CALL,
     390             :                                 "NS parsing init: failed to parse entry %s",
     391             :                                 dent->d_name);
     392           0 :                         continue;
     393             :                 }
     394           0 :                 if (S_ISDIR(st.st_mode)) {
     395           0 :                         zlog_debug("NS parsing init: %s is not a NS",
     396             :                                    dent->d_name);
     397           0 :                         continue;
     398             :                 }
     399             :                 /* check default name is not already set */
     400           0 :                 if (strmatch(VRF_DEFAULT_NAME, basename(dent->d_name))) {
     401           0 :                         zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", dent->d_name);
     402           0 :                         continue;
     403             :                 }
     404           0 :                 if (zebra_ns_notify_is_default_netns(dent->d_name)) {
     405           0 :                         zlog_warn(
     406             :                                 "NS notify : NS %s is default VRF. Ignore VRF creation",
     407             :                                 dent->d_name);
     408           0 :                         continue;
     409             :                 }
     410           0 :                 zebra_ns_notify_create_context_from_entry_name(dent->d_name);
     411             :         }
     412           0 :         closedir(srcdir);
     413             : }
     414             : 
     415           0 : void zebra_ns_notify_init(void)
     416             : {
     417           0 :         int fd_monitor;
     418             : 
     419           0 :         fd_monitor = inotify_init();
     420           0 :         if (fd_monitor < 0) {
     421           0 :                 flog_err_sys(
     422             :                         EC_LIB_SYSTEM_CALL,
     423             :                         "NS notify init: failed to initialize inotify (%s)",
     424             :                         safe_strerror(errno));
     425             :         }
     426           0 :         if (inotify_add_watch(fd_monitor, NS_RUN_DIR,
     427             :                               IN_CREATE | IN_DELETE) < 0) {
     428           0 :                 flog_err_sys(EC_LIB_SYSTEM_CALL,
     429             :                              "NS notify watch: failed to add watch (%s)",
     430             :                              safe_strerror(errno));
     431             :         }
     432           0 :         thread_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor,
     433             :                         &zebra_netns_notify_current);
     434           0 : }
     435             : 
     436           8 : void zebra_ns_notify_close(void)
     437             : {
     438           8 :         if (zebra_netns_notify_current == NULL)
     439             :                 return;
     440             : 
     441           0 :         int fd = 0;
     442             : 
     443           0 :         if (zebra_netns_notify_current->u.fd > 0)
     444             :                 fd = zebra_netns_notify_current->u.fd;
     445             : 
     446           0 :         if (zebra_netns_notify_current->master != NULL)
     447           0 :                 THREAD_OFF(zebra_netns_notify_current);
     448             : 
     449             :         /* auto-removal of notify items */
     450           0 :         if (fd > 0)
     451           0 :                 close(fd);
     452             : }
     453             : 
     454             : #else
     455             : void zebra_ns_notify_parse(void)
     456             : {
     457             : }
     458             : 
     459             : void zebra_ns_notify_init(void)
     460             : {
     461             : }
     462             : 
     463             : void zebra_ns_notify_close(void)
     464             : {
     465             : }
     466             : #endif /* !HAVE_NETLINK */

Generated by: LCOV version v1.16-topotato