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