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