]> git.proxmox.com Git - mirror_frr.git/blob - zebra/rule_netlink.c
Merge pull request #7635 from AnuradhaKaruppiah/ead-evi-knobs
[mirror_frr.git] / zebra / rule_netlink.c
1 /*
2 * Zebra Policy Based Routing (PBR) interaction with the kernel using
3 * netlink.
4 * Copyright (C) 2018 Cumulus Networks, Inc.
5 *
6 * This file is part of FRR.
7 *
8 * FRR is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * FRR is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with FRR; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24 #include <zebra.h>
25
26 #ifdef HAVE_NETLINK
27
28 #include "if.h"
29 #include "prefix.h"
30 #include "vrf.h"
31
32 #include <linux/fib_rules.h>
33 #include "zebra/zserv.h"
34 #include "zebra/zebra_ns.h"
35 #include "zebra/zebra_vrf.h"
36 #include "zebra/rt.h"
37 #include "zebra/interface.h"
38 #include "zebra/debug.h"
39 #include "zebra/rtadv.h"
40 #include "zebra/kernel_netlink.h"
41 #include "zebra/rule_netlink.h"
42 #include "zebra/zebra_pbr.h"
43 #include "zebra/zebra_errors.h"
44 #include "zebra/zebra_dplane.h"
45
46 /* definitions */
47
48 /* static function declarations */
49
50 /* Private functions */
51
52
53 /*
54 * netlink_rule_msg_encode
55 *
56 * Encodes netlink RTM_ADDRULE/RTM_DELRULE message to buffer buf of size buflen.
57 *
58 * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
59 * or the number of bytes written to buf.
60 */
61 static ssize_t
62 netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
63 uint32_t filter_bm, uint32_t priority, uint32_t table,
64 const struct prefix *src_ip,
65 const struct prefix *dst_ip, uint32_t fwmark,
66 uint8_t dsfield, void *buf, size_t buflen)
67 {
68 uint8_t protocol = RTPROT_ZEBRA;
69 int family;
70 int bytelen;
71 struct {
72 struct nlmsghdr n;
73 struct fib_rule_hdr frh;
74 char buf[];
75 } *req = buf;
76
77 const char *ifname = dplane_ctx_rule_get_ifname(ctx);
78
79 if (buflen < sizeof(*req))
80 return 0;
81 memset(req, 0, sizeof(*req));
82
83 /* Assume ipv4 if no src/dst set, we only support ipv4/ipv6 */
84 if (PREFIX_FAMILY(src_ip))
85 family = PREFIX_FAMILY(src_ip);
86 else if (PREFIX_FAMILY(dst_ip))
87 family = PREFIX_FAMILY(dst_ip);
88 else
89 family = AF_INET;
90
91 bytelen = (family == AF_INET ? 4 : 16);
92
93 req->n.nlmsg_type = cmd;
94 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
95 req->n.nlmsg_flags = NLM_F_REQUEST;
96
97 req->frh.family = family;
98 req->frh.action = FR_ACT_TO_TBL;
99
100 if (!nl_attr_put(&req->n, buflen, FRA_PROTOCOL, &protocol,
101 sizeof(protocol)))
102 return 0;
103
104 /* rule's pref # */
105 if (!nl_attr_put32(&req->n, buflen, FRA_PRIORITY, priority))
106 return 0;
107
108 /* interface on which applied */
109 if (!nl_attr_put(&req->n, buflen, FRA_IFNAME, ifname,
110 strlen(ifname) + 1))
111 return 0;
112
113 /* source IP, if specified */
114 if (filter_bm & PBR_FILTER_SRC_IP) {
115 req->frh.src_len = src_ip->prefixlen;
116 if (!nl_attr_put(&req->n, buflen, FRA_SRC, &src_ip->u.prefix,
117 bytelen))
118 return 0;
119 }
120
121 /* destination IP, if specified */
122 if (filter_bm & PBR_FILTER_DST_IP) {
123 req->frh.dst_len = dst_ip->prefixlen;
124 if (!nl_attr_put(&req->n, buflen, FRA_DST, &dst_ip->u.prefix,
125 bytelen))
126 return 0;
127 }
128
129 /* fwmark, if specified */
130 if (filter_bm & PBR_FILTER_FWMARK) {
131 if (!nl_attr_put32(&req->n, buflen, FRA_FWMARK, fwmark))
132 return 0;
133 }
134
135 /* dsfield, if specified */
136 if (filter_bm & PBR_FILTER_DSFIELD)
137 req->frh.tos = dsfield;
138
139 /* Route table to use to forward, if filter criteria matches. */
140 if (table < 256)
141 req->frh.table = table;
142 else {
143 req->frh.table = RT_TABLE_UNSPEC;
144 if (!nl_attr_put32(&req->n, buflen, FRA_TABLE, table))
145 return 0;
146 }
147
148 if (IS_ZEBRA_DEBUG_KERNEL)
149 zlog_debug(
150 "Tx %s family %s IF %s Pref %u Fwmark %u Src %pFX Dst %pFX Table %u",
151 nl_msg_type_to_str(cmd), nl_family_to_str(family),
152 ifname, priority, fwmark, src_ip, dst_ip, table);
153
154 return NLMSG_ALIGN(req->n.nlmsg_len);
155 }
156
157 static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
158 size_t buflen)
159 {
160 int cmd = RTM_NEWRULE;
161
162 if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE)
163 cmd = RTM_DELRULE;
164
165 return netlink_rule_msg_encode(
166 cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
167 dplane_ctx_rule_get_priority(ctx),
168 dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
169 dplane_ctx_rule_get_dst_ip(ctx),
170 dplane_ctx_rule_get_fwmark(ctx),
171 dplane_ctx_rule_get_dsfield(ctx), buf, buflen);
172 }
173
174 static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
175 void *buf, size_t buflen)
176 {
177 return netlink_rule_msg_encode(
178 RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx),
179 dplane_ctx_rule_get_old_priority(ctx),
180 dplane_ctx_rule_get_old_table(ctx),
181 dplane_ctx_rule_get_old_src_ip(ctx),
182 dplane_ctx_rule_get_old_dst_ip(ctx),
183 dplane_ctx_rule_get_old_fwmark(ctx),
184 dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen);
185 }
186
187 /* Public functions */
188
189 enum netlink_msg_status
190 netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
191 {
192 enum dplane_op_e op;
193 enum netlink_msg_status ret;
194
195 op = dplane_ctx_get_op(ctx);
196 if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE
197 || op == DPLANE_OP_RULE_DELETE)) {
198 flog_err(
199 EC_ZEBRA_PBR_RULE_UPDATE,
200 "Context received for kernel rule update with incorrect OP code (%u)",
201 op);
202 return FRR_NETLINK_ERROR;
203 }
204
205 ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false);
206
207 /**
208 * Delete the old one.
209 *
210 * Don't care about this result right?
211 */
212 if (op == DPLANE_OP_RULE_UPDATE)
213 netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder,
214 true);
215
216 return ret;
217 }
218
219 /*
220 * Handle netlink notification informing a rule add or delete.
221 * Handling of an ADD is TBD.
222 * DELs are notified up, if other attributes indicate it may be a
223 * notification of interest. The expectation is that if this corresponds
224 * to a PBR rule added by FRR, it will be readded.
225 *
226 * If startup and we see a rule we created, delete it as its leftover
227 * from a previous instance and should have been removed on shutdown.
228 *
229 */
230 int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
231 {
232 struct zebra_ns *zns;
233 struct fib_rule_hdr *frh;
234 struct rtattr *tb[FRA_MAX + 1];
235 int len;
236 char *ifname;
237 struct zebra_pbr_rule rule = {};
238 uint8_t proto = 0;
239
240 /* Basic validation followed by extracting attributes. */
241 if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE)
242 return 0;
243
244 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
245 if (len < 0) {
246 zlog_err(
247 "%s: Message received from netlink is of a broken size: %d %zu",
248 __func__, h->nlmsg_len,
249 (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr)));
250 return -1;
251 }
252
253 frh = NLMSG_DATA(h);
254
255 if (frh->family != AF_INET && frh->family != AF_INET6) {
256 if (frh->family == RTNL_FAMILY_IPMR
257 || frh->family == RTNL_FAMILY_IP6MR) {
258 if (IS_ZEBRA_DEBUG_KERNEL)
259 zlog_debug(
260 "Received rule netlink that we are ignoring for family %u, rule change: %u",
261 frh->family, h->nlmsg_type);
262 return 0;
263 }
264 flog_warn(
265 EC_ZEBRA_NETLINK_INVALID_AF,
266 "Invalid address family: %u received from kernel rule change: %u",
267 frh->family, h->nlmsg_type);
268 return 0;
269 }
270 if (frh->action != FR_ACT_TO_TBL)
271 return 0;
272
273 memset(tb, 0, sizeof(tb));
274 netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len);
275
276 if (tb[FRA_PRIORITY])
277 rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
278
279 if (tb[FRA_SRC]) {
280 if (frh->family == AF_INET)
281 memcpy(&rule.rule.filter.src_ip.u.prefix4,
282 RTA_DATA(tb[FRA_SRC]), 4);
283 else
284 memcpy(&rule.rule.filter.src_ip.u.prefix6,
285 RTA_DATA(tb[FRA_SRC]), 16);
286 rule.rule.filter.src_ip.prefixlen = frh->src_len;
287 rule.rule.filter.src_ip.family = frh->family;
288 rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
289 }
290
291 if (tb[FRA_DST]) {
292 if (frh->family == AF_INET)
293 memcpy(&rule.rule.filter.dst_ip.u.prefix4,
294 RTA_DATA(tb[FRA_DST]), 4);
295 else
296 memcpy(&rule.rule.filter.dst_ip.u.prefix6,
297 RTA_DATA(tb[FRA_DST]), 16);
298 rule.rule.filter.dst_ip.prefixlen = frh->dst_len;
299 rule.rule.filter.dst_ip.family = frh->family;
300 rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
301 }
302
303 if (tb[FRA_TABLE])
304 rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]);
305 else
306 rule.rule.action.table = frh->table;
307
308 /* TBD: We don't care about rules not specifying an IIF. */
309 if (tb[FRA_IFNAME] == NULL)
310 return 0;
311
312 if (tb[FRA_PROTOCOL])
313 proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]);
314
315 ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
316 strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
317
318 if (h->nlmsg_type == RTM_NEWRULE) {
319 /*
320 * If we see a rule at startup we created, delete it now.
321 * It should have been flushed on a previous shutdown.
322 */
323 if (startup && proto == RTPROT_ZEBRA) {
324 enum zebra_dplane_result ret;
325
326 ret = dplane_pbr_rule_delete(&rule);
327
328 zlog_debug(
329 "%s: %s leftover rule: family %s IF %s Pref %u Src %pFX Dst %pFX Table %u",
330 __func__,
331 ((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
332 ? "Failed to remove"
333 : "Removed"),
334 nl_family_to_str(frh->family), rule.ifname,
335 rule.rule.priority, &rule.rule.filter.src_ip,
336 &rule.rule.filter.dst_ip,
337 rule.rule.action.table);
338 }
339
340 /* TBD */
341 return 0;
342 }
343
344 zns = zebra_ns_lookup(ns_id);
345
346 /* If we don't know the interface, we don't care. */
347 if (!if_lookup_by_name_per_ns(zns, ifname))
348 return 0;
349
350 if (IS_ZEBRA_DEBUG_KERNEL)
351 zlog_debug(
352 "Rx %s family %s IF %s Pref %u Src %pFX Dst %pFX Table %u",
353 nl_msg_type_to_str(h->nlmsg_type),
354 nl_family_to_str(frh->family), rule.ifname,
355 rule.rule.priority, &rule.rule.filter.src_ip,
356 &rule.rule.filter.dst_ip, rule.rule.action.table);
357
358 return kernel_pbr_rule_del(&rule);
359 }
360
361 /*
362 * Request rules from the kernel
363 */
364 static int netlink_request_rules(struct zebra_ns *zns, int family, int type)
365 {
366 struct {
367 struct nlmsghdr n;
368 struct fib_rule_hdr frh;
369 char buf[NL_PKT_BUF_SIZE];
370 } req;
371
372 memset(&req, 0, sizeof(req));
373 req.n.nlmsg_type = type;
374 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
375 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
376 req.frh.family = family;
377
378 return netlink_request(&zns->netlink_cmd, &req);
379 }
380
381 /*
382 * Get to know existing PBR rules in the kernel - typically called at startup.
383 */
384 int netlink_rules_read(struct zebra_ns *zns)
385 {
386 int ret;
387 struct zebra_dplane_info dp_info;
388
389 zebra_dplane_info_from_zns(&dp_info, zns, true);
390
391 ret = netlink_request_rules(zns, AF_INET, RTM_GETRULE);
392 if (ret < 0)
393 return ret;
394
395 ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
396 &dp_info, 0, 1);
397 if (ret < 0)
398 return ret;
399
400 ret = netlink_request_rules(zns, AF_INET6, RTM_GETRULE);
401 if (ret < 0)
402 return ret;
403
404 ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
405 &dp_info, 0, 1);
406 return ret;
407 }
408
409 #endif /* HAVE_NETLINK */