]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/route/route_ctl.c
Make <add|del|change>_route() static to finish the transition to the new kpi.
[FreeBSD/FreeBSD.git] / sys / net / route / route_ctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Alexander V. Chernikov
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 #include "opt_mpath.h"
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/sysctl.h>
40 #include <sys/syslog.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/rmlock.h>
44
45 #include <net/if.h>
46 #include <net/if_var.h>
47 #include <net/if_dl.h>
48 #include <net/vnet.h>
49 #include <net/route.h>
50 #include <net/route/route_ctl.h>
51 #include <net/route/route_var.h>
52 #include <net/route/nhop_utils.h>
53 #include <net/route/nhop.h>
54 #include <net/route/nhop_var.h>
55 #include <net/route/shared.h>
56 #include <netinet/in.h>
57
58 #ifdef RADIX_MPATH
59 #include <net/radix_mpath.h>
60 #endif
61
62 #include <vm/uma.h>
63
64
65 /*
66  * This file contains control plane routing tables functions.
67  *
68  * All functions assumes they are called in net epoch.
69  */
70
71 struct rib_subscription {
72         CK_STAILQ_ENTRY(rib_subscription)       next;
73         rib_subscription_cb_t                   *func;
74         void                                    *arg;
75         enum rib_subscription_type              type;
76         struct epoch_context                    epoch_ctx;
77 };
78
79 static int add_route(struct rib_head *rnh, struct rt_addrinfo *info,
80     struct rib_cmd_info *rc);
81 static int del_route(struct rib_head *rnh, struct rt_addrinfo *info,
82     struct rib_cmd_info *rc);
83 static int change_route(struct rib_head *, struct rt_addrinfo *,
84     struct rib_cmd_info *rc);
85 static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
86     struct rib_cmd_info *rc);
87
88 static void destroy_subscription_epoch(epoch_context_t ctx);
89
90 static struct rib_head *
91 get_rnh(uint32_t fibnum, const struct rt_addrinfo *info)
92 {
93         struct rib_head *rnh;
94         struct sockaddr *dst;
95
96         KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum"));
97
98         dst = info->rti_info[RTAX_DST];
99         rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
100
101         return (rnh);
102 }
103
104 /*
105  * Adds route defined by @info into the kernel table specified by @fibnum and
106  * sa_family in @info->rti_info[RTAX_DST].
107  *
108  * Returns 0 on success and fills in operation metadata into @rc.
109  */
110 int
111 rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
112     struct rib_cmd_info *rc)
113 {
114         struct rib_head *rnh;
115
116         NET_EPOCH_ASSERT();
117
118         rnh = get_rnh(fibnum, info);
119         if (rnh == NULL)
120                 return (EAFNOSUPPORT);
121
122         /*
123          * Check consistency between RTF_HOST flag and netmask
124          * existence.
125          */
126         if (info->rti_flags & RTF_HOST)
127                 info->rti_info[RTAX_NETMASK] = NULL;
128         else if (info->rti_info[RTAX_NETMASK] == NULL)
129                 return (EINVAL);
130
131         bzero(rc, sizeof(struct rib_cmd_info));
132         rc->rc_cmd = RTM_ADD;
133
134         return (add_route(rnh, info, rc));
135 }
136
137 static int
138 add_route(struct rib_head *rnh, struct rt_addrinfo *info,
139     struct rib_cmd_info *rc)
140 {
141         struct sockaddr *dst, *ndst, *gateway, *netmask;
142         struct rtentry *rt, *rt_old;
143         struct nhop_object *nh;
144         struct radix_node *rn;
145         struct ifaddr *ifa;
146         int error, flags;
147         struct epoch_tracker et;
148
149         dst = info->rti_info[RTAX_DST];
150         gateway = info->rti_info[RTAX_GATEWAY];
151         netmask = info->rti_info[RTAX_NETMASK];
152         flags = info->rti_flags;
153
154         if ((flags & RTF_GATEWAY) && !gateway)
155                 return (EINVAL);
156         if (dst && gateway && (dst->sa_family != gateway->sa_family) && 
157             (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK))
158                 return (EINVAL);
159
160         if (dst->sa_len > sizeof(((struct rtentry *)NULL)->rt_dstb))
161                 return (EINVAL);
162
163         if (info->rti_ifa == NULL) {
164                 error = rt_getifa_fib(info, rnh->rib_fibnum);
165                 if (error)
166                         return (error);
167         } else {
168                 ifa_ref(info->rti_ifa);
169         }
170
171         NET_EPOCH_ENTER(et);
172         error = nhop_create_from_info(rnh, info, &nh);
173         NET_EPOCH_EXIT(et);
174         if (error != 0) {
175                 ifa_free(info->rti_ifa);
176                 return (error);
177         }
178
179         rt = uma_zalloc(V_rtzone, M_NOWAIT);
180         if (rt == NULL) {
181                 ifa_free(info->rti_ifa);
182                 nhop_free(nh);
183                 return (ENOBUFS);
184         }
185         rt->rt_flags = RTF_UP | flags;
186         rt->rt_nhop = nh;
187
188         /* Fill in dst */
189         memcpy(&rt->rt_dst, dst, dst->sa_len);
190         rt_key(rt) = &rt->rt_dst;
191
192         /*
193          * point to the (possibly newly malloc'd) dest address.
194          */
195         ndst = (struct sockaddr *)rt_key(rt);
196
197         /*
198          * make sure it contains the value we want (masked if needed).
199          */
200         if (netmask) {
201                 rt_maskedcopy(dst, ndst, netmask);
202         } else
203                 bcopy(dst, ndst, dst->sa_len);
204
205         /*
206          * We use the ifa reference returned by rt_getifa_fib().
207          * This moved from below so that rnh->rnh_addaddr() can
208          * examine the ifa and  ifa->ifa_ifp if it so desires.
209          */
210         ifa = info->rti_ifa;
211         rt->rt_weight = 1;
212
213         rt_setmetrics(info, rt);
214         rt_old = NULL;
215
216         RIB_WLOCK(rnh);
217         RT_LOCK(rt);
218 #ifdef RADIX_MPATH
219         /* do not permit exactly the same dst/mask/gw pair */
220         if (rt_mpath_capable(rnh) &&
221                 rt_mpath_conflict(rnh, rt, netmask)) {
222                 RIB_WUNLOCK(rnh);
223
224                 nhop_free(nh);
225                 uma_zfree(V_rtzone, rt);
226                 return (EEXIST);
227         }
228 #endif
229
230         rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes);
231
232         if (rn != NULL) {
233                 /* Most common usecase */
234                 if (rt->rt_expire > 0)
235                         tmproutes_update(rnh, rt);
236
237                 /* Finalize notification */
238                 rnh->rnh_gen++;
239
240                 rc->rc_rt = RNTORT(rn);
241                 rc->rc_nh_new = nh;
242
243                 rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
244         } else if ((info->rti_flags & RTF_PINNED) != 0) {
245
246                 /*
247                  * Force removal and re-try addition
248                  * TODO: better multipath&pinned support
249                  */
250                 struct sockaddr *info_dst = info->rti_info[RTAX_DST];
251                 info->rti_info[RTAX_DST] = ndst;
252                 /* Do not delete existing PINNED(interface) routes */
253                 info->rti_flags &= ~RTF_PINNED;
254                 rt_old = rt_unlinkrte(rnh, info, &error);
255                 info->rti_flags |= RTF_PINNED;
256                 info->rti_info[RTAX_DST] = info_dst;
257                 if (rt_old != NULL) {
258                         rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head,
259                             rt->rt_nodes);
260
261                         /* Finalize notification */
262                         rnh->rnh_gen++;
263
264                         if (rn != NULL) {
265                                 rc->rc_cmd = RTM_CHANGE;
266                                 rc->rc_rt = RNTORT(rn);
267                                 rc->rc_nh_old = rt_old->rt_nhop;
268                                 rc->rc_nh_new = nh;
269                         } else {
270                                 rc->rc_cmd = RTM_DELETE;
271                                 rc->rc_rt = RNTORT(rn);
272                                 rc->rc_nh_old = rt_old->rt_nhop;
273                                 rc->rc_nh_new = nh;
274                         }
275                         rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
276                 }
277         }
278         RIB_WUNLOCK(rnh);
279
280         if ((rn != NULL) || (rt_old != NULL))
281                 rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
282
283         if (rt_old != NULL)
284                 rtfree(rt_old);
285
286         /*
287          * If it still failed to go into the tree,
288          * then un-make it (this should be a function)
289          */
290         if (rn == NULL) {
291                 nhop_free(nh);
292                 uma_zfree(V_rtzone, rt);
293                 return (EEXIST);
294         }
295
296         RT_UNLOCK(rt);
297
298         return (0);
299 }
300
301
302 /*
303  * Removes route defined by @info from the kernel table specified by @fibnum and
304  * sa_family in @info->rti_info[RTAX_DST].
305  *
306  * Returns 0 on success and fills in operation metadata into @rc.
307  */
308 int
309 rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc)
310 {
311         struct rib_head *rnh;
312
313         NET_EPOCH_ASSERT();
314
315         rnh = get_rnh(fibnum, info);
316         if (rnh == NULL)
317                 return (EAFNOSUPPORT);
318
319         bzero(rc, sizeof(struct rib_cmd_info));
320         rc->rc_cmd = RTM_DELETE;
321
322         return (del_route(rnh, info, rc));
323 }
324
325 /*
326  * Conditionally unlinks rtentry matching data inside @info from @rnh.
327  * Returns unlinked, locked and referenced @rtentry on success,
328  * Returns NULL and sets @perror to:
329  * ESRCH - if prefix was not found,
330  * EADDRINUSE - if trying to delete PINNED route without appropriate flag.
331  * ENOENT - if supplied filter function returned 0 (not matched).
332  */
333 struct rtentry *
334 rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, int *perror)
335 {
336         struct sockaddr *dst, *netmask;
337         struct rtentry *rt;
338         struct radix_node *rn;
339
340         dst = info->rti_info[RTAX_DST];
341         netmask = info->rti_info[RTAX_NETMASK];
342
343         rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, &rnh->head);
344         if (rt == NULL) {
345                 *perror = ESRCH;
346                 return (NULL);
347         }
348
349         if ((info->rti_flags & RTF_PINNED) == 0) {
350                 /* Check if target route can be deleted */
351                 if (rt->rt_flags & RTF_PINNED) {
352                         *perror = EADDRINUSE;
353                         return (NULL);
354                 }
355         }
356
357         if (info->rti_filter != NULL) {
358                 if (info->rti_filter(rt, rt->rt_nhop, info->rti_filterdata)==0){
359                         /* Not matched */
360                         *perror = ENOENT;
361                         return (NULL);
362                 }
363
364                 /*
365                  * Filter function requested rte deletion.
366                  * Ease the caller work by filling in remaining info
367                  * from that particular entry.
368                  */
369                 info->rti_info[RTAX_GATEWAY] = &rt->rt_nhop->gw_sa;
370         }
371
372         /*
373          * Remove the item from the tree and return it.
374          * Complain if it is not there and do no more processing.
375          */
376         *perror = ESRCH;
377 #ifdef RADIX_MPATH
378         if (rt_mpath_capable(rnh))
379                 rn = rt_mpath_unlink(rnh, info, rt, perror);
380         else
381 #endif
382         rn = rnh->rnh_deladdr(dst, netmask, &rnh->head);
383         if (rn == NULL)
384                 return (NULL);
385
386         if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
387                 panic ("rtrequest delete");
388
389         rt = RNTORT(rn);
390         RT_LOCK(rt);
391         rt->rt_flags &= ~RTF_UP;
392
393         *perror = 0;
394
395         return (rt);
396 }
397
398 static int
399 del_route(struct rib_head *rnh, struct rt_addrinfo *info,
400     struct rib_cmd_info *rc)
401 {
402         struct sockaddr *dst, *netmask;
403         struct sockaddr_storage mdst;
404         struct rtentry *rt;
405         int error;
406
407         dst = info->rti_info[RTAX_DST];
408         netmask = info->rti_info[RTAX_NETMASK];
409
410         if (netmask) {
411                 if (dst->sa_len > sizeof(mdst))
412                         return (EINVAL);
413                 rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask);
414                 dst = (struct sockaddr *)&mdst;
415         }
416
417         RIB_WLOCK(rnh);
418         rt = rt_unlinkrte(rnh, info, &error);
419         if (rt != NULL) {
420                 /* Finalize notification */
421                 rnh->rnh_gen++;
422                 rc->rc_rt = rt;
423                 rc->rc_nh_old = rt->rt_nhop;
424                 rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
425         }
426         RIB_WUNLOCK(rnh);
427         if (error != 0)
428                 return (error);
429
430         rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
431
432         /*
433          * If the caller wants it, then it can have it,
434          * the entry will be deleted after the end of the current epoch.
435          */
436         rtfree(rt);
437
438         return (0);
439 }
440
441 int
442 rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
443     struct rib_cmd_info *rc)
444 {
445         struct rib_head *rnh;
446
447         NET_EPOCH_ASSERT();
448
449         rnh = get_rnh(fibnum, info);
450         if (rnh == NULL)
451                 return (EAFNOSUPPORT);
452
453         bzero(rc, sizeof(struct rib_cmd_info));
454         rc->rc_cmd = RTM_CHANGE;
455
456         return (change_route(rnh, info, rc));
457 }
458
459 static int
460 change_route_one(struct rib_head *rnh, struct rt_addrinfo *info,
461     struct rib_cmd_info *rc)
462 {
463         RIB_RLOCK_TRACKER;
464         struct rtentry *rt = NULL;
465         int error = 0;
466         int free_ifa = 0;
467         struct nhop_object *nh, *nh_orig;
468
469         RIB_RLOCK(rnh);
470         rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
471             info->rti_info[RTAX_NETMASK], &rnh->head);
472
473         if (rt == NULL) {
474                 RIB_RUNLOCK(rnh);
475                 return (ESRCH);
476         }
477
478 #ifdef RADIX_MPATH
479         /*
480          * If we got multipath routes,
481          * we require users to specify a matching RTAX_GATEWAY.
482          */
483         if (rt_mpath_capable(rnh)) {
484                 rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]);
485                 if (rt == NULL) {
486                         RIB_RUNLOCK(rnh);
487                         return (ESRCH);
488                 }
489         }
490 #endif
491         nh_orig = rt->rt_nhop;
492
493         RIB_RUNLOCK(rnh);
494
495         rt = NULL;
496         nh = NULL;
497
498         /*
499          * New gateway could require new ifaddr, ifp;
500          * flags may also be different; ifp may be specified
501          * by ll sockaddr when protocol address is ambiguous
502          */
503         if (((nh_orig->nh_flags & NHF_GATEWAY) &&
504             info->rti_info[RTAX_GATEWAY] != NULL) ||
505             info->rti_info[RTAX_IFP] != NULL ||
506             (info->rti_info[RTAX_IFA] != NULL &&
507              !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) {
508                 error = rt_getifa_fib(info, rnh->rib_fibnum);
509                 if (info->rti_ifa != NULL)
510                         free_ifa = 1;
511
512                 if (error != 0) {
513                         if (free_ifa) {
514                                 ifa_free(info->rti_ifa);
515                                 info->rti_ifa = NULL;
516                         }
517
518                         return (error);
519                 }
520         }
521
522         error = nhop_create_from_nhop(rnh, nh_orig, info, &nh);
523         if (free_ifa) {
524                 ifa_free(info->rti_ifa);
525                 info->rti_ifa = NULL;
526         }
527         if (error != 0)
528                 return (error);
529
530         RIB_WLOCK(rnh);
531
532         /* Lookup rtentry once again and check if nexthop is still the same */
533         rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
534             info->rti_info[RTAX_NETMASK], &rnh->head);
535
536         if (rt == NULL) {
537                 RIB_WUNLOCK(rnh);
538                 nhop_free(nh);
539                 return (ESRCH);
540         }
541
542         if (rt->rt_nhop != nh_orig) {
543                 RIB_WUNLOCK(rnh);
544                 nhop_free(nh);
545                 return (EAGAIN);
546         }
547
548         /* Proceed with the update */
549         RT_LOCK(rt);
550
551         /* Provide notification to the protocols.*/
552         rt->rt_nhop = nh;
553         rt_setmetrics(info, rt);
554
555         /* Finalize notification */
556         rc->rc_rt = rt;
557         rc->rc_nh_old = nh_orig;
558         rc->rc_nh_new = rt->rt_nhop;
559
560         RT_UNLOCK(rt);
561
562         /* Update generation id to reflect rtable change */
563         rnh->rnh_gen++;
564         rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
565
566         RIB_WUNLOCK(rnh);
567
568         rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
569
570         nhop_free(nh_orig);
571
572         return (0);
573 }
574
575 static int
576 change_route(struct rib_head *rnh, struct rt_addrinfo *info,
577     struct rib_cmd_info *rc)
578 {
579         int error;
580
581         /* Check if updated gateway exists */
582         if ((info->rti_flags & RTF_GATEWAY) &&
583             (info->rti_info[RTAX_GATEWAY] == NULL))
584                 return (EINVAL);
585
586         /*
587          * route change is done in multiple steps, with dropping and
588          * reacquiring lock. In the situations with multiple processes
589          * changes the same route in can lead to the case when route
590          * is changed between the steps. Address it by retrying the operation
591          * multiple times before failing.
592          */
593         for (int i = 0; i < RIB_MAX_RETRIES; i++) {
594                 error = change_route_one(rnh, info, rc);
595                 if (error != EAGAIN)
596                         break;
597         }
598
599         return (error);
600 }
601
602 /*
603  * Performs modification of routing table specificed by @action.
604  * Table is specified by @fibnum and sa_family in @info->rti_info[RTAX_DST].
605  * Needs to be run in network epoch.
606  *
607  * Returns 0 on success and fills in @rc with action result.
608  */
609 int
610 rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
611     struct rib_cmd_info *rc)
612 {
613         int error;
614
615         switch (action) {
616         case RTM_ADD:
617                 error = rib_add_route(fibnum, info, rc);
618                 break;
619         case RTM_DELETE:
620                 error = rib_del_route(fibnum, info, rc);
621                 break;
622         case RTM_CHANGE:
623                 error = rib_change_route(fibnum, info, rc);
624                 break;
625         default:
626                 error = ENOTSUP;
627         }
628
629         return (error);
630 }
631
632
633 struct rt_delinfo
634 {
635         struct rt_addrinfo info;
636         struct rib_head *rnh;
637         struct rtentry *head;
638         struct rib_cmd_info rc;
639 };
640
641 /*
642  * Conditionally unlinks @rn from radix tree based
643  * on info data passed in @arg.
644  */
645 static int
646 rt_checkdelroute(struct radix_node *rn, void *arg)
647 {
648         struct rt_delinfo *di;
649         struct rt_addrinfo *info;
650         struct rtentry *rt;
651         int error;
652
653         di = (struct rt_delinfo *)arg;
654         rt = (struct rtentry *)rn;
655         info = &di->info;
656         error = 0;
657
658         info->rti_info[RTAX_DST] = rt_key(rt);
659         info->rti_info[RTAX_NETMASK] = rt_mask(rt);
660         info->rti_info[RTAX_GATEWAY] = &rt->rt_nhop->gw_sa;
661
662         rt = rt_unlinkrte(di->rnh, info, &error);
663         if (rt == NULL) {
664                 /* Either not allowed or not matched. Skip entry */
665                 return (0);
666         }
667
668         /* Entry was unlinked. Notify subscribers */
669         di->rnh->rnh_gen++;
670         di->rc.rc_rt = rt;
671         di->rc.rc_nh_old = rt->rt_nhop;
672         rib_notify(di->rnh, RIB_NOTIFY_IMMEDIATE, &di->rc);
673
674         /* Add to the list and return */
675         rt->rt_chain = di->head;
676         di->head = rt;
677
678         return (0);
679 }
680
681 /*
682  * Iterates over a routing table specified by @fibnum and @family and
683  *  deletes elements marked by @filter_f.
684  * @fibnum: rtable id
685  * @family: AF_ address family
686  * @filter_f: function returning non-zero value for items to delete
687  * @arg: data to pass to the @filter_f function
688  * @report: true if rtsock notification is needed.
689  */
690 void
691 rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report)
692 {
693         struct rib_head *rnh;
694         struct rt_delinfo di;
695         struct rtentry *rt;
696         struct epoch_tracker et;
697
698         rnh = rt_tables_get_rnh(fibnum, family);
699         if (rnh == NULL)
700                 return;
701
702         bzero(&di, sizeof(di));
703         di.info.rti_filter = filter_f;
704         di.info.rti_filterdata = arg;
705         di.rnh = rnh;
706         di.rc.rc_cmd = RTM_DELETE;
707
708         NET_EPOCH_ENTER(et);
709
710         RIB_WLOCK(rnh);
711         rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
712         RIB_WUNLOCK(rnh);
713
714         /* We might have something to reclaim. */
715         while (di.head != NULL) {
716                 rt = di.head;
717                 di.head = rt->rt_chain;
718                 rt->rt_chain = NULL;
719
720                 di.rc.rc_rt = rt;
721                 di.rc.rc_nh_old = rt->rt_nhop;
722                 rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc);
723
724                 /* TODO std rt -> rt_addrinfo export */
725                 di.info.rti_info[RTAX_DST] = rt_key(rt);
726                 di.info.rti_info[RTAX_NETMASK] = rt_mask(rt);
727
728                 if (report)
729                         rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0,
730                             fibnum);
731                 rtfree(rt);
732         }
733
734         NET_EPOCH_EXIT(et);
735 }
736
737 static void
738 rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
739     struct rib_cmd_info *rc)
740 {
741         struct rib_subscription *rs;
742
743         CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
744                 if (rs->type == type)
745                         rs->func(rnh, rc, rs->arg);
746         }
747 }
748
749 /*
750  * Subscribe for the changes in the routing table specified by @fibnum and
751  *  @family.
752  *
753  * Returns pointer to the subscription structure on success.
754  */
755 struct rib_subscription *
756 rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
757     enum rib_subscription_type type, bool waitok)
758 {
759         struct rib_head *rnh;
760         struct rib_subscription *rs;
761         struct epoch_tracker et;
762         int flags = M_ZERO | (waitok ? M_WAITOK : 0);
763
764         rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
765         if (rs == NULL)
766                 return (NULL);
767
768         NET_EPOCH_ENTER(et);
769         KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
770         rnh = rt_tables_get_rnh(fibnum, family);
771
772         rs->func = f;
773         rs->arg = arg;
774         rs->type = type;
775
776         RIB_WLOCK(rnh);
777         CK_STAILQ_INSERT_TAIL(&rnh->rnh_subscribers, rs, next);
778         RIB_WUNLOCK(rnh);
779         NET_EPOCH_EXIT(et);
780
781         return (rs);
782 }
783
784 /*
785  * Remove rtable subscription @rs from the table specified by @fibnum
786  *  and @family.
787  * Needs to be run in network epoch.
788  *
789  * Returns 0 on success.
790  */
791 int
792 rib_unsibscribe(uint32_t fibnum, int family, struct rib_subscription *rs)
793 {
794         struct rib_head *rnh;
795
796         NET_EPOCH_ASSERT();
797         KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
798         rnh = rt_tables_get_rnh(fibnum, family);
799
800         if (rnh == NULL)
801                 return (ENOENT);
802
803         RIB_WLOCK(rnh);
804         CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
805         RIB_WUNLOCK(rnh);
806
807         epoch_call(net_epoch_preempt, destroy_subscription_epoch,
808             &rs->epoch_ctx);
809
810         return (0);
811 }
812
813 /*
814  * Epoch callback indicating subscription is safe to destroy
815  */
816 static void
817 destroy_subscription_epoch(epoch_context_t ctx)
818 {
819         struct rib_subscription *rs;
820
821         rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
822
823         free(rs, M_RTABLE);
824 }
825
826 void
827 rib_init_subscriptions(struct rib_head *rnh)
828 {
829
830         CK_STAILQ_INIT(&rnh->rnh_subscribers);
831 }
832
833 void
834 rib_destroy_subscriptions(struct rib_head *rnh)
835 {
836         struct rib_subscription *rs;
837         struct epoch_tracker et;
838
839         NET_EPOCH_ENTER(et);
840         RIB_WLOCK(rnh);
841         while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
842                 CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
843                 epoch_call(net_epoch_preempt, destroy_subscription_epoch,
844                     &rs->epoch_ctx);
845         }
846         RIB_WUNLOCK(rnh);
847         NET_EPOCH_EXIT(et);
848 }
849