]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/route/route_ctl.c
Revert uma zone alignemnt cache unadvertenly committed in r364950.
[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 <netinet/in.h>
56
57 #ifdef RADIX_MPATH
58 #include <net/radix_mpath.h>
59 #endif
60
61 #include <vm/uma.h>
62
63
64 /*
65  * This file contains control plane routing tables functions.
66  *
67  * All functions assumes they are called in net epoch.
68  */
69
70 struct rib_subscription {
71         CK_STAILQ_ENTRY(rib_subscription)       next;
72         rib_subscription_cb_t                   *func;
73         void                                    *arg;
74         enum rib_subscription_type              type;
75         struct epoch_context                    epoch_ctx;
76 };
77
78 static int add_route(struct rib_head *rnh, struct rt_addrinfo *info,
79     struct rib_cmd_info *rc);
80 static int add_route_nhop(struct rib_head *rnh, struct rtentry *rt,
81     struct rt_addrinfo *info, struct route_nhop_data *rnd,
82     struct rib_cmd_info *rc);
83 static int del_route(struct rib_head *rnh, struct rt_addrinfo *info,
84     struct rib_cmd_info *rc);
85 static int change_route(struct rib_head *rnh, struct rt_addrinfo *info,
86     struct route_nhop_data *nhd_orig, struct rib_cmd_info *rc);
87 static int change_route_nhop(struct rib_head *rnh, struct rtentry *rt,
88     struct rt_addrinfo *info, struct route_nhop_data *rnd,
89     struct rib_cmd_info *rc);
90 static void rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
91     struct rib_cmd_info *rc);
92
93 static void destroy_subscription_epoch(epoch_context_t ctx);
94
95 /* Routing table UMA zone */
96 VNET_DEFINE_STATIC(uma_zone_t, rtzone);
97 #define V_rtzone        VNET(rtzone)
98
99 void
100 vnet_rtzone_init()
101 {
102         
103         V_rtzone = uma_zcreate("rtentry", sizeof(struct rtentry),
104                 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
105 }
106
107 #ifdef VIMAGE
108 void
109 vnet_rtzone_destroy()
110 {
111
112         uma_zdestroy(V_rtzone);
113 }
114 #endif
115
116 static void
117 destroy_rtentry(struct rtentry *rt)
118 {
119
120         /*
121          * At this moment rnh, nh_control may be already freed.
122          * nhop interface may have been migrated to a different vnet.
123          * Use vnet stored in the nexthop to delete the entry.
124          */
125         CURVNET_SET(nhop_get_vnet(rt->rt_nhop));
126
127         /* Unreference nexthop */
128         nhop_free(rt->rt_nhop);
129
130         uma_zfree(V_rtzone, rt);
131
132         CURVNET_RESTORE();
133 }
134
135 /*
136  * Epoch callback indicating rtentry is safe to destroy
137  */
138 static void
139 destroy_rtentry_epoch(epoch_context_t ctx)
140 {
141         struct rtentry *rt;
142
143         rt = __containerof(ctx, struct rtentry, rt_epoch_ctx);
144
145         destroy_rtentry(rt);
146 }
147
148 /*
149  * Schedule rtentry deletion
150  */
151 static void
152 rtfree(struct rtentry *rt)
153 {
154
155         KASSERT(rt != NULL, ("%s: NULL rt", __func__));
156
157         epoch_call(net_epoch_preempt, destroy_rtentry_epoch,
158             &rt->rt_epoch_ctx);
159 }
160
161
162
163 static struct rib_head *
164 get_rnh(uint32_t fibnum, const struct rt_addrinfo *info)
165 {
166         struct rib_head *rnh;
167         struct sockaddr *dst;
168
169         KASSERT((fibnum < rt_numfibs), ("rib_add_route: bad fibnum"));
170
171         dst = info->rti_info[RTAX_DST];
172         rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
173
174         return (rnh);
175 }
176
177 /*
178  * Adds route defined by @info into the kernel table specified by @fibnum and
179  * sa_family in @info->rti_info[RTAX_DST].
180  *
181  * Returns 0 on success and fills in operation metadata into @rc.
182  */
183 int
184 rib_add_route(uint32_t fibnum, struct rt_addrinfo *info,
185     struct rib_cmd_info *rc)
186 {
187         struct rib_head *rnh;
188
189         NET_EPOCH_ASSERT();
190
191         rnh = get_rnh(fibnum, info);
192         if (rnh == NULL)
193                 return (EAFNOSUPPORT);
194
195         /*
196          * Check consistency between RTF_HOST flag and netmask
197          * existence.
198          */
199         if (info->rti_flags & RTF_HOST)
200                 info->rti_info[RTAX_NETMASK] = NULL;
201         else if (info->rti_info[RTAX_NETMASK] == NULL)
202                 return (EINVAL);
203
204         bzero(rc, sizeof(struct rib_cmd_info));
205         rc->rc_cmd = RTM_ADD;
206
207         return (add_route(rnh, info, rc));
208 }
209
210 /*
211  * Creates rtentry and nexthop based on @info data.
212  * Return 0 and fills in rtentry into @prt on success,
213  * return errno otherwise.
214  */
215 static int
216 create_rtentry(struct rib_head *rnh, struct rt_addrinfo *info,
217     struct rtentry **prt)
218 {
219         struct sockaddr *dst, *ndst, *gateway, *netmask;
220         struct rtentry *rt;
221         struct nhop_object *nh;
222         struct ifaddr *ifa;
223         int error, flags;
224
225         dst = info->rti_info[RTAX_DST];
226         gateway = info->rti_info[RTAX_GATEWAY];
227         netmask = info->rti_info[RTAX_NETMASK];
228         flags = info->rti_flags;
229
230         if ((flags & RTF_GATEWAY) && !gateway)
231                 return (EINVAL);
232         if (dst && gateway && (dst->sa_family != gateway->sa_family) && 
233             (gateway->sa_family != AF_UNSPEC) && (gateway->sa_family != AF_LINK))
234                 return (EINVAL);
235
236         if (dst->sa_len > sizeof(((struct rtentry *)NULL)->rt_dstb))
237                 return (EINVAL);
238
239         if (info->rti_ifa == NULL) {
240                 error = rt_getifa_fib(info, rnh->rib_fibnum);
241                 if (error)
242                         return (error);
243         } else {
244                 ifa_ref(info->rti_ifa);
245         }
246
247         error = nhop_create_from_info(rnh, info, &nh);
248         if (error != 0) {
249                 ifa_free(info->rti_ifa);
250                 return (error);
251         }
252
253         rt = uma_zalloc(V_rtzone, M_NOWAIT | M_ZERO);
254         if (rt == NULL) {
255                 ifa_free(info->rti_ifa);
256                 nhop_free(nh);
257                 return (ENOBUFS);
258         }
259         rt->rte_flags = RTF_UP | flags;
260         rt->rt_nhop = nh;
261
262         /* Fill in dst */
263         memcpy(&rt->rt_dst, dst, dst->sa_len);
264         rt_key(rt) = &rt->rt_dst;
265
266         /*
267          * point to the (possibly newly malloc'd) dest address.
268          */
269         ndst = (struct sockaddr *)rt_key(rt);
270
271         /*
272          * make sure it contains the value we want (masked if needed).
273          */
274         if (netmask) {
275                 rt_maskedcopy(dst, ndst, netmask);
276         } else
277                 bcopy(dst, ndst, dst->sa_len);
278
279         /*
280          * We use the ifa reference returned by rt_getifa_fib().
281          * This moved from below so that rnh->rnh_addaddr() can
282          * examine the ifa and  ifa->ifa_ifp if it so desires.
283          */
284         ifa = info->rti_ifa;
285         rt->rt_weight = 1;
286
287         rt_setmetrics(info, rt);
288
289         *prt = rt;
290         return (0);
291 }
292
293 static int
294 add_route(struct rib_head *rnh, struct rt_addrinfo *info,
295     struct rib_cmd_info *rc)
296 {
297         struct sockaddr *ndst, *netmask;
298         struct route_nhop_data rnd;
299         struct nhop_object *nh;
300         struct rtentry *rt;
301         int error;
302
303         error = create_rtentry(rnh, info, &rt);
304         if (error != 0)
305                 return (error);
306
307         rnd.rnd_nhop = rt->rt_nhop;
308         rnd.rnd_weight = rt->rt_weight;
309         nh = rt->rt_nhop;
310
311         RIB_WLOCK(rnh);
312 #ifdef RADIX_MPATH
313         netmask = info->rti_info[RTAX_NETMASK];
314         /* do not permit exactly the same dst/mask/gw pair */
315         if (rt_mpath_capable(rnh) &&
316                 rt_mpath_conflict(rnh, rt, netmask)) {
317                 RIB_WUNLOCK(rnh);
318
319                 nhop_free(nh);
320                 uma_zfree(V_rtzone, rt);
321                 return (EEXIST);
322         }
323 #endif
324         error = add_route_nhop(rnh, rt, info, &rnd, rc);
325         if (error == 0) {
326                 rt = NULL;
327                 nh = NULL;
328         } else if ((error == EEXIST) && ((info->rti_flags & RTF_PINNED) != 0)) {
329                 struct rtentry *rt_orig;
330                 struct nhop_object *nh_orig;
331                 struct radix_node *rn;
332
333                 ndst = (struct sockaddr *)rt_key(rt);
334                 netmask = info->rti_info[RTAX_NETMASK];
335                 rn = rnh->rnh_lookup(ndst, netmask, &rnh->head);
336                 rt_orig = (struct rtentry *)rn;
337                 if (rt_orig != NULL) {
338                         nh_orig = rt_orig->rt_nhop;
339                         if ((nhop_get_rtflags(nh_orig) & RTF_PINNED) == 0) {
340                                 /* Current nexhop is not PINNED, can update */
341                                 error = change_route_nhop(rnh, rt_orig,
342                                     info, &rnd, rc);
343                                 if (error == 0)
344                                         nh = NULL;
345                         }
346                 } else
347                         error = ENOBUFS;
348         }
349         RIB_WUNLOCK(rnh);
350
351         if (error == 0)
352                 rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
353
354         if (nh != NULL)
355                 nhop_free(nh);
356         if (rt != NULL)
357                 uma_zfree(V_rtzone, rt);
358
359         return (error);
360 }
361
362
363 /*
364  * Removes route defined by @info from the kernel table specified by @fibnum and
365  * sa_family in @info->rti_info[RTAX_DST].
366  *
367  * Returns 0 on success and fills in operation metadata into @rc.
368  */
369 int
370 rib_del_route(uint32_t fibnum, struct rt_addrinfo *info, struct rib_cmd_info *rc)
371 {
372         struct rib_head *rnh;
373
374         NET_EPOCH_ASSERT();
375
376         rnh = get_rnh(fibnum, info);
377         if (rnh == NULL)
378                 return (EAFNOSUPPORT);
379
380         bzero(rc, sizeof(struct rib_cmd_info));
381         rc->rc_cmd = RTM_DELETE;
382
383         return (del_route(rnh, info, rc));
384 }
385
386 /*
387  * Conditionally unlinks rtentry matching data inside @info from @rnh.
388  * Returns unlinked, locked and referenced @rtentry on success,
389  * Returns NULL and sets @perror to:
390  * ESRCH - if prefix was not found,
391  * EADDRINUSE - if trying to delete PINNED route without appropriate flag.
392  * ENOENT - if supplied filter function returned 0 (not matched).
393  */
394 struct rtentry *
395 rt_unlinkrte(struct rib_head *rnh, struct rt_addrinfo *info, int *perror)
396 {
397         struct sockaddr *dst, *netmask;
398         struct rtentry *rt;
399         struct nhop_object *nh;
400         struct radix_node *rn;
401
402         dst = info->rti_info[RTAX_DST];
403         netmask = info->rti_info[RTAX_NETMASK];
404
405         rt = (struct rtentry *)rnh->rnh_lookup(dst, netmask, &rnh->head);
406         if (rt == NULL) {
407                 *perror = ESRCH;
408                 return (NULL);
409         }
410
411         nh = rt->rt_nhop;
412
413         if ((info->rti_flags & RTF_PINNED) == 0) {
414                 /* Check if target route can be deleted */
415                 if (NH_IS_PINNED(nh)) {
416                         *perror = EADDRINUSE;
417                         return (NULL);
418                 }
419         }
420
421         if (info->rti_filter != NULL) {
422                 if (info->rti_filter(rt, nh, info->rti_filterdata)==0){
423                         /* Not matched */
424                         *perror = ENOENT;
425                         return (NULL);
426                 }
427
428                 /*
429                  * Filter function requested rte deletion.
430                  * Ease the caller work by filling in remaining info
431                  * from that particular entry.
432                  */
433                 info->rti_info[RTAX_GATEWAY] = &nh->gw_sa;
434         }
435
436         /*
437          * Remove the item from the tree and return it.
438          * Complain if it is not there and do no more processing.
439          */
440         *perror = ESRCH;
441 #ifdef RADIX_MPATH
442         if (rt_mpath_capable(rnh))
443                 rn = rt_mpath_unlink(rnh, info, rt, perror);
444         else
445 #endif
446         rn = rnh->rnh_deladdr(dst, netmask, &rnh->head);
447         if (rn == NULL)
448                 return (NULL);
449
450         if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
451                 panic ("rtrequest delete");
452
453         rt = RNTORT(rn);
454         rt->rte_flags &= ~RTF_UP;
455
456         *perror = 0;
457
458         return (rt);
459 }
460
461 static int
462 del_route(struct rib_head *rnh, struct rt_addrinfo *info,
463     struct rib_cmd_info *rc)
464 {
465         struct sockaddr *dst, *netmask;
466         struct sockaddr_storage mdst;
467         struct rtentry *rt;
468         int error;
469
470         dst = info->rti_info[RTAX_DST];
471         netmask = info->rti_info[RTAX_NETMASK];
472
473         if (netmask) {
474                 if (dst->sa_len > sizeof(mdst))
475                         return (EINVAL);
476                 rt_maskedcopy(dst, (struct sockaddr *)&mdst, netmask);
477                 dst = (struct sockaddr *)&mdst;
478         }
479
480         RIB_WLOCK(rnh);
481         rt = rt_unlinkrte(rnh, info, &error);
482         if (rt != NULL) {
483                 /* Finalize notification */
484                 rnh->rnh_gen++;
485                 rc->rc_rt = rt;
486                 rc->rc_nh_old = rt->rt_nhop;
487                 rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
488         }
489         RIB_WUNLOCK(rnh);
490         if (error != 0)
491                 return (error);
492
493         rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
494
495         /*
496          * If the caller wants it, then it can have it,
497          * the entry will be deleted after the end of the current epoch.
498          */
499         rtfree(rt);
500
501         return (0);
502 }
503
504 int
505 rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
506     struct rib_cmd_info *rc)
507 {
508         RIB_RLOCK_TRACKER;
509         struct route_nhop_data rnd_orig;
510         struct rib_head *rnh;
511         struct rtentry *rt;
512         int error;
513
514         NET_EPOCH_ASSERT();
515
516         rnh = get_rnh(fibnum, info);
517         if (rnh == NULL)
518                 return (EAFNOSUPPORT);
519
520         bzero(rc, sizeof(struct rib_cmd_info));
521         rc->rc_cmd = RTM_CHANGE;
522
523         /* Check if updated gateway exists */
524         if ((info->rti_flags & RTF_GATEWAY) &&
525             (info->rti_info[RTAX_GATEWAY] == NULL))
526                 return (EINVAL);
527
528         /*
529          * route change is done in multiple steps, with dropping and
530          * reacquiring lock. In the situations with multiple processes
531          * changes the same route in can lead to the case when route
532          * is changed between the steps. Address it by retrying the operation
533          * multiple times before failing.
534          */
535
536         RIB_RLOCK(rnh);
537         rt = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
538             info->rti_info[RTAX_NETMASK], &rnh->head);
539
540         if (rt == NULL) {
541                 RIB_RUNLOCK(rnh);
542                 return (ESRCH);
543         }
544
545 #ifdef RADIX_MPATH
546         /*
547          * If we got multipath routes,
548          * we require users to specify a matching RTAX_GATEWAY.
549          */
550         if (rt_mpath_capable(rnh)) {
551                 rt = rt_mpath_matchgate(rt, info->rti_info[RTAX_GATEWAY]);
552                 if (rt == NULL) {
553                         RIB_RUNLOCK(rnh);
554                         return (ESRCH);
555                 }
556         }
557 #endif
558         rnd_orig.rnd_nhop = rt->rt_nhop;
559         rnd_orig.rnd_weight = rt->rt_weight;
560
561         RIB_RUNLOCK(rnh);
562
563         for (int i = 0; i < RIB_MAX_RETRIES; i++) {
564                 error = change_route(rnh, info, &rnd_orig, rc);
565                 if (error != EAGAIN)
566                         break;
567         }
568
569         return (error);
570 }
571
572 static int
573 change_route(struct rib_head *rnh, struct rt_addrinfo *info,
574     struct route_nhop_data *rnd_orig, struct rib_cmd_info *rc)
575 {
576         int error = 0;
577         int free_ifa = 0;
578         struct nhop_object *nh, *nh_orig;
579         struct route_nhop_data rnd_new;
580
581         nh = NULL;
582         nh_orig = rnd_orig->rnd_nhop;
583         if (nh_orig == NULL)
584                 return (ESRCH);
585
586         /*
587          * New gateway could require new ifaddr, ifp;
588          * flags may also be different; ifp may be specified
589          * by ll sockaddr when protocol address is ambiguous
590          */
591         if (((nh_orig->nh_flags & NHF_GATEWAY) &&
592             info->rti_info[RTAX_GATEWAY] != NULL) ||
593             info->rti_info[RTAX_IFP] != NULL ||
594             (info->rti_info[RTAX_IFA] != NULL &&
595              !sa_equal(info->rti_info[RTAX_IFA], nh_orig->nh_ifa->ifa_addr))) {
596                 error = rt_getifa_fib(info, rnh->rib_fibnum);
597                 if (info->rti_ifa != NULL)
598                         free_ifa = 1;
599
600                 if (error != 0) {
601                         if (free_ifa) {
602                                 ifa_free(info->rti_ifa);
603                                 info->rti_ifa = NULL;
604                         }
605
606                         return (error);
607                 }
608         }
609
610         error = nhop_create_from_nhop(rnh, nh_orig, info, &nh);
611         if (free_ifa) {
612                 ifa_free(info->rti_ifa);
613                 info->rti_ifa = NULL;
614         }
615         if (error != 0)
616                 return (error);
617
618         rnd_new.rnd_nhop = nh;
619         if (info->rti_mflags & RTV_WEIGHT)
620                 rnd_new.rnd_weight = info->rti_rmx->rmx_weight;
621         else
622                 rnd_new.rnd_weight = rnd_orig->rnd_weight;
623
624         error = change_route_conditional(rnh, NULL, info, rnd_orig, &rnd_new, rc);
625
626         return (error);
627 }
628
629 /*
630  * Insert @rt with nhop data from @rnd_new to @rnh.
631  * Returns 0 on success.
632  */
633 static int
634 add_route_nhop(struct rib_head *rnh, struct rtentry *rt,
635     struct rt_addrinfo *info, struct route_nhop_data *rnd,
636     struct rib_cmd_info *rc)
637 {
638         struct sockaddr *ndst, *netmask;
639         struct radix_node *rn;
640         int error = 0;
641
642         RIB_WLOCK_ASSERT(rnh);
643
644         ndst = (struct sockaddr *)rt_key(rt);
645         netmask = info->rti_info[RTAX_NETMASK];
646
647         rt->rt_nhop = rnd->rnd_nhop;
648         rt->rt_weight = rnd->rnd_weight;
649         rn = rnh->rnh_addaddr(ndst, netmask, &rnh->head, rt->rt_nodes);
650
651         if (rn != NULL) {
652                 if (rt->rt_expire > 0)
653                         tmproutes_update(rnh, rt);
654
655                 /* Finalize notification */
656                 rnh->rnh_gen++;
657
658                 rc->rc_cmd = RTM_ADD;
659                 rc->rc_rt = rt;
660                 rc->rc_nh_old = NULL;
661                 rc->rc_nh_new = rnd->rnd_nhop;
662                 rc->rc_nh_weight = rnd->rnd_weight;
663
664                 rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
665         } else {
666                 /* Existing route or memory allocation failure */
667                 error = EEXIST;
668         }
669
670         return (error);
671 }
672
673 /*
674  * Switch @rt nhop/weigh to the ones specified in @rnd.
675  *  Conditionally set rt_expire if set in @info.
676  * Returns 0 on success.
677  */
678 static int
679 change_route_nhop(struct rib_head *rnh, struct rtentry *rt,
680     struct rt_addrinfo *info, struct route_nhop_data *rnd,
681     struct rib_cmd_info *rc)
682 {
683         struct nhop_object *nh_orig;
684
685         RIB_WLOCK_ASSERT(rnh);
686
687         nh_orig = rt->rt_nhop;
688
689         if (rnd->rnd_nhop != NULL) {
690                 /* Changing expiration & nexthop & weight to a new one */
691                 rt_setmetrics(info, rt);
692                 rt->rt_nhop = rnd->rnd_nhop;
693                 rt->rt_weight = rnd->rnd_weight;
694                 if (rt->rt_expire > 0)
695                         tmproutes_update(rnh, rt);
696         } else {
697                 /* Route deletion requested. */
698                 struct sockaddr *ndst, *netmask;
699                 struct radix_node *rn;
700
701                 ndst = (struct sockaddr *)rt_key(rt);
702                 netmask = info->rti_info[RTAX_NETMASK];
703                 rn = rnh->rnh_deladdr(ndst, netmask, &rnh->head);
704                 if (rn == NULL)
705                         return (ESRCH);
706         }
707
708         /* Finalize notification */
709         rnh->rnh_gen++;
710
711         rc->rc_cmd = (rnd->rnd_nhop != NULL) ? RTM_CHANGE : RTM_DELETE;
712         rc->rc_rt = rt;
713         rc->rc_nh_old = nh_orig;
714         rc->rc_nh_new = rnd->rnd_nhop;
715         rc->rc_nh_weight = rnd->rnd_weight;
716
717         rib_notify(rnh, RIB_NOTIFY_IMMEDIATE, rc);
718
719         return (0);
720 }
721
722 /*
723  * Conditionally update route nhop/weight IFF data in @nhd_orig is
724  *  consistent with the current route data.
725  * Nexthop in @nhd_new is consumed.
726  */
727 int
728 change_route_conditional(struct rib_head *rnh, struct rtentry *rt,
729     struct rt_addrinfo *info, struct route_nhop_data *rnd_orig,
730     struct route_nhop_data *rnd_new, struct rib_cmd_info *rc)
731 {
732         struct rtentry *rt_new;
733         int error = 0;
734
735         RIB_WLOCK(rnh);
736
737         rt_new = (struct rtentry *)rnh->rnh_lookup(info->rti_info[RTAX_DST],
738             info->rti_info[RTAX_NETMASK], &rnh->head);
739
740         if (rt_new == NULL) {
741                 if (rnd_orig->rnd_nhop == NULL)
742                         error = add_route_nhop(rnh, rt, info, rnd_new, rc);
743                 else {
744                         /*
745                          * Prefix does not exist, which was not our assumption.
746                          * Update @rnd_orig with the new data and return
747                          */
748                         rnd_orig->rnd_nhop = NULL;
749                         rnd_orig->rnd_weight = 0;
750                         error = EAGAIN;
751                 }
752         } else {
753                 /* Prefix exists, try to update */
754                 if (rnd_orig->rnd_nhop == rt_new->rt_nhop) {
755
756                         /*
757                          * Nhop/mpath group hasn't changed. Flip
758                          * to the new precalculated one and return
759                          */
760                         error = change_route_nhop(rnh, rt_new, info, rnd_new, rc);
761                 } else {
762                         /* Update and retry */
763                         rnd_orig->rnd_nhop = rt_new->rt_nhop;
764                         rnd_orig->rnd_weight = rt_new->rt_weight;
765                         error = EAGAIN;
766                 }
767         }
768
769         RIB_WUNLOCK(rnh);
770
771         if (error == 0) {
772                 rib_notify(rnh, RIB_NOTIFY_DELAYED, rc);
773
774                 if (rnd_orig->rnd_nhop != NULL)
775                         nhop_free_any(rnd_orig->rnd_nhop);
776
777         } else {
778                 if (rnd_new->rnd_nhop != NULL)
779                         nhop_free_any(rnd_new->rnd_nhop);
780         }
781
782         return (error);
783 }
784
785 /*
786  * Performs modification of routing table specificed by @action.
787  * Table is specified by @fibnum and sa_family in @info->rti_info[RTAX_DST].
788  * Needs to be run in network epoch.
789  *
790  * Returns 0 on success and fills in @rc with action result.
791  */
792 int
793 rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
794     struct rib_cmd_info *rc)
795 {
796         int error;
797
798         switch (action) {
799         case RTM_ADD:
800                 error = rib_add_route(fibnum, info, rc);
801                 break;
802         case RTM_DELETE:
803                 error = rib_del_route(fibnum, info, rc);
804                 break;
805         case RTM_CHANGE:
806                 error = rib_change_route(fibnum, info, rc);
807                 break;
808         default:
809                 error = ENOTSUP;
810         }
811
812         return (error);
813 }
814
815
816 struct rt_delinfo
817 {
818         struct rt_addrinfo info;
819         struct rib_head *rnh;
820         struct rtentry *head;
821         struct rib_cmd_info rc;
822 };
823
824 /*
825  * Conditionally unlinks @rn from radix tree based
826  * on info data passed in @arg.
827  */
828 static int
829 rt_checkdelroute(struct radix_node *rn, void *arg)
830 {
831         struct rt_delinfo *di;
832         struct rt_addrinfo *info;
833         struct rtentry *rt;
834         int error;
835
836         di = (struct rt_delinfo *)arg;
837         rt = (struct rtentry *)rn;
838         info = &di->info;
839         error = 0;
840
841         info->rti_info[RTAX_DST] = rt_key(rt);
842         info->rti_info[RTAX_NETMASK] = rt_mask(rt);
843         info->rti_info[RTAX_GATEWAY] = &rt->rt_nhop->gw_sa;
844
845         rt = rt_unlinkrte(di->rnh, info, &error);
846         if (rt == NULL) {
847                 /* Either not allowed or not matched. Skip entry */
848                 return (0);
849         }
850
851         /* Entry was unlinked. Notify subscribers */
852         di->rnh->rnh_gen++;
853         di->rc.rc_rt = rt;
854         di->rc.rc_nh_old = rt->rt_nhop;
855         rib_notify(di->rnh, RIB_NOTIFY_IMMEDIATE, &di->rc);
856
857         /* Add to the list and return */
858         rt->rt_chain = di->head;
859         di->head = rt;
860
861         return (0);
862 }
863
864 /*
865  * Iterates over a routing table specified by @fibnum and @family and
866  *  deletes elements marked by @filter_f.
867  * @fibnum: rtable id
868  * @family: AF_ address family
869  * @filter_f: function returning non-zero value for items to delete
870  * @arg: data to pass to the @filter_f function
871  * @report: true if rtsock notification is needed.
872  */
873 void
874 rib_walk_del(u_int fibnum, int family, rt_filter_f_t *filter_f, void *arg, bool report)
875 {
876         struct rib_head *rnh;
877         struct rt_delinfo di;
878         struct rtentry *rt;
879         struct epoch_tracker et;
880
881         rnh = rt_tables_get_rnh(fibnum, family);
882         if (rnh == NULL)
883                 return;
884
885         bzero(&di, sizeof(di));
886         di.info.rti_filter = filter_f;
887         di.info.rti_filterdata = arg;
888         di.rnh = rnh;
889         di.rc.rc_cmd = RTM_DELETE;
890
891         NET_EPOCH_ENTER(et);
892
893         RIB_WLOCK(rnh);
894         rnh->rnh_walktree(&rnh->head, rt_checkdelroute, &di);
895         RIB_WUNLOCK(rnh);
896
897         /* We might have something to reclaim. */
898         while (di.head != NULL) {
899                 rt = di.head;
900                 di.head = rt->rt_chain;
901                 rt->rt_chain = NULL;
902
903                 di.rc.rc_rt = rt;
904                 di.rc.rc_nh_old = rt->rt_nhop;
905                 rib_notify(rnh, RIB_NOTIFY_DELAYED, &di.rc);
906
907                 /* TODO std rt -> rt_addrinfo export */
908                 di.info.rti_info[RTAX_DST] = rt_key(rt);
909                 di.info.rti_info[RTAX_NETMASK] = rt_mask(rt);
910
911                 if (report)
912                         rt_routemsg(RTM_DELETE, rt, rt->rt_nhop->nh_ifp, 0,
913                             fibnum);
914                 rtfree(rt);
915         }
916
917         NET_EPOCH_EXIT(et);
918 }
919
920 static void
921 rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
922     struct rib_cmd_info *rc)
923 {
924         struct rib_subscription *rs;
925
926         CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
927                 if (rs->type == type)
928                         rs->func(rnh, rc, rs->arg);
929         }
930 }
931
932 static struct rib_subscription *
933 allocate_subscription(rib_subscription_cb_t *f, void *arg,
934     enum rib_subscription_type type, bool waitok)
935 {
936         struct rib_subscription *rs;
937         int flags = M_ZERO | (waitok ? M_WAITOK : 0);
938
939         rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
940         if (rs == NULL)
941                 return (NULL);
942
943         rs->func = f;
944         rs->arg = arg;
945         rs->type = type;
946
947         return (rs);
948 }
949
950
951 /*
952  * Subscribe for the changes in the routing table specified by @fibnum and
953  *  @family.
954  *
955  * Returns pointer to the subscription structure on success.
956  */
957 struct rib_subscription *
958 rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
959     enum rib_subscription_type type, bool waitok)
960 {
961         struct rib_head *rnh;
962         struct rib_subscription *rs;
963         struct epoch_tracker et;
964
965         if ((rs = allocate_subscription(f, arg, type, waitok)) == NULL)
966                 return (NULL);
967
968         NET_EPOCH_ENTER(et);
969         KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
970         rnh = rt_tables_get_rnh(fibnum, family);
971
972         RIB_WLOCK(rnh);
973         CK_STAILQ_INSERT_TAIL(&rnh->rnh_subscribers, rs, next);
974         RIB_WUNLOCK(rnh);
975         NET_EPOCH_EXIT(et);
976
977         return (rs);
978 }
979
980 struct rib_subscription *
981 rib_subscribe_internal(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
982     enum rib_subscription_type type, bool waitok)
983 {
984         struct rib_subscription *rs;
985         struct epoch_tracker et;
986
987         if ((rs = allocate_subscription(f, arg, type, waitok)) == NULL)
988                 return (NULL);
989
990         NET_EPOCH_ENTER(et);
991         RIB_WLOCK(rnh);
992         CK_STAILQ_INSERT_TAIL(&rnh->rnh_subscribers, rs, next);
993         RIB_WUNLOCK(rnh);
994         NET_EPOCH_EXIT(et);
995
996         return (rs);
997 }
998
999 /*
1000  * Remove rtable subscription @rs from the table specified by @fibnum
1001  *  and @family.
1002  * Needs to be run in network epoch.
1003  *
1004  * Returns 0 on success.
1005  */
1006 int
1007 rib_unsibscribe(uint32_t fibnum, int family, struct rib_subscription *rs)
1008 {
1009         struct rib_head *rnh;
1010
1011         NET_EPOCH_ASSERT();
1012         KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
1013         rnh = rt_tables_get_rnh(fibnum, family);
1014
1015         if (rnh == NULL)
1016                 return (ENOENT);
1017
1018         RIB_WLOCK(rnh);
1019         CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
1020         RIB_WUNLOCK(rnh);
1021
1022         epoch_call(net_epoch_preempt, destroy_subscription_epoch,
1023             &rs->epoch_ctx);
1024
1025         return (0);
1026 }
1027
1028 /*
1029  * Epoch callback indicating subscription is safe to destroy
1030  */
1031 static void
1032 destroy_subscription_epoch(epoch_context_t ctx)
1033 {
1034         struct rib_subscription *rs;
1035
1036         rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
1037
1038         free(rs, M_RTABLE);
1039 }
1040
1041 void
1042 rib_init_subscriptions(struct rib_head *rnh)
1043 {
1044
1045         CK_STAILQ_INIT(&rnh->rnh_subscribers);
1046 }
1047
1048 void
1049 rib_destroy_subscriptions(struct rib_head *rnh)
1050 {
1051         struct rib_subscription *rs;
1052         struct epoch_tracker et;
1053
1054         NET_EPOCH_ENTER(et);
1055         RIB_WLOCK(rnh);
1056         while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
1057                 CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
1058                 epoch_call(net_epoch_preempt, destroy_subscription_epoch,
1059                     &rs->epoch_ctx);
1060         }
1061         RIB_WUNLOCK(rnh);
1062         NET_EPOCH_EXIT(et);
1063 }
1064