1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include "opt_inet.h"
30 #include "opt_inet6.h"
31 #include "opt_route.h"
32
33 #include <sys/param.h>
34 #include <sys/eventhandler.h>
35 #include <sys/kernel.h>
36 #include <sys/sbuf.h>
37 #include <sys/lock.h>
38 #include <sys/rmlock.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/module.h>
42 #include <sys/kernel.h>
43 #include <sys/priv.h>
44 #include <sys/proc.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/stdarg.h>
48 #include <sys/sysctl.h>
49 #include <sys/syslog.h>
50 #include <sys/queue.h>
51 #include <net/vnet.h>
52
53 #include <net/if.h>
54 #include <net/if_var.h>
55
56 #include <netinet/in.h>
57 #include <netinet/in_var.h>
58 #include <netinet/ip.h>
59 #include <netinet/ip_var.h>
60 #ifdef INET6
61 #include <netinet/ip6.h>
62 #include <netinet6/ip6_var.h>
63 #endif
64
65 #include <net/route.h>
66 #include <net/route/nhop.h>
67 #include <net/route/route_ctl.h>
68 #include <net/route/route_var.h>
69 #include <net/route/fib_algo.h>
70
71 /*
72 * Fib lookup framework.
73 *
74 * This framework enables accelerated longest-prefix-match lookups for the
75 * routing tables by adding the ability to dynamically attach/detach lookup
76 * algorithms implementation to/from the datapath.
77 *
78 * flm - fib lookup modules - implementation of particular lookup algorithm
79 * fd - fib data - instance of an flm bound to specific routing table
80 *
81 * This file provides main framework functionality.
82 *
83 * The following are the features provided by the framework
84 *
85 * 1) nexhops abstraction -> provides transparent referencing, indexing
86 * and efficient idx->ptr mappings for nexthop and nexthop groups.
87 * 2) Routing table synchronisation
88 * 3) dataplane attachment points
89 * 4) automatic algorithm selection based on the provided preference.
90 *
91 *
92 * DATAPATH
93 * For each supported address family, there is a an allocated array of fib_dp
94 * structures, indexed by fib number. Each array entry contains callback function
95 * and its argument. This function will be called with a family-specific lookup key,
96 * scope and provided argument. This array gets re-created every time when new algo
97 * instance gets created. Please take a look at the replace_rtables_family() function
98 * for more details.
99 *
100 */
101
102 SYSCTL_DECL(_net_route);
103 SYSCTL_NODE(_net_route, OID_AUTO, algo, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
104 "Fib algorithm lookups");
105
106 /* Algorithm sync policy */
107
108 /* Time interval to bucket updates */
109 VNET_DEFINE_STATIC(unsigned int, update_bucket_time_ms) = 50;
110 #define V_update_bucket_time_ms VNET(update_bucket_time_ms)
111 SYSCTL_UINT(_net_route_algo, OID_AUTO, bucket_time_ms, CTLFLAG_RW | CTLFLAG_VNET,
112 &VNET_NAME(update_bucket_time_ms), 0, "Time interval to calculate update rate");
113
114 /* Minimum update rate to delay sync */
115 VNET_DEFINE_STATIC(unsigned int, bucket_change_threshold_rate) = 500;
116 #define V_bucket_change_threshold_rate VNET(bucket_change_threshold_rate)
117 SYSCTL_UINT(_net_route_algo, OID_AUTO, bucket_change_threshold_rate, CTLFLAG_RW | CTLFLAG_VNET,
118 &VNET_NAME(bucket_change_threshold_rate), 0, "Minimum update rate to delay sync");
119
120 /* Max allowed delay to sync */
121 VNET_DEFINE_STATIC(unsigned int, fib_max_sync_delay_ms) = 1000;
122 #define V_fib_max_sync_delay_ms VNET(fib_max_sync_delay_ms)
123 SYSCTL_UINT(_net_route_algo, OID_AUTO, fib_max_sync_delay_ms, CTLFLAG_RW | CTLFLAG_VNET,
124 &VNET_NAME(fib_max_sync_delay_ms), 0, "Maximum time to delay sync (ms)");
125
126
127 #ifdef INET6
128 VNET_DEFINE_STATIC(bool, algo_fixed_inet6) = false;
129 #define V_algo_fixed_inet6 VNET(algo_fixed_inet6)
130 SYSCTL_NODE(_net_route_algo, OID_AUTO, inet6, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
131 "IPv6 longest prefix match lookups");
132 #endif
133 #ifdef INET
134 VNET_DEFINE_STATIC(bool, algo_fixed_inet) = false;
135 #define V_algo_fixed_inet VNET(algo_fixed_inet)
136 SYSCTL_NODE(_net_route_algo, OID_AUTO, inet, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
137 "IPv4 longest prefix match lookups");
138 #endif
139
140 /* Fib instance counter */
141 static uint32_t fib_gen = 0;
142
143 struct nhop_ref_table {
144 uint32_t count;
145 int32_t refcnt[0];
146 };
147
148 /*
149 * Nexthop indexes are allocated per-rib, and ribs are keyed by
150 * (fibnum, neighbor family). A route with a gateway belonging to another
151 * family (an IPv4 prefix via an IPv6 nexthop, RFC 5549) therefore gets
152 * its index from that other family's space, where it can collide with the
153 * index of a native nexthop.
154 *
155 * Keep one flat idx->nhop array, but give every index space a contiguous
156 * segment within it, so the array offset is nhaf_base + nhop index.
157 * The rib's own family always owns the first segment, making the offset
158 * identical to the nexthop index for every table with no cross-family nexthops.
159 */
160 struct nhop_af_table {
161 uint8_t nhaf_family; /* AF owning this index space */
162 bool nhaf_hit; /* true if out of index space */
163 uint32_t nhaf_count; /* # of indexes reserved */
164 uint32_t nhaf_base; /* offset within fd->nh_idx */
165 };
166
167 /* # of nhop index spaces per instance, currently 4o6 for now */
168 #define FD_MAX_NH_AF 2
169
170 enum fib_callout_action {
171 FDA_NONE, /* No callout scheduled */
172 FDA_REBUILD, /* Asks to rebuild algo instance */
173 FDA_EVAL, /* Asks to evaluate if the current algo is still be best */
174 FDA_BATCH, /* Asks to submit batch of updates to the algo */
175 };
176
177 struct fib_sync_status {
178 struct timeval diverge_time; /* ts when diverged */
179 uint32_t num_changes; /* number of changes since sync */
180 uint32_t bucket_changes; /* num changes within the current bucket */
181 uint64_t bucket_id; /* 50ms bucket # */
182 struct fib_change_queue fd_change_queue;/* list of scheduled entries */
183 };
184
185 /*
186 * Data structure for the fib lookup instance tied to the particular rib.
187 *
188 * Key:
189 * (f) - Protected by the FIB_MOD lock
190 * (r) - Protected by the RIB lock
191 */
192 struct fib_data {
193 uint32_t number_nhops; /* (r) total size of the nhop arrays */
194 uint32_t fd_dead:1, /* (f) Scheduled for deletion */
195 fd_linked:1; /* (f) true if linked */
196 uint32_t init_done:1, /* (r) true if init is competed */
197 fd_need_rebuild:1, /* (r) true if rebuild scheduled */
198 fd_batch:1, /* (r) true if batched notification scheduled */
199 hit_nhops:1; /* (r) true if out of nhop limit */
200 uint8_t fd_num_af; /* (r) # of nhop index spaces in use */
201 uint8_t fd_family; /* family */
202 uint32_t fd_fibnum; /* fibnum */
203 uint32_t fd_failed_rebuilds; /* stat: failed rebuilds */
204 uint32_t fd_gen; /* instance gen# */
205 struct callout fd_callout; /* rebuild callout */
206 enum fib_callout_action fd_callout_action; /* Callout action to take */
207 void *fd_algo_data; /* algorithm data */
208 struct nhop_af_table fd_af[FD_MAX_NH_AF]; /* (r) index space descriptors */
209 struct nhop_object **nh_idx; /* nhop idx->ptr array */
210 struct nhop_ref_table *nh_ref_table; /* array with # of nhop references */
211 struct rib_head *fd_rh; /* RIB table we're attached to */
212 struct rib_subscription *fd_rs; /* storing table subscription */
213 struct fib_dp fd_dp; /* fib datapath data */
214 struct vnet *fd_vnet; /* vnet fib belongs to */
215 struct epoch_context fd_epoch_ctx; /* epoch context for deletion */
216 struct fib_lookup_module *fd_flm;/* pointer to the lookup module */
217 struct fib_sync_status fd_ss; /* State relevant to the rib sync */
218 uint32_t fd_num_changes; /* number of changes since last callout */
219 TAILQ_ENTRY(fib_data) entries; /* list of all fds in vnet */
220 };
221
222 static bool rebuild_fd(struct fib_data *fd, const char *reason);
223 static bool rebuild_fd_flm(struct fib_data *fd, struct fib_lookup_module *flm_new);
224 static void handle_fd_callout(void *_data);
225 static void destroy_fd_instance_epoch(epoch_context_t ctx);
226 static bool is_idx_free(struct fib_data *fd, uint32_t index);
227 static void set_algo_fixed(struct rib_head *rh);
228 static bool is_algo_fixed(struct rib_head *rh);
229
230 static uint32_t fib_ref_nhop(struct fib_data *fd, struct nhop_object *nh);
231 static void fib_unref_nhop(struct fib_data *fd, struct nhop_object *nh);
232
233 static struct fib_lookup_module *fib_check_best_algo(struct rib_head *rh,
234 struct fib_lookup_module *orig_flm);
235 static void fib_unref_algo(struct fib_lookup_module *flm);
236 static bool flm_error_check(const struct fib_lookup_module *flm, uint32_t fibnum);
237
238 struct mtx fib_mtx;
239 #define FIB_MOD_LOCK() mtx_lock(&fib_mtx)
240 #define FIB_MOD_UNLOCK() mtx_unlock(&fib_mtx)
241 #define FIB_MOD_LOCK_ASSERT() mtx_assert(&fib_mtx, MA_OWNED)
242
243 MTX_SYSINIT(fib_mtx, &fib_mtx, "algo list mutex", MTX_DEF);
244
245 /* Algorithm has to be this percent better than the current to switch */
246 #define BEST_DIFF_PERCENT (5 * 256 / 100)
247 /* Schedule algo re-evaluation X seconds after a change */
248 #define ALGO_EVAL_DELAY_MS 30000
249 /* Force algo re-evaluation after X changes */
250 #define ALGO_EVAL_NUM_ROUTES 100
251 /* Try to setup algorithm X times */
252 #define FIB_MAX_TRIES 32
253 /* Max amount of supported nexthops */
254 #define FIB_MAX_NHOPS 262144
255 #define FIB_CALLOUT_DELAY_MS 50
256
257
258 /* Debug */
259 static int flm_debug_level = LOG_NOTICE;
260 SYSCTL_INT(_net_route_algo, OID_AUTO, debug_level, CTLFLAG_RW | CTLFLAG_RWTUN,
261 &flm_debug_level, 0, "debuglevel");
262 #define FLM_MAX_DEBUG_LEVEL LOG_DEBUG
263 #ifndef LOG_DEBUG2
264 #define LOG_DEBUG2 8
265 #endif
266
267 #define _PASS_MSG(_l) (flm_debug_level >= (_l))
268 #define ALGO_PRINTF(_l, _fmt, ...) if (_PASS_MSG(_l)) { \
269 printf("[fib_algo] %s: " _fmt "\n", __func__, ##__VA_ARGS__); \
270 }
271 #define _ALGO_PRINTF(_fib, _fam, _aname, _gen, _func, _fmt, ...) \
272 printf("[fib_algo] %s.%u (%s#%u) %s: " _fmt "\n",\
273 print_family(_fam), _fib, _aname, _gen, _func, ## __VA_ARGS__)
274 #define _RH_PRINTF(_fib, _fam, _func, _fmt, ...) \
275 printf("[fib_algo] %s.%u %s: " _fmt "\n", print_family(_fam), _fib, _func, ## __VA_ARGS__)
276 #define RH_PRINTF(_l, _rh, _fmt, ...) if (_PASS_MSG(_l)) { \
277 _RH_PRINTF(_rh->rib_fibnum, _rh->rib_family, __func__, _fmt, ## __VA_ARGS__);\
278 }
279 #define FD_PRINTF(_l, _fd, _fmt, ...) FD_PRINTF_##_l(_l, _fd, _fmt, ## __VA_ARGS__)
280 #define _FD_PRINTF(_l, _fd, _fmt, ...) if (_PASS_MSG(_l)) { \
281 _ALGO_PRINTF(_fd->fd_fibnum, _fd->fd_family, _fd->fd_flm->flm_name, \
282 _fd->fd_gen, __func__, _fmt, ## __VA_ARGS__); \
283 }
284 #if FLM_MAX_DEBUG_LEVEL>=LOG_DEBUG2
285 #define FD_PRINTF_LOG_DEBUG2 _FD_PRINTF
286 #else
287 #define FD_PRINTF_LOG_DEBUG2(_l, _fd, _fmt, ...)
288 #endif
289 #if FLM_MAX_DEBUG_LEVEL>=LOG_DEBUG
290 #define FD_PRINTF_LOG_DEBUG _FD_PRINTF
291 #else
292 #define FD_PRINTF_LOG_DEBUG()
293 #endif
294 #if FLM_MAX_DEBUG_LEVEL>=LOG_INFO
295 #define FD_PRINTF_LOG_INFO _FD_PRINTF
296 #else
297 #define FD_PRINTF_LOG_INFO()
298 #endif
299 #define FD_PRINTF_LOG_NOTICE _FD_PRINTF
300 #define FD_PRINTF_LOG_ERR _FD_PRINTF
301 #define FD_PRINTF_LOG_WARNING _FD_PRINTF
302
303
304 /* List of all registered lookup algorithms */
305 static TAILQ_HEAD(, fib_lookup_module) all_algo_list = TAILQ_HEAD_INITIALIZER(all_algo_list);
306
307 /* List of all fib lookup instances in the vnet */
308 VNET_DEFINE_STATIC(TAILQ_HEAD(fib_data_head, fib_data), fib_data_list);
309 #define V_fib_data_list VNET(fib_data_list)
310
311 /* Datastructure for storing non-transient fib lookup module failures */
312 struct fib_error {
313 int fe_family;
314 uint32_t fe_fibnum; /* failed rtable */
315 struct fib_lookup_module *fe_flm; /* failed module */
316 TAILQ_ENTRY(fib_error) entries;/* list of all errored entries */
317 };
318 VNET_DEFINE_STATIC(TAILQ_HEAD(fib_error_head, fib_error), fib_error_list);
319 #define V_fib_error_list VNET(fib_error_list)
320
321 /* Per-family array of fibnum -> {func, arg} mappings used in datapath */
322 struct fib_dp_header {
323 struct epoch_context fdh_epoch_ctx;
324 uint32_t fdh_num_tables;
325 struct fib_dp fdh_idx[0];
326 };
327
328 /*
329 * Tries to add new non-transient algorithm error to the list of
330 * errors.
331 * Returns true on success.
332 */
333 static bool
flm_error_add(struct fib_lookup_module * flm,uint32_t fibnum)334 flm_error_add(struct fib_lookup_module *flm, uint32_t fibnum)
335 {
336 struct fib_error *fe;
337
338 fe = malloc(sizeof(struct fib_error), M_TEMP, M_NOWAIT | M_ZERO);
339 if (fe == NULL)
340 return (false);
341 fe->fe_flm = flm;
342 fe->fe_family = flm->flm_family;
343 fe->fe_fibnum = fibnum;
344
345 FIB_MOD_LOCK();
346 /* Avoid duplicates by checking if error already exists first */
347 if (flm_error_check(flm, fibnum)) {
348 FIB_MOD_UNLOCK();
349 free(fe, M_TEMP);
350 return (true);
351 }
352 TAILQ_INSERT_HEAD(&V_fib_error_list, fe, entries);
353 FIB_MOD_UNLOCK();
354
355 return (true);
356 }
357
358 /*
359 * True if non-transient error has been registered for @flm in @fibnum.
360 */
361 static bool
flm_error_check(const struct fib_lookup_module * flm,uint32_t fibnum)362 flm_error_check(const struct fib_lookup_module *flm, uint32_t fibnum)
363 {
364 const struct fib_error *fe;
365
366 TAILQ_FOREACH(fe, &V_fib_error_list, entries) {
367 if ((fe->fe_flm == flm) && (fe->fe_fibnum == fibnum))
368 return (true);
369 }
370
371 return (false);
372 }
373
374 /*
375 * Clear all errors of algo specified by @flm.
376 */
377 static void
fib_error_clear_flm(struct fib_lookup_module * flm)378 fib_error_clear_flm(struct fib_lookup_module *flm)
379 {
380 struct fib_error *fe, *fe_tmp;
381
382 FIB_MOD_LOCK_ASSERT();
383
384 TAILQ_FOREACH_SAFE(fe, &V_fib_error_list, entries, fe_tmp) {
385 if (fe->fe_flm == flm) {
386 TAILQ_REMOVE(&V_fib_error_list, fe, entries);
387 free(fe, M_TEMP);
388 }
389 }
390 }
391
392 /*
393 * Clears all errors in current VNET.
394 */
395 static void
fib_error_clear(void)396 fib_error_clear(void)
397 {
398 struct fib_error *fe, *fe_tmp;
399
400 FIB_MOD_LOCK_ASSERT();
401
402 TAILQ_FOREACH_SAFE(fe, &V_fib_error_list, entries, fe_tmp) {
403 TAILQ_REMOVE(&V_fib_error_list, fe, entries);
404 free(fe, M_TEMP);
405 }
406 }
407
408 static const char *
print_op_result(enum flm_op_result result)409 print_op_result(enum flm_op_result result)
410 {
411 switch (result) {
412 case FLM_SUCCESS:
413 return "success";
414 case FLM_REBUILD:
415 return "rebuild";
416 case FLM_BATCH:
417 return "batch";
418 case FLM_ERROR:
419 return "error";
420 }
421
422 return "unknown";
423 }
424
425 static const char *
print_family(int family)426 print_family(int family)
427 {
428
429 if (family == AF_INET)
430 return ("inet");
431 else if (family == AF_INET6)
432 return ("inet6");
433 else
434 return ("unknown");
435 }
436
437 /*
438 * Debug function used by lookup algorithms.
439 * Outputs message denoted by @fmt, prepended by "[fib_algo] inetX.Y (algo) "
440 */
441 void
fib_printf(int level,struct fib_data * fd,const char * func,char * fmt,...)442 fib_printf(int level, struct fib_data *fd, const char *func, char *fmt, ...)
443 {
444 char buf[128];
445 va_list ap;
446
447 if (level > flm_debug_level)
448 return;
449
450 va_start(ap, fmt);
451 vsnprintf(buf, sizeof(buf), fmt, ap);
452 va_end(ap);
453
454 _ALGO_PRINTF(fd->fd_fibnum, fd->fd_family, fd->fd_flm->flm_name,
455 fd->fd_gen, func, "%s", buf);
456 }
457
458 /*
459 * Outputs list of algorithms supported by the provided address family.
460 */
461 static int
print_algos_sysctl(struct sysctl_req * req,int family)462 print_algos_sysctl(struct sysctl_req *req, int family)
463 {
464 struct fib_lookup_module *flm;
465 struct sbuf sbuf;
466 int error, count = 0;
467
468 error = sysctl_wire_old_buffer(req, 0);
469 if (error == 0) {
470 sbuf_new_for_sysctl(&sbuf, NULL, 512, req);
471 TAILQ_FOREACH(flm, &all_algo_list, entries) {
472 if (flm->flm_family == family) {
473 if (count++ > 0)
474 sbuf_cat(&sbuf, ", ");
475 sbuf_cat(&sbuf, flm->flm_name);
476 }
477 }
478 error = sbuf_finish(&sbuf);
479 sbuf_delete(&sbuf);
480 }
481 return (error);
482 }
483
484 #ifdef INET6
485 static int
print_algos_sysctl_inet6(SYSCTL_HANDLER_ARGS)486 print_algos_sysctl_inet6(SYSCTL_HANDLER_ARGS)
487 {
488
489 return (print_algos_sysctl(req, AF_INET6));
490 }
491 SYSCTL_PROC(_net_route_algo_inet6, OID_AUTO, algo_list,
492 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
493 print_algos_sysctl_inet6, "A", "List of IPv6 lookup algorithms");
494 #endif
495
496 #ifdef INET
497 static int
print_algos_sysctl_inet(SYSCTL_HANDLER_ARGS)498 print_algos_sysctl_inet(SYSCTL_HANDLER_ARGS)
499 {
500
501 return (print_algos_sysctl(req, AF_INET));
502 }
503 SYSCTL_PROC(_net_route_algo_inet, OID_AUTO, algo_list,
504 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0,
505 print_algos_sysctl_inet, "A", "List of IPv4 lookup algorithms");
506 #endif
507
508 /*
509 * Calculate delay between repeated failures.
510 * Returns current delay in milliseconds.
511 */
512 static uint32_t
callout_calc_delay_ms(struct fib_data * fd)513 callout_calc_delay_ms(struct fib_data *fd)
514 {
515 uint32_t shift;
516
517 if (fd->fd_failed_rebuilds > 10)
518 shift = 10;
519 else
520 shift = fd->fd_failed_rebuilds;
521
522 return ((1 << shift) * FIB_CALLOUT_DELAY_MS);
523 }
524
525 static void
schedule_callout(struct fib_data * fd,enum fib_callout_action action,int delay_ms)526 schedule_callout(struct fib_data *fd, enum fib_callout_action action, int delay_ms)
527 {
528
529 FD_PRINTF(LOG_DEBUG, fd, "delay=%d action=%d", delay_ms, action);
530 fd->fd_callout_action = action;
531 callout_reset_sbt(&fd->fd_callout, SBT_1MS * delay_ms, 0,
532 handle_fd_callout, fd, 0);
533 }
534
535 static void
schedule_fd_rebuild(struct fib_data * fd,const char * reason)536 schedule_fd_rebuild(struct fib_data *fd, const char *reason)
537 {
538
539 RIB_WLOCK_ASSERT(fd->fd_rh);
540
541 if (!fd->fd_need_rebuild) {
542 fd->fd_need_rebuild = true;
543 /* Stop batch updates */
544 fd->fd_batch = false;
545
546 /*
547 * Potentially re-schedules pending callout
548 * initiated by schedule_algo_eval.
549 */
550 FD_PRINTF(LOG_INFO, fd, "Scheduling rebuild: %s (failures=%d)",
551 reason, fd->fd_failed_rebuilds);
552 schedule_callout(fd, FDA_REBUILD, callout_calc_delay_ms(fd));
553 }
554 }
555
556 static void
sync_rib_gen(struct fib_data * fd)557 sync_rib_gen(struct fib_data *fd)
558 {
559 FD_PRINTF(LOG_DEBUG, fd, "Sync gen %u -> %u", fd->fd_rh->rnh_gen, fd->fd_rh->rnh_gen_rib);
560 fd->fd_rh->rnh_gen = fd->fd_rh->rnh_gen_rib;
561 }
562
563 static int64_t
get_tv_diff_ms(const struct timeval * old_tv,const struct timeval * new_tv)564 get_tv_diff_ms(const struct timeval *old_tv, const struct timeval *new_tv)
565 {
566 int64_t diff = 0;
567
568 diff = ((int64_t)(new_tv->tv_sec - old_tv->tv_sec)) * 1000;
569 diff += (new_tv->tv_usec - old_tv->tv_usec) / 1000;
570
571 return (diff);
572 }
573
574 static void
add_tv_diff_ms(struct timeval * tv,int ms)575 add_tv_diff_ms(struct timeval *tv, int ms)
576 {
577 tv->tv_sec += ms / 1000;
578 ms = ms % 1000;
579 if (ms * 1000 + tv->tv_usec < 1000000)
580 tv->tv_usec += ms * 1000;
581 else {
582 tv->tv_sec += 1;
583 tv->tv_usec = ms * 1000 + tv->tv_usec - 1000000;
584 }
585 }
586
587 /*
588 * Marks the time when algo state diverges from the rib state.
589 */
590 static void
mark_diverge_time(struct fib_data * fd)591 mark_diverge_time(struct fib_data *fd)
592 {
593 struct fib_sync_status *fd_ss = &fd->fd_ss;
594
595 getmicrouptime(&fd_ss->diverge_time);
596 fd_ss->bucket_id = 0;
597 fd_ss->bucket_changes = 0;
598 }
599
600 /*
601 * Calculates and updates the next algorithm sync time, based on the current activity.
602 *
603 * The intent is to provide reasonable balance between the update
604 * latency and efficient batching when changing large amount of routes.
605 *
606 * High-level algorithm looks the following:
607 * 1) all changes are bucketed in 50ms intervals
608 * 2) If amount of changes within the bucket is greater than the threshold,
609 * the update gets delayed, up to maximum delay threshold.
610 */
611 static void
update_rebuild_delay(struct fib_data * fd,enum fib_callout_action action)612 update_rebuild_delay(struct fib_data *fd, enum fib_callout_action action)
613 {
614 uint32_t bucket_id, new_delay = 0;
615 struct timeval tv;
616
617 /* Fetch all variables at once to ensure consistent reads */
618 uint32_t bucket_time_ms = V_update_bucket_time_ms;
619 uint32_t threshold_rate = V_bucket_change_threshold_rate;
620 uint32_t max_delay_ms = V_fib_max_sync_delay_ms;
621
622 if (bucket_time_ms == 0)
623 bucket_time_ms = 50;
624 /* calculate per-bucket threshold rate */
625 threshold_rate = threshold_rate * bucket_time_ms / 1000;
626
627 getmicrouptime(&tv);
628
629 struct fib_sync_status *fd_ss = &fd->fd_ss;
630
631 bucket_id = get_tv_diff_ms(&fd_ss->diverge_time, &tv) / bucket_time_ms;
632
633 if (fd_ss->bucket_id == bucket_id) {
634 fd_ss->bucket_changes++;
635 if (fd_ss->bucket_changes == threshold_rate) {
636 new_delay = (bucket_id + 2) * bucket_time_ms;
637 if (new_delay <= max_delay_ms) {
638 FD_PRINTF(LOG_DEBUG, fd,
639 "hit threshold of %u routes, delay update,"
640 "bucket: %u, total delay: %u",
641 threshold_rate, bucket_id + 1, new_delay);
642 } else {
643 new_delay = 0;
644 FD_PRINTF(LOG_DEBUG, fd,
645 "maximum sync delay (%u ms) reached", max_delay_ms);
646 }
647 } else if ((bucket_id == 0) && (fd_ss->bucket_changes == 1))
648 new_delay = bucket_time_ms;
649 } else {
650 fd_ss->bucket_id = bucket_id;
651 fd_ss->bucket_changes = 1;
652 }
653
654 if (new_delay > 0) {
655 /* Calculated time has been updated */
656 struct timeval new_tv = fd_ss->diverge_time;
657 add_tv_diff_ms(&new_tv, new_delay);
658
659 int32_t delay_ms = get_tv_diff_ms(&tv, &new_tv);
660 schedule_callout(fd, action, delay_ms);
661 }
662 }
663
664 static void
update_algo_state(struct fib_data * fd)665 update_algo_state(struct fib_data *fd)
666 {
667
668 RIB_WLOCK_ASSERT(fd->fd_rh);
669
670 if (fd->fd_batch || fd->fd_need_rebuild) {
671 enum fib_callout_action action = fd->fd_need_rebuild ? FDA_REBUILD : FDA_BATCH;
672 update_rebuild_delay(fd, action);
673 return;
674 }
675
676 if (fd->fd_num_changes++ == 0) {
677 /* Start callout to consider switch */
678 if (!callout_pending(&fd->fd_callout))
679 schedule_callout(fd, FDA_EVAL, ALGO_EVAL_DELAY_MS);
680 } else if (fd->fd_num_changes == ALGO_EVAL_NUM_ROUTES) {
681 /* Reset callout to exec immediately */
682 if (fd->fd_callout_action == FDA_EVAL)
683 schedule_callout(fd, FDA_EVAL, 1);
684 }
685 }
686
687 static bool
need_immediate_sync(struct fib_data * fd,struct rib_cmd_info * rc)688 need_immediate_sync(struct fib_data *fd, struct rib_cmd_info *rc)
689 {
690 struct nhop_object *nh;
691
692 /* Sync addition/removal of interface routes */
693 switch (rc->rc_cmd) {
694 case RTM_ADD:
695 nh = rc->rc_nh_new;
696 if (!NH_IS_NHGRP(nh)) {
697 if (!(nh->nh_flags & NHF_GATEWAY))
698 return (true);
699 if (nhop_get_rtflags(nh) & RTF_STATIC)
700 return (true);
701 }
702 break;
703 case RTM_DELETE:
704 nh = rc->rc_nh_old;
705 if (!NH_IS_NHGRP(nh)) {
706 if (!(nh->nh_flags & NHF_GATEWAY))
707 return (true);
708 if (nhop_get_rtflags(nh) & RTF_STATIC)
709 return (true);
710 }
711 break;
712 }
713
714 return (false);
715 }
716
717 static bool
apply_rtable_changes(struct fib_data * fd)718 apply_rtable_changes(struct fib_data *fd)
719 {
720 enum flm_op_result result;
721 struct fib_change_queue *q = &fd->fd_ss.fd_change_queue;
722
723 result = fd->fd_flm->flm_change_rib_items_cb(fd->fd_rh, q, fd->fd_algo_data);
724
725 if (result == FLM_SUCCESS) {
726 sync_rib_gen(fd);
727 for (int i = 0; i < q->count; i++)
728 if (q->entries[i].nh_old)
729 fib_unref_nhop(fd, q->entries[i].nh_old);
730 q->count = 0;
731 }
732 fd->fd_batch = false;
733
734 return (result == FLM_SUCCESS);
735 }
736
737 static bool
fill_change_entry(struct fib_data * fd,struct fib_change_entry * ce,struct rib_cmd_info * rc)738 fill_change_entry(struct fib_data *fd, struct fib_change_entry *ce, struct rib_cmd_info *rc)
739 {
740 int plen = 0;
741
742 switch (fd->fd_family) {
743 #ifdef INET
744 case AF_INET:
745 rt_get_inet_prefix_plen(rc->rc_rt, &ce->addr4, &plen, &ce->scopeid);
746 break;
747 #endif
748 #ifdef INET6
749 case AF_INET6:
750 rt_get_inet6_prefix_plen(rc->rc_rt, &ce->addr6, &plen, &ce->scopeid);
751 break;
752 #endif
753 }
754
755 ce->plen = plen;
756 ce->nh_old = rc->rc_nh_old;
757 ce->nh_new = rc->rc_nh_new;
758 if (ce->nh_new != NULL) {
759 if (fib_ref_nhop(fd, ce->nh_new) == 0)
760 return (false);
761 }
762
763 return (true);
764 }
765
766 static bool
queue_rtable_change(struct fib_data * fd,struct rib_cmd_info * rc)767 queue_rtable_change(struct fib_data *fd, struct rib_cmd_info *rc)
768 {
769 struct fib_change_queue *q = &fd->fd_ss.fd_change_queue;
770
771 if (q->count >= q->size) {
772 uint32_t q_size;
773
774 if (q->size == 0)
775 q_size = 256; /* ~18k memory */
776 else
777 q_size = q->size * 2;
778
779 size_t size = q_size * sizeof(struct fib_change_entry);
780 void *a = realloc(q->entries, size, M_TEMP, M_NOWAIT | M_ZERO);
781 if (a == NULL) {
782 FD_PRINTF(LOG_INFO, fd, "Unable to realloc queue for %u elements",
783 q_size);
784 return (false);
785 }
786 q->entries = a;
787 q->size = q_size;
788 }
789
790 return (fill_change_entry(fd, &q->entries[q->count++], rc));
791 }
792
793 /*
794 * Rib subscription handler. Checks if the algorithm is ready to
795 * receive updates, handles nexthop refcounting and passes change
796 * data to the algorithm callback.
797 */
798 static void
handle_rtable_change_cb(struct rib_head * rnh,struct rib_cmd_info * rc,void * _data)799 handle_rtable_change_cb(struct rib_head *rnh, struct rib_cmd_info *rc,
800 void *_data)
801 {
802 struct fib_data *fd = (struct fib_data *)_data;
803 enum flm_op_result result;
804
805 RIB_WLOCK_ASSERT(rnh);
806
807 /*
808 * There is a small gap between subscribing for route changes
809 * and initiating rtable dump. Avoid receiving route changes
810 * prior to finishing rtable dump by checking `init_done`.
811 */
812 if (!fd->init_done)
813 return;
814
815 bool immediate_sync = need_immediate_sync(fd, rc);
816
817 /* Consider scheduling algorithm re-evaluation */
818 update_algo_state(fd);
819
820 /*
821 * If algo requested rebuild, stop sending updates by default.
822 * This simplifies nexthop refcount handling logic.
823 */
824 if (fd->fd_need_rebuild) {
825 if (immediate_sync)
826 rebuild_fd(fd, "rtable change type enforced sync");
827 return;
828 }
829
830 /*
831 * Algo requested updates to be delivered in batches.
832 * Add the current change to the queue and return.
833 */
834 if (fd->fd_batch) {
835 if (immediate_sync) {
836 if (!queue_rtable_change(fd, rc) || !apply_rtable_changes(fd))
837 rebuild_fd(fd, "batch sync failed");
838 } else {
839 if (!queue_rtable_change(fd, rc))
840 schedule_fd_rebuild(fd, "batch queue failed");
841 }
842 return;
843 }
844
845 /*
846 * Maintain guarantee that every nexthop returned by the dataplane
847 * lookup has > 0 refcount, so can be safely referenced within current
848 * epoch.
849 */
850 if (rc->rc_nh_new != NULL) {
851 if (fib_ref_nhop(fd, rc->rc_nh_new) == 0) {
852 if (immediate_sync)
853 rebuild_fd(fd, "ran out of nhop indexes");
854 else
855 schedule_fd_rebuild(fd, "ran out of nhop indexes");
856 return;
857 }
858 }
859
860 result = fd->fd_flm->flm_change_rib_item_cb(rnh, rc, fd->fd_algo_data);
861
862 switch (result) {
863 case FLM_SUCCESS:
864 sync_rib_gen(fd);
865 /* Unref old nexthop on success */
866 if (rc->rc_nh_old != NULL)
867 fib_unref_nhop(fd, rc->rc_nh_old);
868 break;
869 case FLM_BATCH:
870
871 /*
872 * Algo asks to batch the changes.
873 */
874 if (queue_rtable_change(fd, rc)) {
875 if (!immediate_sync) {
876 fd->fd_batch = true;
877 mark_diverge_time(fd);
878 update_rebuild_delay(fd, FDA_BATCH);
879 break;
880 }
881 if (apply_rtable_changes(fd))
882 break;
883 }
884 FD_PRINTF(LOG_ERR, fd, "batched sync failed, force the rebuild");
885
886 case FLM_REBUILD:
887
888 /*
889 * Algo is not able to apply the update.
890 * Schedule algo rebuild.
891 */
892 if (!immediate_sync) {
893 mark_diverge_time(fd);
894 schedule_fd_rebuild(fd, "algo requested rebuild");
895 break;
896 }
897
898 FD_PRINTF(LOG_INFO, fd, "running sync rebuild");
899 rebuild_fd(fd, "rtable change type enforced sync");
900 break;
901 case FLM_ERROR:
902
903 /*
904 * Algo reported a non-recoverable error.
905 * Record the error and schedule rebuild, which will
906 * trigger best algo selection.
907 */
908 FD_PRINTF(LOG_ERR, fd, "algo reported non-recoverable error");
909 if (!flm_error_add(fd->fd_flm, fd->fd_fibnum))
910 FD_PRINTF(LOG_ERR, fd, "failed to ban algo");
911 schedule_fd_rebuild(fd, "algo reported non-recoverable error");
912 }
913 }
914
915 static void
estimate_nhop_scale(const struct fib_data * old_fd,struct fib_data * fd)916 estimate_nhop_scale(const struct fib_data *old_fd, struct fib_data *fd)
917 {
918 uint32_t base = 0;
919
920 if (old_fd == NULL) {
921 fd->fd_num_af = 1;
922 fd->fd_af[0].nhaf_family = fd->fd_family;
923 // TODO: read from rtable
924 fd->fd_af[0].nhaf_count = 16;
925 } else {
926 fd->fd_num_af = old_fd->fd_num_af;
927 memcpy(fd->fd_af, old_fd->fd_af, sizeof(fd->fd_af));
928
929 for (int i = 0; i < fd->fd_num_af; i++) {
930 struct nhop_af_table *nt;
931
932 nt = &fd->fd_af[i];
933 nt->nhaf_hit = false;
934 if (!old_fd->fd_af[i].nhaf_hit)
935 continue;
936 if (nt->nhaf_count == 0)
937 /* half of the main family */
938 nt->nhaf_count = 8;
939 else if (nt->nhaf_count < FIB_MAX_NHOPS)
940 nt->nhaf_count *= 2;
941 }
942 }
943
944 for (int i = 0; i < fd->fd_num_af; i++) {
945 fd->fd_af[i].nhaf_base = base;
946 base += fd->fd_af[i].nhaf_count;
947 }
948 fd->number_nhops = base;
949 }
950
951 struct walk_cbdata {
952 struct fib_data *fd;
953 flm_dump_t *func;
954 enum flm_op_result result;
955 };
956
957 /*
958 * Handler called after all rtenties have been dumped.
959 * Performs post-dump framework checks and calls
960 * algo:flm_dump_end_cb().
961 *
962 * Updates walk_cbdata result.
963 */
964 static void
sync_algo_end_cb(struct rib_head * rnh,enum rib_walk_hook stage,void * _data)965 sync_algo_end_cb(struct rib_head *rnh, enum rib_walk_hook stage, void *_data)
966 {
967 struct walk_cbdata *w = (struct walk_cbdata *)_data;
968 struct fib_data *fd = w->fd;
969
970 RIB_WLOCK_ASSERT(w->fd->fd_rh);
971
972 if (rnh->rib_dying) {
973 w->result = FLM_ERROR;
974 return;
975 }
976
977 if (fd->hit_nhops) {
978 FD_PRINTF(LOG_INFO, fd, "ran out of nexthops at %u nhops",
979 fd->nh_ref_table->count);
980 if (w->result == FLM_SUCCESS)
981 w->result = FLM_REBUILD;
982 return;
983 }
984
985 if (stage != RIB_WALK_HOOK_POST || w->result != FLM_SUCCESS)
986 return;
987
988 /* Post-dump hook, dump successful */
989 w->result = fd->fd_flm->flm_dump_end_cb(fd->fd_algo_data, &fd->fd_dp);
990
991 if (w->result == FLM_SUCCESS) {
992 /* Mark init as done to allow routing updates */
993 fd->init_done = true;
994 }
995 }
996
997 /*
998 * Callback for each entry in rib.
999 * Calls algo:flm_dump_rib_item_cb func as a part of initial
1000 * route table synchronisation.
1001 */
1002 static int
sync_algo_cb(struct rtentry * rt,void * _data)1003 sync_algo_cb(struct rtentry *rt, void *_data)
1004 {
1005 struct walk_cbdata *w = (struct walk_cbdata *)_data;
1006
1007 RIB_WLOCK_ASSERT(w->fd->fd_rh);
1008
1009 if (w->result == FLM_SUCCESS && w->func) {
1010
1011 /*
1012 * Reference nexthops to maintain guarantee that
1013 * each nexthop returned by datapath has > 0 references
1014 * and can be safely referenced within current epoch.
1015 */
1016 struct nhop_object *nh = rt_get_raw_nhop(rt);
1017 if (fib_ref_nhop(w->fd, nh) != 0)
1018 w->result = w->func(rt, w->fd->fd_algo_data);
1019 else
1020 w->result = FLM_REBUILD;
1021 }
1022
1023 return (0);
1024 }
1025
1026 /*
1027 * Dump all routing table state to the algo instance.
1028 */
1029 static enum flm_op_result
sync_algo(struct fib_data * fd)1030 sync_algo(struct fib_data *fd)
1031 {
1032 struct walk_cbdata w = {
1033 .fd = fd,
1034 .func = fd->fd_flm->flm_dump_rib_item_cb,
1035 .result = FLM_SUCCESS,
1036 };
1037
1038 rib_walk_ext_locked(fd->fd_rh, sync_algo_cb, sync_algo_end_cb, &w);
1039
1040 FD_PRINTF(LOG_INFO, fd,
1041 "initial dump completed (rtable version: %d), result: %s",
1042 fd->fd_rh->rnh_gen, print_op_result(w.result));
1043
1044 return (w.result);
1045 }
1046
1047 /*
1048 * Schedules epoch-backed @fd instance deletion.
1049 * * Unlinks @fd from the list of active algo instances.
1050 * * Removes rib subscription.
1051 * * Stops callout.
1052 * * Schedules actual deletion.
1053 *
1054 * Assume @fd is already unlinked from the datapath.
1055 */
1056 static int
schedule_destroy_fd_instance(struct fib_data * fd,bool in_callout)1057 schedule_destroy_fd_instance(struct fib_data *fd, bool in_callout)
1058 {
1059 bool is_dead;
1060
1061 NET_EPOCH_ASSERT();
1062 RIB_WLOCK_ASSERT(fd->fd_rh);
1063
1064 FIB_MOD_LOCK();
1065 is_dead = fd->fd_dead;
1066 if (!is_dead)
1067 fd->fd_dead = true;
1068 if (fd->fd_linked) {
1069 TAILQ_REMOVE(&V_fib_data_list, fd, entries);
1070 fd->fd_linked = false;
1071 }
1072 FIB_MOD_UNLOCK();
1073 if (is_dead)
1074 return (0);
1075
1076 FD_PRINTF(LOG_INFO, fd, "DETACH");
1077
1078 if (fd->fd_rs != NULL)
1079 rib_unsubscribe_locked(fd->fd_rs);
1080
1081 /*
1082 * After rib_unsubscribe() no _new_ handle_rtable_change_cb() calls
1083 * will be executed, hence no _new_ callout schedules will happen.
1084 */
1085 callout_stop(&fd->fd_callout);
1086
1087 fib_epoch_call(destroy_fd_instance_epoch, &fd->fd_epoch_ctx);
1088
1089 return (0);
1090 }
1091
1092 /*
1093 * Wipe all fd instances from the list matching rib specified by @rh.
1094 * If @keep_first is set, remove all but the first record.
1095 */
1096 static void
fib_cleanup_algo(struct rib_head * rh,bool keep_first,bool in_callout)1097 fib_cleanup_algo(struct rib_head *rh, bool keep_first, bool in_callout)
1098 {
1099 struct fib_data_head tmp_head = TAILQ_HEAD_INITIALIZER(tmp_head);
1100 struct fib_data *fd, *fd_tmp;
1101 struct epoch_tracker et;
1102
1103 FIB_MOD_LOCK();
1104 TAILQ_FOREACH_SAFE(fd, &V_fib_data_list, entries, fd_tmp) {
1105 if (fd->fd_rh == rh) {
1106 if (keep_first) {
1107 keep_first = false;
1108 continue;
1109 }
1110 TAILQ_REMOVE(&V_fib_data_list, fd, entries);
1111 fd->fd_linked = false;
1112 TAILQ_INSERT_TAIL(&tmp_head, fd, entries);
1113 }
1114 }
1115 FIB_MOD_UNLOCK();
1116
1117 /* Pass 2: remove each entry */
1118 NET_EPOCH_ENTER(et);
1119 TAILQ_FOREACH_SAFE(fd, &tmp_head, entries, fd_tmp) {
1120 if (!in_callout)
1121 RIB_WLOCK(fd->fd_rh);
1122 schedule_destroy_fd_instance(fd, in_callout);
1123 if (!in_callout)
1124 RIB_WUNLOCK(fd->fd_rh);
1125 }
1126 NET_EPOCH_EXIT(et);
1127 }
1128
1129 void
fib_destroy_rib(struct rib_head * rh)1130 fib_destroy_rib(struct rib_head *rh)
1131 {
1132
1133 /*
1134 * rnh has `is_dying` flag set, so setup of new fd's will fail at
1135 * sync_algo() stage, preventing new entries to be added to the list
1136 * of active algos. Remove all existing entries for the particular rib.
1137 */
1138 fib_cleanup_algo(rh, false, false);
1139 }
1140
1141 /*
1142 * Finalises fd destruction by freeing all fd resources.
1143 */
1144 static void
destroy_fd_instance(struct fib_data * fd)1145 destroy_fd_instance(struct fib_data *fd)
1146 {
1147
1148 FD_PRINTF(LOG_INFO, fd, "destroy fd %p", fd);
1149
1150 /* Call destroy callback first */
1151 if (fd->fd_algo_data != NULL)
1152 fd->fd_flm->flm_destroy_cb(fd->fd_algo_data);
1153
1154 /* Nhop table */
1155 if ((fd->nh_idx != NULL) && (fd->nh_ref_table != NULL)) {
1156 for (int i = 0; i < fd->number_nhops; i++) {
1157 if (!is_idx_free(fd, i)) {
1158 FD_PRINTF(LOG_DEBUG2, fd, " FREE nhop %d %p",
1159 i, fd->nh_idx[i]);
1160 nhop_free_any(fd->nh_idx[i]);
1161 }
1162 }
1163 free(fd->nh_idx, M_RTABLE);
1164 }
1165 if (fd->nh_ref_table != NULL)
1166 free(fd->nh_ref_table, M_RTABLE);
1167
1168 if (fd->fd_ss.fd_change_queue.entries != NULL)
1169 free(fd->fd_ss.fd_change_queue.entries, M_TEMP);
1170
1171 fib_unref_algo(fd->fd_flm);
1172
1173 free(fd, M_RTABLE);
1174 }
1175
1176 /*
1177 * Epoch callback indicating fd is safe to destroy
1178 */
1179 static void
destroy_fd_instance_epoch(epoch_context_t ctx)1180 destroy_fd_instance_epoch(epoch_context_t ctx)
1181 {
1182 struct fib_data *fd;
1183
1184 fd = __containerof(ctx, struct fib_data, fd_epoch_ctx);
1185
1186 CURVNET_SET(fd->fd_vnet);
1187 destroy_fd_instance(fd);
1188 CURVNET_RESTORE();
1189 }
1190
1191 /*
1192 * Tries to setup fd instance.
1193 * - Allocates fd/nhop table
1194 * - Runs algo:flm_init_cb algo init
1195 * - Subscribes fd to the rib
1196 * - Runs rtable dump
1197 * - Adds instance to the list of active instances.
1198 *
1199 * Returns: operation result. Fills in @pfd with resulting fd on success.
1200 *
1201 */
1202 static enum flm_op_result
try_setup_fd_instance(struct fib_lookup_module * flm,struct rib_head * rh,struct fib_data * old_fd,struct fib_data ** pfd)1203 try_setup_fd_instance(struct fib_lookup_module *flm, struct rib_head *rh,
1204 struct fib_data *old_fd, struct fib_data **pfd)
1205 {
1206 struct fib_data *fd;
1207 size_t size;
1208 enum flm_op_result result;
1209
1210 /* Allocate */
1211 fd = malloc(sizeof(struct fib_data), M_RTABLE, M_NOWAIT | M_ZERO);
1212 if (fd == NULL) {
1213 *pfd = NULL;
1214 RH_PRINTF(LOG_INFO, rh, "Unable to allocate fib_data structure");
1215 return (FLM_REBUILD);
1216 }
1217 *pfd = fd;
1218
1219 fd->fd_rh = rh;
1220 fd->fd_family = rh->rib_family;
1221 fd->fd_fibnum = rh->rib_fibnum;
1222 callout_init_rm(&fd->fd_callout, &rh->rib_lock, 0);
1223 fd->fd_vnet = curvnet;
1224 fd->fd_flm = flm;
1225
1226 estimate_nhop_scale(old_fd, fd);
1227
1228 FIB_MOD_LOCK();
1229 flm->flm_refcount++;
1230 fd->fd_gen = ++fib_gen;
1231 FIB_MOD_UNLOCK();
1232
1233 FD_PRINTF(LOG_DEBUG, fd, "allocated fd %p", fd);
1234
1235 /* Allocate nhidx -> nhop_ptr table */
1236 size = fd->number_nhops * sizeof(void *);
1237 fd->nh_idx = malloc(size, M_RTABLE, M_NOWAIT | M_ZERO);
1238 if (fd->nh_idx == NULL) {
1239 FD_PRINTF(LOG_INFO, fd, "Unable to allocate nhop table idx (sz:%zu)", size);
1240 return (FLM_REBUILD);
1241 }
1242
1243 /* Allocate nhop index refcount table */
1244 size = sizeof(struct nhop_ref_table);
1245 size += fd->number_nhops * sizeof(uint32_t);
1246 fd->nh_ref_table = malloc(size, M_RTABLE, M_NOWAIT | M_ZERO);
1247 if (fd->nh_ref_table == NULL) {
1248 FD_PRINTF(LOG_INFO, fd, "Unable to allocate nhop refcount table (sz:%zu)", size);
1249 return (FLM_REBUILD);
1250 }
1251 FD_PRINTF(LOG_DEBUG, fd, "Allocated %u nhop indexes", fd->number_nhops);
1252
1253 /* Okay, we're ready for algo init */
1254 void *old_algo_data = (old_fd != NULL) ? old_fd->fd_algo_data : NULL;
1255 result = flm->flm_init_cb(fd->fd_fibnum, fd, old_algo_data, &fd->fd_algo_data);
1256 if (result != FLM_SUCCESS) {
1257 FD_PRINTF(LOG_INFO, fd, "%s algo init failed", flm->flm_name);
1258 return (result);
1259 }
1260
1261 /* Try to subscribe */
1262 if (flm->flm_change_rib_item_cb != NULL) {
1263 fd->fd_rs = rib_subscribe_locked(fd->fd_rh,
1264 handle_rtable_change_cb, fd, RIB_NOTIFY_IMMEDIATE);
1265 if (fd->fd_rs == NULL) {
1266 FD_PRINTF(LOG_INFO, fd, "failed to subscribe to the rib changes");
1267 return (FLM_REBUILD);
1268 }
1269 }
1270
1271 /* Dump */
1272 result = sync_algo(fd);
1273 if (result != FLM_SUCCESS) {
1274 FD_PRINTF(LOG_INFO, fd, "rib sync failed");
1275 return (result);
1276 }
1277 FD_PRINTF(LOG_INFO, fd, "DUMP completed successfully.");
1278
1279 FIB_MOD_LOCK();
1280 /*
1281 * Insert fd in the beginning of a list, to maintain invariant
1282 * that first matching entry for the AF/fib is always the active
1283 * one.
1284 */
1285 TAILQ_INSERT_HEAD(&V_fib_data_list, fd, entries);
1286 fd->fd_linked = true;
1287 FIB_MOD_UNLOCK();
1288
1289 return (FLM_SUCCESS);
1290 }
1291
1292 /*
1293 * Sets up algo @flm for table @rh and links it to the datapath.
1294 *
1295 */
1296 static enum flm_op_result
setup_fd_instance(struct fib_lookup_module * flm,struct rib_head * rh,struct fib_data * orig_fd,struct fib_data ** pfd,bool attach)1297 setup_fd_instance(struct fib_lookup_module *flm, struct rib_head *rh,
1298 struct fib_data *orig_fd, struct fib_data **pfd, bool attach)
1299 {
1300 struct fib_data *prev_fd, *new_fd;
1301 enum flm_op_result result;
1302
1303 NET_EPOCH_ASSERT();
1304 RIB_WLOCK_ASSERT(rh);
1305
1306 prev_fd = orig_fd;
1307 new_fd = NULL;
1308 for (int i = 0; i < FIB_MAX_TRIES; i++) {
1309 result = try_setup_fd_instance(flm, rh, prev_fd, &new_fd);
1310
1311 if ((result == FLM_SUCCESS) && attach) {
1312 if (fib_set_datapath_ptr(new_fd, &new_fd->fd_dp))
1313 sync_rib_gen(new_fd);
1314 else
1315 result = FLM_REBUILD;
1316 }
1317
1318 if ((prev_fd != NULL) && (prev_fd != orig_fd)) {
1319 schedule_destroy_fd_instance(prev_fd, false);
1320 prev_fd = NULL;
1321 }
1322
1323 RH_PRINTF(LOG_INFO, rh, "try %d: fib algo result: %s", i,
1324 print_op_result(result));
1325
1326 if (result == FLM_REBUILD) {
1327 prev_fd = new_fd;
1328 new_fd = NULL;
1329 continue;
1330 }
1331
1332 break;
1333 }
1334
1335 if (result != FLM_SUCCESS) {
1336 RH_PRINTF(LOG_WARNING, rh,
1337 "%s algo instance setup failed, failures=%d", flm->flm_name,
1338 orig_fd ? orig_fd->fd_failed_rebuilds + 1 : 0);
1339 /* update failure count */
1340 FIB_MOD_LOCK();
1341 if (orig_fd != NULL)
1342 orig_fd->fd_failed_rebuilds++;
1343 FIB_MOD_UNLOCK();
1344
1345 /* Ban algo on non-recoverable error */
1346 if (result == FLM_ERROR)
1347 flm_error_add(flm, rh->rib_fibnum);
1348
1349 if ((prev_fd != NULL) && (prev_fd != orig_fd))
1350 schedule_destroy_fd_instance(prev_fd, false);
1351 if (new_fd != NULL) {
1352 schedule_destroy_fd_instance(new_fd, false);
1353 new_fd = NULL;
1354 }
1355 }
1356
1357 *pfd = new_fd;
1358 return (result);
1359 }
1360
1361 /*
1362 * Tries to sync algo with the current rtable state, either
1363 * by executing batch update or rebuilding.
1364 * Returns true on success.
1365 */
1366 static bool
execute_callout_action(struct fib_data * fd)1367 execute_callout_action(struct fib_data *fd)
1368 {
1369 enum fib_callout_action action = fd->fd_callout_action;
1370 struct fib_lookup_module *flm_new = NULL;
1371 bool result = true;
1372
1373 NET_EPOCH_ASSERT();
1374 RIB_WLOCK_ASSERT(fd->fd_rh);
1375
1376 fd->fd_need_rebuild = false;
1377 fd->fd_batch = false;
1378 fd->fd_num_changes = 0;
1379
1380 /* First, check if we're still OK to use this algo */
1381 if (!is_algo_fixed(fd->fd_rh))
1382 flm_new = fib_check_best_algo(fd->fd_rh, fd->fd_flm);
1383 if (flm_new != NULL)
1384 action = FDA_REBUILD;
1385
1386 if (action == FDA_BATCH) {
1387 /* Try to sync */
1388 if (!apply_rtable_changes(fd))
1389 action = FDA_REBUILD;
1390 }
1391
1392 if (action == FDA_REBUILD)
1393 result = rebuild_fd_flm(fd, flm_new != NULL ? flm_new : fd->fd_flm);
1394 if (flm_new != NULL)
1395 fib_unref_algo(flm_new);
1396
1397 return (result);
1398 }
1399
1400 /*
1401 * Callout for all scheduled fd-related work.
1402 * - Checks if the current algo is still the best algo
1403 * - Synchronises algo instance to the rtable (batch usecase)
1404 * - Creates a new instance of an algo for af/fib if desired.
1405 */
1406 static void
handle_fd_callout(void * _data)1407 handle_fd_callout(void *_data)
1408 {
1409 struct fib_data *fd = (struct fib_data *)_data;
1410 struct epoch_tracker et;
1411
1412 FD_PRINTF(LOG_INFO, fd, "running callout type=%d", fd->fd_callout_action);
1413
1414 NET_EPOCH_ENTER(et);
1415 CURVNET_SET(fd->fd_vnet);
1416 execute_callout_action(fd);
1417 CURVNET_RESTORE();
1418 NET_EPOCH_EXIT(et);
1419 }
1420
1421 /*
1422 * Tries to create new algo instance based on @fd data.
1423 * Returns true on success.
1424 */
1425 static bool
rebuild_fd_flm(struct fib_data * fd,struct fib_lookup_module * flm_new)1426 rebuild_fd_flm(struct fib_data *fd, struct fib_lookup_module *flm_new)
1427 {
1428 struct fib_data *fd_new, *fd_tmp = NULL;
1429 bool result;
1430
1431 if (flm_new == fd->fd_flm)
1432 fd_tmp = fd;
1433 else
1434 FD_PRINTF(LOG_INFO, fd, "switching algo to %s", flm_new->flm_name);
1435
1436 result = setup_fd_instance(flm_new, fd->fd_rh, fd_tmp, &fd_new, true);
1437 if (result != FLM_SUCCESS) {
1438 FD_PRINTF(LOG_NOTICE, fd, "table rebuild failed");
1439 return (false);
1440 }
1441 FD_PRINTF(LOG_INFO, fd_new, "switched to new instance");
1442
1443 /* Remove old instance */
1444 schedule_destroy_fd_instance(fd, true);
1445
1446 return (true);
1447 }
1448
1449 static bool
rebuild_fd(struct fib_data * fd,const char * reason)1450 rebuild_fd(struct fib_data *fd, const char *reason)
1451 {
1452 struct fib_lookup_module *flm_new = NULL;
1453 bool result;
1454
1455 if (!is_algo_fixed(fd->fd_rh))
1456 flm_new = fib_check_best_algo(fd->fd_rh, fd->fd_flm);
1457
1458 FD_PRINTF(LOG_INFO, fd, "running sync rebuild: %s", reason);
1459 result = rebuild_fd_flm(fd, flm_new != NULL ? flm_new : fd->fd_flm);
1460 if (flm_new != NULL)
1461 fib_unref_algo(flm_new);
1462
1463 if (!result) {
1464 FD_PRINTF(LOG_ERR, fd, "sync rebuild failed");
1465 schedule_fd_rebuild(fd, "sync rebuild failed");
1466 }
1467
1468 return (result);
1469 }
1470
1471 /*
1472 * Finds algo by name/family.
1473 * Returns referenced algo or NULL.
1474 */
1475 static struct fib_lookup_module *
fib_find_algo(const char * algo_name,int family)1476 fib_find_algo(const char *algo_name, int family)
1477 {
1478 struct fib_lookup_module *flm;
1479
1480 FIB_MOD_LOCK();
1481 TAILQ_FOREACH(flm, &all_algo_list, entries) {
1482 if ((strcmp(flm->flm_name, algo_name) == 0) &&
1483 (family == flm->flm_family)) {
1484 flm->flm_refcount++;
1485 FIB_MOD_UNLOCK();
1486 return (flm);
1487 }
1488 }
1489 FIB_MOD_UNLOCK();
1490
1491 return (NULL);
1492 }
1493
1494 static void
fib_unref_algo(struct fib_lookup_module * flm)1495 fib_unref_algo(struct fib_lookup_module *flm)
1496 {
1497
1498 FIB_MOD_LOCK();
1499 flm->flm_refcount--;
1500 FIB_MOD_UNLOCK();
1501 }
1502
1503 static int
set_fib_algo(uint32_t fibnum,int family,struct sysctl_oid * oidp,struct sysctl_req * req)1504 set_fib_algo(uint32_t fibnum, int family, struct sysctl_oid *oidp, struct sysctl_req *req)
1505 {
1506 struct fib_lookup_module *flm = NULL;
1507 struct fib_data *fd = NULL;
1508 char old_algo_name[32], algo_name[32];
1509 struct rib_head *rh = NULL;
1510 enum flm_op_result result;
1511 struct epoch_tracker et;
1512 int error;
1513
1514 /* Fetch current algo/rib for af/family */
1515 FIB_MOD_LOCK();
1516 TAILQ_FOREACH(fd, &V_fib_data_list, entries) {
1517 if ((fd->fd_family == family) && (fd->fd_fibnum == fibnum))
1518 break;
1519 }
1520 if (fd == NULL) {
1521 FIB_MOD_UNLOCK();
1522 return (ENOENT);
1523 }
1524 rh = fd->fd_rh;
1525 strlcpy(old_algo_name, fd->fd_flm->flm_name,
1526 sizeof(old_algo_name));
1527 FIB_MOD_UNLOCK();
1528
1529 strlcpy(algo_name, old_algo_name, sizeof(algo_name));
1530 error = sysctl_handle_string(oidp, algo_name, sizeof(algo_name), req);
1531 if (error != 0 || req->newptr == NULL)
1532 return (error);
1533
1534 if (strcmp(algo_name, old_algo_name) == 0)
1535 return (0);
1536
1537 /* New algorithm name is different */
1538 flm = fib_find_algo(algo_name, family);
1539 if (flm == NULL) {
1540 RH_PRINTF(LOG_INFO, rh, "unable to find algo %s", algo_name);
1541 return (ESRCH);
1542 }
1543
1544 fd = NULL;
1545 NET_EPOCH_ENTER(et);
1546 RIB_WLOCK(rh);
1547 result = setup_fd_instance(flm, rh, NULL, &fd, true);
1548 RIB_WUNLOCK(rh);
1549 NET_EPOCH_EXIT(et);
1550 fib_unref_algo(flm);
1551 if (result != FLM_SUCCESS)
1552 return (EINVAL);
1553
1554 /* Disable automated jumping between algos */
1555 FIB_MOD_LOCK();
1556 set_algo_fixed(rh);
1557 FIB_MOD_UNLOCK();
1558 /* Remove old instance(s) */
1559 fib_cleanup_algo(rh, true, false);
1560
1561 /* Drain cb so user can unload the module after userret if so desired */
1562 NET_EPOCH_DRAIN_CALLBACKS();
1563
1564 return (0);
1565 }
1566
1567 #ifdef INET
1568 static int
set_algo_inet_sysctl_handler(SYSCTL_HANDLER_ARGS)1569 set_algo_inet_sysctl_handler(SYSCTL_HANDLER_ARGS)
1570 {
1571
1572 return (set_fib_algo(curthread->td_proc->p_fibnum, AF_INET, oidp, req));
1573 }
1574 SYSCTL_PROC(_net_route_algo_inet, OID_AUTO, algo,
1575 CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1576 set_algo_inet_sysctl_handler, "A", "Set IPv4 lookup algo");
1577 #endif
1578
1579 #ifdef INET6
1580 static int
set_algo_inet6_sysctl_handler(SYSCTL_HANDLER_ARGS)1581 set_algo_inet6_sysctl_handler(SYSCTL_HANDLER_ARGS)
1582 {
1583
1584 return (set_fib_algo(curthread->td_proc->p_fibnum, AF_INET6, oidp, req));
1585 }
1586 SYSCTL_PROC(_net_route_algo_inet6, OID_AUTO, algo,
1587 CTLFLAG_VNET | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0,
1588 set_algo_inet6_sysctl_handler, "A", "Set IPv6 lookup algo");
1589 #endif
1590
1591 static struct nhop_object *
dummy_lookup(void * algo_data,const struct flm_lookup_key key,uint32_t scopeid)1592 dummy_lookup(void *algo_data, const struct flm_lookup_key key, uint32_t scopeid)
1593 {
1594 return (NULL);
1595 }
1596
1597 static void
destroy_fdh_epoch(epoch_context_t ctx)1598 destroy_fdh_epoch(epoch_context_t ctx)
1599 {
1600 struct fib_dp_header *fdh;
1601
1602 fdh = __containerof(ctx, struct fib_dp_header, fdh_epoch_ctx);
1603 free(fdh, M_RTABLE);
1604 }
1605
1606 static struct fib_dp_header *
alloc_fib_dp_array(uint32_t num_tables,bool waitok)1607 alloc_fib_dp_array(uint32_t num_tables, bool waitok)
1608 {
1609 size_t sz;
1610 struct fib_dp_header *fdh;
1611
1612 sz = sizeof(struct fib_dp_header);
1613 sz += sizeof(struct fib_dp) * num_tables;
1614 fdh = malloc(sz, M_RTABLE, (waitok ? M_WAITOK : M_NOWAIT) | M_ZERO);
1615 if (fdh != NULL) {
1616 fdh->fdh_num_tables = num_tables;
1617 /*
1618 * Set dummy lookup function ptr always returning NULL, so
1619 * we can delay algo init.
1620 */
1621 for (uint32_t i = 0; i < num_tables; i++)
1622 fdh->fdh_idx[i].f = dummy_lookup;
1623 }
1624 return (fdh);
1625 }
1626
1627 static struct fib_dp_header *
get_fib_dp_header(struct fib_dp * dp)1628 get_fib_dp_header(struct fib_dp *dp)
1629 {
1630
1631 return (__containerof((void *)dp, struct fib_dp_header, fdh_idx));
1632 }
1633
1634 /*
1635 * Replace per-family index pool @pdp with a new one which
1636 * contains updated callback/algo data from @fd.
1637 * Returns true on success.
1638 */
1639 static bool
replace_rtables_family(struct fib_dp ** pdp,struct fib_data * fd,struct fib_dp * dp)1640 replace_rtables_family(struct fib_dp **pdp, struct fib_data *fd, struct fib_dp *dp)
1641 {
1642 struct fib_dp_header *new_fdh, *old_fdh;
1643
1644 NET_EPOCH_ASSERT();
1645
1646 FD_PRINTF(LOG_DEBUG, fd, "[vnet %p] replace with f:%p arg:%p",
1647 curvnet, dp->f, dp->arg);
1648
1649 FIB_MOD_LOCK();
1650 old_fdh = get_fib_dp_header(*pdp);
1651
1652 if (old_fdh->fdh_idx[fd->fd_fibnum].f == dp->f) {
1653 /*
1654 * Function is the same, data pointer needs update.
1655 * Perform in-line replace without reallocation.
1656 */
1657 old_fdh->fdh_idx[fd->fd_fibnum].arg = dp->arg;
1658 FD_PRINTF(LOG_DEBUG, fd, "FDH %p inline update", old_fdh);
1659 FIB_MOD_UNLOCK();
1660 return (true);
1661 }
1662
1663 new_fdh = alloc_fib_dp_array(old_fdh->fdh_num_tables, false);
1664 FD_PRINTF(LOG_DEBUG, fd, "OLD FDH: %p NEW FDH: %p", old_fdh, new_fdh);
1665 if (new_fdh == NULL) {
1666 FIB_MOD_UNLOCK();
1667 FD_PRINTF(LOG_WARNING, fd, "error attaching datapath");
1668 return (false);
1669 }
1670
1671 memcpy(&new_fdh->fdh_idx[0], &old_fdh->fdh_idx[0],
1672 old_fdh->fdh_num_tables * sizeof(struct fib_dp));
1673 /* Update relevant data structure for @fd */
1674 new_fdh->fdh_idx[fd->fd_fibnum] = *dp;
1675
1676 /* Ensure memcpy() writes have completed */
1677 atomic_thread_fence_rel();
1678 /* Set new datapath pointer */
1679 *pdp = &new_fdh->fdh_idx[0];
1680 FIB_MOD_UNLOCK();
1681 FD_PRINTF(LOG_DEBUG, fd, "update %p -> %p", old_fdh, new_fdh);
1682
1683 fib_epoch_call(destroy_fdh_epoch, &old_fdh->fdh_epoch_ctx);
1684
1685 return (true);
1686 }
1687
1688 static struct fib_dp **
get_family_dp_ptr(int family)1689 get_family_dp_ptr(int family)
1690 {
1691 switch (family) {
1692 #ifdef INET
1693 case AF_INET:
1694 return (&V_inet_dp);
1695 #endif
1696 #ifdef INET6
1697 case AF_INET6:
1698 return (&V_inet6_dp);
1699 #endif
1700 }
1701 return (NULL);
1702 }
1703
1704 /*
1705 * Make datapath use fib instance @fd
1706 */
1707 bool
fib_set_datapath_ptr(struct fib_data * fd,struct fib_dp * dp)1708 fib_set_datapath_ptr(struct fib_data *fd, struct fib_dp *dp)
1709 {
1710 struct fib_dp **pdp;
1711
1712 pdp = get_family_dp_ptr(fd->fd_family);
1713 return (replace_rtables_family(pdp, fd, dp));
1714 }
1715
1716 /*
1717 * Grow datapath pointers array.
1718 * Called from sysctl handler on growing number of routing tables.
1719 */
1720 static void
grow_rtables_family(struct fib_dp ** pdp,uint32_t new_num_tables)1721 grow_rtables_family(struct fib_dp **pdp, uint32_t new_num_tables)
1722 {
1723 struct fib_dp_header *new_fdh, *old_fdh = NULL;
1724
1725 new_fdh = alloc_fib_dp_array(new_num_tables, true);
1726
1727 FIB_MOD_LOCK();
1728 if (*pdp != NULL) {
1729 old_fdh = get_fib_dp_header(*pdp);
1730 memcpy(&new_fdh->fdh_idx[0], &old_fdh->fdh_idx[0],
1731 old_fdh->fdh_num_tables * sizeof(struct fib_dp));
1732 }
1733
1734 /* Wait till all writes completed */
1735 atomic_thread_fence_rel();
1736
1737 *pdp = &new_fdh->fdh_idx[0];
1738 FIB_MOD_UNLOCK();
1739
1740 if (old_fdh != NULL)
1741 fib_epoch_call(destroy_fdh_epoch, &old_fdh->fdh_epoch_ctx);
1742 }
1743
1744 /*
1745 * Grows per-AF arrays of datapath pointers for each supported family.
1746 * Called from fibs resize sysctl handler.
1747 */
1748 void
fib_grow_rtables(uint32_t new_num_tables)1749 fib_grow_rtables(uint32_t new_num_tables)
1750 {
1751
1752 #ifdef INET
1753 grow_rtables_family(get_family_dp_ptr(AF_INET), new_num_tables);
1754 #endif
1755 #ifdef INET6
1756 grow_rtables_family(get_family_dp_ptr(AF_INET6), new_num_tables);
1757 #endif
1758 }
1759
1760 void
fib_get_rtable_info(struct rib_head * rh,struct rib_rtable_info * rinfo)1761 fib_get_rtable_info(struct rib_head *rh, struct rib_rtable_info *rinfo)
1762 {
1763
1764 bzero(rinfo, sizeof(struct rib_rtable_info));
1765 rinfo->num_prefixes = rh->rnh_prefixes;
1766 rinfo->num_nhops = nhops_get_count(rh);
1767 rinfo->num_nhgrp = nhgrp_get_count(rh);
1768 }
1769
1770 /*
1771 * Updates pointer to the algo data for the @fd.
1772 */
1773 void
fib_set_algo_ptr(struct fib_data * fd,void * algo_data)1774 fib_set_algo_ptr(struct fib_data *fd, void *algo_data)
1775 {
1776 RIB_WLOCK_ASSERT(fd->fd_rh);
1777
1778 fd->fd_algo_data = algo_data;
1779 }
1780
1781 /*
1782 * Calls @callback with @ctx after the end of a current epoch.
1783 */
1784 void
fib_epoch_call(epoch_callback_t callback,epoch_context_t ctx)1785 fib_epoch_call(epoch_callback_t callback, epoch_context_t ctx)
1786 {
1787 NET_EPOCH_CALL(callback, ctx);
1788 }
1789
1790 /*
1791 * Accessor to get rib instance @fd is attached to.
1792 */
1793 struct rib_head *
fib_get_rh(struct fib_data * fd)1794 fib_get_rh(struct fib_data *fd)
1795 {
1796
1797 return (fd->fd_rh);
1798 }
1799
1800 /*
1801 * Accessor to export idx->nhop array
1802 */
1803 struct nhop_object **
fib_get_nhop_array(struct fib_data * fd)1804 fib_get_nhop_array(struct fib_data *fd)
1805 {
1806
1807 return (fd->nh_idx);
1808 }
1809
1810 static uint32_t
get_nhop_idx(struct nhop_object * nh)1811 get_nhop_idx(struct nhop_object *nh)
1812 {
1813 if (NH_IS_NHGRP(nh))
1814 return (nhgrp_get_idx((struct nhgrp_object *)nh));
1815
1816 return (nhop_get_idx(nh));
1817 }
1818
1819 static uint8_t
get_nhop_family(struct nhop_object * nh)1820 get_nhop_family(struct nhop_object *nh)
1821 {
1822
1823 if (NH_IS_NHGRP(nh))
1824 return (nhgrp_get_neigh_family((struct nhgrp_object *)nh));
1825
1826 return (nhop_get_neigh_family(nh));
1827 }
1828
1829 /*
1830 * Returns the index space of fd owning family, or NULL.
1831 */
1832 static struct nhop_af_table *
find_af_table(struct fib_data * fd,uint8_t family)1833 find_af_table(struct fib_data *fd, uint8_t family)
1834 {
1835
1836 for (int i = 0; i < fd->fd_num_af; i++) {
1837 if (fd->fd_af[i].nhaf_family == family)
1838 return (&fd->fd_af[i]);
1839 }
1840
1841 return (NULL);
1842 }
1843
1844 /*
1845 * Maps nh to its offset within the flat idx->nhop array of fd.
1846 */
1847 static uint32_t
get_nhop_off(struct fib_data * fd,struct nhop_object * nh)1848 get_nhop_off(struct fib_data *fd, struct nhop_object *nh)
1849 {
1850 struct nhop_af_table *nt = find_af_table(fd, get_nhop_family(nh));
1851 uint32_t idx = get_nhop_idx(nh);
1852
1853 KASSERT(nt != NULL, ("no index space for the nhop family"));
1854 KASSERT(idx < nt->nhaf_count, ("invalid nhop index"));
1855
1856 return (nt->nhaf_base + idx);
1857 }
1858
1859 uint32_t
fib_get_nhop_idx(struct fib_data * fd,struct nhop_object * nh)1860 fib_get_nhop_idx(struct fib_data *fd, struct nhop_object *nh)
1861 {
1862
1863 return (get_nhop_off(fd, nh));
1864 }
1865
1866 static bool
is_idx_free(struct fib_data * fd,uint32_t index)1867 is_idx_free(struct fib_data *fd, uint32_t index)
1868 {
1869
1870 return (fd->nh_ref_table->refcnt[index] == 0);
1871 }
1872
1873 static uint32_t
fib_ref_nhop(struct fib_data * fd,struct nhop_object * nh)1874 fib_ref_nhop(struct fib_data *fd, struct nhop_object *nh)
1875 {
1876 struct nhop_af_table *nt;
1877 uint32_t idx;
1878 uint8_t family;
1879
1880 RIB_WLOCK_ASSERT(fd->fd_rh);
1881
1882 family = get_nhop_family(nh);
1883 nt = find_af_table(fd, family);
1884 if (nt == NULL) {
1885 KASSERT(fd->fd_num_af < FD_MAX_NH_AF,
1886 ("out of nhop index spaces for %s", print_family(family)));
1887 nt = &fd->fd_af[fd->fd_num_af++];
1888 nt->nhaf_family = family;
1889 }
1890
1891 idx = get_nhop_idx(nh);
1892 if (idx >= nt->nhaf_count) {
1893 nt->nhaf_hit = true;
1894 fd->hit_nhops = 1;
1895 return (0);
1896 }
1897 idx += nt->nhaf_base;
1898
1899 if (is_idx_free(fd, idx)) {
1900 nhop_ref_any(nh);
1901 fd->nh_idx[idx] = nh;
1902 fd->nh_ref_table->count++;
1903 FD_PRINTF(LOG_DEBUG2, fd, " REF nhop %u %p", idx, fd->nh_idx[idx]);
1904 }
1905 fd->nh_ref_table->refcnt[idx]++;
1906
1907 return (idx);
1908 }
1909
1910 struct nhop_release_data {
1911 struct nhop_object *nh;
1912 struct epoch_context ctx;
1913 };
1914
1915 static void
release_nhop_epoch(epoch_context_t ctx)1916 release_nhop_epoch(epoch_context_t ctx)
1917 {
1918 struct nhop_release_data *nrd;
1919
1920 nrd = __containerof(ctx, struct nhop_release_data, ctx);
1921 nhop_free_any(nrd->nh);
1922 free(nrd, M_TEMP);
1923 }
1924
1925 /*
1926 * Delays nexthop refcount release.
1927 * Datapath may have the datastructures not updated yet, so the old
1928 * nexthop may still be returned till the end of current epoch. Delay
1929 * refcount removal, as we may be removing the last instance, which will
1930 * trigger nexthop deletion, rendering returned nexthop invalid.
1931 */
1932 static void
fib_schedule_release_nhop(struct fib_data * fd,struct nhop_object * nh)1933 fib_schedule_release_nhop(struct fib_data *fd, struct nhop_object *nh)
1934 {
1935 struct nhop_release_data *nrd;
1936
1937 nrd = malloc(sizeof(struct nhop_release_data), M_TEMP, M_NOWAIT | M_ZERO);
1938 if (nrd != NULL) {
1939 nrd->nh = nh;
1940 fib_epoch_call(release_nhop_epoch, &nrd->ctx);
1941 } else {
1942 /*
1943 * Unable to allocate memory. Leak nexthop to maintain guarantee
1944 * that each nhop can be referenced.
1945 */
1946 FD_PRINTF(LOG_ERR, fd, "unable to schedule nhop %p deletion", nh);
1947 }
1948 }
1949
1950 static void
fib_unref_nhop(struct fib_data * fd,struct nhop_object * nh)1951 fib_unref_nhop(struct fib_data *fd, struct nhop_object *nh)
1952 {
1953 uint32_t idx = get_nhop_off(fd, nh);
1954
1955 KASSERT(idx < fd->number_nhops, ("invalid nhop index"));
1956 KASSERT(nh == fd->nh_idx[idx], ("index table contains whong nh"));
1957
1958 fd->nh_ref_table->refcnt[idx]--;
1959 if (fd->nh_ref_table->refcnt[idx] == 0) {
1960 FD_PRINTF(LOG_DEBUG, fd, " FREE nhop %d %p", idx, fd->nh_idx[idx]);
1961 fib_schedule_release_nhop(fd, fd->nh_idx[idx]);
1962 }
1963 }
1964
1965 static void
set_algo_fixed(struct rib_head * rh)1966 set_algo_fixed(struct rib_head *rh)
1967 {
1968 switch (rh->rib_family) {
1969 #ifdef INET
1970 case AF_INET:
1971 V_algo_fixed_inet = true;
1972 break;
1973 #endif
1974 #ifdef INET6
1975 case AF_INET6:
1976 V_algo_fixed_inet6 = true;
1977 break;
1978 #endif
1979 }
1980 }
1981
1982 static bool
is_algo_fixed(struct rib_head * rh)1983 is_algo_fixed(struct rib_head *rh)
1984 {
1985
1986 switch (rh->rib_family) {
1987 #ifdef INET
1988 case AF_INET:
1989 return (V_algo_fixed_inet);
1990 #endif
1991 #ifdef INET6
1992 case AF_INET6:
1993 return (V_algo_fixed_inet6);
1994 #endif
1995 }
1996 return (false);
1997 }
1998
1999 /*
2000 * Runs the check on what would be the best algo for rib @rh, assuming
2001 * that the current algo is the one specified by @orig_flm. Note that
2002 * it can be NULL for initial selection.
2003 *
2004 * Returns referenced new algo or NULL if the current one is the best.
2005 */
2006 static struct fib_lookup_module *
fib_check_best_algo(struct rib_head * rh,struct fib_lookup_module * orig_flm)2007 fib_check_best_algo(struct rib_head *rh, struct fib_lookup_module *orig_flm)
2008 {
2009 uint8_t preference, curr_preference = 0, best_preference = 0;
2010 struct fib_lookup_module *flm, *best_flm = NULL;
2011 struct rib_rtable_info rinfo;
2012 int candidate_algos = 0;
2013
2014 fib_get_rtable_info(rh, &rinfo);
2015
2016 FIB_MOD_LOCK();
2017 TAILQ_FOREACH(flm, &all_algo_list, entries) {
2018 if (flm->flm_family != rh->rib_family)
2019 continue;
2020 candidate_algos++;
2021 preference = flm->flm_get_pref(&rinfo);
2022 if (preference > best_preference) {
2023 if (!flm_error_check(flm, rh->rib_fibnum)) {
2024 best_preference = preference;
2025 best_flm = flm;
2026 }
2027 }
2028 if (flm == orig_flm)
2029 curr_preference = preference;
2030 }
2031 if ((best_flm != NULL) && (curr_preference + BEST_DIFF_PERCENT < best_preference))
2032 best_flm->flm_refcount++;
2033 else
2034 best_flm = NULL;
2035 FIB_MOD_UNLOCK();
2036
2037 RH_PRINTF(LOG_DEBUG, rh, "candidate_algos: %d, curr: %s(%d) result: %s(%d)",
2038 candidate_algos, orig_flm ? orig_flm->flm_name : "NULL", curr_preference,
2039 best_flm ? best_flm->flm_name : (orig_flm ? orig_flm->flm_name : "NULL"),
2040 best_preference);
2041
2042 return (best_flm);
2043 }
2044
2045 /*
2046 * Called when new route table is created.
2047 * Selects, allocates and attaches fib algo for the table.
2048 */
2049 static bool
fib_select_algo_initial(struct rib_head * rh,struct fib_dp * dp)2050 fib_select_algo_initial(struct rib_head *rh, struct fib_dp *dp)
2051 {
2052 struct fib_lookup_module *flm;
2053 struct fib_data *fd = NULL;
2054 enum flm_op_result result;
2055 struct epoch_tracker et;
2056
2057 flm = fib_check_best_algo(rh, NULL);
2058 if (flm == NULL) {
2059 RH_PRINTF(LOG_CRIT, rh, "no algo selected");
2060 return (false);
2061 }
2062 RH_PRINTF(LOG_INFO, rh, "selected algo %s", flm->flm_name);
2063
2064 NET_EPOCH_ENTER(et);
2065 RIB_WLOCK(rh);
2066 result = setup_fd_instance(flm, rh, NULL, &fd, false);
2067 RIB_WUNLOCK(rh);
2068 NET_EPOCH_EXIT(et);
2069
2070 RH_PRINTF(LOG_DEBUG, rh, "result=%d fd=%p", result, fd);
2071 if (result == FLM_SUCCESS)
2072 *dp = fd->fd_dp;
2073 else
2074 RH_PRINTF(LOG_CRIT, rh, "unable to setup algo %s", flm->flm_name);
2075
2076 fib_unref_algo(flm);
2077
2078 return (result == FLM_SUCCESS);
2079 }
2080
2081 /*
2082 * Sets up fib algo instances for the non-initialized RIBs in the @family.
2083 * Allocates temporary datapath index to amortize datapaint index updates
2084 * with large @num_tables.
2085 */
2086 void
fib_setup_family(int family,uint32_t num_tables)2087 fib_setup_family(int family, uint32_t num_tables)
2088 {
2089 struct fib_dp_header *new_fdh = alloc_fib_dp_array(num_tables, false);
2090 if (new_fdh == NULL) {
2091 ALGO_PRINTF(LOG_CRIT, "Unable to setup framework for %s", print_family(family));
2092 return;
2093 }
2094
2095 for (int i = 0; i < num_tables; i++) {
2096 struct rib_head *rh = rt_tables_get_rnh(i, family);
2097 if (rh->rib_algo_init)
2098 continue;
2099 if (!fib_select_algo_initial(rh, &new_fdh->fdh_idx[i]))
2100 continue;
2101
2102 rh->rib_algo_init = true;
2103 }
2104
2105 FIB_MOD_LOCK();
2106 struct fib_dp **pdp = get_family_dp_ptr(family);
2107 struct fib_dp_header *old_fdh = get_fib_dp_header(*pdp);
2108
2109 /* Update the items not touched by the new init, from the old data pointer */
2110 for (int i = 0; i < num_tables; i++) {
2111 if (new_fdh->fdh_idx[i].f == dummy_lookup)
2112 new_fdh->fdh_idx[i] = old_fdh->fdh_idx[i];
2113 }
2114
2115 /* Ensure all index writes have completed */
2116 atomic_thread_fence_rel();
2117 /* Set new datapath pointer */
2118 *pdp = &new_fdh->fdh_idx[0];
2119
2120 FIB_MOD_UNLOCK();
2121
2122 fib_epoch_call(destroy_fdh_epoch, &old_fdh->fdh_epoch_ctx);
2123 }
2124
2125 /*
2126 * Registers fib lookup module within the subsystem.
2127 */
2128 int
fib_module_register(struct fib_lookup_module * flm)2129 fib_module_register(struct fib_lookup_module *flm)
2130 {
2131
2132 FIB_MOD_LOCK();
2133 ALGO_PRINTF(LOG_INFO, "attaching %s to %s", flm->flm_name,
2134 print_family(flm->flm_family));
2135 TAILQ_INSERT_TAIL(&all_algo_list, flm, entries);
2136 FIB_MOD_UNLOCK();
2137
2138 return (0);
2139 }
2140
2141 /*
2142 * Tries to unregister fib lookup module.
2143 *
2144 * Returns 0 on success, EBUSY if module is still used
2145 * by some of the tables.
2146 */
2147 int
fib_module_unregister(struct fib_lookup_module * flm)2148 fib_module_unregister(struct fib_lookup_module *flm)
2149 {
2150
2151 FIB_MOD_LOCK();
2152 if (flm->flm_refcount > 0) {
2153 FIB_MOD_UNLOCK();
2154 return (EBUSY);
2155 }
2156 fib_error_clear_flm(flm);
2157 ALGO_PRINTF(LOG_INFO, "detaching %s from %s", flm->flm_name,
2158 print_family(flm->flm_family));
2159 TAILQ_REMOVE(&all_algo_list, flm, entries);
2160 FIB_MOD_UNLOCK();
2161
2162 return (0);
2163 }
2164
2165 void
vnet_fib_init(void)2166 vnet_fib_init(void)
2167 {
2168
2169 TAILQ_INIT(&V_fib_data_list);
2170 }
2171
2172 void
vnet_fib_destroy(void)2173 vnet_fib_destroy(void)
2174 {
2175
2176 FIB_MOD_LOCK();
2177 fib_error_clear();
2178 FIB_MOD_UNLOCK();
2179 }
2180