Coverage Report

Created: 2025-10-23 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/zebra/zebra_netns_id.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/* zebra NETNS ID handling routines
3
 * those routines are implemented locally to avoid having external dependencies.
4
 * Copyright (C) 2018 6WIND
5
 */
6
7
#include <zebra.h>
8
9
#include "ns.h"
10
#include "vrf.h"
11
#include "log.h"
12
#include "lib_errors.h"
13
#include "network.h"
14
15
#include "zebra/rib.h"
16
#include "zebra/zebra_dplane.h"
17
#if defined(HAVE_NETLINK)
18
19
#include <linux/net_namespace.h>
20
#include <linux/netlink.h>
21
#include <linux/rtnetlink.h>
22
23
#include "zebra_ns.h"
24
#include "kernel_netlink.h"
25
#endif /* defined(HAVE_NETLINK) */
26
27
#include "zebra/zebra_netns_id.h"
28
#include "zebra/zebra_errors.h"
29
30
/* in case NEWNSID not available, the NSID will be locally obtained
31
 */
32
#define NS_BASE_NSID 0
33
34
#if defined(HAVE_NETLINK)
35
36
0
#define NETLINK_SOCKET_BUFFER_SIZE 512
37
0
#define NETLINK_ALIGNTO             4
38
#define NETLINK_ALIGN(len)                                                     \
39
0
  (((len) + NETLINK_ALIGNTO - 1) & ~(NETLINK_ALIGNTO - 1))
40
0
#define NETLINK_NLATTR_LEN(_a, _b)   (unsigned int)((char *)_a - (char *)_b)
41
42
#endif /* defined(HAVE_NETLINK) */
43
44
static ns_id_t zebra_ns_id_get_fallback(const char *netnspath)
45
0
{
46
0
  static int zebra_ns_id_local;
47
48
0
  return zebra_ns_id_local++;
49
0
}
50
51
#if defined(HAVE_NETLINK)
52
53
static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type)
54
0
{
55
0
  struct nlmsghdr *nlh;
56
57
0
  nlh = (struct nlmsghdr *)buf;
58
0
  nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr));
59
60
0
  nlh->nlmsg_type = type;
61
0
  nlh->nlmsg_flags = NLM_F_REQUEST;
62
0
  if (type == RTM_NEWNSID)
63
0
    nlh->nlmsg_flags |= NLM_F_ACK;
64
0
  nlh->nlmsg_seq = *seq = frr_sequence32_next();
65
0
  return nlh;
66
0
}
67
68
static int send_receive(int sock, struct nlmsghdr *nlh, unsigned int seq,
69
      char *buf)
70
0
{
71
0
  int ret;
72
0
  static const struct sockaddr_nl snl = {.nl_family = AF_NETLINK};
73
74
0
  ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0,
75
0
         (struct sockaddr *)&snl, (socklen_t)sizeof(snl));
76
0
  if (ret < 0) {
77
0
    flog_err_sys(EC_LIB_SOCKET, "netlink( %u) sendmsg() error: %s",
78
0
           sock, safe_strerror(errno));
79
0
    return -1;
80
0
  }
81
82
  /* reception */
83
0
  struct sockaddr_nl addr;
84
0
  struct iovec iov = {
85
0
    .iov_base = buf, .iov_len = NETLINK_SOCKET_BUFFER_SIZE,
86
0
  };
87
0
  struct msghdr msg = {
88
0
    .msg_name = &addr,
89
0
    .msg_namelen = sizeof(struct sockaddr_nl),
90
0
    .msg_iov = &iov,
91
0
    .msg_iovlen = 1,
92
0
    .msg_control = NULL,
93
0
    .msg_controllen = 0,
94
0
    .msg_flags = 0,
95
0
  };
96
0
  ret = recvmsg(sock, &msg, 0);
97
0
  if (ret < 0) {
98
0
    flog_err_sys(EC_LIB_SOCKET,
99
0
           "netlink recvmsg: error %d (errno %u)", ret,
100
0
           errno);
101
0
    return -1;
102
0
  }
103
0
  if (msg.msg_flags & MSG_TRUNC) {
104
0
    flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
105
0
       "netlink recvmsg : error message truncated");
106
0
    return -1;
107
0
  }
108
  /* nlh already points to buf */
109
0
  if (nlh->nlmsg_seq != seq) {
110
0
    flog_err(
111
0
      EC_ZEBRA_NETLINK_BAD_SEQUENCE,
112
0
      "netlink recvmsg: bad sequence number %x (expected %x)",
113
0
      seq, nlh->nlmsg_seq);
114
0
    return -1;
115
0
  }
