Coverage Report

Created: 2026-07-16 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/zebra/netconf_netlink.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * netconf_netlink.c - netconf interaction with the kernel using
4
 * netlink
5
 * Copyright (C) 2021  Nvidia, Inc.
6
 *                     Donald Sharp
7
 */
8
#include <zebra.h>
9
10
#ifdef HAVE_NETLINK /* Netlink OSes only */
11
12
#include <ns.h>
13
14
#include "linux/netconf.h"
15
16
#include "lib/lib_errors.h"
17
#include "zebra/zebra_ns.h"
18
#include "zebra/zebra_dplane.h"
19
#include "zebra/kernel_netlink.h"
20
#include "zebra/netconf_netlink.h"
21
#include "zebra/debug.h"
22
23
static struct rtattr *netconf_rta(struct netconfmsg *ncm)
24
0
{
25
0
  return (struct rtattr *)((char *)ncm +
26
0
         NLMSG_ALIGN(sizeof(struct netconfmsg)));
27
0
}
28
29
/*
30
 * Handle netconf update about a single interface: create dplane
31
 * context, and enqueue for processing in the main zebra pthread.
32
 */
33
static int
34
netlink_netconf_dplane_update(ns_id_t ns_id, afi_t afi, ifindex_t ifindex,
35
            enum dplane_netconf_status_e mpls_on,
36
            enum dplane_netconf_status_e mcast_on,
37
            enum dplane_netconf_status_e linkdown_on)
38
0
{
39
0
  struct zebra_dplane_ctx *ctx;
40
41
0
  ctx = dplane_ctx_alloc();
42
0
  dplane_ctx_set_op(ctx, DPLANE_OP_INTF_NETCONFIG);
43
0
  dplane_ctx_set_ns_id(ctx, ns_id);
44
0
  dplane_ctx_set_afi(ctx, afi);
45
0
  dplane_ctx_set_ifindex(ctx, ifindex);
46
47
0
  dplane_ctx_set_netconf_mpls(ctx, mpls_on);
48
0
  dplane_ctx_set_netconf_mcast(ctx, mcast_on);
49
0
  dplane_ctx_set_netconf_linkdown(ctx, linkdown_on);
50
51
  /* Enqueue ctx for main pthread to process */
52
0
  dplane_provider_enqueue_to_zebra(ctx);
53
54
0
  return 0;
55
0
}
56
57
/*
58
 * Parse and process an incoming netlink netconf update.
59
 */
60
int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
61
0
{
62
0
  struct netconfmsg *ncm;
63
0
  struct rtattr *tb[NETCONFA_MAX + 1] = {};
64
0
  int len;
65
0
  ifindex_t ifindex;
66
0
  uint32_t ival;
67
0
  afi_t afi;
68
0
  enum dplane_netconf_status_e mpls_on = DPLANE_NETCONF_STATUS_UNKNOWN;
69
0
  enum dplane_netconf_status_e mcast_on = DPLANE_NETCONF_STATUS_UNKNOWN;
70
0
  enum dplane_netconf_status_e linkdown_on =
71
0
    DPLANE_NETCONF_STATUS_UNKNOWN;
72
73
0
  if (h->nlmsg_type != RTM_NEWNETCONF && h->nlmsg_type != RTM_DELNETCONF)
74
0
    return 0;
75
76
0
  len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct netconfmsg));
77
0
  if (len < 0) {
78
0
    zlog_err("%s: Message received from netlink is of a broken size: %d, min %zu",
79
0
       __func__, h->nlmsg_len,
80
0
       (size_t)NLMSG_LENGTH(sizeof(struct netconfmsg)));
81
0
    return -1;
82
0
  }
83
84
0
  ncm = NLMSG_DATA(h);
85
86
  /*
87
   * FRR does not have an internal representation of afi_t for
88
   * the MPLS Address Family that the kernel has.  So let's
89
   * just call it v4.  This is ok because the kernel appears
90
   * to do a good job of not sending data that is mixed/matched
91
   * across families
92
   */
93
0
#ifdef AF_MPLS
94
0
  if (ncm->ncm_family == AF_MPLS)
95
0
    afi = AFI_IP;
96
0
  else
97
0
#endif /* AF_MPLS */
98
0
    afi = family2afi(ncm->ncm_family);
99
100
0
  netlink_parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), len);
101
102
0
  if (!tb[NETCONFA_IFINDEX]) {
103
0
    zlog_err("NETCONF message received from netlink without an ifindex");
104
0
    return 0;
105
0
  }
106
107
0
  ifindex = *(ifindex_t *)RTA_DATA(tb[NETCONFA_IFINDEX]);
108
109
0
  if (tb[NETCONFA_INPUT]) {
110
0
    ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_INPUT]);
111
0
    if (ival != 0)
112
0
      mpls_on = DPLANE_NETCONF_STATUS_ENABLED;
113
0
    else
