]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
ebb61fca DS |
2 | /* |
3 | * netconf_netlink.c - netconf interaction with the kernel using | |
4 | * netlink | |
5 | * Copyright (C) 2021 Nvidia, Inc. | |
6 | * Donald Sharp | |
ebb61fca DS |
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 | ||
39ffa8e8 | 16 | #include "lib/lib_errors.h" |
ebb61fca DS |
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 | { | |
cd787a8a MS |
25 | return (struct rtattr *)((char *)ncm + |
26 | NLMSG_ALIGN(sizeof(struct netconfmsg))); | |
ebb61fca DS |
27 | } |
28 | ||
cd787a8a MS |
29 | /* |
30 | * Handle netconf update about a single interface: create dplane | |
31 | * context, and enqueue for processing in the main zebra pthread. | |
32 | */ | |
3689905d | 33 | static int |
d53dc9bd | 34 | netlink_netconf_dplane_update(ns_id_t ns_id, afi_t afi, ifindex_t ifindex, |
3689905d DS |
35 | enum dplane_netconf_status_e mpls_on, |
36 | enum dplane_netconf_status_e mcast_on, | |
37 | enum dplane_netconf_status_e linkdown_on) | |
cd787a8a MS |
38 | { |
39 | struct zebra_dplane_ctx *ctx; | |
40 | ||
41 | ctx = dplane_ctx_alloc(); | |
42 | dplane_ctx_set_op(ctx, DPLANE_OP_INTF_NETCONFIG); | |
2b9dc841 | 43 | dplane_ctx_set_ns_id(ctx, ns_id); |
d53dc9bd | 44 | dplane_ctx_set_afi(ctx, afi); |
2b9dc841 | 45 | dplane_ctx_set_ifindex(ctx, ifindex); |
cd787a8a MS |
46 | |
47 | dplane_ctx_set_netconf_mpls(ctx, mpls_on); | |
48 | dplane_ctx_set_netconf_mcast(ctx, mcast_on); | |
3689905d | 49 | dplane_ctx_set_netconf_linkdown(ctx, linkdown_on); |
cd787a8a MS |
50 | |
51 | /* Enqueue ctx for main pthread to process */ | |
52 | dplane_provider_enqueue_to_zebra(ctx); | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | /* | |
58 | * Parse and process an incoming netlink netconf update. | |
59 | */ | |
ebb61fca DS |
60 | int netlink_netconf_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) |
61 | { | |
62 | struct netconfmsg *ncm; | |
63 | struct rtattr *tb[NETCONFA_MAX + 1] = {}; | |
64 | int len; | |
65 | ifindex_t ifindex; | |
cd787a8a | 66 | uint32_t ival; |
d53dc9bd | 67 | afi_t afi; |
cd787a8a MS |
68 | enum dplane_netconf_status_e mpls_on = DPLANE_NETCONF_STATUS_UNKNOWN; |
69 | enum dplane_netconf_status_e mcast_on = DPLANE_NETCONF_STATUS_UNKNOWN; | |
3689905d DS |
70 | enum dplane_netconf_status_e linkdown_on = |
71 | DPLANE_NETCONF_STATUS_UNKNOWN; | |
ebb61fca DS |
72 | |
73 | if (h->nlmsg_type != RTM_NEWNETCONF && h->nlmsg_type != RTM_DELNETCONF) | |
74 | return 0; | |
75 | ||
76 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct netconfmsg)); | |
77 | if (len < 0) { | |
cd787a8a | 78 | zlog_err("%s: Message received from netlink is of a broken size: %d, min %zu", |
ebb61fca DS |
79 | __func__, h->nlmsg_len, |
80 | (size_t)NLMSG_LENGTH(sizeof(struct netconfmsg))); | |
81 | return -1; | |
82 | } | |
83 | ||
84 | ncm = NLMSG_DATA(h); | |
85 | ||
d53dc9bd DS |
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 | */ | |
fa60f2c2 | 93 | #ifdef AF_MPLS |
d53dc9bd DS |
94 | if (ncm->ncm_family == AF_MPLS) |
95 | afi = AFI_IP; | |
96 | else | |
fa60f2c2 | 97 | #endif /* AF_MPLS */ |
d53dc9bd DS |
98 | afi = family2afi(ncm->ncm_family); |
99 | ||
ebb61fca DS |
100 | netlink_parse_rtattr(tb, NETCONFA_MAX, netconf_rta(ncm), len); |
101 | ||
102 | if (!tb[NETCONFA_IFINDEX]) { | |
cd787a8a | 103 | zlog_err("NETCONF message received from netlink without an ifindex"); |
ebb61fca DS |
104 | return 0; |
105 | } | |
106 | ||
107 | ifindex = *(ifindex_t *)RTA_DATA(tb[NETCONFA_IFINDEX]); | |
108 | ||
ebb61fca | 109 | if (tb[NETCONFA_INPUT]) { |
cd787a8a MS |
110 | ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_INPUT]); |
111 | if (ival != 0) | |
112 | mpls_on = DPLANE_NETCONF_STATUS_ENABLED; | |
113 | else | |
114 | mpls_on = DPLANE_NETCONF_STATUS_DISABLED; | |
ebb61fca DS |
115 | } |
116 | ||
117 | if (tb[NETCONFA_MC_FORWARDING]) { | |
cd787a8a MS |
118 | ival = *(uint32_t *)RTA_DATA(tb[NETCONFA_MC_FORWARDING]); |
119 | if (ival != 0) | |
120 | mcast_on = DPLANE_NETCONF_STATUS_ENABLED; | |
121 | else | |
122 | mcast_on = DPLANE_NETCONF_STATUS_DISABLED; | |
ebb61fca DS |
123 | } |
124 | ||
3689905d DS |
125 | if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) { |
126 | ival = *(uint32_t *)RTA_DATA( | |
127 | tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]); | |
128 | if (ival != 0) | |
129 | linkdown_on = DPLANE_NETCONF_STATUS_ENABLED; | |
130 | else | |
131 | linkdown_on = DPLANE_NETCONF_STATUS_DISABLED; | |
132 | } | |
133 | ||
ebb61fca | 134 | if (IS_ZEBRA_DEBUG_KERNEL) |
3689905d DS |
135 | zlog_debug( |
136 | "%s: interface %u is mpls on: %d multicast on: %d linkdown: %d", | |
137 | __func__, ifindex, mpls_on, mcast_on, linkdown_on); | |
cd787a8a MS |
138 | |
139 | /* Create a dplane context and pass it along for processing */ | |
d53dc9bd | 140 | netlink_netconf_dplane_update(ns_id, afi, ifindex, mpls_on, mcast_on, |
3689905d | 141 | linkdown_on); |
ebb61fca DS |
142 | |
143 | return 0; | |
144 | } | |
145 | ||
cd787a8a MS |
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) | |
ebb61fca | 151 | { |
cd787a8a | 152 | struct nlsock *nls; |
ebb61fca DS |
153 | struct { |
154 | struct nlmsghdr n; | |
155 | struct netconfmsg ncm; | |
156 | char buf[1024]; | |
cd787a8a MS |
157 | } req = {}; |
158 | ||
159 | nls = kernel_netlink_nlsock_lookup(sockfd); | |
160 | ||
161 | if (IS_ZEBRA_DEBUG_KERNEL) | |
162 | zlog_debug("%s: nlsock %s", __func__, nls ? nls->name : "NULL"); | |
163 | ||
164 | if (nls == NULL) | |
165 | return -1; | |
ebb61fca | 166 | |
ebb61fca DS |
167 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct netconfmsg)); |
168 | req.n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; | |
169 | req.n.nlmsg_type = RTM_GETNETCONF; | |
170 | req.ncm.ncm_family = AF_UNSPEC; | |
171 | ||
cd787a8a | 172 | return netlink_request(nls, &req); |
ebb61fca DS |
173 | } |
174 | ||
39ffa8e8 DS |
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 | { | |
184 | const char *ifname = dplane_ctx_get_ifname(ctx); | |
185 | enum dplane_netconf_status_e mpls_on = dplane_ctx_get_netconf_mpls(ctx); | |
186 | char set[64]; | |
187 | char mpls_proc[PATH_MAX]; | |
188 | int fd, ret = FRR_NETLINK_ERROR; | |
189 | ||
190 | snprintf(mpls_proc, sizeof(mpls_proc), | |
191 | "/proc/sys/net/mpls/conf/%s/input", ifname); | |
192 | ||
193 | if (mpls_on == DPLANE_NETCONF_STATUS_ENABLED) | |
194 | snprintf(set, sizeof(set), "1\n"); | |
195 | else if (mpls_on == DPLANE_NETCONF_STATUS_DISABLED) | |
196 | snprintf(set, sizeof(set), "0\n"); | |
197 | else { | |
198 | flog_err_sys( | |
199 | EC_LIB_DEVELOPMENT, | |
200 | "%s: Expected interface %s to be set to ENABLED or DISABLED was %d", | |
201 | __func__, ifname, mpls_on); | |
202 | return ret; | |
203 | } | |
204 | ||
205 | frr_with_privs (&zserv_privs) { | |
206 | fd = open(mpls_proc, O_WRONLY); | |
207 | if (fd < 0) { | |
208 | flog_err_sys( | |
209 | EC_LIB_SOCKET, | |
210 | "%s: Unable to open %s for writing: %s(%d)", | |
211 | __func__, mpls_proc, safe_strerror(errno), | |
212 | errno); | |
213 | return ret; | |
214 | } | |
215 | if (write(fd, set, 2) == 2) | |
216 | ret = FRR_NETLINK_SUCCESS; | |
217 | else | |
218 | flog_err_sys(EC_LIB_SOCKET, | |
219 | "%s: Unsuccessful write to %s: %s(%d)", | |
220 | __func__, mpls_proc, safe_strerror(errno), | |
221 | errno); | |
222 | close(fd); | |
223 | } | |
224 | return ret; | |
225 | } | |
226 | ||
ebb61fca | 227 | #endif /* HAVE_NETLINK */ |