116
0
  return ret;
117
0
}
118
119
/* extract on a valid nlmsg the nsid
120
 * valid nlmsghdr - not a nlmsgerr
121
 */
122
static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf)
123
0
{
124
0
  ns_id_t ns_id = NS_UNKNOWN;
125
0
  int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr))
126
0
         + NETLINK_ALIGN(sizeof(struct rtgenmsg));
127
0
  void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len));
128
0
  struct nlattr *attr;
129
130
0
  for (attr = (struct nlattr *)(buf + offset);
131
0
       NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr)
132
0
       && attr->nla_len >= sizeof(struct nlattr)
133
0
       && attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr);
134
0
       attr += NETLINK_ALIGN(attr->nla_len)) {
135
0
    if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) {
136
0
      uint32_t *ptr = (uint32_t *)(attr);
137
138
0
      ns_id = ptr[1];
139
0
      break;
140
0
    }
141
0
  }
142
0
  return ns_id;
143
0
}
144
145
/* fd_param = -1 is ignored.
146
 * netnspath set to null is ignored.
147
 * one of the 2 params is mandatory. netnspath is looked in priority
148
 */
149
ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param)
150
0
{
151
0
  int ns_id = -1;
152
0
  struct sockaddr_nl snl;
153
0
  int fd = -1, sock, ret;
154
0
  unsigned int seq;
155
0
  ns_id_t return_nsid = NS_UNKNOWN;
156
157
  /* netns path check */
158
0
  if (!netnspath && fd_param == -1)
159
0
    return NS_UNKNOWN;
160
0
  if (netnspath)  {
161
0
    fd = open(netnspath, O_RDONLY);
162
0
    if (fd == -1)
163
0
      return NS_UNKNOWN;
164
0
  } else if (fd_param != -1)
165
0
    fd = fd_param;
166
  /* netlink socket */
167
0
  sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
168
0
  if (sock < 0) {
169
0
    flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s",
170
0
           sock, safe_strerror(errno));
171
0
    if (netnspath)
172
0
      close(fd);
173
0
    return NS_UNKNOWN;
174
0
  }
175
0
  memset(&snl, 0, sizeof(snl));
176
0
  snl.nl_family = AF_NETLINK;
177
0
  snl.nl_groups = RTNLGRP_NSID;
178
0
  snl.nl_pid = 0; /* AUTO PID */
179
0
  ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl));
180
0
  if (ret < 0) {
181
0
    flog_err_sys(EC_LIB_SOCKET,
182
0
           "netlink( %u) socket() bind error: %s", sock,
183
0
           safe_strerror(errno));
184
0
    close(sock);
185
0
    if (netnspath)
186
0
      close(fd);
187
0
    return NS_UNKNOWN;
188
0
  }
189
190
  /* message to send to netlink,and response : NEWNSID */
191
0
  char buf[NETLINK_SOCKET_BUFFER_SIZE];
192
0
  struct nlmsghdr *nlh;
193
0
  struct rtgenmsg *rt;
194
0
  int len;
195
196
0
  memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
197
0
  nlh = initiate_nlh(buf, &seq, RTM_NEWNSID);
198
0
  rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
199
0
  nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
200
0
  rt->rtgen_family = AF_UNSPEC;
201
202
0
  nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
203
0
  nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
204
205
0
  ret = send_receive(sock, nlh, seq, buf);
206
0
  if (ret < 0) {
207
0
    close(sock);
208
0
    if (netnspath)
209
0
      close(fd);
210
0
    return NS_UNKNOWN;
211
0
  }
212
0
  nlh = (struct nlmsghdr *)buf;
213
214
  /* message to analyse : NEWNSID response */
215
0
  ret = 0;
216
0
  if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
217
0
    return_nsid = extract_nsid(nlh, buf);
218
0
  } else {
219
0
    if (nlh->nlmsg_type == NLMSG_ERROR) {
220
0
      struct nlmsgerr *err =
221
0
        (struct nlmsgerr
222
0
           *)((char *)nlh
223
0
              + NETLINK_ALIGN(
224
0
                  sizeof(struct nlmsghdr)));
225
226
0
      ret = -1;
227
0
      if (err->error < 0)
228
0
        errno = -err->error;
229
0
      else
230
0
        errno = err->error;
231
0
      if (errno == 0) {
232
        /* request NEWNSID was successfull
233
         * return EEXIST error to get GETNSID
234
         */
235
0
        errno = EEXIST;
236
0
      }
237
0
    } else {
238
      /* other errors ignored
239
       * attempt to get nsid
240
       */
241
0
      ret = -1;
242
0
      errno = EEXIST;
243
0
    }
244
0
  }
