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