114
0
      mpls_on = DPLANE_NETCONF_STATUS_DISABLED;
115
0
  }
116
117
0
  if (tb[NETCONFA_MC_FORWARDING]) {
118
0
    ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]);
119
0
    if (ival != 0)
120
0
      mcast_on = DPLANE_NETCONF_STATUS_ENABLED;
121
0
    else
122
0
      mcast_on = DPLANE_NETCONF_STATUS_DISABLED;
123
0
  }
124
125
0
  if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) {
126
0
    ival = *(uint32_t *)RTA_DATA(
127
0
      tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]);
128
0
    if (ival != 0)
129
0
      linkdown_on = DPLANE_NETCONF_STATUS_ENABLED;
130
0
    else
131
0
      linkdown_on = DPLANE_NETCONF_STATUS_DISABLED;
132
0
  }
133
134
0
  if (IS_ZEBRA_DEBUG_KERNEL)
135
0
    zlog_debug(
136
0
      "%s: interface %u is mpls on: %d multicast on: %d linkdown: %d",
137
0
      __func__, ifindex, mpls_on, mcast_on, linkdown_on);
138
139
  /* Create a dplane context and pass it along for processing */
140
0
  netlink_netconf_dplane_update(ns_id, afi, ifindex, mpls_on, mcast_on,
141
0
              linkdown_on);
142
143
0
  return 0;
144
0
}
145
146
/*
147
 * Request info from the host OS. This only sends the request; any replies
148
 * are processed asynchronously.
149
 */
150
int netlink_request_netconf(int sockfd)
151
0
{
152
0
  struct nlsock *nls;
153
0
  struct {
154
0
    struct nlmsghdr n;
155
0
    struct netconfmsg ncm;
156
0
    char buf[1024];
157
0
  } req = {};
158
159
0
  nls = kernel_netlink_nlsock_lookup(sockfd);
160
161
0
  if (IS_ZEBRA_DEBUG_KERNEL)
162
0
    zlog_debug("%s: nlsock %s", __func__, nls ? nls->name : "NULL");
163
164
0
  if (nls == NULL)
165
0
    return -1;
166
167
0
  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg));
168
0
  req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
169
0
  req.n.nlmsg_type = RTM_GETNETCONF;
170
0
  req.ncm.ncm_family = AF_UNSPEC;
171
172
0
  return netlink_request(nls, &req);
173
0
}
174
175
extern struct zebra_privs_t zserv_privs;
176
/*
177
 * Currently netconf has no ability to set from netlink.
178
 * So we've received a request to do this work in the data plane.
179
 * as such we need to set the value via the /proc system
180
 */
181
enum netlink_msg_status netlink_put_intf_netconfig(struct nl_batch *bth,
182
               struct zebra_dplane_ctx *ctx)
183
0
{
184
0
  const char *ifname = dplane_ctx_get_ifname(ctx);
185
0
  enum dplane_netconf_status_e mpls_on = dplane_ctx_get_netconf_mpls(ctx);
186
0
  char set[64];
187
0
  char mpls_proc[PATH_MAX];
188
0
  int fd, ret = FRR_NETLINK_ERROR;
189
190
0
  snprintf(mpls_proc, sizeof(mpls_proc),
191
0
     "/proc/sys/net/mpls/conf/%s/input", ifname);
192
193
0
  if (mpls_on == DPLANE_NETCONF_STATUS_ENABLED)
194
0
    snprintf(set, sizeof(set), "1\n");
195
0
  else if (mpls_on == DPLANE_NETCONF_STATUS_DISABLED)
196
0
    snprintf(set, sizeof(set), "0\n");
197
0
  else {
198
0
    flog_err_sys(
199
0
      EC_LIB_DEVELOPMENT,
200
0
      "%s: Expected interface %s to be set to ENABLED or DISABLED was %d",
201
0
      __func__, ifname, mpls_on);
202
0
    return ret;
203
0
  }
204
205
0
  frr_with_privs (&zserv_privs) {
206
0
    fd = open(mpls_proc, O_WRONLY);
207
0
    if (fd < 0) {
208
0
      flog_err_sys(
209
0
        EC_LIB_SOCKET,
210
0
        "%s: Unable to open %s for writing: %s(%d)",
211
0
        __func__, mpls_proc, safe_strerror(errno),
212
0
        errno);
213
0
      return ret;
214
0
    }
215
0
    if (write(fd, set, 2) == 2)
216
0
      ret = FRR_NETLINK_SUCCESS;
217
0
    else
218
0
      flog_err_sys(EC_LIB_SOCKET,
219
0
             "%s: Unsuccessful write to %s: %s(%d)",
220
0
             __func__, mpls_proc, safe_strerror(errno),
221
0
             errno);
222
0
    close(fd);
223
0
  }
224
0
  return ret;
225
0
}
226
227
#endif  /* HAVE_NETLINK */