245
246
0
  if (errno != EEXIST && ret != 0) {
247
0
    flog_err(EC_LIB_SOCKET,
248
0
       "netlink( %u) recvfrom() error 2 when reading: %s", fd,
249
0
       safe_strerror(errno));
250
0
    close(sock);
251
0
    if (netnspath)
252
0
      close(fd);
253
0
    if (errno == ENOTSUP) {
254
0
      zlog_debug("NEWNSID locally generated");
255
0
      return zebra_ns_id_get_fallback(netnspath);
256
0
    }
257
0
    return NS_UNKNOWN;
258
0
  }
259
  /* message to send to netlink : GETNSID */
260
0
  memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE);
261
0
  nlh = initiate_nlh(buf, &seq, RTM_GETNSID);
262
0
  rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len);
263
0
  nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg));
264
0
  rt->rtgen_family = AF_UNSPEC;
265
266
0
  nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd);
267
0
  nl_attr_put32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id);
268
269
0
  ret = send_receive(sock, nlh, seq, buf);
270
0
  if (ret < 0) {
271
0
    close(sock);
272
0
    if (netnspath)
273
0
      close(fd);
274
0
    return NS_UNKNOWN;
275
0
  }
276
0
  nlh = (struct nlmsghdr *)buf;
277
0
  len = ret;
278
0
  ret = 0;
279
0
  do {
280
0
    if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
281
0
      return_nsid = extract_nsid(nlh, buf);
282
0
      if (return_nsid != NS_UNKNOWN)
283
0
        break;
284
0
    } else if (nlh->nlmsg_type == NLMSG_ERROR) {
285
0
      struct nlmsgerr *err =
286
0
        (struct nlmsgerr *)((char *)nlh +
287
0
                NETLINK_ALIGN(sizeof(
288
0
                  struct nlmsghdr)));
289
0
      if (err->error < 0)
290
0
        errno = -err->error;
291
0
      else
292
0
        errno = err->error;
293
0
      break;
294
0
    }
295
0
    len = len - NETLINK_ALIGN(nlh->nlmsg_len);
296
0
    nlh = (struct nlmsghdr *)((char *)nlh +
297
0
            NETLINK_ALIGN(nlh->nlmsg_len));
298
0
  } while (len != 0 && ret == 0);
299
300
0
  if (netnspath)
301
0
    close(fd);
302
0
  close(sock);
303
0
  return return_nsid;
304
0
}
305
306
#else
307
ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused)))
308
{
309
  return zebra_ns_id_get_fallback(netnspath);
310
}
311
312
#endif /* ! defined(HAVE_NETLINK) */
313
314
#ifdef HAVE_NETNS
315
static void zebra_ns_create_netns_directory(void)
316
0
{
317
  /* check that /var/run/netns is created */
318
  /* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */
319
0
  if (mkdir(NS_RUN_DIR, 0755)) {
320
0
    if (errno != EEXIST) {
321
0
      flog_warn(EC_ZEBRA_NAMESPACE_DIR_INACCESSIBLE,
322
0
          "NS check: failed to access %s", NS_RUN_DIR);
323
0
      return;
324
0
    }
325
0
  }
326
0
}
327
#endif
328
329
ns_id_t zebra_ns_id_get_default(void)
330
1
{
331
1
#ifdef HAVE_NETNS
332
1
  int fd;
333
1
#endif /* !HAVE_NETNS */
334
335
1
#ifdef HAVE_NETNS
336
1
  if (vrf_is_backend_netns())
337
0
    zebra_ns_create_netns_directory();
338
1
  fd = open(NS_DEFAULT_NAME, O_RDONLY);
339
340
1
  if (fd == -1)
341
0
    return NS_DEFAULT;
342
1
  if (!vrf_is_backend_netns()) {
343
1
    close(fd);
344
1
    return NS_DEFAULT;
345
1
  }
346
0
  close(fd);
347
0
  return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1);
348
#else  /* HAVE_NETNS */
349
  return NS_DEFAULT;
350
#endif /* !HAVE_NETNS */
351
1
}