]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netlink/route/route.c
Fix kernel build after fcb3f813f379f544f9cd2a10d18045588da0e132 .
[FreeBSD/FreeBSD.git] / sys / netlink / route / route.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
31 #include "opt_inet.h"
32 #include "opt_inet6.h"
33 #include "opt_route.h"
34 #include <sys/types.h>
35 #include <sys/malloc.h>
36 #include <sys/rmlock.h>
37 #include <sys/socket.h>
38
39 #include <net/if.h>
40 #include <net/route.h>
41 #include <net/route/nhop.h>
42 #include <net/route/route_ctl.h>
43 #include <net/route/route_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_DEBUG);
53
54 static unsigned char
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
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
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         int upper_family;
127
128         switch (nhop_get_neigh_family(nh)) {
129         case AF_LINK:
130                 /* onlink prefix, skip */
131                 break;
132         case AF_INET:
133                 nlattr_add(nw, NL_RTA_GATEWAY, 4, &nh->gw4_sa.sin_addr);
134                 break;
135         case AF_INET6:
136                 upper_family = nhop_get_upper_family(nh);
137                 if (upper_family == AF_INET6) {
138                         nlattr_add(nw, NL_RTA_GATEWAY, 16, &nh->gw6_sa.sin6_addr);
139                 } else if (upper_family == AF_INET) {
140                         /* IPv4 over IPv6 */
141                         char buf[20];
142                         struct rtvia *via = (struct rtvia *)&buf[0];
143                         via->rtvia_family = AF_INET6;
144                         memcpy(via->rtvia_addr, &nh->gw6_sa.sin6_addr, 16);
145                         nlattr_add(nw, NL_RTA_VIA, 17, via);
146                 }
147                 break;
148         }
149 }
150
151 static void
152 dump_rc_nhop_mtu(struct nl_writer *nw, const struct nhop_object *nh)
153 {
154         int nla_len = sizeof(struct nlattr) * 2 + sizeof(uint32_t);
155         struct nlattr *nla = nlmsg_reserve_data(nw, nla_len, struct nlattr);
156
157         if (nla == NULL)
158                 return;
159         nla->nla_type = NL_RTA_METRICS;
160         nla->nla_len = nla_len;
161         nla++;
162         nla->nla_type = NL_RTAX_MTU;
163         nla->nla_len = sizeof(struct nlattr) + sizeof(uint32_t);
164         *((uint32_t *)(nla + 1)) = nh->nh_mtu;
165 }
166
167 #ifdef ROUTE_MPATH
168 static void
169 dump_rc_nhg(struct nl_writer *nw, const struct nhgrp_object *nhg, struct rtmsg *rtm)
170 {
171         uint32_t uidx = nhgrp_get_uidx(nhg);
172         uint32_t num_nhops;
173         const struct weightened_nhop *wn = nhgrp_get_nhops(nhg, &num_nhops);
174         uint32_t base_rtflags = nhop_get_rtflags(wn[0].nh);
175
176         if (uidx != 0)
177                 nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
178
179         nlattr_add_u32(nw, NL_RTA_RTFLAGS, base_rtflags);
180         int off = nlattr_add_nested(nw, NL_RTA_MULTIPATH);
181         if (off == 0)
182                 return;
183
184         for (int i = 0; i < num_nhops; i++) {
185                 int nh_off = nlattr_save_offset(nw);
186                 struct rtnexthop *rtnh = nlmsg_reserve_object(nw, struct rtnexthop);
187                 if (rtnh == NULL)
188                         return;
189                 rtnh->rtnh_flags = 0;
190                 rtnh->rtnh_ifindex = wn[i].nh->nh_ifp->if_index;
191                 rtnh->rtnh_hops = wn[i].weight;
192                 dump_rc_nhop_gw(nw, wn[i].nh);
193                 uint32_t rtflags = nhop_get_rtflags(wn[i].nh);
194                 if (rtflags != base_rtflags)
195                         nlattr_add_u32(nw, NL_RTA_RTFLAGS, rtflags);
196                 if (rtflags & RTF_FIXEDMTU)
197                         dump_rc_nhop_mtu(nw, wn[i].nh);
198                 rtnh = nlattr_restore_offset(nw, nh_off, struct rtnexthop);
199                 /*
200                  * nlattr_add() allocates 4-byte aligned storage, no need to aligh
201                  * length here
202                  * */
203                 rtnh->rtnh_len = nlattr_save_offset(nw) - nh_off;
204         }
205         nlattr_set_len(nw, off);
206 }
207 #endif
208
209 static void
210 dump_rc_nhop(struct nl_writer *nw, const struct nhop_object *nh, struct rtmsg *rtm)
211 {
212 #ifdef ROUTE_MPATH
213         if (NH_IS_NHGRP(nh)) {
214                 dump_rc_nhg(nw, (const struct nhgrp_object *)nh, rtm);
215                 return;
216         }
217 #endif
218         uint32_t rtflags = nhop_get_rtflags(nh);
219
220         /*
221          * IPv4 over IPv6
222          *    ('RTA_VIA', {'family': 10, 'addr': 'fe80::20c:29ff:fe67:2dd'}), ('RTA_OIF', 2),
223          * IPv4 w/ gw
224          *    ('RTA_GATEWAY', '172.16.107.131'), ('RTA_OIF', 2)],
225          * Direct route:
226          *    ('RTA_OIF', 2)
227          */
228         if (nh->nh_flags & NHF_GATEWAY)
229                 dump_rc_nhop_gw(nw, nh);
230
231         uint32_t uidx = nhop_get_uidx(nh);
232         if (uidx != 0)
233                 nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
234         nlattr_add_u32(nw, NL_RTA_KNH_ID, nhop_get_idx(nh));
235         nlattr_add_u32(nw, NL_RTA_RTFLAGS, rtflags);
236
237         if (rtflags & RTF_FIXEDMTU)
238                 dump_rc_nhop_mtu(nw, nh);
239         uint32_t nh_expire = nhop_get_expire(nh);
240         if (nh_expire > 0)
241                 nlattr_add_u32(nw, NL_RTA_EXPIRES, nh_expire - time_uptime);
242
243         /* In any case, fill outgoing interface */
244         nlattr_add_u32(nw, NL_RTA_OIF, nh->nh_ifp->if_index);
245 }
246
247 /*
248  * Dumps output from a rib command into an rtmsg
249  */
250
251 static int
252 dump_px(uint32_t fibnum, const struct nlmsghdr *hdr,
253     const struct rtentry *rt, struct route_nhop_data *rnd,
254     struct nl_writer *nw)
255 {
256         struct rtmsg *rtm;
257         int error = 0;
258
259         NET_EPOCH_ASSERT();
260
261         if (!nlmsg_reply(nw, hdr, sizeof(struct rtmsg)))
262                 goto enomem;
263
264         int family = rt_get_family(rt);
265         int rtm_off = nlattr_save_offset(nw);
266         rtm = nlmsg_reserve_object(nw, struct rtmsg);
267         rtm->rtm_family = family;
268         rtm->rtm_dst_len = 0;
269         rtm->rtm_src_len = 0;
270         rtm->rtm_tos = 0;
271         if (fibnum < 255)
272                 rtm->rtm_table = (unsigned char)fibnum;
273         rtm->rtm_scope = RT_SCOPE_UNIVERSE;
274         if (!NH_IS_NHGRP(rnd->rnd_nhop)) {
275                 rtm->rtm_protocol = nl_get_rtm_protocol(rnd->rnd_nhop);
276                 rtm->rtm_type = get_rtm_type(rnd->rnd_nhop);
277         } else {
278                 rtm->rtm_protocol = RTPROT_UNSPEC; /* TODO: protocol from nhg? */
279                 rtm->rtm_type = RTN_UNICAST;
280         }
281
282         nlattr_add_u32(nw, NL_RTA_TABLE, fibnum);
283
284         int plen = 0;
285 #if defined(INET) || defined(INET6)
286         uint32_t scopeid;
287 #endif
288         switch (family) {
289 #ifdef INET
290         case AF_INET:
291                 {
292                         struct in_addr addr;
293                         rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
294                         nlattr_add(nw, NL_RTA_DST, 4, &addr);
295                         break;
296                 }
297 #endif
298 #ifdef INET6
299         case AF_INET6:
300                 {
301                         struct in6_addr addr;
302                         rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
303                         nlattr_add(nw, NL_RTA_DST, 16, &addr);
304                         break;
305                 }
306 #endif
307         default:
308                 FIB_LOG(LOG_NOTICE, fibnum, family, "unsupported rt family: %d", family);
309                 error = EAFNOSUPPORT;
310                 goto flush;
311         }
312
313         rtm = nlattr_restore_offset(nw, rtm_off, struct rtmsg);
314         if (plen > 0)
315                 rtm->rtm_dst_len = plen;
316         dump_rc_nhop(nw, rnd->rnd_nhop, rtm);
317
318         if (nlmsg_end(nw))
319                 return (0);
320 enomem:
321         error = ENOMEM;
322 flush:
323         nlmsg_abort(nw);
324         return (error);
325 }
326
327 static int
328 family_to_group(int family)
329 {
330         switch (family) {
331         case AF_INET:
332                 return (RTNLGRP_IPV4_ROUTE);
333         case AF_INET6:
334                 return (RTNLGRP_IPV6_ROUTE);
335         }
336         return (0);
337 }
338
339
340 static void
341 report_operation(uint32_t fibnum, struct rib_cmd_info *rc,
342     struct nlpcb *nlp, struct nlmsghdr *hdr)
343 {
344         struct nl_writer nw;
345
346         uint32_t group_id = family_to_group(rt_get_family(rc->rc_rt));
347         if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_ROUTE, group_id)) {
348                 struct route_nhop_data rnd = {
349                         .rnd_nhop = rc_get_nhop(rc),
350                         .rnd_weight = rc->rc_nh_weight,
351                 };
352                 hdr->nlmsg_flags &= ~(NLM_F_REPLACE | NLM_F_CREATE);
353                 hdr->nlmsg_flags &= ~(NLM_F_EXCL | NLM_F_APPEND);
354                 switch (rc->rc_cmd) {
355                 case RTM_ADD:
356                         hdr->nlmsg_type = NL_RTM_NEWROUTE;
357                         hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
358                         break;
359                 case RTM_CHANGE:
360                         hdr->nlmsg_type = NL_RTM_NEWROUTE;
361                         hdr->nlmsg_flags |= NLM_F_REPLACE;
362                         break;
363                 case RTM_DELETE:
364                         hdr->nlmsg_type = NL_RTM_DELROUTE;
365                         break;
366                 }
367                 dump_px(fibnum, hdr, rc->rc_rt, &rnd, &nw);
368                 nlmsg_flush(&nw);
369         }
370
371         rtsock_callback_p->route_f(fibnum, rc);
372 }
373
374 struct rta_mpath_nh {
375         struct sockaddr *gw;
376         struct ifnet    *ifp;
377         uint8_t         rtnh_flags;
378         uint8_t         rtnh_weight;
379 };
380
381 #define _IN(_field)     offsetof(struct rtnexthop, _field)
382 #define _OUT(_field)    offsetof(struct rta_mpath_nh, _field)
383 const static struct nlattr_parser nla_p_rtnh[] = {
384         { .type = NL_RTA_GATEWAY, .off = _OUT(gw), .cb = nlattr_get_ip },
385         { .type = NL_RTA_VIA, .off = _OUT(gw), .cb = nlattr_get_ipvia },
386 };
387 const static struct nlfield_parser nlf_p_rtnh[] = {
388         { .off_in = _IN(rtnh_flags), .off_out = _OUT(rtnh_flags), .cb = nlf_get_u8 },
389         { .off_in = _IN(rtnh_hops), .off_out = _OUT(rtnh_weight), .cb = nlf_get_u8 },
390         { .off_in = _IN(rtnh_ifindex), .off_out = _OUT(ifp), .cb = nlf_get_ifpz },
391 };
392 #undef _IN
393 #undef _OUT
394 NL_DECLARE_PARSER(mpath_parser, struct rtnexthop, nlf_p_rtnh, nla_p_rtnh);
395
396 struct rta_mpath {
397         int num_nhops;
398         struct rta_mpath_nh nhops[0];
399 };
400
401 static int
402 nlattr_get_multipath(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
403 {
404         int data_len = nla->nla_len - sizeof(struct nlattr);
405         struct rtnexthop *rtnh;
406
407         int max_nhops = data_len / sizeof(struct rtnexthop);
408
409         struct rta_mpath *mp = npt_alloc(npt, (max_nhops + 2) * sizeof(struct rta_mpath_nh));
410         mp->num_nhops = 0;
411
412         for (rtnh = (struct rtnexthop *)(nla + 1); data_len > 0; ) {
413                 struct rta_mpath_nh *mpnh = &mp->nhops[mp->num_nhops++];
414
415                 int error = nl_parse_header(rtnh, rtnh->rtnh_len, &mpath_parser,
416                     npt, mpnh);
417                 if (error != 0) {
418                         NLMSG_REPORT_ERR_MSG(npt, "RTA_MULTIPATH: nexhop %d: parse failed",
419                             mp->num_nhops - 1);
420                         return (error);
421                 }
422
423                 int len = NL_ITEM_ALIGN(rtnh->rtnh_len);
424                 data_len -= len;
425                 rtnh = (struct rtnexthop *)((char *)rtnh + len);
426         }
427         if (data_len != 0 || mp->num_nhops == 0) {
428                 NLMSG_REPORT_ERR_MSG(npt, "invalid RTA_MULTIPATH attr");
429                 return (EINVAL);
430         }
431
432         *((struct rta_mpath **)target) = mp;
433         return (0);
434 }
435
436
437 struct nl_parsed_route {
438         struct sockaddr         *rta_dst;
439         struct sockaddr         *rta_gw;
440         struct ifnet            *rta_oif;
441         struct rta_mpath        *rta_multipath;
442         uint32_t                rta_table;
443         uint32_t                rta_rtflags;
444         uint32_t                rta_nh_id;
445         uint32_t                rtax_mtu;
446         uint8_t                 rtm_family;
447         uint8_t                 rtm_dst_len;
448 };
449
450 #define _IN(_field)     offsetof(struct rtmsg, _field)
451 #define _OUT(_field)    offsetof(struct nl_parsed_route, _field)
452 static struct nlattr_parser nla_p_rtmetrics[] = {
453         { .type = NL_RTAX_MTU, .off = _OUT(rtax_mtu), .cb = nlattr_get_uint32 },
454 };
455 NL_DECLARE_ATTR_PARSER(metrics_parser, nla_p_rtmetrics);
456
457 static const struct nlattr_parser nla_p_rtmsg[] = {
458         { .type = NL_RTA_DST, .off = _OUT(rta_dst), .cb = nlattr_get_ip },
459         { .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = nlattr_get_ifp },
460         { .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = nlattr_get_ip },
461         { .type = NL_RTA_METRICS, .arg = &metrics_parser, .cb = nlattr_get_nested },
462         { .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = nlattr_get_multipath },
463         { .type = NL_RTA_RTFLAGS, .off = _OUT(rta_rtflags), .cb = nlattr_get_uint32 },
464         { .type = NL_RTA_TABLE, .off = _OUT(rta_table), .cb = nlattr_get_uint32 },
465         { .type = NL_RTA_VIA, .off = _OUT(rta_gw), .cb = nlattr_get_ipvia },
466         { .type = NL_RTA_NH_ID, .off = _OUT(rta_nh_id), .cb = nlattr_get_uint32 },
467 };
468
469 static const struct nlfield_parser nlf_p_rtmsg[] = {
470         {.off_in = _IN(rtm_family), .off_out = _OUT(rtm_family), .cb = nlf_get_u8 },
471         {.off_in = _IN(rtm_dst_len), .off_out = _OUT(rtm_dst_len), .cb = nlf_get_u8 },
472 };
473 #undef _IN
474 #undef _OUT
475 NL_DECLARE_PARSER(rtm_parser, struct rtmsg, nlf_p_rtmsg, nla_p_rtmsg);
476
477 struct netlink_walkargs {
478         struct nl_writer *nw;
479         struct route_nhop_data rnd;
480         struct nlmsghdr hdr;
481         struct nlpcb *nlp;
482         uint32_t fibnum;
483         int family;
484         int error;
485         int count;
486         int dumped;
487         int dumped_tables;
488 };
489
490 static int
491 dump_rtentry(struct rtentry *rt, void *_arg)
492 {
493         struct netlink_walkargs *wa = (struct netlink_walkargs *)_arg;
494         int error;
495
496         wa->count++;
497         if (wa->error != 0)
498                 return (0);
499         wa->dumped++;
500
501         rt_get_rnd(rt, &wa->rnd);
502
503         error = dump_px(wa->fibnum, &wa->hdr, rt, &wa->rnd, wa->nw);
504
505         IF_DEBUG_LEVEL(LOG_DEBUG3) {
506                 char rtbuf[INET6_ADDRSTRLEN + 5];
507                 FIB_LOG(LOG_DEBUG3, wa->fibnum, wa->family,
508                     "Dump %s, offset %u, error %d",
509                     rt_print_buf(rt, rtbuf, sizeof(rtbuf)),
510                     wa->nw->offset, error);
511         }
512         wa->error = error;
513
514         return (0);
515 }
516
517 static void
518 dump_rtable_one(struct netlink_walkargs *wa, uint32_t fibnum, int family)
519 {
520         FIB_LOG(LOG_DEBUG2, fibnum, family, "Start dump");
521         wa->count = 0;
522         wa->dumped = 0;
523
524         rib_walk(fibnum, family, false, dump_rtentry, wa);
525
526         wa->dumped_tables++;
527
528         FIB_LOG(LOG_DEBUG2, fibnum, family, "End dump, iterated %d dumped %d",
529             wa->count, wa->dumped);
530         NL_LOG(LOG_DEBUG2, "Current offset: %d", wa->nw->offset);
531 }
532
533 static int
534 dump_rtable_fib(struct netlink_walkargs *wa, uint32_t fibnum, int family)
535 {
536         wa->fibnum = fibnum;
537
538         if (family == AF_UNSPEC) {
539                 for (int i = 0; i < AF_MAX; i++) {
540                         if (rt_tables_get_rnh(fibnum, i) != 0) {
541                                 wa->family = i;
542                                 dump_rtable_one(wa, fibnum, i);
543                                 if (wa->error != 0)
544                                         break;
545                         }
546                 }
547         } else {
548                 if (rt_tables_get_rnh(fibnum, family) != 0) {
549                         wa->family = family;
550                         dump_rtable_one(wa, fibnum, family);
551                 }
552         }
553
554         return (wa->error);
555 }
556
557 static int
558 handle_rtm_getroute(struct nlpcb *nlp, struct nl_parsed_route *attrs,
559     struct nlmsghdr *hdr, struct nl_pstate *npt)
560 {
561         RIB_RLOCK_TRACKER;
562         struct rib_head *rnh;
563         struct rtentry *rt;
564         uint32_t fibnum = attrs->rta_table;
565         sa_family_t family = attrs->rtm_family;
566
567         if (attrs->rta_dst == NULL) {
568                 NLMSG_REPORT_ERR_MSG(npt, "No RTA_DST supplied");
569                         return (EINVAL);
570         }
571
572         FIB_LOG(LOG_DEBUG, fibnum, family, "getroute called");
573
574         rnh = rt_tables_get_rnh(fibnum, family);
575         if (rnh == NULL)
576                 return (EAFNOSUPPORT);
577
578         RIB_RLOCK(rnh);
579
580         rt = (struct rtentry *)rnh->rnh_matchaddr(attrs->rta_dst, &rnh->head);
581         if (rt == NULL) {
582                 RIB_RUNLOCK(rnh);
583                 return (ESRCH);
584         }
585
586         struct route_nhop_data rnd;
587         rt_get_rnd(rt, &rnd);
588         rnd.rnd_nhop = nhop_select_func(rnd.rnd_nhop, 0);
589
590         RIB_RUNLOCK(rnh);
591
592         IF_DEBUG_LEVEL(LOG_DEBUG2) {
593                 char rtbuf[NHOP_PRINT_BUFSIZE] __unused, nhbuf[NHOP_PRINT_BUFSIZE] __unused;
594                 FIB_LOG(LOG_DEBUG2, fibnum, family, "getroute completed: got %s for %s",
595                     nhop_print_buf_any(rnd.rnd_nhop, nhbuf, sizeof(nhbuf)),
596                     rt_print_buf(rt, rtbuf, sizeof(rtbuf)));
597         }
598
599         hdr->nlmsg_type = NL_RTM_NEWROUTE;
600         dump_px(fibnum, hdr, rt, &rnd, npt->nw);
601
602         return (0);
603 }
604
605 static int
606 handle_rtm_dump(struct nlpcb *nlp, uint32_t fibnum, int family,
607     struct nlmsghdr *hdr, struct nl_writer *nw)
608 {
609         struct netlink_walkargs wa = {
610                 .nlp = nlp,
611                 .nw = nw,
612                 .hdr.nlmsg_pid = hdr->nlmsg_pid,
613                 .hdr.nlmsg_seq = hdr->nlmsg_seq,
614                 .hdr.nlmsg_type = NL_RTM_NEWROUTE,
615                 .hdr.nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI,
616         };
617
618         if (fibnum == RT_TABLE_UNSPEC) {
619                 for (int i = 0; i < V_rt_numfibs; i++) {
620                         dump_rtable_fib(&wa, fibnum, family);
621                         if (wa.error != 0)
622                                 break;
623                 }
624         } else
625                 dump_rtable_fib(&wa, fibnum, family);
626
627         if (wa.error == 0 && wa.dumped_tables == 0) {
628                 FIB_LOG(LOG_DEBUG, fibnum, family, "incorrect fibnum/family");
629                 wa.error = ESRCH;
630                 // How do we propagate it?
631         }
632
633         if (!nlmsg_end_dump(wa.nw, wa.error, &wa.hdr)) {
634                 NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
635                 return (ENOMEM);
636         }
637
638         return (wa.error);
639 }
640
641 static struct nhop_object *
642 finalize_nhop(struct nhop_object *nh, int *perror)
643 {
644         /*
645          * The following MUST be filled:
646          *  nh_ifp, nh_ifa, nh_gw
647          */
648         if (nh->gw_sa.sa_family == 0) {
649                 /*
650                  * Empty gateway. Can be direct route with RTA_OIF set.
651                  */
652                 if (nh->nh_ifp != NULL)
653                         nhop_set_direct_gw(nh, nh->nh_ifp);
654                 else {
655                         NL_LOG(LOG_DEBUG, "empty gateway and interface, skipping");
656                         *perror = EINVAL;
657                         return (NULL);
658                 }
659                 /* Both nh_ifp and gateway are set */
660         } else {
661                 /* Gateway is set up, we can derive ifp if not set */
662                 if (nh->nh_ifp == NULL) {
663                         struct ifaddr *ifa = ifa_ifwithnet(&nh->gw_sa, 1, nhop_get_fibnum(nh));
664                         if (ifa == NULL) {
665                                 NL_LOG(LOG_DEBUG, "Unable to determine ifp, skipping");
666                                 *perror = EINVAL;
667                                 return (NULL);
668                         }
669                         nhop_set_transmit_ifp(nh, ifa->ifa_ifp);
670                 }
671         }
672         /* Both nh_ifp and gateway are set */
673         if (nh->nh_ifa == NULL) {
674                 struct ifaddr *ifa = ifaof_ifpforaddr(&nh->gw_sa, nh->nh_ifp);
675                 if (ifa == NULL) {
676                         NL_LOG(LOG_DEBUG, "Unable to determine ifa, skipping");
677                         *perror = EINVAL;
678                         return (NULL);
679                 }
680                 nhop_set_src(nh, ifa);
681         }
682
683         return (nhop_get_nhop(nh, perror));
684 }
685
686 static int
687 get_pxflag(const struct nl_parsed_route *attrs)
688 {
689         int pxflag = 0;
690         switch (attrs->rtm_family) {
691         case AF_INET:
692                 if (attrs->rtm_dst_len == 32)
693                         pxflag = NHF_HOST;
694                 else if (attrs->rtm_dst_len == 0)
695                         pxflag = NHF_DEFAULT;
696                 break;
697         case AF_INET6:
698                 if (attrs->rtm_dst_len == 32)
699                         pxflag = NHF_HOST;
700                 else if (attrs->rtm_dst_len == 0)
701                         pxflag = NHF_DEFAULT;
702                 break;
703         }
704
705         return (pxflag);
706 }
707
708 static int
709 get_op_flags(int nlm_flags)
710 {
711         int op_flags = 0;
712
713         op_flags |= (nlm_flags & NLM_F_REPLACE) ? RTM_F_REPLACE : 0;
714         op_flags |= (nlm_flags & NLM_F_EXCL) ? RTM_F_EXCL : 0;
715         op_flags |= (nlm_flags & NLM_F_CREATE) ? RTM_F_CREATE : 0;
716         op_flags |= (nlm_flags & NLM_F_APPEND) ? RTM_F_APPEND : 0;
717
718         return (op_flags);
719 }
720
721 #ifdef ROUTE_MPATH
722 static int
723 create_nexthop_one(struct nl_parsed_route *attrs, struct rta_mpath_nh *mpnh,
724     struct nl_pstate *npt, struct nhop_object **pnh)
725 {
726         int error;
727
728         if (mpnh->gw == NULL)
729                 return (EINVAL);
730
731         struct nhop_object *nh = nhop_alloc(attrs->rta_table, attrs->rtm_family);
732         if (nh == NULL)
733                 return (ENOMEM);
734
735         nhop_set_gw(nh, mpnh->gw, true);
736         if (mpnh->ifp != NULL)
737                 nhop_set_transmit_ifp(nh, mpnh->ifp);
738         nhop_set_rtflags(nh, attrs->rta_rtflags);
739
740         *pnh = finalize_nhop(nh, &error);
741
742         return (error);
743 }
744 #endif
745
746 static struct nhop_object *
747 create_nexthop_from_attrs(struct nl_parsed_route *attrs,
748     struct nl_pstate *npt, int *perror)
749 {
750         struct nhop_object *nh = NULL;
751         int error = 0;
752
753         if (attrs->rta_multipath != NULL) {
754 #ifdef ROUTE_MPATH
755                 /* Multipath w/o explicit nexthops */
756                 int num_nhops = attrs->rta_multipath->num_nhops;
757                 struct weightened_nhop *wn = npt_alloc(npt, sizeof(*wn) * num_nhops);
758
759                 for (int i = 0; i < num_nhops; i++) {
760                         struct rta_mpath_nh *mpnh = &attrs->rta_multipath->nhops[i];
761
762                         error = create_nexthop_one(attrs, mpnh, npt, &wn[i].nh);
763                         if (error != 0) {
764                                 for (int j = 0; j < i; j++)
765                                         nhop_free(wn[j].nh);
766                                 break;
767                         }
768                         wn[i].weight = mpnh->rtnh_weight > 0 ? mpnh->rtnh_weight : 1;
769                 }
770                 if (error == 0) {
771                         struct rib_head *rh = nhop_get_rh(wn[0].nh);
772
773                         error = nhgrp_get_group(rh, wn, num_nhops, 0,
774                             (struct nhgrp_object **)&nh);
775
776                         for (int i = 0; i < num_nhops; i++)
777                                 nhop_free(wn[i].nh);
778                 }
779 #else
780                 error = ENOTSUP;
781 #endif
782                 *perror = error;
783         } else {
784                 nh = nhop_alloc(attrs->rta_table, attrs->rtm_family);
785                 if (nh == NULL) {
786                         *perror = ENOMEM;
787                         return (NULL);
788                 }
789                 if (attrs->rta_gw != NULL)
790                         nhop_set_gw(nh, attrs->rta_gw, true);
791                 if (attrs->rta_oif != NULL)
792                         nhop_set_transmit_ifp(nh, attrs->rta_oif);
793                 if (attrs->rtax_mtu != 0)
794                         nhop_set_mtu(nh, attrs->rtax_mtu, true);
795                 if (attrs->rta_rtflags & RTF_BROADCAST)
796                         nhop_set_broadcast(nh, true);
797                 if (attrs->rta_rtflags & RTF_BLACKHOLE)
798                         nhop_set_blackhole(nh, NHF_BLACKHOLE);
799                 if (attrs->rta_rtflags & RTF_REJECT)
800                         nhop_set_blackhole(nh, NHF_REJECT);
801                 nhop_set_rtflags(nh, attrs->rta_rtflags);
802                 nh = finalize_nhop(nh, perror);
803         }
804
805         return (nh);
806 }
807
808 static int
809 rtnl_handle_newroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
810     struct nl_pstate *npt)
811 {
812         struct rib_cmd_info rc = {};
813         struct nhop_object *nh = NULL;
814         int error;
815
816         struct nl_parsed_route attrs = {};
817         error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
818         if (error != 0)
819                 return (error);
820
821         /* Check if we have enough data */
822         if (attrs.rta_dst == NULL) {
823                 NL_LOG(LOG_DEBUG, "missing RTA_DST");
824                 return (EINVAL);
825         }
826
827         if (attrs.rta_nh_id != 0) {
828                 /* Referenced uindex */
829                 int pxflag = get_pxflag(&attrs);
830                 nh = nl_find_nhop(attrs.rta_table, attrs.rtm_family, attrs.rta_nh_id,
831                     pxflag, &error);
832                 if (error != 0)
833                         return (error);
834         } else {
835                 nh = create_nexthop_from_attrs(&attrs, npt, &error);
836                 if (error != 0) {
837                         NL_LOG(LOG_DEBUG, "Error creating nexthop");
838                         return (error);
839                 }
840         }
841
842         int weight = NH_IS_NHGRP(nh) ? 0 : RT_DEFAULT_WEIGHT;
843         struct route_nhop_data rnd = { .rnd_nhop = nh, .rnd_weight = weight };
844         int op_flags = get_op_flags(hdr->nlmsg_flags);
845
846         error = rib_add_route_px(attrs.rta_table, attrs.rta_dst, attrs.rtm_dst_len,
847             &rnd, op_flags, &rc);
848         if (error == 0)
849                 report_operation(attrs.rta_table, &rc, nlp, hdr);
850         return (error);
851 }
852
853 static int
854 path_match_func(const struct rtentry *rt, const struct nhop_object *nh, void *_data)
855 {
856         struct nl_parsed_route *attrs = (struct nl_parsed_route *)_data;
857
858         if ((attrs->rta_gw != NULL) && !rib_match_gw(rt, nh, attrs->rta_gw))
859                 return (0);
860
861         if ((attrs->rta_oif != NULL) && (attrs->rta_oif != nh->nh_ifp))
862                 return (0);
863
864         return (1);
865 }
866
867 static int
868 rtnl_handle_delroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
869     struct nl_pstate *npt)
870 {
871         struct rib_cmd_info rc;
872         int error;
873
874         struct nl_parsed_route attrs = {};
875         error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
876         if (error != 0)
877                 return (error);
878
879         if (attrs.rta_dst == NULL) {
880                 NLMSG_REPORT_ERR_MSG(npt, "RTA_DST is not set");
881                 return (ESRCH);
882         }
883
884         error = rib_del_route_px(attrs.rta_table, attrs.rta_dst,
885             attrs.rtm_dst_len, path_match_func, &attrs, 0, &rc);
886         if (error == 0)
887                 report_operation(attrs.rta_table, &rc, nlp, hdr);
888         return (error);
889 }
890
891 static int
892 rtnl_handle_getroute(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
893 {
894         int error;
895
896         struct nl_parsed_route attrs = {};
897         error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
898         if (error != 0)
899                 return (error);
900
901         if (hdr->nlmsg_flags & NLM_F_DUMP)
902                 error = handle_rtm_dump(nlp, attrs.rta_table, attrs.rtm_family, hdr, npt->nw);
903         else
904                 error = handle_rtm_getroute(nlp, &attrs, hdr, npt);
905
906         return (error);
907 }
908
909 void
910 rtnl_handle_route_event(uint32_t fibnum, const struct rib_cmd_info *rc)
911 {
912         int family, nlm_flags = 0;
913
914         struct nl_writer nw;
915
916         family = rt_get_family(rc->rc_rt);
917
918         /* XXX: check if there are active listeners first */
919
920         /* TODO: consider passing PID/type/seq */
921         switch (rc->rc_cmd) {
922         case RTM_ADD:
923                 nlm_flags = NLM_F_EXCL | NLM_F_CREATE;
924                 break;
925         case RTM_CHANGE:
926                 nlm_flags = NLM_F_REPLACE;
927                 break;
928         case RTM_DELETE:
929                 nlm_flags = 0;
930                 break;
931         }
932         IF_DEBUG_LEVEL(LOG_DEBUG2) {
933                 char rtbuf[NHOP_PRINT_BUFSIZE] __unused;
934                 FIB_LOG(LOG_DEBUG2, fibnum, family,
935                     "received event %s for %s / nlm_flags=%X",
936                     rib_print_cmd(rc->rc_cmd),
937                     rt_print_buf(rc->rc_rt, rtbuf, sizeof(rtbuf)),
938                     nlm_flags);
939         }
940
941         struct nlmsghdr hdr = {
942                 .nlmsg_flags = nlm_flags,
943                 .nlmsg_type = get_rtmsg_type_from_rtsock(rc->rc_cmd),
944         };
945
946         struct route_nhop_data rnd = {
947                 .rnd_nhop = rc_get_nhop(rc),
948                 .rnd_weight = rc->rc_nh_weight,
949         };
950
951         uint32_t group_id = family_to_group(family);
952         if (!nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_ROUTE, group_id)) {
953                 NL_LOG(LOG_DEBUG, "error allocating event buffer");
954                 return;
955         }
956
957         dump_px(fibnum, &hdr, rc->rc_rt, &rnd, &nw);
958         nlmsg_flush(&nw);
959 }
960
961 static const struct rtnl_cmd_handler cmd_handlers[] = {
962         {
963                 .cmd = NL_RTM_GETROUTE,
964                 .name = "RTM_GETROUTE",
965                 .cb = &rtnl_handle_getroute,
966         },
967         {
968                 .cmd = NL_RTM_DELROUTE,
969                 .name = "RTM_DELROUTE",
970                 .cb = &rtnl_handle_delroute,
971                 .priv = PRIV_NET_ROUTE,
972         },
973         {
974                 .cmd = NL_RTM_NEWROUTE,
975                 .name = "RTM_NEWROUTE",
976                 .cb = &rtnl_handle_newroute,
977                 .priv = PRIV_NET_ROUTE,
978         }
979 };
980
981 static const struct nlhdr_parser *all_parsers[] = {&mpath_parser, &metrics_parser, &rtm_parser};
982
983 void
984 rtnl_routes_init()
985 {
986         NL_VERIFY_PARSERS(all_parsers);
987         rtnl_register_messages(cmd_handlers, NL_ARRAY_LEN(cmd_handlers));
988 }