xref: /freebsd/sys/netlink/route/rt.c (revision 675382f12b67e8b7b2f35bd1f8dfd96b8d8e4aae)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021 Ng Peng Nam Sean
5  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 #include <sys/types.h>
33 #include <sys/malloc.h>
34 #include <sys/rmlock.h>
35 #include <sys/socket.h>
36 
37 #include <net/if.h>
38 #include <net/route.h>
39 #include <net/route/nhop.h>
40 #include <net/route/route_ctl.h>
41 #include <net/route/route_var.h>
42 #include <netinet6/scope6_var.h>
43 #include <netlink/netlink.h>
44 #include <netlink/netlink_ctl.h>
45 #include <netlink/netlink_route.h>
46 #include <netlink/route/route_var.h>
47 
48 #define	DEBUG_MOD_NAME	nl_route
49 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
50 #include <netlink/netlink_debug.h>
51 _DECLARE_DEBUG(LOG_INFO);
52 
53 static unsigned char
54 get_rtm_type(const struct nhop_object *nh)
55 {
56 	int nh_flags = nh->nh_flags;
57 
58 	/* Use the fact that nhg runtime flags are only NHF_MULTIPATH */
59 	if (nh_flags & NHF_BLACKHOLE)
60 		return (RTN_BLACKHOLE);
61 	else if (nh_flags & NHF_REJECT)
62 		return (RTN_PROHIBIT);
63 	return (RTN_UNICAST);
64 }
65 
66 static uint8_t
67 nl_get_rtm_protocol(const struct nhop_object *nh)
68 {
69 	const struct nhgrp_object *nhg = (const struct nhgrp_object *)nh;
70 	int rt_flags;
71 	uint8_t origin;
72 
73 	if (NH_IS_NHGRP(nh)) {
74 		origin = nhgrp_get_origin(nhg);
75 		if (origin != RTPROT_UNSPEC)
76 			return (origin);
77 		nh = nhg->nhops[0];
78 	}
79 	origin = nhop_get_origin(nh);
80 	if (origin != RTPROT_UNSPEC)
81 		return (origin);
82 	/* TODO: remove guesswork once all kernel users fill in origin */
83 	rt_flags = nhop_get_rtflags(nh);
84 	if (rt_flags & RTF_PROTO1)
85 		return (RTPROT_ZEBRA);
86 	if (rt_flags & RTF_STATIC)
87 		return (RTPROT_STATIC);
88 	return (RTPROT_KERNEL);
89 }
90 
91 static int
92 get_rtmsg_type_from_rtsock(int cmd)
93 {
94 	switch (cmd) {
95 	case RTM_ADD:
96 	case RTM_CHANGE:
97 	case RTM_GET:
98 		return NL_RTM_NEWROUTE;
99 	case RTM_DELETE:
100 		return NL_RTM_DELROUTE;
101 	}
102 
103 	return (0);
104 }
105 
106 /*
107  * fibnum heuristics
108  *
109  * if (dump && rtm_table == 0 && !rta_table) RT_ALL_FIBS
110  * msg                rtm_table     RTA_TABLE            result
111  * RTM_GETROUTE/dump          0             -       RT_ALL_FIBS
112  * RTM_GETROUTE/dump          1             -                 1
113  * RTM_GETROUTE/get           0             -                 0
114  *
115  */
116 
117 static struct nhop_object *
118 rc_get_nhop(const struct rib_cmd_info *rc)
119 {
120 	return ((rc->rc_cmd == RTM_DELETE) ? rc->rc_nh_old : rc->rc_nh_new);
121 }
122 
123 static void
124 dump_rc_nhop_gw(struct nl_writer *nw, const struct nhop_object *nh)
125 {
126 #ifdef INET6
127 	int upper_family;
128 #endif
129 
130 	switch (nhop_get_neigh_family(nh)) {
131 	case AF_LINK:
132 		/* onlink prefix, skip */
133 		break;
134 	case AF_INET:
135 		nlattr_add(nw, NL_RTA_GATEWAY, 4, &nh->gw4_sa.sin_addr);
136 		break;
137 #ifdef INET6
138 	case AF_INET6:
139 		upper_family = nhop_get_upper_family(nh);
140 		if (upper_family == AF_INET6) {
141 			struct in6_addr gw6 = nh->gw6_sa.sin6_addr;
142 			in6_clearscope(&gw6);
143 
144 			nlattr_add(nw, NL_RTA_GATEWAY, 16, &gw6);
145 		} else if (upper_family == AF_INET) {
146 			/* IPv4 over IPv6 */
147 			struct in6_addr gw6 = nh->gw6_sa.sin6_addr;
148 			in6_clearscope(&gw6);
149 
150 			char buf[20];
151 			struct rtvia *via = (struct rtvia *)&buf[0];
152 			via->rtvia_family = AF_INET6;
153 			memcpy(via->rtvia_addr, &gw6, 16);
154 			nlattr_add(nw, NL_RTA_VIA, 17, via);
155 		}
156 		break;
157 #endif
158 	}
159 }
160 
161 static void
162 dump_rc_nhop_mtu(struct nl_writer *nw, const struct nhop_object *nh)
163 {
164 	int nla_len = sizeof(struct nlattr) * 2 + sizeof(uint32_t);
165 	struct nlattr *nla = nlmsg_reserve_data(nw, nla_len, struct nlattr);
166 
167 	if (nla == NULL)
168 		return;
169 	nla->nla_type = NL_RTA_METRICS;
170 	nla->nla_len = nla_len;
171 	nla++;
172 	nla->nla_type = NL_RTAX_MTU;
173 	nla->nla_len = sizeof(struct nlattr) + sizeof(uint32_t);
174 	*((uint32_t *)(nla + 1)) = nh->nh_mtu;
175 }
176 
177 static void
178 dump_rc_nhg(struct nl_writer *nw, const struct route_nhop_data *rnd, struct rtmsg *rtm)
179 {
180 	const struct nhgrp_object *nhg = rnd->rnd_nhgrp;
181 	const struct weightened_nhop *wn;
182 	struct nhop_object *nh;
183 	uint32_t uidx, num_nhops, nh_expire;
184 	uint32_t base_rtflags, rtflags, nhop_weight, nhop_metric;
185 
186 	MPASS((NH_IS_NHGRP(rnd->rnd_nhop)));
187 
188 	/* select a nhop from nhgrp to not confuse non-mpath consumers */
189 	nhop_weight = RT_DEFAULT_WEIGHT;
190 	nhop_metric = RT_DEFAULT_METRIC;
191 	nh = nhop_select_func(rnd->rnd_nhop, 0);
192 	rtflags = nhop_get_rtflags(nh);
193 	if (nh->nh_flags & NHF_GATEWAY)
194 		dump_rc_nhop_gw(nw, nh);
195 
196 	wn = nhgrp_get_nhops(nhg, &num_nhops);
197 	base_rtflags = nhop_get_rtflags(wn[0].nh);
198 	uidx = nhgrp_get_uidx(nhg);
199 	if (uidx != 0)
200 		nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
201 	nlattr_add_u32(nw, NL_RTA_KNH_ID, nhgrp_get_idx(nhg));
202 	nlattr_add_u32(nw, NL_RTA_RTFLAGS, base_rtflags);
203 
204 	if (rtflags & RTF_FIXEDMTU)
205 		dump_rc_nhop_mtu(nw, nh);
206 	/* In any case, fill outgoing interface */
207 	nlattr_add_u32(nw, NL_RTA_OIF, if_getindex(nh->nh_ifp));
208 
209 	int off = nlattr_add_nested(nw, NL_RTA_MULTIPATH);
210 	if (off == 0)
211 		return;
212 
213 	for (int i = 0; i < num_nhops; i++) {
214 		int nh_off = nlattr_save_offset(nw);
215 		struct rtnexthop *rtnh = nlmsg_reserve_object(nw, struct rtnexthop);
216 		if (rtnh == NULL)
217 			return;
218 		rtnh->rtnh_flags = 0;
219 		rtnh->rtnh_ifindex = if_getindex(wn[i].nh->nh_ifp);
220 		rtnh->rtnh_hops = wn[i].weight;
221 		dump_rc_nhop_gw(nw, wn[i].nh);
222 		uint32_t rtflags = nhop_get_rtflags(wn[i].nh);
223 		if (rtflags != base_rtflags)
224 			nlattr_add_u32(nw, NL_RTA_RTFLAGS, rtflags);
225 		if (rtflags & RTF_FIXEDMTU)
226 			dump_rc_nhop_mtu(nw, wn[i].nh);
227 		nlattr_add_u32(nw, NL_RTA_PRIORITY, nhop_get_metric(wn[i].nh));
228 		nh_expire = nhop_get_expire(wn[i].nh);
229 		if (nh_expire > 0)
230 			nlattr_add_u32(nw, NL_RTA_EXPIRES, nh_expire - time_uptime);
231 		rtnh = nlattr_restore_offset(nw, nh_off, struct rtnexthop);
232 
233 		if (nh == wn[i].nh) {
234 			nhop_weight = wn[i].weight;
235 			nhop_metric = nhop_get_metric(wn[i].nh);
236 		}
237 		/*
238 		 * nlattr_add() allocates 4-byte aligned storage, no need to aligh
239 		 * length here
240 		 * */
241 		rtnh->rtnh_len = nlattr_save_offset(nw) - nh_off;
242 	}
243 	nlattr_set_len(nw, off);
244 	nlattr_add_u32(nw, NL_RTA_PRIORITY, nhop_metric);
245 	nlattr_add_u32(nw, NL_RTA_WEIGHT, nhop_weight);
246 }
247 
248 static void
249 dump_rc_nhop(struct nl_writer *nw, const struct route_nhop_data *rnd, struct rtmsg *rtm)
250 {
251 	const struct nhop_object *nh = rnd->rnd_nhop;
252 	uint32_t rtflags, uidx, nh_expire;
253 
254 	if (NH_IS_NHGRP(rnd->rnd_nhop)) {
255 		dump_rc_nhg(nw, rnd, rtm);
256 		return;
257 	}
258 
259 	rtflags = nhop_get_rtflags(nh);
260 	/*
261 	 * IPv4 over IPv6
262 	 *    ('RTA_VIA', {'family': 10, 'addr': 'fe80::20c:29ff:fe67:2dd'}), ('RTA_OIF', 2),
263 	 * IPv4 w/ gw
264 	 *    ('RTA_GATEWAY', '172.16.107.131'), ('RTA_OIF', 2)],
265 	 * Direct route:
266 	 *    ('RTA_OIF', 2)
267 	 */
268 	if (nh->nh_flags & NHF_GATEWAY)
269 		dump_rc_nhop_gw(nw, nh);
270 
271 	uidx = nhop_get_uidx(nh);
272 	if (uidx != 0)
273 		nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
274 	nlattr_add_u32(nw, NL_RTA_KNH_ID, nhop_get_idx(nh));
275 	nlattr_add_u32(nw, NL_RTA_RTFLAGS, rtflags);
276 
277 	if (rtflags & RTF_FIXEDMTU)
278 		dump_rc_nhop_mtu(nw, nh);
279 	nh_expire = nhop_get_expire(nh);
280 	if (nh_expire > 0)
281 		nlattr_add_u32(nw, NL_RTA_EXPIRES, nh_expire - time_uptime);
282 
283 	/* In any case, fill outgoing interface */
284 	nlattr_add_u32(nw, NL_RTA_OIF, if_getindex(nh->nh_ifp));
285 
286 	nlattr_add_u32(nw, NL_RTA_PRIORITY, nhop_get_metric(nh));
287 	if (rnd->rnd_weight != RT_DEFAULT_WEIGHT)
288 		nlattr_add_u32(nw, NL_RTA_WEIGHT, rnd->rnd_weight);
289 }
290 
291 /*
292  * Dumps output from a rib command into an rtmsg
293  */
294 
295 static int
296 dump_px(uint32_t fibnum, const struct nlmsghdr *hdr,
297     const struct rtentry *rt, struct route_nhop_data *rnd,
298     struct nl_writer *nw)
299 {
300 	struct rtmsg *rtm;
301 	int error = 0;
302 
303 	NET_EPOCH_ASSERT();
304 
305 	if (!nlmsg_reply(nw, hdr, sizeof(struct rtmsg)))
306 		goto enomem;
307 
308 	int family = rt_get_family(rt);
309 	int rtm_off = nlattr_save_offset(nw);
310 	rtm = nlmsg_reserve_object(nw, struct rtmsg);
311 	rtm->rtm_family = family;
312 	rtm->rtm_dst_len = 0;
313 	rtm->rtm_src_len = 0;
314 	rtm->rtm_tos = 0;
315 	if (fibnum < 255)
316 		rtm->rtm_table = (unsigned char)fibnum;
317 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
318 	rtm->rtm_protocol = nl_get_rtm_protocol(rnd->rnd_nhop);
319 	rtm->rtm_type = get_rtm_type(rnd->rnd_nhop);
320 
321 	nlattr_add_u32(nw, NL_RTA_TABLE, fibnum);
322 
323 	int plen = 0;
324 #if defined(INET) || defined(INET6)
325 	uint32_t scopeid;
326 #endif
327 	switch (family) {
328 #ifdef INET
329 	case AF_INET:
330 		{
331 			struct in_addr addr;
332 			rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
333 			nlattr_add(nw, NL_RTA_DST, 4, &addr);
334 			break;
335 		}
336 #endif
337 #ifdef INET6
338 	case AF_INET6:
339 		{
340 			struct in6_addr addr;
341 			rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
342 			nlattr_add(nw, NL_RTA_DST, 16, &addr);
343 			break;
344 		}
345 #endif
346 	default:
347 		FIB_LOG(LOG_NOTICE, fibnum, family, "unsupported rt family: %d", family);
348 		error = EAFNOSUPPORT;
349 		goto flush;
350 	}
351 
352 	rtm = nlattr_restore_offset(nw, rtm_off, struct rtmsg);
353 	if (plen > 0)
354 		rtm->rtm_dst_len = plen;
355 	if (NH_IS_NHGRP(rnd->rnd_nhop))
356 		dump_rc_nhg(nw, rnd, rtm);
357 	else
358 		dump_rc_nhop(nw, rnd, rtm);
359 
360 	if (nlmsg_end(nw))
361 		return (0);
362 enomem:
363 	error = ENOMEM;
364 flush:
365 	nlmsg_abort(nw);
366 	return (error);
367 }
368 
369 static int
370 family_to_group(int family)
371 {
372 	switch (family) {
373 	case AF_INET:
374 		return (RTNLGRP_IPV4_ROUTE);
375 	case AF_INET6:
376 		return (RTNLGRP_IPV6_ROUTE);
377 	}
378 	return (0);
379 }
380 
381 static void
382 report_operation(uint32_t fibnum, struct rib_cmd_info *rc,
383     struct nlpcb *nlp, struct nlmsghdr *hdr)
384 {
385 	struct nl_writer nw;
386 	uint32_t group_id = family_to_group(rt_get_family(rc->rc_rt));
387 
388 	if (nl_writer_group(&nw, NLMSG_SMALL, NETLINK_ROUTE, group_id, 0,
389 	    false)) {
390 		struct route_nhop_data rnd = {
391 			.rnd_nhop = rc_get_nhop(rc),
392 			.rnd_weight = rc->rc_nh_weight,
393 		};
394 		hdr->nlmsg_flags &= ~(NLM_F_REPLACE | NLM_F_CREATE);
395 		hdr->nlmsg_flags &= ~(NLM_F_EXCL | NLM_F_APPEND);
396 		switch (rc->rc_cmd) {
397 		case RTM_ADD:
398 			hdr->nlmsg_type = NL_RTM_NEWROUTE;
399 			hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
400 			break;
401 		case RTM_CHANGE:
402 			hdr->nlmsg_type = NL_RTM_NEWROUTE;
403 			hdr->nlmsg_flags |= NLM_F_REPLACE;
404 			break;
405 		case RTM_DELETE:
406 			hdr->nlmsg_type = NL_RTM_DELROUTE;
407 			break;
408 		}
409 		dump_px(fibnum, hdr, rc->rc_rt, &rnd, &nw);
410 		nlmsg_flush(&nw);
411 	}
412 
413 	rtsock_callback_p->route_f(fibnum, rc);
414 }
415 
416 static void
417 set_scope6(struct sockaddr *sa, struct ifnet *ifp)
418 {
419 #ifdef INET6
420 	if (sa != NULL && sa->sa_family == AF_INET6 && ifp != NULL) {
421 		struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
422 
423 		if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
424 			in6_set_unicast_scopeid(&sa6->sin6_addr, if_getindex(ifp));
425 	}
426 #endif
427 }
428 
429 struct rta_mpath_nh {
430 	struct sockaddr	*gw;
431 	struct ifnet	*ifp;
432 	uint8_t		rtnh_flags;
433 	uint8_t		rtnh_weight;
434 };
435 
436 #define	_IN(_field)	offsetof(struct rtnexthop, _field)
437 #define	_OUT(_field)	offsetof(struct rta_mpath_nh, _field)
438 const static struct nlattr_parser nla_p_rtnh[] = {
439 	{ .type = NL_RTA_GATEWAY, .off = _OUT(gw), .cb = nlattr_get_ip },
440 	{ .type = NL_RTA_VIA, .off = _OUT(gw), .cb = nlattr_get_ipvia },
441 };
442 const static struct nlfield_parser nlf_p_rtnh[] = {
443 	{ .off_in = _IN(rtnh_flags), .off_out = _OUT(rtnh_flags), .cb = nlf_get_u8 },
444 	{ .off_in = _IN(rtnh_hops), .off_out = _OUT(rtnh_weight), .cb = nlf_get_u8 },
445 	{ .off_in = _IN(rtnh_ifindex), .off_out = _OUT(ifp), .cb = nlf_get_ifpz },
446 };
447 #undef _IN
448 #undef _OUT
449 
450 static bool
451 post_p_rtnh(void *_attrs, struct nl_pstate *npt __unused)
452 {
453 	struct rta_mpath_nh *attrs = (struct rta_mpath_nh *)_attrs;
454 
455 	set_scope6(attrs->gw, attrs->ifp);
456 	return (true);
457 }
458 NL_DECLARE_PARSER_EXT(mpath_parser, struct rtnexthop, NULL, nlf_p_rtnh, nla_p_rtnh, post_p_rtnh);
459 
460 struct rta_mpath {
461 	u_int num_nhops;
462 	struct rta_mpath_nh nhops[0];
463 };
464 
465 static int
466 nlattr_get_multipath(struct nlattr *nla, struct nl_pstate *npt,
467     const void *arg, void *target)
468 {
469 	struct rta_mpath *mp;
470 	struct rtnexthop *rtnh;
471 	uint16_t data_len, len;
472 	u_int max_nhops;
473 	int error;
474 
475 	data_len = nla->nla_len - sizeof(struct nlattr);
476 	max_nhops = data_len / sizeof(struct rtnexthop);
477 
478 	mp = npt_alloc(npt, (max_nhops + 2) * sizeof(struct rta_mpath_nh));
479 	if (mp == NULL) {
480 		NLMSG_REPORT_ERR_MSG(npt, "%s: too many RTA_MULTIPATH", __func__);
481 		return (ENOMEM);
482 	}
483 	mp->num_nhops = 0;
484 
485 	for (rtnh = (struct rtnexthop *)(nla + 1); data_len > 0; ) {
486 		struct rta_mpath_nh *mpnh;
487 
488 		len = NL_ITEM_ALIGN(rtnh->rtnh_len);
489 		if (__predict_false(rtnh->rtnh_len <= sizeof(*rtnh) ||
490 		    len < rtnh->rtnh_len || len > data_len)) {
491 			NLMSG_REPORT_ERR_MSG(npt, "%s: bad length %u",
492 			    __func__, rtnh->rtnh_len);
493 			return (EINVAL);
494 		}
495 		mpnh = &mp->nhops[mp->num_nhops++];
496 		error = nl_parse_header(rtnh, rtnh->rtnh_len, &mpath_parser,
497 		    npt, mpnh);
498 		if (error != 0) {
499 			NLMSG_REPORT_ERR_MSG(npt,
500 			    "RTA_MULTIPATH: nexthop %u: parse failed",
501 			    mp->num_nhops - 1);
502 			return (error);
503 		}
504 		data_len -= len;
505 		rtnh = (struct rtnexthop *)((char *)rtnh + len);
506 	}
507 	if (data_len != 0 || mp->num_nhops == 0) {
508 		NLMSG_REPORT_ERR_MSG(npt, "invalid RTA_MULTIPATH attr");
509 		return (EINVAL);
510 	}
511 
512 	*((struct rta_mpath **)target) = mp;
513 	return (0);
514 }
515 
516 
517 struct nl_parsed_route {
518 	struct sockaddr		*rta_dst;
519 	struct sockaddr		*rta_gw;
520 	struct ifnet		*rta_oif;
521 	struct rta_mpath	*rta_multipath;
522 	uint32_t		rta_table;
523 	uint32_t		rta_rtflags;
524 	uint32_t		rta_nh_id;
525 	uint32_t		rta_metric;
526 	uint32_t		rta_weight;
527 	uint32_t		rta_expire;
528 	uint32_t		rtax_mtu;
529 	uint8_t			rtm_table;
530 	uint8_t			rtm_family;
531 	uint8_t			rtm_dst_len;
532 	uint8_t			rtm_protocol;
533 	uint8_t			rtm_type;
534 	uint32_t		rtm_flags;
535 };
536 
537 #define	_IN(_field)	offsetof(struct rtmsg, _field)
538 #define	_OUT(_field)	offsetof(struct nl_parsed_route, _field)
539 static struct nlattr_parser nla_p_rtmetrics[] = {
540 	{ .type = NL_RTAX_MTU, .off = _OUT(rtax_mtu), .cb = nlattr_get_uint32 },
541 };
542 NL_DECLARE_ATTR_PARSER(metrics_parser, nla_p_rtmetrics);
543 
544 static const struct nlattr_parser nla_p_rtmsg[] = {
545 	{ .type = NL_RTA_DST, .off = _OUT(rta_dst), .cb = nlattr_get_ip },
546 	{ .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = nlattr_get_ifp },
547 	{ .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = nlattr_get_ip },
548 	{ .type = NL_RTA_PRIORITY, .off = _OUT(rta_metric), .cb = nlattr_get_uint32 },
549 	{ .type = NL_RTA_METRICS, .arg = &metrics_parser, .cb = nlattr_get_nested },
550 	{ .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = nlattr_get_multipath },
551 	{ .type = NL_RTA_WEIGHT, .off = _OUT(rta_weight), .cb = nlattr_get_uint32 },
552 	{ .type = NL_RTA_RTFLAGS, .off = _OUT(rta_rtflags), .cb = nlattr_get_uint32 },
553 	{ .type = NL_RTA_TABLE, .off = _OUT(rta_table), .cb = nlattr_get_uint32 },
554 	{ .type = NL_RTA_VIA, .off = _OUT(rta_gw), .cb = nlattr_get_ipvia },
555 	{ .type = NL_RTA_EXPIRES, .off = _OUT(rta_expire), .cb = nlattr_get_uint32 },
556 	{ .type = NL_RTA_NH_ID, .off = _OUT(rta_nh_id), .cb = nlattr_get_uint32 },
557 };
558 
559 static const struct nlfield_parser nlf_p_rtmsg[] = {
560 	{ .off_in = _IN(rtm_family), .off_out = _OUT(rtm_family), .cb = nlf_get_u8 },
561 	{ .off_in = _IN(rtm_dst_len), .off_out = _OUT(rtm_dst_len), .cb = nlf_get_u8 },
562 	{ .off_in = _IN(rtm_protocol), .off_out = _OUT(rtm_protocol), .cb = nlf_get_u8 },
563 	{ .off_in = _IN(rtm_type), .off_out = _OUT(rtm_type), .cb = nlf_get_u8 },
564 	{ .off_in = _IN(rtm_table), .off_out = _OUT(rtm_table), .cb = nlf_get_u8 },
565 	{ .off_in = _IN(rtm_flags), .off_out = _OUT(rtm_flags), .cb = nlf_get_u32 },
566 };
567 #undef _IN
568 #undef _OUT
569 
570 static bool
571 post_p_rtmsg(void *_attrs, struct nl_pstate *npt __unused)
572 {
573 	struct nl_parsed_route *attrs = (struct nl_parsed_route *)_attrs;
574 
575 	set_scope6(attrs->rta_dst, attrs->rta_oif);
576 	set_scope6(attrs->rta_gw, attrs->rta_oif);
577 	return (true);
578 }
579 NL_DECLARE_PARSER_EXT(rtm_parser, struct rtmsg, NULL, nlf_p_rtmsg, nla_p_rtmsg, post_p_rtmsg);
580 
581 struct netlink_walkargs {
582 	struct nl_writer *nw;
583 	struct route_nhop_data rnd;
584 	struct nlmsghdr hdr;
585 	struct nlpcb *nlp;
586 	uint32_t fibnum;
587 	int family;
588 	int error;
589 	int count;
590 	int dumped;
591 	int dumped_tables;
592 };
593 
594 static int
595 dump_rtentry(struct rtentry *rt, void *_arg)
596 {
597 	struct netlink_walkargs *wa = (struct netlink_walkargs *)_arg;
598 	int error;
599 
600 	wa->count++;
601 	if (wa->error != 0)
602 		return (0);
603 	if (!rt_is_exportable(rt, nlp_get_cred(wa->nlp)))
604 		return (0);
605 	wa->dumped++;
606 
607 	rt_get_rnd(rt, &wa->rnd);
608 
609 	error = dump_px(wa->fibnum, &wa->hdr, rt, &wa->rnd, wa->nw);
610 
611 	IF_DEBUG_LEVEL(LOG_DEBUG3) {
612 		char rtbuf[INET6_ADDRSTRLEN + 5];
613 		FIB_LOG(LOG_DEBUG3, wa->fibnum, wa->family,
614 		    "Dump %s, error %d",
615 		    rt_print_buf(rt, rtbuf, sizeof(rtbuf)), error);
616 	}
617 	wa->error = error;
618 
619 	return (0);
620 }
621 
622 static void
623 dump_rtable_one(struct netlink_walkargs *wa, uint32_t fibnum, int family)
624 {
625 	FIB_LOG(LOG_DEBUG2, fibnum, family, "Start dump");
626 	wa->count = 0;
627 	wa->dumped = 0;
628 
629 	rib_walk(fibnum, family, false, dump_rtentry, wa);
630 
631 	wa->dumped_tables++;
632 
633 	FIB_LOG(LOG_DEBUG2, fibnum, family, "End dump, iterated %d dumped %d",
634 	    wa->count, wa->dumped);
635 }
636 
637 static int
638 dump_rtable_fib(struct netlink_walkargs *wa, uint32_t fibnum, int family)
639 {
640 	wa->fibnum = fibnum;
641 
642 	if (family == AF_UNSPEC) {
643 		for (int i = 0; i < AF_MAX; i++) {
644 			if (rt_tables_get_rnh(fibnum, i) != 0) {
645 				wa->family = i;
646 				dump_rtable_one(wa, fibnum, i);
647 				if (wa->error != 0)
648 					break;
649 			}
650 		}
651 	} else {
652 		if (rt_tables_get_rnh(fibnum, family) != 0) {
653 			wa->family = family;
654 			dump_rtable_one(wa, fibnum, family);
655 		}
656 	}
657 
658 	return (wa->error);
659 }
660 
661 static int
662 handle_rtm_getroute(struct nlpcb *nlp, struct nl_parsed_route *attrs,
663     struct nlmsghdr *hdr, struct nl_pstate *npt)
664 {
665 	RIB_RLOCK_TRACKER;
666 	struct rib_head *rnh;
667 	const struct rtentry *rt;
668 	struct route_nhop_data rnd;
669 	uint32_t fibnum = attrs->rta_table;
670 	sa_family_t family = attrs->rtm_family;
671 
672 	if (attrs->rta_dst == NULL) {
673 		NLMSG_REPORT_ERR_MSG(npt, "No RTA_DST supplied");
674 			return (EINVAL);
675 	}
676 
677 	rnh = rt_tables_get_rnh(fibnum, family);
678 	if (rnh == NULL)
679 		return (EAFNOSUPPORT);
680 
681 	RIB_RLOCK(rnh);
682 
683 	struct sockaddr *dst = attrs->rta_dst;
684 
685 	if (attrs->rtm_flags & RTM_F_PREFIX)
686 		rt = rib_lookup_prefix_plen(rnh, dst, attrs->rtm_dst_len, &rnd);
687 	else
688 		rt = (const struct rtentry *)rnh->rnh_matchaddr(dst, &rnh->head);
689 	if (rt == NULL) {
690 		RIB_RUNLOCK(rnh);
691 		return (ESRCH);
692 	}
693 
694 	rt_get_rnd(rt, &rnd);
695 
696 	RIB_RUNLOCK(rnh);
697 
698 	if (!rt_is_exportable(rt, nlp_get_cred(nlp)))
699 		return (ESRCH);
700 
701 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
702 		char rtbuf[NHOP_PRINT_BUFSIZE] __unused, nhbuf[NHOP_PRINT_BUFSIZE] __unused;
703 		FIB_LOG(LOG_DEBUG2, fibnum, family, "getroute completed: got %s for %s",
704 		    nhop_print_buf_any(rnd.rnd_nhop, nhbuf, sizeof(nhbuf)),
705 		    rt_print_buf(rt, rtbuf, sizeof(rtbuf)));
706 	}
707 
708 	hdr->nlmsg_type = NL_RTM_NEWROUTE;
709 	dump_px(fibnum, hdr, rt, &rnd, npt->nw);
710 
711 	return (0);
712 }
713 
714 static int
715 handle_rtm_dump(struct nlpcb *nlp, uint32_t fibnum, int family,
716     struct nlmsghdr *hdr, struct nl_writer *nw)
717 {
718 	struct netlink_walkargs wa = {
719 		.nlp = nlp,
720 		.nw = nw,
721 		.hdr.nlmsg_pid = hdr->nlmsg_pid,
722 		.hdr.nlmsg_seq = hdr->nlmsg_seq,
723 		.hdr.nlmsg_type = NL_RTM_NEWROUTE,
724 		.hdr.nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI,
725 	};
726 
727 	if (fibnum == RT_TABLE_UNSPEC) {
728 		for (int i = 0; i < V_rt_numfibs; i++) {
729 			dump_rtable_fib(&wa, i, family);
730 			if (wa.error != 0)
731 				break;
732 		}
733 	} else
734 		dump_rtable_fib(&wa, fibnum, family);
735 
736 	if (wa.error == 0 && wa.dumped_tables == 0) {
737 		FIB_LOG(LOG_DEBUG, fibnum, family, "incorrect fibnum/family");
738 		wa.error = ESRCH;
739 		// How do we propagate it?
740 	}
741 
742 	if (!nlmsg_end_dump(wa.nw, wa.error, &wa.hdr)) {
743                 NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
744                 return (ENOMEM);
745         }
746 
747 	return (wa.error);
748 }
749 
750 static struct nhop_object *
751 finalize_nhop(struct nhop_object *nh, const struct sockaddr *dst, int *perror)
752 {
753 	/*
754 	 * The following MUST be filled:
755 	 *  nh_ifp, nh_ifa, nh_gw
756 	 */
757 	if (nh->gw_sa.sa_family == 0) {
758 		/*
759 		 * Empty gateway. Can be direct route with RTA_OIF set.
760 		 */
761 		if (nh->nh_ifp != NULL)
762 			nhop_set_direct_gw(nh, nh->nh_ifp);
763 		else {
764 			NL_LOG(LOG_DEBUG, "empty gateway and interface, skipping");
765 			*perror = EINVAL;
766 			return (NULL);
767 		}
768 		/* Both nh_ifp and gateway are set */
769 	} else {
770 		/* Gateway is set up, we can derive ifp if not set */
771 		if (nh->nh_ifp == NULL) {
772 			uint32_t fibnum = nhop_get_fibnum(nh);
773 			uint32_t flags = 0;
774 
775 			if (nh->nh_flags & NHF_GATEWAY)
776 				flags = RTF_GATEWAY;
777 			else if (nh->nh_flags & NHF_HOST)
778 				flags = RTF_HOST;
779 
780 			struct ifaddr *ifa = ifa_ifwithroute(flags, dst, &nh->gw_sa, fibnum);
781 			if (ifa == NULL) {
782 				NL_LOG(LOG_DEBUG, "Unable to determine ifp, skipping");
783 				*perror = EINVAL;
784 				return (NULL);
785 			}
786 			nhop_set_transmit_ifp(nh, ifa->ifa_ifp);
787 		}
788 	}
789 	/* Both nh_ifp and gateway are set */
790 	if (nh->nh_ifa == NULL) {
791 		const struct sockaddr *gw_sa = &nh->gw_sa;
792 
793 		if (gw_sa->sa_family != dst->sa_family) {
794 			/*
795 			 * Use dst as the target for determining the default
796 			 * preferred ifa IF
797 			 * 1) the gateway is link-level (e.g. direct route)
798 			 * 2) the gateway family is different (e.g. IPv4 over IPv6).
799 			 */
800 			gw_sa = dst;
801 		}
802 
803 		struct ifaddr *ifa = ifaof_ifpforaddr(gw_sa, nh->nh_ifp);
804 		if (ifa == NULL) {
805 			/* Try link-level ifa. */
806 			gw_sa = &nh->gw_sa;
807 			ifa = ifaof_ifpforaddr(gw_sa, nh->nh_ifp);
808 			if (ifa == NULL) {
809 				NL_LOG(LOG_DEBUG, "Unable to determine ifa, skipping");
810 				*perror = EINVAL;
811 				return (NULL);
812 			}
813 		}
814 		nhop_set_src(nh, ifa);
815 	}
816 
817 	return (nhop_get_nhop(nh, perror));
818 }
819 
820 static int
821 get_pxflag(const struct nl_parsed_route *attrs)
822 {
823 	int pxflag = 0;
824 	switch (attrs->rtm_family) {
825 	case AF_INET:
826 		if (attrs->rtm_dst_len == 32)
827 			pxflag = NHF_HOST;
828 		else if (attrs->rtm_dst_len == 0)
829 			pxflag = NHF_DEFAULT;
830 		break;
831 	case AF_INET6:
832 		if (attrs->rtm_dst_len == 128)
833 			pxflag = NHF_HOST;
834 		else if (attrs->rtm_dst_len == 0)
835 			pxflag = NHF_DEFAULT;
836 		break;
837 	}
838 
839 	return (pxflag);
840 }
841 
842 static int
843 get_op_flags(int nlm_flags)
844 {
845 	int op_flags = 0;
846 
847 	op_flags |= (nlm_flags & NLM_F_REPLACE) ? RTM_F_REPLACE : 0;
848 	op_flags |= (nlm_flags & NLM_F_EXCL) ? RTM_F_EXCL : 0;
849 	op_flags |= (nlm_flags & NLM_F_CREATE) ? RTM_F_CREATE : 0;
850 	op_flags |= (nlm_flags & NLM_F_APPEND) ? RTM_F_APPEND : 0;
851 
852 	return (op_flags);
853 }
854 
855 static int
856 create_nexthop_one(struct nl_parsed_route *attrs, struct rta_mpath_nh *mpnh,
857     struct nl_pstate *npt, struct nhop_object **pnh)
858 {
859 	int error;
860 
861 	if (mpnh->gw == NULL)
862 		return (EINVAL);
863 
864 	struct nhop_object *nh = nhop_alloc(attrs->rta_table, attrs->rtm_family);
865 	if (nh == NULL)
866 		return (ENOMEM);
867 
868 	error = nl_set_nexthop_gw(nh, mpnh->gw, mpnh->ifp, npt);
869 	if (error != 0) {
870 		nhop_free(nh);
871 		return (error);
872 	}
873 	if (mpnh->ifp != NULL)
874 		nhop_set_transmit_ifp(nh, mpnh->ifp);
875 	nhop_set_pxtype_flag(nh, get_pxflag(attrs));
876 	nhop_set_rtflags(nh, attrs->rta_rtflags);
877 	nhop_set_metric(nh, attrs->rta_metric);
878 	if (attrs->rtm_protocol > RTPROT_STATIC)
879 		nhop_set_origin(nh, attrs->rtm_protocol);
880 
881 	*pnh = finalize_nhop(nh, attrs->rta_dst, &error);
882 
883 	return (error);
884 }
885 
886 static struct nhop_object *
887 create_nexthop_from_attrs(struct nl_parsed_route *attrs,
888     struct nl_pstate *npt, int *perror)
889 {
890 	struct nhop_object *nh = NULL;
891 	int error = 0;
892 	uint32_t nh_expire = 0;
893 
894 	if (attrs->rta_multipath != NULL) {
895 		/* Multipath w/o explicit nexthops */
896 		int num_nhops = attrs->rta_multipath->num_nhops;
897 		struct weightened_nhop *wn = npt_alloc(npt, sizeof(*wn) * num_nhops);
898 
899 		if (wn == NULL) {
900 			*perror = ENOMEM;
901 			return (NULL);
902 		}
903 		for (int i = 0; i < num_nhops; i++) {
904 			struct rta_mpath_nh *mpnh = &attrs->rta_multipath->nhops[i];
905 
906 			error = create_nexthop_one(attrs, mpnh, npt, &wn[i].nh);
907 			if (error != 0) {
908 				for (int j = 0; j < i; j++)
909 					nhop_free(wn[j].nh);
910 				break;
911 			}
912 			wn[i].weight = mpnh->rtnh_weight > 0 ? mpnh->rtnh_weight : 1;
913 		}
914 		if (error == 0) {
915 			struct rib_head *rh = nhop_get_rh(wn[0].nh);
916 			struct nhgrp_object *nhg;
917 
918 			nhg = nhgrp_alloc(rh->rib_fibnum, rh->rib_family,
919 			    wn, num_nhops, perror);
920 			if (nhg != NULL) {
921 				if (attrs->rtm_protocol > RTPROT_STATIC)
922 					nhgrp_set_origin(nhg, attrs->rtm_protocol);
923 				nhg = nhgrp_get_nhgrp(nhg, perror);
924 			}
925 			for (int i = 0; i < num_nhops; i++)
926 				nhop_free(wn[i].nh);
927 			if (nhg != NULL)
928 				return ((struct nhop_object *)nhg);
929 			error = *perror;
930 		}
931 		*perror = error;
932 	} else {
933 		nh = nhop_alloc(attrs->rta_table, attrs->rtm_family);
934 		if (nh == NULL) {
935 			*perror = ENOMEM;
936 			return (NULL);
937 		}
938 		if (attrs->rta_gw != NULL) {
939 			*perror = nl_set_nexthop_gw(nh, attrs->rta_gw, attrs->rta_oif, npt);
940 			if (*perror != 0) {
941 				nhop_free(nh);
942 				return (NULL);
943 			}
944 		}
945 		if (attrs->rta_oif != NULL)
946 			nhop_set_transmit_ifp(nh, attrs->rta_oif);
947 		if (attrs->rtax_mtu != 0)
948 			nhop_set_mtu(nh, attrs->rtax_mtu, true);
949 		if (attrs->rta_expire > 0) {
950 			nh_expire = attrs->rta_expire - time_second + time_uptime;
951 			nhop_set_expire(nh, nh_expire);
952 		}
953 		if (attrs->rta_rtflags & RTF_BROADCAST)
954 			nhop_set_broadcast(nh, true);
955 		if (attrs->rtm_protocol > RTPROT_STATIC)
956 			nhop_set_origin(nh, attrs->rtm_protocol);
957 		nhop_set_metric(nh, attrs->rta_metric);
958 		nhop_set_pxtype_flag(nh, get_pxflag(attrs));
959 		nhop_set_rtflags(nh, attrs->rta_rtflags);
960 
961 		switch (attrs->rtm_type) {
962 		case RTN_UNICAST:
963 			break;
964 		case RTN_BLACKHOLE:
965 			nhop_set_blackhole(nh, RTF_BLACKHOLE);
966 			break;
967 		case RTN_PROHIBIT:
968 		case RTN_UNREACHABLE:
969 			nhop_set_blackhole(nh, RTF_REJECT);
970 			break;
971 		/* TODO: return ENOTSUP for other types if strict option is set */
972 		}
973 
974 		nh = finalize_nhop(nh, attrs->rta_dst, perror);
975 	}
976 
977 	return (nh);
978 }
979 
980 static int
981 rtnl_handle_newroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
982     struct nl_pstate *npt)
983 {
984 	struct rib_cmd_info rc = {};
985 	struct nhop_object *nh = NULL;
986 	int error;
987 
988 	struct nl_parsed_route attrs = {};
989 	error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
990 	if (error != 0)
991 		return (error);
992 
993 	/* Check if we have enough data */
994 	if (attrs.rta_dst == NULL) {
995 		NL_LOG(LOG_DEBUG, "missing RTA_DST");
996 		return (EINVAL);
997 	}
998 
999 	/* pre-2.6.19 Linux API compatibility */
1000 	if (attrs.rtm_table > 0 && attrs.rta_table == 0)
1001 		attrs.rta_table = attrs.rtm_table;
1002 	if (attrs.rta_table >= V_rt_numfibs || attrs.rtm_family > AF_MAX) {
1003 		NLMSG_REPORT_ERR_MSG(npt, "invalid fib");
1004 		return (EINVAL);
1005 	}
1006 
1007 	if (attrs.rta_nh_id != 0) {
1008 		/* Referenced uindex */
1009 		int pxflag = get_pxflag(&attrs);
1010 		nh = nl_find_nhop(attrs.rta_table, attrs.rtm_family, attrs.rta_nh_id,
1011 		    pxflag, &error);
1012 		if (error != 0)
1013 			return (error);
1014 	} else {
1015 		nh = create_nexthop_from_attrs(&attrs, npt, &error);
1016 		if (error != 0) {
1017 			NL_LOG(LOG_DEBUG, "Error creating nexthop");
1018 			return (error);
1019 		}
1020 	}
1021 
1022 	if (!NH_IS_NHGRP(nh) && attrs.rta_weight == 0)
1023 		attrs.rta_weight = RT_DEFAULT_WEIGHT;
1024 	struct route_nhop_data rnd = { .rnd_nhop = nh, .rnd_weight = attrs.rta_weight };
1025 	int op_flags = get_op_flags(hdr->nlmsg_flags);
1026 
1027 	error = rib_add_route_px(attrs.rta_table, attrs.rta_dst, attrs.rtm_dst_len,
1028 	    &rnd, op_flags, &rc);
1029 	if (error == 0)
1030 		report_operation(attrs.rta_table, &rc, nlp, hdr);
1031 	return (error);
1032 }
1033 
1034 static int
1035 path_match_func(const struct rtentry *rt, const struct nhop_object *nh, void *_data)
1036 {
1037 	struct nl_parsed_route *attrs = (struct nl_parsed_route *)_data;
1038 
1039 	if (attrs->rta_metric != 0 && attrs->rta_metric != nhop_get_metric(nh))
1040 		return (0);
1041 
1042 	if ((attrs->rta_gw != NULL) && !rib_match_gw(rt, nh, attrs->rta_gw))
1043 		return (0);
1044 
1045 	if ((attrs->rta_oif != NULL) && (attrs->rta_oif != nh->nh_ifp))
1046 		return (0);
1047 
1048 	return (1);
1049 }
1050 
1051 static int
1052 rtnl_handle_delroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
1053     struct nl_pstate *npt)
1054 {
1055 	struct rib_cmd_info rc;
1056 	int error;
1057 
1058 	struct nl_parsed_route attrs = {};
1059 	error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
1060 	if (error != 0)
1061 		return (error);
1062 
1063 	if (attrs.rta_dst == NULL) {
1064 		NLMSG_REPORT_ERR_MSG(npt, "RTA_DST is not set");
1065 		return (ESRCH);
1066 	}
1067 
1068 	if (attrs.rta_table >= V_rt_numfibs || attrs.rtm_family > AF_MAX) {
1069 		NLMSG_REPORT_ERR_MSG(npt, "invalid fib");
1070 		return (EINVAL);
1071 	}
1072 
1073 	error = rib_del_route_px(attrs.rta_table, attrs.rta_dst,
1074 	    attrs.rtm_dst_len, path_match_func, &attrs,
1075 	    (attrs.rta_rtflags & RTF_PINNED) ? RTM_F_FORCE : 0, &rc);
1076 	if (error == 0)
1077 		report_operation(attrs.rta_table, &rc, nlp, hdr);
1078 	return (error);
1079 }
1080 
1081 static int
1082 rtnl_handle_getroute(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
1083 {
1084 	int error;
1085 
1086 	struct nl_parsed_route attrs = {};
1087 	error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
1088 	if (error != 0)
1089 		return (error);
1090 
1091 	if (attrs.rta_table >= V_rt_numfibs || attrs.rtm_family > AF_MAX) {
1092 		NLMSG_REPORT_ERR_MSG(npt, "invalid fib");
1093 		return (EINVAL);
1094 	}
1095 
1096 	if (hdr->nlmsg_flags & NLM_F_DUMP)
1097 		error = handle_rtm_dump(nlp, attrs.rta_table, attrs.rtm_family, hdr, npt->nw);
1098 	else
1099 		error = handle_rtm_getroute(nlp, &attrs, hdr, npt);
1100 
1101 	return (error);
1102 }
1103 
1104 void
1105 rtnl_handle_route_event(uint32_t fibnum, const struct rib_cmd_info *rc)
1106 {
1107 	struct nl_writer nw;
1108 	int family, nlm_flags = 0;
1109 
1110 	family = rt_get_family(rc->rc_rt);
1111 
1112 	/* XXX: check if there are active listeners first */
1113 
1114 	/* TODO: consider passing PID/type/seq */
1115 	switch (rc->rc_cmd) {
1116 	case RTM_ADD:
1117 		nlm_flags = NLM_F_EXCL | NLM_F_CREATE;
1118 		break;
1119 	case RTM_CHANGE:
1120 		nlm_flags = NLM_F_REPLACE;
1121 		break;
1122 	case RTM_DELETE:
1123 		nlm_flags = 0;
1124 		break;
1125 	}
1126 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
1127 		char rtbuf[NHOP_PRINT_BUFSIZE] __unused;
1128 		FIB_LOG(LOG_DEBUG2, fibnum, family,
1129 		    "received event %s for %s / nlm_flags=%X",
1130 		    rib_print_cmd(rc->rc_cmd),
1131 		    rt_print_buf(rc->rc_rt, rtbuf, sizeof(rtbuf)),
1132 		    nlm_flags);
1133 	}
1134 
1135 	struct nlmsghdr hdr = {
1136 		.nlmsg_flags = nlm_flags,
1137 		.nlmsg_type = get_rtmsg_type_from_rtsock(rc->rc_cmd),
1138 	};
1139 
1140 	struct route_nhop_data rnd = {
1141 		.rnd_nhop = rc_get_nhop(rc),
1142 		.rnd_weight = rc->rc_nh_weight,
1143 	};
1144 
1145 	uint32_t group_id = family_to_group(family);
1146 	if (!nl_writer_group(&nw, NLMSG_SMALL, NETLINK_ROUTE, group_id, 0,
1147 	    false)) {
1148 		NL_LOG(LOG_DEBUG, "error allocating event buffer");
1149 		return;
1150 	}
1151 
1152 	dump_px(fibnum, &hdr, rc->rc_rt, &rnd, &nw);
1153 	nlmsg_flush(&nw);
1154 }
1155 
1156 static const struct rtnl_cmd_handler cmd_handlers[] = {
1157 	{
1158 		.cmd = NL_RTM_GETROUTE,
1159 		.name = "RTM_GETROUTE",
1160 		.cb = &rtnl_handle_getroute,
1161 		.flags = RTNL_F_ALLOW_NONVNET_JAIL,
1162 	},
1163 	{
1164 		.cmd = NL_RTM_DELROUTE,
1165 		.name = "RTM_DELROUTE",
1166 		.cb = &rtnl_handle_delroute,
1167 		.priv = PRIV_NET_ROUTE,
1168 		.flags = RTNL_F_ALLOW_NONVNET_JAIL,
1169 	},
1170 	{
1171 		.cmd = NL_RTM_NEWROUTE,
1172 		.name = "RTM_NEWROUTE",
1173 		.cb = &rtnl_handle_newroute,
1174 		.priv = PRIV_NET_ROUTE,
1175 		.flags = RTNL_F_ALLOW_NONVNET_JAIL,
1176 	}
1177 };
1178 
1179 static const struct nlhdr_parser *all_parsers[] = {&mpath_parser, &metrics_parser, &rtm_parser};
1180 
1181 void
1182 rtnl_routes_init(void)
1183 {
1184 	NL_VERIFY_PARSERS(all_parsers);
1185 	rtnl_register_messages(cmd_handlers, nitems(cmd_handlers));
1186 }
1187