]>
Commit | Line | Data |
---|---|---|
ebb61fca DS |
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 | ||
39ffa8e8 | 32 | #include "lib/lib_errors.h" |
ebb61fca DS |
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 | static struct rtattr *netconf_rta(struct netconfmsg *ncm) | |
40 | { | |
cd787a8a MS |
41 | return (struct rtattr *)((char *)ncm + |
42 | NLMSG_ALIGN(sizeof(struct netconfmsg))); | |
ebb61fca DS |
43 | } |
44 | ||
cd787a8a MS |
45 | /* |
46 | * Handle netconf update about a single interface: create dplane | |
47 | * context, and enqueue for processing in the main zebra pthread. | |
48 | */ | |
3689905d | 49 | static int |
d53dc9bd | 50 | netlink_netconf_dplane_update(ns_id_t ns_id, afi_t afi, ifindex_t ifindex, |
3689905d DS |
51 | enum dplane_netconf_status_e mpls_on, |
52 | enum dplane_netconf_status_e mcast_on, | |
53 | enum dplane_netconf_status_e linkdown_on) | |
cd787a8a MS |
54 | { |
55 | struct zebra_dplane_ctx *ctx; | |
56 | ||
57 | ctx = dplane_ctx_alloc(); | |
58 | dplane_ctx_set_op(ctx, DPLANE_OP_INTF_NETCONFIG); | |
2b9dc841 | 59 | dplane_ctx_set_ns_id(ctx, ns_id); |
d53dc9bd | 60 | dplane_ctx_set_afi(ctx, afi); |
2b9dc841 | 61 | dplane_ctx_set_ifindex(ctx, ifindex); |
cd787a8a MS |
62 | |
63 | dplane_ctx_set_netconf_mpls(ctx, mpls_on); | |
64 | dplane_ctx_set_netconf_mcast(ctx, mcast_on); | |
3689905d | 65 | dplane_ctx_set_netconf_linkdown(ctx, linkdown_on); |
cd787a8a MS |
66 | |
67 | /* Enqueue ctx for main pthread to process */ | |
68 | dplane_provider_enqueue_to_zebra(ctx); | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
73 | /* | |
74 | * Parse and process an incoming netlink netconf update. | |
75 | */ | |
ebb61fca DS |
76 | int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) |
77 | { | |
78 | struct netconfmsg *ncm; | |
79 | struct rtattr *tb[NETCONFA_MAX + 1] = {}; | |
80 | int len; | |
81 | ifindex_t ifindex; | |
cd787a8a | 82 | uint32_t ival; |
d53dc9bd | 83 | afi_t afi; |
cd787a8a MS |
84 | enum dplane_netconf_status_e mpls_on = DPLANE_NETCONF_STATUS_UNKNOWN; |
85 | enum dplane_netconf_status_e mcast_on = DPLANE_NETCONF_STATUS_UNKNOWN; | |
3689905d DS |
86 | enum dplane_netconf_status_e linkdown_on = |
87 | DPLANE_NETCONF_STATUS_UNKNOWN; | |
ebb61fca DS |
88 | |
89 | if (h->nlmsg_type != RTM_NEWNETCONF && h->nlmsg_type != RTM_DELNETCONF) | |
90 | return 0; | |
91 | ||
92 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct netconfmsg)); | |
93 | if (len < 0) { | |
cd787a8a | 94 | zlog_err("%s: Message received from netlink is of a broken size: %d, min %zu", |
ebb61fca DS |
95 | __func__, h->nlmsg_len, |
96 | (size_t)NLMSG_LENGTH(sizeof(struct netconfmsg))); | |
97 | return -1; | |
98 | } | |
99 | ||
100 | ncm = NLMSG_DATA(h); | |
101 | ||
d53dc9bd DS |
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 | if (ncm->ncm_family == AF_MPLS) | |
110 | afi = AFI_IP; | |
111 | else | |
112 | afi = family2afi(ncm->ncm_family); | |
113 | ||
ebb61fca DS |
114 | netlink_parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), len); |
115 | ||
116 | if (!tb[NETCONFA_IFINDEX]) { | |
cd787a8a | 117 | zlog_err("NETCONF message received from netlink without an ifindex"); |
ebb61fca DS |
118 | return 0; |
119 | } | |
120 | ||
121 | ifindex = *(ifindex_t *)RTA_DATA(tb[NETCONFA_IFINDEX]); | |
122 | ||
ebb61fca | 123 | if (tb[NETCONFA_INPUT]) { |
cd787a8a MS |
124 | ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_INPUT]); |
125 | if (ival != 0) | |
126 | mpls_on = DPLANE_NETCONF_STATUS_ENABLED; | |
127 | else | |
128 | mpls_on = DPLANE_NETCONF_STATUS_DISABLED; | |
ebb61fca DS |
129 | } |
130 | ||
131 | if (tb[NETCONFA_MC_FORWARDING]) { | |
cd787a8a MS |
132 | ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]); |
133 | if (ival != 0) | |
134 | mcast_on = DPLANE_NETCONF_STATUS_ENABLED; | |
135 | else | |
136 | mcast_on = DPLANE_NETCONF_STATUS_DISABLED; | |
ebb61fca DS |
137 | } |
138 | ||
3689905d DS |
139 | if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) { |
140 | ival = *(uint32_t *)RTA_DATA( | |
141 | tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]); | |
142 | if (ival != 0) | |
143 | linkdown_on = DPLANE_NETCONF_STATUS_ENABLED; | |
144 | else | |
145 | linkdown_on = DPLANE_NETCONF_STATUS_DISABLED; | |
146 | } | |
147 | ||
ebb61fca | 148 | if (IS_ZEBRA_DEBUG_KERNEL) |
3689905d DS |
149 | zlog_debug( |
150 | "%s: interface %u is mpls on: %d multicast on: %d linkdown: %d", | |
151 | __func__, ifindex, mpls_on, mcast_on, linkdown_on); | |
cd787a8a MS |
152 | |
153 | /* Create a dplane context and pass it along for processing */ | |
d53dc9bd | 154 | netlink_netconf_dplane_update(ns_id, afi, ifindex, mpls_on, mcast_on, |
3689905d | 155 | linkdown_on); |
ebb61fca DS |
156 | |
157 | return 0; | |
158 | } | |
159 | ||
cd787a8a MS |
160 | /* |
161 | * Request info from the host OS. This only sends the request; any replies | |
162 | * are processed asynchronously. | |
163 | */ | |
164 | int netlink_request_netconf(int sockfd) | |
ebb61fca | 165 | { |
cd787a8a | 166 | struct nlsock *nls; |
ebb61fca DS |
167 | struct { |
168 | struct nlmsghdr n; | |
169 | struct netconfmsg ncm; | |
170 | char buf[1024]; | |
cd787a8a MS |
171 | } req = {}; |
172 | ||
173 | nls = kernel_netlink_nlsock_lookup(sockfd); | |
174 | ||
175 | if (IS_ZEBRA_DEBUG_KERNEL) | |
176 | zlog_debug("%s: nlsock %s", __func__, nls ? nls->name : "NULL"); | |
177 | ||
178 | if (nls == NULL) | |
179 | return -1; | |
ebb61fca | 180 | |
ebb61fca DS |
181 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg)); |
182 | req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; | |
183 | req.n.nlmsg_type = RTM_GETNETCONF; | |
184 | req.ncm.ncm_family = AF_UNSPEC; | |
185 | ||
cd787a8a | 186 | return netlink_request(nls, &req); |
ebb61fca DS |
187 | } |
188 | ||
39ffa8e8 DS |
189 | extern struct zebra_privs_t zserv_privs; |
190 | /* | |
191 | * Currently netconf has no ability to set from netlink. | |
192 | * So we've received a request to do this work in the data plane. | |
193 | * as such we need to set the value via the /proc system | |
194 | */ | |
195 | enum netlink_msg_status netlink_put_intf_netconfig(struct nl_batch *bth, | |
196 | struct zebra_dplane_ctx *ctx) | |
197 | { | |
198 | const char *ifname = dplane_ctx_get_ifname(ctx); | |
199 | enum dplane_netconf_status_e mpls_on = dplane_ctx_get_netconf_mpls(ctx); | |
200 | char set[64]; | |
201 | char mpls_proc[PATH_MAX]; | |
202 | int fd, ret = FRR_NETLINK_ERROR; | |
203 | ||
204 | snprintf(mpls_proc, sizeof(mpls_proc), | |
205 | "/proc/sys/net/mpls/conf/%s/input", ifname); | |
206 | ||
207 | if (mpls_on == DPLANE_NETCONF_STATUS_ENABLED) | |
208 | snprintf(set, sizeof(set), "1\n"); | |
209 | else if (mpls_on == DPLANE_NETCONF_STATUS_DISABLED) | |
210 | snprintf(set, sizeof(set), "0\n"); | |
211 | else { | |
212 | flog_err_sys( | |
213 | EC_LIB_DEVELOPMENT, | |
214 | "%s: Expected interface %s to be set to ENABLED or DISABLED was %d", | |
215 | __func__, ifname, mpls_on); | |
216 | return ret; | |
217 | } | |
218 | ||
219 | frr_with_privs (&zserv_privs) { | |
220 | fd = open(mpls_proc, O_WRONLY); | |
221 | if (fd < 0) { | |
222 | flog_err_sys( | |
223 | EC_LIB_SOCKET, | |
224 | "%s: Unable to open %s for writing: %s(%d)", | |
225 | __func__, mpls_proc, safe_strerror(errno), | |
226 | errno); | |
227 | return ret; | |
228 | } | |
229 | if (write(fd, set, 2) == 2) | |
230 | ret = FRR_NETLINK_SUCCESS; | |
231 | else | |
232 | flog_err_sys(EC_LIB_SOCKET, | |
233 | "%s: Unsuccessful write to %s: %s(%d)", | |
234 | __func__, mpls_proc, safe_strerror(errno), | |
235 | errno); | |
236 | close(fd); | |
237 | } | |
238 | return ret; | |
239 | } | |
240 | ||
ebb61fca | 241 | #endif /* HAVE_NETLINK */ |