]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
d62a17ae | 2 | /* |
65efcfce LB |
3 | * |
4 | * Copyright 2009-2016, LabN Consulting, L.L.C. | |
5 | * | |
65efcfce LB |
6 | */ |
7 | ||
f8b6f499 LB |
8 | #include "lib/zebra.h" |
9 | #include "lib/prefix.h" | |
fe08ba7e | 10 | #include "lib/agg_table.h" |
f8b6f499 LB |
11 | #include "lib/vty.h" |
12 | #include "lib/memory.h" | |
13 | #include "lib/routemap.h" | |
14 | #include "lib/log.h" | |
15 | #include "lib/linklist.h" | |
16 | #include "lib/command.h" | |
17 | #include "lib/stream.h" | |
65efcfce | 18 | |
f8b6f499 LB |
19 | #include "bgpd/bgpd.h" |
20 | #include "bgpd/bgp_ecommunity.h" | |
21 | #include "bgpd/bgp_attr.h" | |
65efcfce | 22 | |
f8b6f499 LB |
23 | #include "bgpd/rfapi/bgp_rfapi_cfg.h" |
24 | #include "bgpd/rfapi/rfapi.h" | |
25 | #include "bgpd/rfapi/rfapi_backend.h" | |
65efcfce | 26 | |
f8b6f499 | 27 | #include "bgpd/bgp_route.h" |
c016b6c7 | 28 | #include "bgpd/bgp_mplsvpn.h" |
f8b6f499 LB |
29 | #include "bgpd/bgp_aspath.h" |
30 | #include "bgpd/bgp_advertise.h" | |
65efcfce | 31 | |
f8b6f499 LB |
32 | #include "bgpd/rfapi/rfapi_import.h" |
33 | #include "bgpd/rfapi/rfapi_private.h" | |
34 | #include "bgpd/rfapi/rfapi_monitor.h" | |
35 | #include "bgpd/rfapi/rfapi_vty.h" | |
36 | #include "bgpd/rfapi/vnc_export_bgp.h" | |
37 | #include "bgpd/rfapi/vnc_export_bgp_p.h" | |
38 | #include "bgpd/rfapi/vnc_zebra.h" | |
39 | #include "bgpd/rfapi/vnc_import_bgp.h" | |
40 | #include "bgpd/rfapi/rfapi_rib.h" | |
65efcfce | 41 | |
f8b6f499 | 42 | #include "bgpd/rfapi/rfapi_ap.h" |
a3b55c25 | 43 | #include "bgpd/rfapi/vnc_debug.h" |
65efcfce LB |
44 | |
45 | /* | |
46 | * Per-NVE Advertised prefixes | |
47 | * | |
48 | * We maintain a list of prefixes advertised by each NVE. | |
49 | * There are two indices: by prefix and by lifetime. | |
50 | * | |
51 | * BY-PREFIX skiplist | |
52 | * | |
53 | * key: ptr to struct prefix (when storing, point to prefix that | |
54 | * is part of rfapi_adb). | |
55 | * | |
56 | * value: ptr to struct rfapi_adb | |
57 | * | |
58 | * BY-LIFETIME skiplist | |
59 | * | |
60 | * key: ptr to struct rfapi_adb | |
61 | * value: ptr to struct rfapi_adb | |
62 | * | |
63 | */ | |
64 | ||
65 | /* | |
66 | * Skiplist sort function that sorts first according to lifetime | |
67 | * and then according to adb pointer value. The adb pointer | |
68 | * is used to spread out the sort for adbs with the same lifetime | |
69 | * and thereby make the skip list operations more efficient. | |
70 | */ | |
1a4189d4 | 71 | static int sl_adb_lifetime_cmp(const void *adb1, const void *adb2) |
65efcfce | 72 | { |
1a4189d4 DS |
73 | const struct rfapi_adb *a1 = adb1; |
74 | const struct rfapi_adb *a2 = adb2; | |
65efcfce | 75 | |
d62a17ae | 76 | if (a1->lifetime < a2->lifetime) |
77 | return -1; | |
78 | if (a1->lifetime > a2->lifetime) | |
79 | return 1; | |
65efcfce | 80 | |
d62a17ae | 81 | if (a1 < a2) |
82 | return -1; | |
83 | if (a1 > a2) | |
84 | return 1; | |
65efcfce | 85 | |
d62a17ae | 86 | return 0; |
65efcfce LB |
87 | } |
88 | ||
d62a17ae | 89 | void rfapiApInit(struct rfapi_advertised_prefixes *ap) |
65efcfce | 90 | { |
d62a17ae | 91 | ap->ipN_by_prefix = skiplist_new(0, rfapi_rib_key_cmp, NULL); |
92 | ap->ip0_by_ether = skiplist_new(0, rfapi_rib_key_cmp, NULL); | |
93 | ap->by_lifetime = skiplist_new(0, sl_adb_lifetime_cmp, NULL); | |
65efcfce LB |
94 | } |
95 | ||
d62a17ae | 96 | void rfapiApRelease(struct rfapi_advertised_prefixes *ap) |
65efcfce | 97 | { |
d62a17ae | 98 | struct rfapi_adb *adb; |
99 | ||
100 | /* Free ADBs and lifetime items */ | |
101 | while (0 == skiplist_first(ap->by_lifetime, NULL, (void **)&adb)) { | |
102 | rfapiAdbFree(adb); | |
103 | skiplist_delete_first(ap->by_lifetime); | |
104 | } | |
105 | ||
106 | while (0 == skiplist_delete_first(ap->ipN_by_prefix)) | |
107 | ; | |
108 | while (0 == skiplist_delete_first(ap->ip0_by_ether)) | |
109 | ; | |
110 | ||
111 | /* Free lists */ | |
112 | skiplist_free(ap->ipN_by_prefix); | |
113 | skiplist_free(ap->ip0_by_ether); | |
114 | skiplist_free(ap->by_lifetime); | |
115 | ||
116 | ap->ipN_by_prefix = NULL; | |
117 | ap->ip0_by_ether = NULL; | |
118 | ap->by_lifetime = NULL; | |
65efcfce LB |
119 | } |
120 | ||
d62a17ae | 121 | int rfapiApCount(struct rfapi_descriptor *rfd) |
65efcfce | 122 | { |
d62a17ae | 123 | if (!rfd->advertised.by_lifetime) |
124 | return 0; | |
65efcfce | 125 | |
d62a17ae | 126 | return skiplist_count(rfd->advertised.by_lifetime); |
65efcfce LB |
127 | } |
128 | ||
d62a17ae | 129 | int rfapiApCountAll(struct bgp *bgp) |
65efcfce | 130 | { |
d62a17ae | 131 | struct rfapi *h; |
132 | struct listnode *node; | |
133 | struct rfapi_descriptor *rfd; | |
134 | int total = 0; | |
135 | ||
136 | h = bgp->rfapi; | |
137 | if (h) { | |
138 | for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { | |
139 | total += rfapiApCount(rfd); | |
140 | } | |
141 | } | |
142 | return total; | |
65efcfce LB |
143 | } |
144 | ||
145 | ||
d62a17ae | 146 | void rfapiApReadvertiseAll(struct bgp *bgp, struct rfapi_descriptor *rfd) |
65efcfce | 147 | { |
d62a17ae | 148 | struct rfapi_adb *adb; |
149 | void *cursor = NULL; | |
150 | int rc; | |
151 | ||
152 | for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL, | |
153 | (void **)&adb, &cursor); | |
154 | rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL, | |
155 | (void **)&adb, &cursor)) { | |
156 | ||
157 | struct prefix_rd prd; | |
158 | uint32_t local_pref = rfp_cost_to_localpref(adb->cost); | |
159 | ||
160 | prd = rfd->rd; | |
161 | prd.family = AF_UNSPEC; | |
162 | prd.prefixlen = 64; | |
163 | ||
164 | /* | |
165 | * TBD this is not quite right. When pfx_ip is 0/32 or 0/128, | |
166 | * we need to substitute the VN address as the prefix | |
167 | */ | |
168 | add_vnc_route(rfd, bgp, SAFI_MPLS_VPN, &adb->u.s.prefix_ip, | |
169 | &prd, /* RD to use (0 for ENCAP) */ | |
170 | &rfd->vn_addr, /* nexthop */ | |
171 | &local_pref, &adb->lifetime, NULL, | |
172 | NULL, /* struct rfapi_un_option */ | |
173 | NULL, /* struct rfapi_vn_option */ | |
174 | rfd->rt_export_list, NULL, /* med */ | |
175 | NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0); | |
176 | } | |
65efcfce LB |
177 | } |
178 | ||
d62a17ae | 179 | void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd) |
65efcfce | 180 | { |
d62a17ae | 181 | struct rfapi_adb *adb; |
182 | void *cursor; | |
183 | int rc; | |
184 | ||
185 | ||
186 | cursor = NULL; | |
187 | for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL, | |
188 | (void **)&adb, &cursor); | |
189 | rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL, | |
190 | (void **)&adb, &cursor)) { | |
191 | ||
192 | struct prefix pfx_vn_buf; | |
193 | struct prefix *pfx_ip; | |
194 | ||
195 | if (!(RFAPI_0_PREFIX(&adb->u.s.prefix_ip) | |
196 | && RFAPI_HOST_PREFIX(&adb->u.s.prefix_ip))) { | |
197 | ||
198 | pfx_ip = &adb->u.s.prefix_ip; | |
199 | ||
200 | } else { | |
201 | ||
202 | pfx_ip = NULL; | |
203 | ||
204 | /* | |
205 | * 0/32 or 0/128 => mac advertisement | |
206 | */ | |
207 | if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) { | |
208 | /* | |
209 | * Bad: it means we can't delete the route | |
210 | */ | |
211 | vnc_zlog_debug_verbose( | |
212 | "%s: BAD: handle has bad vn_addr: skipping", | |
213 | __func__); | |
214 | continue; | |
215 | } | |
216 | } | |
217 | ||
218 | del_vnc_route(rfd, rfd->peer, bgp, SAFI_MPLS_VPN, | |
219 | pfx_ip ? pfx_ip : &pfx_vn_buf, | |
220 | &adb->u.s.prd, /* RD to use (0 for ENCAP) */ | |
221 | ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); | |
222 | } | |
65efcfce LB |
223 | } |
224 | ||
225 | /* | |
226 | * returns nonzero if tunnel readvertisement is needed, 0 otherwise | |
227 | */ | |
d62a17ae | 228 | static int rfapiApAdjustLifetimeStats( |
229 | struct rfapi_descriptor *rfd, | |
230 | uint32_t *old_lifetime, /* set if removing/replacing */ | |
231 | uint32_t *new_lifetime) /* set if replacing/adding */ | |
65efcfce | 232 | { |
d62a17ae | 233 | int advertise = 0; |
234 | int find_max = 0; | |
235 | int find_min = 0; | |
236 | ||
237 | vnc_zlog_debug_verbose("%s: rfd=%p, pOldLife=%p, pNewLife=%p", __func__, | |
238 | rfd, old_lifetime, new_lifetime); | |
239 | if (old_lifetime) | |
240 | vnc_zlog_debug_verbose("%s: OldLife=%d", __func__, | |
241 | *old_lifetime); | |
242 | if (new_lifetime) | |
243 | vnc_zlog_debug_verbose("%s: NewLife=%d", __func__, | |
244 | *new_lifetime); | |
245 | ||
246 | if (new_lifetime) { | |
247 | /* | |
248 | * Adding new lifetime | |
249 | */ | |
250 | if (old_lifetime) { | |
251 | /* | |
252 | * replacing existing lifetime | |
253 | */ | |
254 | ||
255 | ||
256 | /* old and new are same */ | |
257 | if (*old_lifetime == *new_lifetime) | |
258 | return 0; | |
259 | ||
260 | if (*old_lifetime == rfd->min_prefix_lifetime) { | |
261 | find_min = 1; | |
262 | } | |
263 | if (*old_lifetime == rfd->max_prefix_lifetime) { | |
264 | find_max = 1; | |
265 | } | |
266 | ||
267 | /* no need to search if new value is at or equals | |
268 | * min|max */ | |
269 | if (*new_lifetime <= rfd->min_prefix_lifetime) { | |
270 | rfd->min_prefix_lifetime = *new_lifetime; | |
271 | find_min = 0; | |
272 | } | |
273 | if (*new_lifetime >= rfd->max_prefix_lifetime) { | |
274 | rfd->max_prefix_lifetime = *new_lifetime; | |
275 | advertise = 1; | |
276 | find_max = 0; | |
277 | } | |
278 | ||
279 | } else { | |
280 | /* | |
281 | * Just adding new lifetime | |
282 | */ | |
283 | if (*new_lifetime < rfd->min_prefix_lifetime) { | |
284 | rfd->min_prefix_lifetime = *new_lifetime; | |
285 | } | |
286 | if (*new_lifetime > rfd->max_prefix_lifetime) { | |
287 | advertise = 1; | |
288 | rfd->max_prefix_lifetime = *new_lifetime; | |
289 | } | |
290 | } | |
291 | } else { | |
292 | /* | |
293 | * Deleting | |
294 | */ | |
295 | ||
296 | /* | |
297 | * See if the max prefix lifetime for this NVE has decreased. | |
298 | * The easy optimization: track min & max; walk the table only | |
299 | * if they are different. | |
300 | * The general optimization: index the advertised_prefixes | |
301 | * table by lifetime. | |
302 | * | |
303 | * Note: for a given nve_descriptor, only one of the | |
304 | * advertised_prefixes[] tables will be used: viz., the | |
305 | * address family that matches the VN address. | |
306 | * | |
307 | */ | |
308 | if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime) { | |
309 | ||
310 | /* | |
311 | * Common case: all lifetimes are the same. Only | |
312 | * thing we need to do here is check if there are | |
313 | * no exported routes left. In that case, reinitialize | |
314 | * the max and min values. | |
315 | */ | |
316 | if (!rfapiApCount(rfd)) { | |
317 | rfd->max_prefix_lifetime = 0; | |
318 | rfd->min_prefix_lifetime = UINT32_MAX; | |
319 | } | |
320 | ||
321 | ||
322 | } else { | |
323 | if (old_lifetime) { | |
324 | if (*old_lifetime == rfd->min_prefix_lifetime) { | |
325 | find_min = 1; | |
326 | } | |
327 | if (*old_lifetime == rfd->max_prefix_lifetime) { | |
328 | find_max = 1; | |
329 | } | |
330 | } | |
331 | } | |
332 | } | |
333 | ||
334 | if (find_min || find_max) { | |
335 | uint32_t min = UINT32_MAX; | |
336 | uint32_t max = 0; | |
337 | ||
338 | struct rfapi_adb *adb_min; | |
339 | struct rfapi_adb *adb_max; | |
340 | ||
341 | if (!skiplist_first(rfd->advertised.by_lifetime, | |
342 | (void **)&adb_min, NULL) | |
343 | && !skiplist_last(rfd->advertised.by_lifetime, | |
344 | (void **)&adb_max, NULL)) { | |
345 | ||
346 | /* | |
347 | * This should always work | |
348 | */ | |
349 | min = adb_min->lifetime; | |
350 | max = adb_max->lifetime; | |
351 | ||
352 | } else { | |
353 | ||
354 | void *cursor; | |
355 | struct rfapi_rib_key rk; | |
356 | struct rfapi_adb *adb; | |
357 | int rc; | |
358 | ||
359 | vnc_zlog_debug_verbose( | |
360 | "%s: walking to find new min/max", __func__); | |
361 | ||
362 | cursor = NULL; | |
363 | for (rc = skiplist_next(rfd->advertised.ipN_by_prefix, | |
364 | (void **)&rk, (void **)&adb, | |
365 | &cursor); | |
366 | !rc; | |
367 | rc = skiplist_next(rfd->advertised.ipN_by_prefix, | |
368 | (void **)&rk, (void **)&adb, | |
369 | &cursor)) { | |
370 | ||
371 | uint32_t lt = adb->lifetime; | |
372 | ||
373 | if (lt > max) | |
374 | max = lt; | |
375 | if (lt < min) | |
376 | min = lt; | |
377 | } | |
378 | cursor = NULL; | |
379 | for (rc = skiplist_next(rfd->advertised.ip0_by_ether, | |
380 | (void **)&rk, (void **)&adb, | |
381 | &cursor); | |
382 | !rc; | |
383 | rc = skiplist_next(rfd->advertised.ip0_by_ether, | |
384 | (void **)&rk, (void **)&adb, | |
385 | &cursor)) { | |
386 | ||
387 | uint32_t lt = adb->lifetime; | |
388 | ||
389 | if (lt > max) | |
390 | max = lt; | |
391 | if (lt < min) | |
392 | min = lt; | |
393 | } | |
394 | } | |
395 | ||
396 | /* | |
397 | * trigger tunnel route update | |
398 | * but only if we found a VPN route and it had | |
399 | * a lifetime greater than 0 | |
400 | */ | |
401 | if (max && rfd->max_prefix_lifetime != max) | |
402 | advertise = 1; | |
403 | rfd->max_prefix_lifetime = max; | |
404 | rfd->min_prefix_lifetime = min; | |
405 | } | |
406 | ||
407 | vnc_zlog_debug_verbose("%s: returning advertise=%d, min=%d, max=%d", | |
408 | __func__, advertise, rfd->min_prefix_lifetime, | |
409 | rfd->max_prefix_lifetime); | |
410 | ||
411 | return (advertise != 0); | |
65efcfce LB |
412 | } |
413 | ||
d62a17ae | 414 | /* |
65efcfce LB |
415 | * Return Value |
416 | * | |
417 | * 0 No need to advertise tunnel route | |
418 | * non-0 advertise tunnel route | |
419 | */ | |
d62a17ae | 420 | int rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd, |
421 | struct prefix *pfx_ip, struct prefix *pfx_eth, | |
422 | struct prefix_rd *prd, uint32_t lifetime, uint8_t cost, | |
423 | struct rfapi_l2address_option *l2o) /* other options TBD */ | |
65efcfce | 424 | { |
d62a17ae | 425 | int rc; |
426 | struct rfapi_adb *adb; | |
427 | uint32_t old_lifetime = 0; | |
428 | int use_ip0 = 0; | |
429 | struct rfapi_rib_key rk; | |
430 | ||
431 | rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk); | |
432 | if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) { | |
433 | use_ip0 = 1; | |
434 | assert(pfx_eth); | |
435 | rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk, | |
436 | (void **)&adb); | |
437 | ||
438 | } else { | |
439 | ||
440 | /* find prefix in advertised prefixes list */ | |
441 | rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk, | |
442 | (void **)&adb); | |
443 | } | |
444 | ||
445 | ||
446 | if (rc) { | |
447 | /* Not found */ | |
448 | adb = XCALLOC(MTYPE_RFAPI_ADB, sizeof(struct rfapi_adb)); | |
d62a17ae | 449 | adb->lifetime = lifetime; |
450 | adb->u.key = rk; | |
451 | ||
452 | if (use_ip0) { | |
453 | assert(pfx_eth); | |
454 | skiplist_insert(rfd->advertised.ip0_by_ether, | |
455 | &adb->u.key, adb); | |
456 | } else { | |
457 | skiplist_insert(rfd->advertised.ipN_by_prefix, | |
458 | &adb->u.key, adb); | |
459 | } | |
460 | ||
461 | skiplist_insert(rfd->advertised.by_lifetime, adb, adb); | |
462 | } else { | |
463 | old_lifetime = adb->lifetime; | |
464 | if (old_lifetime != lifetime) { | |
465 | assert(!skiplist_delete(rfd->advertised.by_lifetime, | |
466 | adb, NULL)); | |
467 | adb->lifetime = lifetime; | |
468 | assert(!skiplist_insert(rfd->advertised.by_lifetime, | |
469 | adb, adb)); | |
470 | } | |
471 | } | |
472 | adb->cost = cost; | |
473 | if (l2o) | |
474 | adb->l2o = *l2o; | |
475 | else | |
476 | memset(&adb->l2o, 0, sizeof(struct rfapi_l2address_option)); | |
477 | ||
478 | if (rfapiApAdjustLifetimeStats(rfd, (rc ? NULL : &old_lifetime), | |
479 | &lifetime)) | |
480 | return 1; | |
481 | ||
482 | return 0; | |
65efcfce LB |
483 | } |
484 | ||
485 | /* | |
486 | * After this function returns successfully, caller should call | |
487 | * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce() | |
488 | */ | |
d62a17ae | 489 | int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd, |
490 | struct prefix *pfx_ip, struct prefix *pfx_eth, | |
491 | struct prefix_rd *prd, int *advertise_tunnel) /* out */ | |
65efcfce | 492 | { |
d62a17ae | 493 | int rc; |
494 | struct rfapi_adb *adb; | |
495 | uint32_t old_lifetime; | |
496 | int use_ip0 = 0; | |
497 | struct rfapi_rib_key rk; | |
498 | ||
499 | if (advertise_tunnel) | |
500 | *advertise_tunnel = 0; | |
501 | ||
502 | rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk); | |
503 | /* find prefix in advertised prefixes list */ | |
504 | if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) { | |
505 | use_ip0 = 1; | |
506 | assert(pfx_eth); | |
507 | ||
508 | rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk, | |
509 | (void **)&adb); | |
510 | ||
511 | } else { | |
512 | ||
513 | /* find prefix in advertised prefixes list */ | |
514 | rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk, | |
515 | (void **)&adb); | |
516 | } | |
517 | ||
518 | if (rc) { | |
519 | return ENOENT; | |
520 | } | |
521 | ||
522 | old_lifetime = adb->lifetime; | |
523 | ||
524 | if (use_ip0) { | |
525 | rc = skiplist_delete(rfd->advertised.ip0_by_ether, &rk, NULL); | |
526 | } else { | |
527 | rc = skiplist_delete(rfd->advertised.ipN_by_prefix, &rk, NULL); | |
528 | } | |
529 | assert(!rc); | |
530 | ||
531 | rc = skiplist_delete(rfd->advertised.by_lifetime, adb, NULL); | |
532 | assert(!rc); | |
533 | ||
534 | rfapiAdbFree(adb); | |
535 | ||
536 | if (rfapiApAdjustLifetimeStats(rfd, &old_lifetime, NULL)) { | |
537 | if (advertise_tunnel) | |
538 | *advertise_tunnel = 1; | |
539 | } | |
540 | ||
541 | return 0; | |
65efcfce | 542 | } |