]>
Commit | Line | Data |
---|---|---|
7758e3f3 | 1 | /* Zebra MPLS code |
2 | * Copyright (C) 2013 Cumulus Networks, Inc. | |
3 | * | |
4 | * This file is part of GNU Zebra. | |
5 | * | |
6 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * GNU Zebra is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * 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 | |
7758e3f3 | 19 | */ |
20 | ||
21 | #include <zebra.h> | |
22 | ||
23 | #include "prefix.h" | |
24 | #include "table.h" | |
25 | #include "memory.h" | |
7758e3f3 | 26 | #include "command.h" |
27 | #include "if.h" | |
28 | #include "log.h" | |
29 | #include "sockunion.h" | |
30 | #include "linklist.h" | |
31 | #include "thread.h" | |
32 | #include "workqueue.h" | |
33 | #include "prefix.h" | |
34 | #include "routemap.h" | |
35 | #include "stream.h" | |
36 | #include "nexthop.h" | |
b78b820d | 37 | #include "lib/json.h" |
7758e3f3 | 38 | |
39 | #include "zebra/rib.h" | |
40 | #include "zebra/rt.h" | |
41 | #include "zebra/zserv.h" | |
42 | #include "zebra/redistribute.h" | |
43 | #include "zebra/debug.h" | |
44 | #include "zebra/zebra_memory.h" | |
45 | #include "zebra/zebra_vrf.h" | |
46 | #include "zebra/zebra_mpls.h" | |
47 | ||
d62a17ae | 48 | DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") |
49 | DEFINE_MTYPE_STATIC(ZEBRA, FEC, "MPLS FEC object") | |
50 | DEFINE_MTYPE_STATIC(ZEBRA, SLSP, "MPLS static LSP config") | |
51 | DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object") | |
52 | DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE, "MPLS static nexthop object") | |
53 | DEFINE_MTYPE_STATIC(ZEBRA, SNHLFE_IFNAME, "MPLS static nexthop ifname") | |
7758e3f3 | 54 | |
fe6c7157 RW |
55 | int mpls_enabled; |
56 | ||
7758e3f3 | 57 | /* Default rtm_table for all clients */ |
58 | extern struct zebra_t zebrad; | |
59 | ||
60 | /* static function declarations */ | |
f31e084c | 61 | |
d62a17ae | 62 | static void fec_evaluate(struct zebra_vrf *zvrf); |
63 | static u_int32_t fec_derive_label_from_index(struct zebra_vrf *vrf, | |
64 | zebra_fec_t *fec); | |
65 | static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, | |
66 | struct route_node *rn, struct route_entry *re); | |
67 | static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label); | |
68 | static int fec_change_update_lsp(struct zebra_vrf *zvrf, zebra_fec_t *fec, | |
69 | mpls_label_t old_label); | |
70 | static int fec_send(zebra_fec_t *fec, struct zserv *client); | |
71 | static void fec_update_clients(zebra_fec_t *fec); | |
72 | static void fec_print(zebra_fec_t *fec, struct vty *vty); | |
73 | static zebra_fec_t *fec_find(struct route_table *table, struct prefix *p); | |
74 | static zebra_fec_t *fec_add(struct route_table *table, struct prefix *p, | |
75 | mpls_label_t label, u_int32_t flags, | |
76 | u_int32_t label_index); | |
77 | static int fec_del(zebra_fec_t *fec); | |
78 | ||
79 | static unsigned int label_hash(void *p); | |
80 | static int label_cmp(const void *p1, const void *p2); | |
81 | static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, | |
82 | struct nexthop *nexthop); | |
83 | static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe, | |
84 | struct nexthop *nexthop); | |
85 | static int nhlfe_nexthop_active(zebra_nhlfe_t *nhlfe); | |
86 | ||
87 | static void lsp_select_best_nhlfe(zebra_lsp_t *lsp); | |
88 | static void lsp_uninstall_from_kernel(struct hash_backet *backet, void *ctxt); | |
89 | static void lsp_schedule(struct hash_backet *backet, void *ctxt); | |
90 | static wq_item_status lsp_process(struct work_queue *wq, void *data); | |
91 | static void lsp_processq_del(struct work_queue *wq, void *data); | |
92 | static void lsp_processq_complete(struct work_queue *wq); | |
93 | static int lsp_processq_add(zebra_lsp_t *lsp); | |
94 | static void *lsp_alloc(void *p); | |
95 | ||
96 | static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size); | |
97 | static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, | |
98 | union g_addr *gate, ifindex_t ifindex); | |
99 | static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, | |
100 | enum nexthop_types_t gtype, union g_addr *gate, | |
101 | ifindex_t ifindex); | |
102 | static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, | |
103 | enum nexthop_types_t gtype, union g_addr *gate, | |
104 | ifindex_t ifindex, mpls_label_t out_label); | |
105 | static int nhlfe_del(zebra_nhlfe_t *snhlfe); | |
106 | static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, | |
107 | struct nexthop_label *nh_label); | |
108 | static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, | |
109 | enum lsp_types_t type); | |
110 | static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, | |
111 | mpls_label_t in_label); | |
112 | static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); | |
113 | static void lsp_print(zebra_lsp_t *lsp, void *ctxt); | |
114 | static void *slsp_alloc(void *p); | |
115 | static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, | |
116 | union g_addr *gate, ifindex_t ifindex); | |
117 | static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, | |
118 | enum nexthop_types_t gtype, | |
119 | union g_addr *gate, ifindex_t ifindex); | |
120 | static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, | |
121 | enum nexthop_types_t gtype, | |
122 | union g_addr *gate, ifindex_t ifindex, | |
123 | mpls_label_t out_label); | |
124 | static int snhlfe_del(zebra_snhlfe_t *snhlfe); | |
125 | static int snhlfe_del_all(zebra_slsp_t *slsp); | |
126 | static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size); | |
127 | static int mpls_processq_init(struct zebra_t *zebra); | |
7758e3f3 | 128 | |
129 | ||
130 | /* Static functions */ | |
131 | ||
a64448ba DS |
132 | /* |
133 | * Install label forwarding entry based on labeled-route entry. | |
134 | */ | |
d62a17ae | 135 | static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, |
136 | struct route_node *rn, struct route_entry *re) | |
137 | { | |
138 | struct hash *lsp_table; | |
139 | zebra_ile_t tmp_ile; | |
140 | zebra_lsp_t *lsp; | |
141 | zebra_nhlfe_t *nhlfe; | |
142 | struct nexthop *nexthop; | |
143 | enum lsp_types_t lsp_type; | |
144 | char buf[BUFSIZ]; | |
145 | int added, changed; | |
146 | ||
147 | /* Lookup table. */ | |
148 | lsp_table = zvrf->lsp_table; | |
149 | if (!lsp_table) | |
150 | return -1; | |
151 | ||
152 | lsp_type = lsp_type_from_re_type(re->type); | |
153 | added = changed = 0; | |
154 | ||
155 | /* Locate or allocate LSP entry. */ | |
156 | tmp_ile.in_label = label; | |
157 | lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); | |
158 | if (!lsp) | |
159 | return -1; | |
160 | ||
161 | /* For each active nexthop, create NHLFE. Note that we deliberately skip | |
162 | * recursive nexthops right now, because intermediate hops won't | |
163 | * understand | |
164 | * the label advertised by the recursive nexthop (plus we don't have the | |
165 | * logic yet to push multiple labels). | |
166 | */ | |
167 | for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { | |
168 | /* Skip inactive and recursive entries. */ | |
169 | if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) | |
170 | continue; | |
171 | if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) | |
172 | continue; | |
173 | ||
174 | nhlfe = nhlfe_find(lsp, lsp_type, nexthop->type, &nexthop->gate, | |
175 | nexthop->ifindex); | |
176 | if (nhlfe) { | |
177 | /* Clear deleted flag (in case it was set) */ | |
178 | UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); | |
179 | if (nexthop_labels_match(nhlfe->nexthop, nexthop)) | |
180 | /* No change */ | |
181 | continue; | |
182 | ||
183 | ||
184 | if (IS_ZEBRA_DEBUG_MPLS) { | |
185 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
186 | zlog_debug( | |
187 | "LSP in-label %u type %d nexthop %s " | |
188 | "out-label changed", | |
189 | lsp->ile.in_label, lsp_type, buf); | |
190 | } | |
191 | ||
192 | /* Update out label, trigger processing. */ | |
193 | nhlfe_out_label_update(nhlfe, nexthop->nh_label); | |
194 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
195 | changed++; | |
196 | } else { | |
197 | /* Add LSP entry to this nexthop */ | |
198 | nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type, | |
199 | &nexthop->gate, nexthop->ifindex, | |
200 | nexthop->nh_label->label[0]); | |
201 | if (!nhlfe) | |
202 | return -1; | |
203 | ||
204 | if (IS_ZEBRA_DEBUG_MPLS) { | |
205 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
206 | zlog_debug( | |
207 | "Add LSP in-label %u type %d nexthop %s " | |
208 | "out-label %u", | |
209 | lsp->ile.in_label, lsp_type, buf, | |
210 | nexthop->nh_label->label[0]); | |
211 | } | |
212 | ||
213 | lsp->addr_family = NHLFE_FAMILY(nhlfe); | |
214 | ||
215 | /* Mark NHLFE as changed. */ | |
216 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
217 | added++; | |
218 | } | |
219 | } | |
220 | ||
221 | /* Queue LSP for processing if necessary. If no NHLFE got added (special | |
222 | * case), delete the LSP entry; this case results in somewhat ugly | |
223 | * logging. | |
224 | */ | |
225 | if (added || changed) { | |
226 | if (lsp_processq_add(lsp)) | |
227 | return -1; | |
228 | } else if (!lsp->nhlfe_list | |
229 | && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { | |
230 | if (IS_ZEBRA_DEBUG_MPLS) | |
231 | zlog_debug("Free LSP in-label %u flags 0x%x", | |
232 | lsp->ile.in_label, lsp->flags); | |
233 | ||
234 | lsp = hash_release(lsp_table, &lsp->ile); | |
235 | if (lsp) | |
236 | XFREE(MTYPE_LSP, lsp); | |
237 | } | |
238 | ||
239 | return 0; | |
a64448ba DS |
240 | } |
241 | ||
242 | /* | |
243 | * Uninstall all non-static NHLFEs of a label forwarding entry. If all | |
244 | * NHLFEs are removed, the entire entry is deleted. | |
245 | */ | |
d62a17ae | 246 | static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) |
247 | { | |
248 | struct hash *lsp_table; | |
249 | zebra_ile_t tmp_ile; | |
250 | zebra_lsp_t *lsp; | |
251 | zebra_nhlfe_t *nhlfe, *nhlfe_next; | |
252 | char buf[BUFSIZ]; | |
253 | ||
254 | /* Lookup table. */ | |
255 | lsp_table = zvrf->lsp_table; | |
256 | if (!lsp_table) | |
257 | return -1; | |
258 | ||
259 | /* If entry is not present, exit. */ | |
260 | tmp_ile.in_label = label; | |
261 | lsp = hash_lookup(lsp_table, &tmp_ile); | |
262 | if (!lsp || !lsp->nhlfe_list) | |
263 | return 0; | |
264 | ||
265 | /* Mark NHLFEs for delete or directly delete, as appropriate. */ | |
266 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { | |
267 | nhlfe_next = nhlfe->next; | |
268 | ||
269 | /* Skip static NHLFEs */ | |
270 | if (nhlfe->type == ZEBRA_LSP_STATIC) | |
271 | continue; | |
272 | ||
273 | if (IS_ZEBRA_DEBUG_MPLS) { | |
274 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
275 | zlog_debug( | |
276 | "Del LSP in-label %u type %d nexthop %s flags 0x%x", | |
277 | label, nhlfe->type, buf, nhlfe->flags); | |
278 | } | |
279 | ||
280 | if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)) { | |
281 | UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
282 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); | |
283 | } else { | |
284 | nhlfe_del(nhlfe); | |
285 | } | |
286 | } | |
287 | ||
288 | /* Queue LSP for processing, if needed, else delete. */ | |
289 | if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { | |
290 | if (lsp_processq_add(lsp)) | |
291 | return -1; | |
292 | } else if (!lsp->nhlfe_list | |
293 | && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { | |
294 | if (IS_ZEBRA_DEBUG_MPLS) | |
295 | zlog_debug("Del LSP in-label %u flags 0x%x", | |
296 | lsp->ile.in_label, lsp->flags); | |
297 | ||
298 | lsp = hash_release(lsp_table, &lsp->ile); | |
299 | if (lsp) | |
300 | XFREE(MTYPE_LSP, lsp); | |
301 | } | |
302 | ||
303 | return 0; | |
a64448ba DS |
304 | } |
305 | ||
28d58fd7 VV |
306 | /* |
307 | * This function is invoked upon change to label block configuration; it | |
308 | * will walk all registered FECs with label-index and appropriately update | |
309 | * their local labels and trigger client updates. | |
310 | */ | |
d62a17ae | 311 | static void fec_evaluate(struct zebra_vrf *zvrf) |
312 | { | |
313 | struct route_node *rn; | |
314 | zebra_fec_t *fec; | |
315 | u_int32_t old_label, new_label; | |
316 | int af; | |
317 | char buf[BUFSIZ]; | |
318 | ||
319 | for (af = AFI_IP; af < AFI_MAX; af++) { | |
320 | if (zvrf->fec_table[af] == NULL) | |
321 | continue; | |
322 | ||
323 | for (rn = route_top(zvrf->fec_table[af]); rn; | |
324 | rn = route_next(rn)) { | |
325 | if ((fec = rn->info) == NULL) | |
326 | continue; | |
327 | ||
328 | /* Skip configured FECs and those without a label index. | |
329 | */ | |
330 | if (fec->flags & FEC_FLAG_CONFIGURED | |
331 | || fec->label_index == MPLS_INVALID_LABEL_INDEX) | |
332 | continue; | |
333 | ||
334 | if (IS_ZEBRA_DEBUG_MPLS) | |
335 | prefix2str(&rn->p, buf, BUFSIZ); | |
336 | ||
337 | /* Save old label, determine new label. */ | |
338 | old_label = fec->label; | |
339 | new_label = | |
340 | zvrf->mpls_srgb.start_label + fec->label_index; | |
341 | if (new_label >= zvrf->mpls_srgb.end_label) | |
342 | new_label = MPLS_INVALID_LABEL; | |
343 | ||
344 | /* If label has changed, update FEC and clients. */ | |
345 | if (new_label == old_label) | |
346 | continue; | |
347 | ||
348 | if (IS_ZEBRA_DEBUG_MPLS) | |
349 | zlog_debug( | |
350 | "Update fec %s new label %u upon label block", | |
351 | buf, new_label); | |
352 | ||
353 | fec->label = new_label; | |
354 | fec_update_clients(fec); | |
355 | ||
356 | /* Update label forwarding entries appropriately */ | |
357 | fec_change_update_lsp(zvrf, fec, old_label); | |
358 | } | |
359 | } | |
28d58fd7 VV |
360 | } |
361 | ||
362 | /* | |
363 | * Derive (if possible) and update the local label for the FEC based on | |
364 | * its label index. The index is "acceptable" if it falls within the | |
365 | * globally configured label block (SRGB). | |
366 | */ | |
d62a17ae | 367 | static u_int32_t fec_derive_label_from_index(struct zebra_vrf *zvrf, |
368 | zebra_fec_t *fec) | |
28d58fd7 | 369 | { |
d62a17ae | 370 | u_int32_t label; |
28d58fd7 | 371 | |
d62a17ae | 372 | if (fec->label_index != MPLS_INVALID_LABEL_INDEX |
373 | && zvrf->mpls_srgb.start_label | |
374 | && ((label = zvrf->mpls_srgb.start_label + fec->label_index) | |
375 | < zvrf->mpls_srgb.end_label)) | |
376 | fec->label = label; | |
377 | else | |
378 | fec->label = MPLS_INVALID_LABEL; | |
28d58fd7 | 379 | |
d62a17ae | 380 | return fec->label; |
28d58fd7 VV |
381 | } |
382 | ||
a64448ba DS |
383 | /* |
384 | * There is a change for this FEC. Install or uninstall label forwarding | |
385 | * entries, as appropriate. | |
386 | */ | |
d62a17ae | 387 | static int fec_change_update_lsp(struct zebra_vrf *zvrf, zebra_fec_t *fec, |
388 | mpls_label_t old_label) | |
a64448ba | 389 | { |
d62a17ae | 390 | struct route_table *table; |
391 | struct route_node *rn; | |
392 | struct route_entry *re; | |
393 | afi_t afi; | |
a64448ba | 394 | |
d62a17ae | 395 | /* Uninstall label forwarding entry, if previously installed. */ |
396 | if (old_label != MPLS_INVALID_LABEL && old_label != MPLS_IMP_NULL_LABEL) | |
397 | lsp_uninstall(zvrf, old_label); | |
a64448ba | 398 | |
d62a17ae | 399 | /* Install label forwarding entry corr. to new label, if needed. */ |
400 | if (fec->label == MPLS_INVALID_LABEL | |
401 | || fec->label == MPLS_IMP_NULL_LABEL) | |
402 | return 0; | |
a64448ba | 403 | |
d62a17ae | 404 | afi = family2afi(PREFIX_FAMILY(&fec->rn->p)); |
405 | table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); | |
406 | if (!table) | |
407 | return 0; | |
a64448ba | 408 | |
d62a17ae | 409 | /* See if labeled route exists. */ |
410 | rn = route_node_lookup(table, &fec->rn->p); | |
411 | if (!rn) | |
412 | return 0; | |
a64448ba | 413 | |
d62a17ae | 414 | RNODE_FOREACH_RE(rn, re) |
415 | { | |
416 | if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) | |
417 | break; | |
418 | } | |
a64448ba | 419 | |
d62a17ae | 420 | if (!re || !zebra_rib_labeled_unicast(re)) |
421 | return 0; | |
a64448ba | 422 | |
d62a17ae | 423 | if (lsp_install(zvrf, fec->label, rn, re)) |
424 | return -1; | |
a64448ba | 425 | |
d62a17ae | 426 | return 0; |
a64448ba DS |
427 | } |
428 | ||
5aba114a DS |
429 | /* |
430 | * Inform about FEC to a registered client. | |
431 | */ | |
d62a17ae | 432 | static int fec_send(zebra_fec_t *fec, struct zserv *client) |
5aba114a | 433 | { |
d62a17ae | 434 | struct stream *s; |
435 | struct route_node *rn; | |
5aba114a | 436 | |
d62a17ae | 437 | rn = fec->rn; |
5aba114a | 438 | |
d62a17ae | 439 | /* Get output stream. */ |
440 | s = client->obuf; | |
441 | stream_reset(s); | |
5aba114a | 442 | |
d62a17ae | 443 | zserv_create_header(s, ZEBRA_FEC_UPDATE, VRF_DEFAULT); |
5aba114a | 444 | |
d62a17ae | 445 | stream_putw(s, rn->p.family); |
446 | stream_put_prefix(s, &rn->p); | |
447 | stream_putl(s, fec->label); | |
448 | stream_putw_at(s, 0, stream_get_endp(s)); | |
449 | return zebra_server_send_message(client); | |
5aba114a DS |
450 | } |
451 | ||
452 | /* | |
453 | * Update all registered clients about this FEC. Caller should've updated | |
454 | * FEC and ensure no duplicate updates. | |
455 | */ | |
d62a17ae | 456 | static void fec_update_clients(zebra_fec_t *fec) |
5aba114a | 457 | { |
d62a17ae | 458 | struct listnode *node; |
459 | struct zserv *client; | |
5aba114a | 460 | |
d62a17ae | 461 | for (ALL_LIST_ELEMENTS_RO(fec->client_list, node, client)) { |
462 | if (IS_ZEBRA_DEBUG_MPLS) | |
463 | zlog_debug("Update client %s", | |
464 | zebra_route_string(client->proto)); | |
465 | fec_send(fec, client); | |
466 | } | |
5aba114a DS |
467 | } |
468 | ||
469 | ||
f31e084c DS |
470 | /* |
471 | * Print a FEC-label binding entry. | |
472 | */ | |
d62a17ae | 473 | static void fec_print(zebra_fec_t *fec, struct vty *vty) |
474 | { | |
475 | struct route_node *rn; | |
476 | struct listnode *node; | |
477 | struct zserv *client; | |
478 | char buf[BUFSIZ]; | |
479 | ||
480 | rn = fec->rn; | |
481 | prefix2str(&rn->p, buf, BUFSIZ); | |
482 | vty_out(vty, "%s\n", buf); | |
483 | vty_out(vty, " Label: %s", label2str(fec->label, buf, BUFSIZ)); | |
484 | if (fec->label_index != MPLS_INVALID_LABEL_INDEX) | |
485 | vty_out(vty, ", Label Index: %u", fec->label_index); | |
486 | vty_out(vty, "\n"); | |
487 | if (!list_isempty(fec->client_list)) { | |
488 | vty_out(vty, " Client list:"); | |
489 | for (ALL_LIST_ELEMENTS_RO(fec->client_list, node, client)) | |
490 | vty_out(vty, " %s(fd %d)", | |
491 | zebra_route_string(client->proto), | |
492 | client->sock); | |
493 | vty_out(vty, "\n"); | |
494 | } | |
f31e084c DS |
495 | } |
496 | ||
497 | /* | |
498 | * Locate FEC-label binding that matches with passed info. | |
499 | */ | |
d62a17ae | 500 | static zebra_fec_t *fec_find(struct route_table *table, struct prefix *p) |
f31e084c | 501 | { |
d62a17ae | 502 | struct route_node *rn; |
f31e084c | 503 | |
d62a17ae | 504 | apply_mask(p); |
505 | rn = route_node_lookup(table, p); | |
506 | if (!rn) | |
507 | return NULL; | |
f31e084c | 508 | |
d62a17ae | 509 | route_unlock_node(rn); |
510 | return (rn->info); | |
f31e084c DS |
511 | } |
512 | ||
513 | /* | |
5aba114a DS |
514 | * Add a FEC. This may be upon a client registering for a binding |
515 | * or when a binding is configured. | |
f31e084c | 516 | */ |
d62a17ae | 517 | static zebra_fec_t *fec_add(struct route_table *table, struct prefix *p, |
518 | mpls_label_t label, u_int32_t flags, | |
519 | u_int32_t label_index) | |
f31e084c | 520 | { |
d62a17ae | 521 | struct route_node *rn; |
522 | zebra_fec_t *fec; | |
f31e084c | 523 | |
d62a17ae | 524 | apply_mask(p); |
f31e084c | 525 | |
d62a17ae | 526 | /* Lookup (or add) route node.*/ |
527 | rn = route_node_get(table, p); | |
528 | if (!rn) | |
529 | return NULL; | |
f31e084c | 530 | |
d62a17ae | 531 | fec = rn->info; |
f31e084c | 532 | |
d62a17ae | 533 | if (!fec) { |
534 | fec = XCALLOC(MTYPE_FEC, sizeof(zebra_fec_t)); | |
535 | if (!fec) | |
536 | return NULL; | |
f31e084c | 537 | |
d62a17ae | 538 | rn->info = fec; |
539 | fec->rn = rn; | |
540 | fec->label = label; | |
541 | fec->client_list = list_new(); | |
542 | } else | |
543 | route_unlock_node(rn); /* for the route_node_get */ | |
f31e084c | 544 | |
d62a17ae | 545 | fec->label_index = label_index; |
546 | fec->flags = flags; | |
f31e084c | 547 | |
d62a17ae | 548 | return fec; |
f31e084c DS |
549 | } |
550 | ||
551 | /* | |
5aba114a DS |
552 | * Delete a FEC. This may be upon the last client deregistering for |
553 | * a FEC and no binding exists or when the binding is deleted and there | |
554 | * are no registered clients. | |
f31e084c | 555 | */ |
d62a17ae | 556 | static int fec_del(zebra_fec_t *fec) |
f31e084c | 557 | { |
d62a17ae | 558 | list_free(fec->client_list); |
559 | fec->rn->info = NULL; | |
560 | route_unlock_node(fec->rn); | |
561 | XFREE(MTYPE_FEC, fec); | |
562 | return 0; | |
f31e084c DS |
563 | } |
564 | ||
7758e3f3 | 565 | /* |
566 | * Hash function for label. | |
567 | */ | |
d62a17ae | 568 | static unsigned int label_hash(void *p) |
7758e3f3 | 569 | { |
d62a17ae | 570 | const zebra_ile_t *ile = p; |
7758e3f3 | 571 | |
d62a17ae | 572 | return (jhash_1word(ile->in_label, 0)); |
7758e3f3 | 573 | } |
574 | ||
575 | /* | |
576 | * Compare 2 LSP hash entries based on in-label. | |
577 | */ | |
d62a17ae | 578 | static int label_cmp(const void *p1, const void *p2) |
7758e3f3 | 579 | { |
d62a17ae | 580 | const zebra_ile_t *ile1 = p1; |
581 | const zebra_ile_t *ile2 = p2; | |
7758e3f3 | 582 | |
d62a17ae | 583 | return (ile1->in_label == ile2->in_label); |
7758e3f3 | 584 | } |
585 | ||
40c7bdb0 | 586 | /* |
587 | * Check if an IPv4 nexthop for a NHLFE is active. Update nexthop based on | |
588 | * the passed flag. | |
589 | * NOTE: Looking only for connected routes right now. | |
590 | */ | |
d62a17ae | 591 | static int nhlfe_nexthop_active_ipv4(zebra_nhlfe_t *nhlfe, |
592 | struct nexthop *nexthop) | |
40c7bdb0 | 593 | { |
d62a17ae | 594 | struct route_table *table; |
595 | struct prefix_ipv4 p; | |
596 | struct route_node *rn; | |
597 | struct route_entry *match; | |
598 | struct nexthop *match_nh; | |
40c7bdb0 | 599 | |
d62a17ae | 600 | table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, VRF_DEFAULT); |
601 | if (!table) | |
602 | return 0; | |
40c7bdb0 | 603 | |
d62a17ae | 604 | /* Lookup nexthop in IPv4 routing table. */ |
605 | memset(&p, 0, sizeof(struct prefix_ipv4)); | |
606 | p.family = AF_INET; | |
607 | p.prefixlen = IPV4_MAX_PREFIXLEN; | |
608 | p.prefix = nexthop->gate.ipv4; | |
40c7bdb0 | 609 | |
d62a17ae | 610 | rn = route_node_match(table, (struct prefix *)&p); |
611 | if (!rn) | |
612 | return 0; | |
40c7bdb0 | 613 | |
d62a17ae | 614 | route_unlock_node(rn); |
88d88a9c | 615 | |
d62a17ae | 616 | /* Locate a valid connected route. */ |
617 | RNODE_FOREACH_RE(rn, match) | |
88d88a9c | 618 | { |
d62a17ae | 619 | if (CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED) |
620 | || !CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) | |
621 | continue; | |
622 | ||
623 | for (match_nh = match->nexthop; match_nh; | |
624 | match_nh = match_nh->next) { | |
625 | if (match->type == ZEBRA_ROUTE_CONNECT | |
626 | || nexthop->ifindex == match_nh->ifindex) { | |
627 | nexthop->ifindex = match_nh->ifindex; | |
628 | return 1; | |
629 | } | |
630 | } | |
88d88a9c | 631 | } |
40c7bdb0 | 632 | |
d62a17ae | 633 | return 0; |
40c7bdb0 | 634 | } |
635 | ||
636 | ||
637 | /* | |
638 | * Check if an IPv6 nexthop for a NHLFE is active. Update nexthop based on | |
639 | * the passed flag. | |
640 | * NOTE: Looking only for connected routes right now. | |
641 | */ | |
d62a17ae | 642 | static int nhlfe_nexthop_active_ipv6(zebra_nhlfe_t *nhlfe, |
643 | struct nexthop *nexthop) | |
40c7bdb0 | 644 | { |
d62a17ae | 645 | struct route_table *table; |
646 | struct prefix_ipv6 p; | |
647 | struct route_node *rn; | |
648 | struct route_entry *match; | |
40c7bdb0 | 649 | |
d62a17ae | 650 | table = zebra_vrf_table(AFI_IP6, SAFI_UNICAST, VRF_DEFAULT); |
651 | if (!table) | |
652 | return 0; | |
40c7bdb0 | 653 | |
d62a17ae | 654 | /* Lookup nexthop in IPv6 routing table. */ |
655 | memset(&p, 0, sizeof(struct prefix_ipv6)); | |
656 | p.family = AF_INET6; | |
657 | p.prefixlen = IPV6_MAX_PREFIXLEN; | |
658 | p.prefix = nexthop->gate.ipv6; | |
40c7bdb0 | 659 | |
d62a17ae | 660 | rn = route_node_match(table, (struct prefix *)&p); |
661 | if (!rn) | |
662 | return 0; | |
40c7bdb0 | 663 | |
d62a17ae | 664 | route_unlock_node(rn); |
40c7bdb0 | 665 | |
d62a17ae | 666 | /* Locate a valid connected route. */ |
667 | RNODE_FOREACH_RE(rn, match) | |
668 | { | |
669 | if ((match->type == ZEBRA_ROUTE_CONNECT) | |
670 | && !CHECK_FLAG(match->status, ROUTE_ENTRY_REMOVED) | |
671 | && CHECK_FLAG(match->flags, ZEBRA_FLAG_SELECTED)) | |
672 | break; | |
673 | } | |
40c7bdb0 | 674 | |
d62a17ae | 675 | if (!match || !match->nexthop) |
676 | return 0; | |
40c7bdb0 | 677 | |
d62a17ae | 678 | nexthop->ifindex = match->nexthop->ifindex; |
679 | return 1; | |
40c7bdb0 | 680 | } |
681 | ||
682 | ||
683 | /* | |
684 | * Check the nexthop reachability for a NHLFE and return if valid (reachable) | |
685 | * or not. | |
686 | * NOTE: Each NHLFE points to only 1 nexthop. | |
687 | */ | |
d62a17ae | 688 | static int nhlfe_nexthop_active(zebra_nhlfe_t *nhlfe) |
689 | { | |
690 | struct nexthop *nexthop; | |
691 | struct interface *ifp; | |
692 | ||
693 | nexthop = nhlfe->nexthop; | |
694 | if (!nexthop) // unexpected | |
695 | return 0; | |
696 | ||
697 | /* Check on nexthop based on type. */ | |
698 | switch (nexthop->type) { | |
699 | case NEXTHOP_TYPE_IPV4: | |
700 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
701 | if (nhlfe_nexthop_active_ipv4(nhlfe, nexthop)) | |
702 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
703 | else | |
704 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
705 | break; | |
706 | ||
707 | case NEXTHOP_TYPE_IPV6: | |
708 | if (nhlfe_nexthop_active_ipv6(nhlfe, nexthop)) | |
709 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
710 | else | |
711 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
712 | break; | |
713 | ||
714 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
715 | if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { | |
716 | ifp = if_lookup_by_index(nexthop->ifindex, VRF_DEFAULT); | |
717 | if (ifp && if_is_operative(ifp)) | |
718 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
719 | else | |
720 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
721 | } else { | |
722 | if (nhlfe_nexthop_active_ipv6(nhlfe, nexthop)) | |
723 | SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
724 | else | |
725 | UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
726 | } | |
727 | break; | |
728 | ||
729 | default: | |
730 | break; | |
731 | } | |
732 | ||
733 | return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); | |
40c7bdb0 | 734 | } |
735 | ||
736 | /* | |
737 | * Walk through NHLFEs for a LSP forwarding entry, verify nexthop | |
738 | * reachability and select the best. Multipath entries are also | |
739 | * marked. This is invoked when an LSP scheduled for processing (due | |
740 | * to some change) is examined. | |
741 | */ | |
d62a17ae | 742 | static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) |
743 | { | |
744 | zebra_nhlfe_t *nhlfe; | |
745 | zebra_nhlfe_t *best; | |
746 | struct nexthop *nexthop; | |
747 | int changed = 0; | |
748 | ||
749 | if (!lsp) | |
750 | return; | |
751 | ||
752 | best = NULL; | |
753 | lsp->num_ecmp = 0; | |
754 | UNSET_FLAG(lsp->flags, LSP_FLAG_CHANGED); | |
755 | ||
756 | /* | |
757 | * First compute the best path, after checking nexthop status. We are | |
758 | * only | |
759 | * concerned with non-deleted NHLFEs. | |
760 | */ | |
761 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { | |
762 | /* Clear selection flags. */ | |
763 | UNSET_FLAG(nhlfe->flags, | |
764 | (NHLFE_FLAG_SELECTED | NHLFE_FLAG_MULTIPATH)); | |
765 | ||
766 | if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED) | |
767 | && nhlfe_nexthop_active(nhlfe)) { | |
768 | if (!best || (nhlfe->distance < best->distance)) | |
769 | best = nhlfe; | |
770 | } | |
771 | } | |
772 | ||
773 | lsp->best_nhlfe = best; | |
774 | if (!lsp->best_nhlfe) | |
775 | return; | |
776 | ||
777 | /* Mark best NHLFE as selected. */ | |
778 | SET_FLAG(lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); | |
779 | ||
780 | /* | |
781 | * If best path exists, see if there is ECMP. While doing this, note if | |
782 | * a | |
783 | * new (uninstalled) NHLFE has been selected, an installed entry that is | |
784 | * still selected has a change or an installed entry is to be removed. | |
785 | */ | |
786 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { | |
787 | int nh_chg, nh_sel, nh_inst; | |
788 | ||
789 | nexthop = nhlfe->nexthop; | |
790 | if (!nexthop) // unexpected | |
791 | continue; | |
792 | ||
793 | if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED) | |
794 | && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) | |
795 | && (nhlfe->distance == lsp->best_nhlfe->distance)) { | |
796 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); | |
797 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_MULTIPATH); | |
798 | lsp->num_ecmp++; | |
799 | } | |
800 | ||
801 | if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) && !changed) { | |
802 | nh_chg = CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
803 | nh_sel = CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); | |
804 | nh_inst = | |
805 | CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); | |
806 | ||
807 | if ((nh_sel && !nh_inst) | |
808 | || (nh_sel && nh_inst && nh_chg) | |
809 | || (nh_inst && !nh_sel)) | |
810 | changed = 1; | |
811 | } | |
812 | ||
813 | /* We have finished examining, clear changed flag. */ | |
814 | UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
815 | } | |
816 | ||
817 | if (changed) | |
818 | SET_FLAG(lsp->flags, LSP_FLAG_CHANGED); | |
40c7bdb0 | 819 | } |
820 | ||
821 | /* | |
822 | * Delete LSP forwarding entry from kernel, if installed. Called upon | |
823 | * process exit. | |
824 | */ | |
d62a17ae | 825 | static void lsp_uninstall_from_kernel(struct hash_backet *backet, void *ctxt) |
40c7bdb0 | 826 | { |
d62a17ae | 827 | zebra_lsp_t *lsp; |
40c7bdb0 | 828 | |
d62a17ae | 829 | lsp = (zebra_lsp_t *)backet->data; |
830 | if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) | |
831 | kernel_del_lsp(lsp); | |
40c7bdb0 | 832 | } |
833 | ||
834 | /* | |
835 | * Schedule LSP forwarding entry for processing. Called upon changes | |
836 | * that may impact LSPs such as nexthop / connected route changes. | |
837 | */ | |
d62a17ae | 838 | static void lsp_schedule(struct hash_backet *backet, void *ctxt) |
40c7bdb0 | 839 | { |
d62a17ae | 840 | zebra_lsp_t *lsp; |
40c7bdb0 | 841 | |
d62a17ae | 842 | lsp = (zebra_lsp_t *)backet->data; |
843 | lsp_processq_add(lsp); | |
40c7bdb0 | 844 | } |
845 | ||
846 | /* | |
847 | * Process a LSP entry that is in the queue. Recalculate best NHLFE and | |
848 | * any multipaths and update or delete from the kernel, as needed. | |
849 | */ | |
d62a17ae | 850 | static wq_item_status lsp_process(struct work_queue *wq, void *data) |
851 | { | |
852 | zebra_lsp_t *lsp; | |
853 | zebra_nhlfe_t *oldbest, *newbest; | |
854 | char buf[BUFSIZ], buf2[BUFSIZ]; | |
855 | struct zebra_vrf *zvrf = vrf_info_lookup(VRF_DEFAULT); | |
856 | ||
857 | lsp = (zebra_lsp_t *)data; | |
858 | if (!lsp) // unexpected | |
859 | return WQ_SUCCESS; | |
860 | ||
861 | oldbest = lsp->best_nhlfe; | |
862 | ||
863 | /* Select best NHLFE(s) */ | |
864 | lsp_select_best_nhlfe(lsp); | |
865 | ||
866 | newbest = lsp->best_nhlfe; | |
867 | ||
868 | if (IS_ZEBRA_DEBUG_MPLS) { | |
869 | if (oldbest) | |
870 | nhlfe2str(oldbest, buf, BUFSIZ); | |
871 | if (newbest) | |
872 | nhlfe2str(newbest, buf2, BUFSIZ); | |
873 | zlog_debug( | |
874 | "Process LSP in-label %u oldbest %s newbest %s " | |
875 | "flags 0x%x ecmp# %d", | |
876 | lsp->ile.in_label, oldbest ? buf : "NULL", | |
877 | newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp); | |
878 | } | |
879 | ||
880 | if (!CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { | |
881 | /* Not already installed */ | |
882 | if (newbest) { | |
883 | kernel_add_lsp(lsp); | |
884 | zvrf->lsp_installs++; | |
885 | } | |
886 | } else { | |
887 | /* Installed, may need an update and/or delete. */ | |
888 | if (!newbest) { | |
889 | kernel_del_lsp(lsp); | |
890 | zvrf->lsp_removals++; | |
891 | } else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) { | |
892 | kernel_upd_lsp(lsp); | |
893 | zvrf->lsp_installs++; | |
894 | } | |
895 | } | |
896 | ||
897 | return WQ_SUCCESS; | |
40c7bdb0 | 898 | } |
899 | ||
900 | ||
901 | /* | |
902 | * Callback upon processing completion of a LSP forwarding entry. | |
903 | */ | |
d62a17ae | 904 | static void lsp_processq_del(struct work_queue *wq, void *data) |
40c7bdb0 | 905 | { |
d62a17ae | 906 | struct zebra_vrf *zvrf; |
907 | zebra_lsp_t *lsp; | |
908 | struct hash *lsp_table; | |
909 | zebra_nhlfe_t *nhlfe, *nhlfe_next; | |
40c7bdb0 | 910 | |
d62a17ae | 911 | zvrf = vrf_info_lookup(VRF_DEFAULT); |
912 | assert(zvrf); | |
40c7bdb0 | 913 | |
d62a17ae | 914 | lsp_table = zvrf->lsp_table; |
915 | if (!lsp_table) // unexpected | |
916 | return; | |
40c7bdb0 | 917 | |
d62a17ae | 918 | lsp = (zebra_lsp_t *)data; |
919 | if (!lsp) // unexpected | |
920 | return; | |
40c7bdb0 | 921 | |
d62a17ae | 922 | /* Clear flag, remove any NHLFEs marked for deletion. If no NHLFEs |
923 | * exist, | |
924 | * delete LSP entry also. | |
925 | */ | |
926 | UNSET_FLAG(lsp->flags, LSP_FLAG_SCHEDULED); | |
40c7bdb0 | 927 | |
d62a17ae | 928 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { |
929 | nhlfe_next = nhlfe->next; | |
930 | if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) | |
931 | nhlfe_del(nhlfe); | |
932 | } | |
40c7bdb0 | 933 | |
d62a17ae | 934 | if (!lsp->nhlfe_list) { |
935 | if (IS_ZEBRA_DEBUG_MPLS) | |
936 | zlog_debug("Free LSP in-label %u flags 0x%x", | |
937 | lsp->ile.in_label, lsp->flags); | |
40c7bdb0 | 938 | |
d62a17ae | 939 | lsp = hash_release(lsp_table, &lsp->ile); |
940 | if (lsp) | |
941 | XFREE(MTYPE_LSP, lsp); | |
942 | } | |
40c7bdb0 | 943 | } |
944 | ||
945 | /* | |
946 | * Callback upon finishing the processing of all scheduled | |
947 | * LSP forwarding entries. | |
948 | */ | |
d62a17ae | 949 | static void lsp_processq_complete(struct work_queue *wq) |
40c7bdb0 | 950 | { |
d62a17ae | 951 | /* Nothing to do for now. */ |
40c7bdb0 | 952 | } |
953 | ||
954 | /* | |
955 | * Add LSP forwarding entry to queue for subsequent processing. | |
956 | */ | |
d62a17ae | 957 | static int lsp_processq_add(zebra_lsp_t *lsp) |
40c7bdb0 | 958 | { |
d62a17ae | 959 | /* If already scheduled, exit. */ |
960 | if (CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) | |
961 | return 0; | |
40c7bdb0 | 962 | |
d62a17ae | 963 | if (zebrad.lsp_process_q == NULL) { |
964 | zlog_err("%s: work_queue does not exist!", __func__); | |
965 | return -1; | |
966 | } | |
33c32282 | 967 | |
d62a17ae | 968 | work_queue_add(zebrad.lsp_process_q, lsp); |
969 | SET_FLAG(lsp->flags, LSP_FLAG_SCHEDULED); | |
970 | return 0; | |
40c7bdb0 | 971 | } |
972 | ||
973 | /* | |
974 | * Callback to allocate LSP forwarding table entry. | |
975 | */ | |
d62a17ae | 976 | static void *lsp_alloc(void *p) |
40c7bdb0 | 977 | { |
d62a17ae | 978 | const zebra_ile_t *ile = p; |
979 | zebra_lsp_t *lsp; | |
40c7bdb0 | 980 | |
d62a17ae | 981 | lsp = XCALLOC(MTYPE_LSP, sizeof(zebra_lsp_t)); |
982 | lsp->ile = *ile; | |
40c7bdb0 | 983 | |
d62a17ae | 984 | if (IS_ZEBRA_DEBUG_MPLS) |
985 | zlog_debug("Alloc LSP in-label %u", lsp->ile.in_label); | |
40c7bdb0 | 986 | |
d62a17ae | 987 | return ((void *)lsp); |
40c7bdb0 | 988 | } |
989 | ||
990 | /* | |
991 | * Create printable string for NHLFE entry. | |
992 | */ | |
d62a17ae | 993 | static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size) |
40c7bdb0 | 994 | { |
d62a17ae | 995 | struct nexthop *nexthop; |
40c7bdb0 | 996 | |
d62a17ae | 997 | buf[0] = '\0'; |
998 | nexthop = nhlfe->nexthop; | |
999 | switch (nexthop->type) { | |
1000 | case NEXTHOP_TYPE_IPV4: | |
1001 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1002 | inet_ntop(AF_INET, &nexthop->gate.ipv4, buf, size); | |
1003 | break; | |
1004 | case NEXTHOP_TYPE_IPV6: | |
1005 | inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, size); | |
1006 | break; | |
1007 | default: | |
1008 | break; | |
1009 | } | |
40c7bdb0 | 1010 | |
d62a17ae | 1011 | return buf; |
40c7bdb0 | 1012 | } |
1013 | ||
1014 | /* | |
1015 | * Check if NHLFE matches with search info passed. | |
1016 | */ | |
d62a17ae | 1017 | static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, |
1018 | union g_addr *gate, ifindex_t ifindex) | |
40c7bdb0 | 1019 | { |
d62a17ae | 1020 | struct nexthop *nhop; |
1021 | int cmp = 1; | |
40c7bdb0 | 1022 | |
d62a17ae | 1023 | nhop = nhlfe->nexthop; |
1024 | if (!nhop) | |
1025 | return 1; | |
40c7bdb0 | 1026 | |
d62a17ae | 1027 | if (nhop->type != gtype) |
1028 | return 1; | |
40c7bdb0 | 1029 | |
d62a17ae | 1030 | switch (nhop->type) { |
1031 | case NEXTHOP_TYPE_IPV4: | |
1032 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1033 | cmp = memcmp(&(nhop->gate.ipv4), &(gate->ipv4), | |
1034 | sizeof(struct in_addr)); | |
1035 | if (!cmp && nhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) | |
1036 | cmp = !(nhop->ifindex == ifindex); | |
1037 | break; | |
1038 | case NEXTHOP_TYPE_IPV6: | |
1039 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1040 | cmp = memcmp(&(nhop->gate.ipv6), &(gate->ipv6), | |
1041 | sizeof(struct in6_addr)); | |
1042 | if (!cmp && nhop->type == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1043 | cmp = !(nhop->ifindex == ifindex); | |
1044 | break; | |
1045 | default: | |
1046 | break; | |
1047 | } | |
40c7bdb0 | 1048 | |
d62a17ae | 1049 | return cmp; |
40c7bdb0 | 1050 | } |
1051 | ||
1052 | ||
1053 | /* | |
1054 | * Locate NHLFE that matches with passed info. | |
1055 | */ | |
d62a17ae | 1056 | static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, |
1057 | enum nexthop_types_t gtype, union g_addr *gate, | |
1058 | ifindex_t ifindex) | |
40c7bdb0 | 1059 | { |
d62a17ae | 1060 | zebra_nhlfe_t *nhlfe; |
40c7bdb0 | 1061 | |
d62a17ae | 1062 | if (!lsp) |
1063 | return NULL; | |
40c7bdb0 | 1064 | |
d62a17ae | 1065 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { |
1066 | if (nhlfe->type != lsp_type) | |
1067 | continue; | |
1068 | if (!nhlfe_nhop_match(nhlfe, gtype, gate, ifindex)) | |
1069 | break; | |
1070 | } | |
40c7bdb0 | 1071 | |
d62a17ae | 1072 | return nhlfe; |
40c7bdb0 | 1073 | } |
1074 | ||
1075 | /* | |
1076 | * Add NHLFE. Base entry must have been created and duplicate | |
1077 | * check done. | |
1078 | */ | |
d62a17ae | 1079 | static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, |
1080 | enum nexthop_types_t gtype, union g_addr *gate, | |
1081 | ifindex_t ifindex, mpls_label_t out_label) | |
1082 | { | |
1083 | zebra_nhlfe_t *nhlfe; | |
1084 | struct nexthop *nexthop; | |
1085 | ||
1086 | if (!lsp) | |
1087 | return NULL; | |
1088 | ||
1089 | nhlfe = XCALLOC(MTYPE_NHLFE, sizeof(zebra_nhlfe_t)); | |
1090 | if (!nhlfe) | |
1091 | return NULL; | |
1092 | ||
1093 | nhlfe->lsp = lsp; | |
1094 | nhlfe->type = lsp_type; | |
1095 | nhlfe->distance = lsp_distance(lsp_type); | |
1096 | ||
1097 | nexthop = nexthop_new(); | |
1098 | if (!nexthop) { | |
1099 | XFREE(MTYPE_NHLFE, nhlfe); | |
1100 | return NULL; | |
1101 | } | |
1102 | nexthop_add_labels(nexthop, lsp_type, 1, &out_label); | |
1103 | ||
1104 | nexthop->type = gtype; | |
1105 | switch (nexthop->type) { | |
1106 | case NEXTHOP_TYPE_IPV4: | |
1107 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1108 | nexthop->gate.ipv4 = gate->ipv4; | |
1109 | if (ifindex) | |
1110 | nexthop->ifindex = ifindex; | |
1111 | break; | |
1112 | case NEXTHOP_TYPE_IPV6: | |
1113 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1114 | nexthop->gate.ipv6 = gate->ipv6; | |
1115 | if (ifindex) | |
1116 | nexthop->ifindex = ifindex; | |
1117 | break; | |
1118 | default: | |
1119 | nexthop_free(nexthop); | |
1120 | XFREE(MTYPE_NHLFE, nhlfe); | |
1121 | return NULL; | |
1122 | break; | |
1123 | } | |
1124 | ||
1125 | nhlfe->nexthop = nexthop; | |
1126 | if (lsp->nhlfe_list) | |
1127 | lsp->nhlfe_list->prev = nhlfe; | |
1128 | nhlfe->next = lsp->nhlfe_list; | |
1129 | lsp->nhlfe_list = nhlfe; | |
1130 | ||
1131 | return nhlfe; | |
40c7bdb0 | 1132 | } |
1133 | ||
1134 | /* | |
1135 | * Delete NHLFE. Entry must be present on list. | |
1136 | */ | |
d62a17ae | 1137 | static int nhlfe_del(zebra_nhlfe_t *nhlfe) |
40c7bdb0 | 1138 | { |
d62a17ae | 1139 | zebra_lsp_t *lsp; |
40c7bdb0 | 1140 | |
d62a17ae | 1141 | if (!nhlfe) |
1142 | return -1; | |
40c7bdb0 | 1143 | |
d62a17ae | 1144 | lsp = nhlfe->lsp; |
1145 | if (!lsp) | |
1146 | return -1; | |
40c7bdb0 | 1147 | |
d62a17ae | 1148 | /* Free nexthop. */ |
1149 | if (nhlfe->nexthop) | |
1150 | nexthop_free(nhlfe->nexthop); | |
40c7bdb0 | 1151 | |
d62a17ae | 1152 | /* Unlink from LSP */ |
1153 | if (nhlfe->next) | |
1154 | nhlfe->next->prev = nhlfe->prev; | |
1155 | if (nhlfe->prev) | |
1156 | nhlfe->prev->next = nhlfe->next; | |
1157 | else | |
1158 | lsp->nhlfe_list = nhlfe->next; | |
40c7bdb0 | 1159 | |
d62a17ae | 1160 | if (nhlfe == lsp->best_nhlfe) |
1161 | lsp->best_nhlfe = NULL; | |
bb49a121 | 1162 | |
d62a17ae | 1163 | XFREE(MTYPE_NHLFE, nhlfe); |
40c7bdb0 | 1164 | |
d62a17ae | 1165 | return 0; |
40c7bdb0 | 1166 | } |
1167 | ||
a64448ba DS |
1168 | /* |
1169 | * Update label for NHLFE entry. | |
1170 | */ | |
d62a17ae | 1171 | static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, |
1172 | struct nexthop_label *nh_label) | |
1173 | { | |
1174 | nhlfe->nexthop->nh_label->label[0] = nh_label->label[0]; | |
1175 | } | |
1176 | ||
1177 | static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, | |
1178 | enum lsp_types_t type) | |
1179 | { | |
1180 | zebra_nhlfe_t *nhlfe, *nhlfe_next; | |
1181 | int schedule_lsp = 0; | |
1182 | char buf[BUFSIZ]; | |
1183 | ||
1184 | /* Mark NHLFEs for delete or directly delete, as appropriate. */ | |
1185 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { | |
1186 | nhlfe_next = nhlfe->next; | |
1187 | ||
1188 | /* Skip non-static NHLFEs */ | |
1189 | if (nhlfe->type != type) | |
1190 | continue; | |
1191 | ||
1192 | if (IS_ZEBRA_DEBUG_MPLS) { | |
1193 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
1194 | zlog_debug( | |
1195 | "Del LSP in-label %u type %d nexthop %s flags 0x%x", | |
1196 | lsp->ile.in_label, type, buf, nhlfe->flags); | |
1197 | } | |
1198 | ||
1199 | if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { | |
1200 | UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
1201 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); | |
1202 | schedule_lsp = 1; | |
1203 | } else { | |
1204 | nhlfe_del(nhlfe); | |
1205 | } | |
1206 | } | |
1207 | ||
1208 | /* Queue LSP for processing, if needed, else delete. */ | |
1209 | if (schedule_lsp) { | |
1210 | if (lsp_processq_add(lsp)) | |
1211 | return -1; | |
1212 | } else if (!lsp->nhlfe_list | |
1213 | && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { | |
1214 | if (IS_ZEBRA_DEBUG_MPLS) | |
1215 | zlog_debug("Free LSP in-label %u flags 0x%x", | |
1216 | lsp->ile.in_label, lsp->flags); | |
1217 | ||
1218 | lsp = hash_release(lsp_table, &lsp->ile); | |
1219 | if (lsp) | |
1220 | XFREE(MTYPE_LSP, lsp); | |
1221 | } | |
1222 | ||
1223 | return 0; | |
40c7bdb0 | 1224 | } |
1225 | ||
ce549947 RW |
1226 | /* |
1227 | * Uninstall all static NHLFEs for a particular LSP forwarding entry. | |
1228 | * If no other NHLFEs exist, the entry would be deleted. | |
1229 | */ | |
d62a17ae | 1230 | static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, |
1231 | mpls_label_t in_label) | |
ce549947 | 1232 | { |
d62a17ae | 1233 | struct hash *lsp_table; |
1234 | zebra_ile_t tmp_ile; | |
1235 | zebra_lsp_t *lsp; | |
ce549947 | 1236 | |
d62a17ae | 1237 | /* Lookup table. */ |
1238 | lsp_table = zvrf->lsp_table; | |
1239 | if (!lsp_table) | |
1240 | return -1; | |
ce549947 | 1241 | |
d62a17ae | 1242 | /* If entry is not present, exit. */ |
1243 | tmp_ile.in_label = in_label; | |
1244 | lsp = hash_lookup(lsp_table, &tmp_ile); | |
1245 | if (!lsp || !lsp->nhlfe_list) | |
1246 | return 0; | |
ce549947 | 1247 | |
d62a17ae | 1248 | return mpls_lsp_uninstall_all(lsp_table, lsp, ZEBRA_LSP_STATIC); |
ce549947 RW |
1249 | } |
1250 | ||
d62a17ae | 1251 | static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) |
b78b820d | 1252 | { |
d62a17ae | 1253 | char buf[BUFSIZ]; |
1254 | json_object *json_nhlfe = NULL; | |
1255 | struct nexthop *nexthop = nhlfe->nexthop; | |
b78b820d | 1256 | |
d62a17ae | 1257 | json_nhlfe = json_object_new_object(); |
1258 | json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); | |
1259 | json_object_int_add(json_nhlfe, "outLabel", | |
1260 | nexthop->nh_label->label[0]); | |
1261 | json_object_int_add(json_nhlfe, "distance", nhlfe->distance); | |
b78b820d | 1262 | |
d62a17ae | 1263 | if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) |
1264 | json_object_boolean_true_add(json_nhlfe, "installed"); | |
b78b820d | 1265 | |
d62a17ae | 1266 | switch (nexthop->type) { |
1267 | case NEXTHOP_TYPE_IPV4: | |
1268 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1269 | json_object_string_add(json_nhlfe, "nexthop", | |
1270 | inet_ntoa(nexthop->gate.ipv4)); | |
1271 | break; | |
1272 | case NEXTHOP_TYPE_IPV6: | |
1273 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1274 | json_object_string_add( | |
1275 | json_nhlfe, "nexthop", | |
1276 | inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); | |
1277 | ||
1278 | if (nexthop->ifindex) | |
1279 | json_object_string_add( | |
1280 | json_nhlfe, "interface", | |
1281 | ifindex2ifname(nexthop->ifindex, VRF_DEFAULT)); | |
1282 | break; | |
1283 | default: | |
1284 | break; | |
1285 | } | |
1286 | return json_nhlfe; | |
b78b820d | 1287 | } |
1288 | ||
3ab18ff2 | 1289 | /* |
1290 | * Print the NHLFE for a LSP forwarding entry. | |
1291 | */ | |
d62a17ae | 1292 | static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) |
1293 | { | |
1294 | struct nexthop *nexthop; | |
1295 | char buf[BUFSIZ]; | |
1296 | ||
1297 | nexthop = nhlfe->nexthop; | |
1298 | if (!nexthop || !nexthop->nh_label) // unexpected | |
1299 | return; | |
1300 | ||
1301 | vty_out(vty, " type: %s remote label: %s distance: %d\n", | |
1302 | nhlfe_type2str(nhlfe->type), | |
1303 | label2str(nexthop->nh_label->label[0], buf, BUFSIZ), | |
1304 | nhlfe->distance); | |
1305 | switch (nexthop->type) { | |
1306 | case NEXTHOP_TYPE_IPV4: | |
1307 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
1308 | vty_out(vty, " via %s", inet_ntoa(nexthop->gate.ipv4)); | |
1309 | if (nexthop->ifindex) | |
1310 | vty_out(vty, " dev %s", | |
1311 | ifindex2ifname(nexthop->ifindex, VRF_DEFAULT)); | |
1312 | break; | |
1313 | case NEXTHOP_TYPE_IPV6: | |
1314 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1315 | vty_out(vty, " via %s", | |
1316 | inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); | |
1317 | if (nexthop->ifindex) | |
1318 | vty_out(vty, " dev %s", | |
1319 | ifindex2ifname(nexthop->ifindex, VRF_DEFAULT)); | |
1320 | break; | |
1321 | default: | |
1322 | break; | |
1323 | } | |
9b67b514 DS |
1324 | vty_out(vty, "%s", CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) |
1325 | ? " (installed)" | |
1326 | : ""); | |
d62a17ae | 1327 | vty_out(vty, "\n"); |
3ab18ff2 | 1328 | } |
1329 | ||
1330 | /* | |
1331 | * Print an LSP forwarding entry. | |
1332 | */ | |
d62a17ae | 1333 | static void lsp_print(zebra_lsp_t *lsp, void *ctxt) |
3ab18ff2 | 1334 | { |
d62a17ae | 1335 | zebra_nhlfe_t *nhlfe; |
1336 | struct vty *vty; | |
3ab18ff2 | 1337 | |
d62a17ae | 1338 | vty = (struct vty *)ctxt; |
3ab18ff2 | 1339 | |
d62a17ae | 1340 | vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label, |
1341 | CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)" | |
1342 | : ""); | |
3ab18ff2 | 1343 | |
d62a17ae | 1344 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) |
1345 | nhlfe_print(nhlfe, vty); | |
3ab18ff2 | 1346 | } |
1347 | ||
1348 | /* | |
b78b820d | 1349 | * JSON objects for an LSP forwarding entry. |
3ab18ff2 | 1350 | */ |
d62a17ae | 1351 | static json_object *lsp_json(zebra_lsp_t *lsp) |
3ab18ff2 | 1352 | { |
d62a17ae | 1353 | zebra_nhlfe_t *nhlfe = NULL; |
1354 | json_object *json = json_object_new_object(); | |
1355 | json_object *json_nhlfe_list = json_object_new_array(); | |
3ab18ff2 | 1356 | |
d62a17ae | 1357 | json_object_int_add(json, "inLabel", lsp->ile.in_label); |
b78b820d | 1358 | |
d62a17ae | 1359 | if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) |
1360 | json_object_boolean_true_add(json, "installed"); | |
3ab18ff2 | 1361 | |
d62a17ae | 1362 | for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) |
1363 | json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); | |
b78b820d | 1364 | |
d62a17ae | 1365 | json_object_object_add(json, "nexthops", json_nhlfe_list); |
1366 | return json; | |
b78b820d | 1367 | } |
1368 | ||
1369 | ||
1370 | /* Return a sorted linked list of the hash contents */ | |
d62a17ae | 1371 | static struct list *hash_get_sorted_list(struct hash *hash, void *cmp) |
b78b820d | 1372 | { |
d62a17ae | 1373 | unsigned int i; |
1374 | struct hash_backet *hb; | |
1375 | struct list *sorted_list = list_new(); | |
b78b820d | 1376 | |
d62a17ae | 1377 | sorted_list->cmp = (int (*)(void *, void *))cmp; |
b78b820d | 1378 | |
d62a17ae | 1379 | for (i = 0; i < hash->size; i++) |
1380 | for (hb = hash->index[i]; hb; hb = hb->next) | |
1381 | listnode_add_sort(sorted_list, hb->data); | |
b78b820d | 1382 | |
d62a17ae | 1383 | return sorted_list; |
3ab18ff2 | 1384 | } |
1385 | ||
7758e3f3 | 1386 | /* |
b78b820d | 1387 | * Compare two LSPs based on their label values. |
7758e3f3 | 1388 | */ |
d62a17ae | 1389 | static int lsp_cmp(zebra_lsp_t *lsp1, zebra_lsp_t *lsp2) |
7758e3f3 | 1390 | { |
d62a17ae | 1391 | if (lsp1->ile.in_label < lsp2->ile.in_label) |
1392 | return -1; | |
7758e3f3 | 1393 | |
d62a17ae | 1394 | if (lsp1->ile.in_label > lsp2->ile.in_label) |
1395 | return 1; | |
7758e3f3 | 1396 | |
d62a17ae | 1397 | return 0; |
7758e3f3 | 1398 | } |
1399 | ||
1400 | /* | |
1401 | * Callback to allocate static LSP. | |
1402 | */ | |
d62a17ae | 1403 | static void *slsp_alloc(void *p) |
7758e3f3 | 1404 | { |
d62a17ae | 1405 | const zebra_ile_t *ile = p; |
1406 | zebra_slsp_t *slsp; | |
7758e3f3 | 1407 | |
d62a17ae | 1408 | slsp = XCALLOC(MTYPE_SLSP, sizeof(zebra_slsp_t)); |
1409 | slsp->ile = *ile; | |
1410 | return ((void *)slsp); | |
7758e3f3 | 1411 | } |
1412 | ||
b78b820d | 1413 | /* |
1414 | * Compare two static LSPs based on their label values. | |
1415 | */ | |
d62a17ae | 1416 | static int slsp_cmp(zebra_slsp_t *slsp1, zebra_slsp_t *slsp2) |
b78b820d | 1417 | { |
d62a17ae | 1418 | if (slsp1->ile.in_label < slsp2->ile.in_label) |
1419 | return -1; | |
b78b820d | 1420 | |
d62a17ae | 1421 | if (slsp1->ile.in_label > slsp2->ile.in_label) |
1422 | return 1; | |
b78b820d | 1423 | |
d62a17ae | 1424 | return 0; |
b78b820d | 1425 | } |
1426 | ||
7758e3f3 | 1427 | /* |
1428 | * Check if static NHLFE matches with search info passed. | |
1429 | */ | |
d62a17ae | 1430 | static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, |
1431 | union g_addr *gate, ifindex_t ifindex) | |
7758e3f3 | 1432 | { |
d62a17ae | 1433 | int cmp = 1; |
7758e3f3 | 1434 | |
d62a17ae | 1435 | if (snhlfe->gtype != gtype) |
1436 | return 1; | |
7758e3f3 | 1437 | |
d62a17ae | 1438 | switch (snhlfe->gtype) { |
1439 | case NEXTHOP_TYPE_IPV4: | |
1440 | cmp = memcmp(&(snhlfe->gate.ipv4), &(gate->ipv4), | |
1441 | sizeof(struct in_addr)); | |
1442 | break; | |
1443 | case NEXTHOP_TYPE_IPV6: | |
1444 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1445 | cmp = memcmp(&(snhlfe->gate.ipv6), &(gate->ipv6), | |
1446 | sizeof(struct in6_addr)); | |
1447 | if (!cmp && snhlfe->gtype == NEXTHOP_TYPE_IPV6_IFINDEX) | |
1448 | cmp = !(snhlfe->ifindex == ifindex); | |
1449 | break; | |
1450 | default: | |
1451 | break; | |
1452 | } | |
7758e3f3 | 1453 | |
d62a17ae | 1454 | return cmp; |
7758e3f3 | 1455 | } |
1456 | ||
1457 | /* | |
1458 | * Locate static NHLFE that matches with passed info. | |
1459 | */ | |
d62a17ae | 1460 | static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, |
1461 | enum nexthop_types_t gtype, | |
1462 | union g_addr *gate, ifindex_t ifindex) | |
7758e3f3 | 1463 | { |
d62a17ae | 1464 | zebra_snhlfe_t *snhlfe; |
7758e3f3 | 1465 | |
d62a17ae | 1466 | if (!slsp) |
1467 | return NULL; | |
7758e3f3 | 1468 | |
d62a17ae | 1469 | for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) { |
1470 | if (!snhlfe_match(snhlfe, gtype, gate, ifindex)) | |
1471 | break; | |
1472 | } | |
7758e3f3 | 1473 | |
d62a17ae | 1474 | return snhlfe; |
7758e3f3 | 1475 | } |
1476 | ||
1477 | ||
1478 | /* | |
1479 | * Add static NHLFE. Base LSP config entry must have been created | |
1480 | * and duplicate check done. | |
1481 | */ | |
d62a17ae | 1482 | static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, |
1483 | enum nexthop_types_t gtype, | |
1484 | union g_addr *gate, ifindex_t ifindex, | |
1485 | mpls_label_t out_label) | |
1486 | { | |
1487 | zebra_snhlfe_t *snhlfe; | |
1488 | ||
1489 | if (!slsp) | |
1490 | return NULL; | |
1491 | ||
1492 | snhlfe = XCALLOC(MTYPE_SNHLFE, sizeof(zebra_snhlfe_t)); | |
1493 | snhlfe->slsp = slsp; | |
1494 | snhlfe->out_label = out_label; | |
1495 | snhlfe->gtype = gtype; | |
1496 | switch (gtype) { | |
1497 | case NEXTHOP_TYPE_IPV4: | |
1498 | snhlfe->gate.ipv4 = gate->ipv4; | |
1499 | break; | |
1500 | case NEXTHOP_TYPE_IPV6: | |
1501 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1502 | snhlfe->gate.ipv6 = gate->ipv6; | |
1503 | if (ifindex) | |
1504 | snhlfe->ifindex = ifindex; | |
1505 | break; | |
1506 | default: | |
1507 | XFREE(MTYPE_SNHLFE, snhlfe); | |
1508 | return NULL; | |
1509 | } | |
1510 | ||
1511 | if (slsp->snhlfe_list) | |
1512 | slsp->snhlfe_list->prev = snhlfe; | |
1513 | snhlfe->next = slsp->snhlfe_list; | |
1514 | slsp->snhlfe_list = snhlfe; | |
1515 | ||
1516 | return snhlfe; | |
7758e3f3 | 1517 | } |
1518 | ||
1519 | /* | |
1520 | * Delete static NHLFE. Entry must be present on list. | |
1521 | */ | |
d62a17ae | 1522 | static int snhlfe_del(zebra_snhlfe_t *snhlfe) |
7758e3f3 | 1523 | { |
d62a17ae | 1524 | zebra_slsp_t *slsp; |
7758e3f3 | 1525 | |
d62a17ae | 1526 | if (!snhlfe) |
1527 | return -1; | |
7758e3f3 | 1528 | |
d62a17ae | 1529 | slsp = snhlfe->slsp; |
1530 | if (!slsp) | |
1531 | return -1; | |
7758e3f3 | 1532 | |
d62a17ae | 1533 | if (snhlfe->next) |
1534 | snhlfe->next->prev = snhlfe->prev; | |
1535 | if (snhlfe->prev) | |
1536 | snhlfe->prev->next = snhlfe->next; | |
1537 | else | |
1538 | slsp->snhlfe_list = snhlfe->next; | |
7758e3f3 | 1539 | |
d62a17ae | 1540 | snhlfe->prev = snhlfe->next = NULL; |
1541 | if (snhlfe->ifname) | |
1542 | XFREE(MTYPE_SNHLFE_IFNAME, snhlfe->ifname); | |
1543 | XFREE(MTYPE_SNHLFE, snhlfe); | |
7758e3f3 | 1544 | |
d62a17ae | 1545 | return 0; |
7758e3f3 | 1546 | } |
1547 | ||
1548 | /* | |
1549 | * Delete all static NHLFE entries for this LSP (in label). | |
1550 | */ | |
d62a17ae | 1551 | static int snhlfe_del_all(zebra_slsp_t *slsp) |
7758e3f3 | 1552 | { |
d62a17ae | 1553 | zebra_snhlfe_t *snhlfe, *snhlfe_next; |
7758e3f3 | 1554 | |
d62a17ae | 1555 | if (!slsp) |
1556 | return -1; | |
7758e3f3 | 1557 | |
d62a17ae | 1558 | for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe_next) { |
1559 | snhlfe_next = snhlfe->next; | |
1560 | snhlfe_del(snhlfe); | |
1561 | } | |
7758e3f3 | 1562 | |
d62a17ae | 1563 | return 0; |
7758e3f3 | 1564 | } |
1565 | ||
1566 | /* | |
1567 | * Create printable string for NHLFE configuration. | |
1568 | */ | |
d62a17ae | 1569 | static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size) |
7758e3f3 | 1570 | { |
d62a17ae | 1571 | buf[0] = '\0'; |
1572 | switch (snhlfe->gtype) { | |
1573 | case NEXTHOP_TYPE_IPV4: | |
1574 | inet_ntop(AF_INET, &snhlfe->gate.ipv4, buf, size); | |
1575 | break; | |
1576 | case NEXTHOP_TYPE_IPV6: | |
1577 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
1578 | inet_ntop(AF_INET6, &snhlfe->gate.ipv6, buf, size); | |
1579 | if (snhlfe->ifindex) | |
1580 | strcat(buf, | |
1581 | ifindex2ifname(snhlfe->ifindex, VRF_DEFAULT)); | |
1582 | break; | |
1583 | default: | |
1584 | break; | |
1585 | } | |
7758e3f3 | 1586 | |
d62a17ae | 1587 | return buf; |
7758e3f3 | 1588 | } |
1589 | ||
40c7bdb0 | 1590 | /* |
1591 | * Initialize work queue for processing changed LSPs. | |
1592 | */ | |
d62a17ae | 1593 | static int mpls_processq_init(struct zebra_t *zebra) |
40c7bdb0 | 1594 | { |
d62a17ae | 1595 | zebra->lsp_process_q = work_queue_new(zebra->master, "LSP processing"); |
1596 | if (!zebra->lsp_process_q) { | |
1597 | zlog_err("%s: could not initialise work queue!", __func__); | |
1598 | return -1; | |
1599 | } | |
40c7bdb0 | 1600 | |
d62a17ae | 1601 | zebra->lsp_process_q->spec.workfunc = &lsp_process; |
1602 | zebra->lsp_process_q->spec.del_item_data = &lsp_processq_del; | |
1603 | zebra->lsp_process_q->spec.errorfunc = NULL; | |
1604 | zebra->lsp_process_q->spec.completion_func = &lsp_processq_complete; | |
1605 | zebra->lsp_process_q->spec.max_retries = 0; | |
1606 | zebra->lsp_process_q->spec.hold = 10; | |
33c32282 | 1607 | |
d62a17ae | 1608 | return 0; |
40c7bdb0 | 1609 | } |
1610 | ||
7758e3f3 | 1611 | |
7758e3f3 | 1612 | /* Public functions */ |
1613 | ||
a22f3f5d | 1614 | /* |
1615 | * String to label conversion, labels separated by '/'. | |
8062bf1c QY |
1616 | * |
1617 | * @param label_str labels separated by / | |
1618 | * @param num_labels number of labels; zero if conversion was unsuccessful | |
1619 | * @param labels preallocated mpls_label_t array of size MPLS_MAX_LABELS; only | |
1620 | * modified if the conversion succeeded | |
1621 | * @return 0 on success | |
1622 | * -1 if the string could not be parsed as integers | |
1623 | * -2 if a label was inside the reserved range (0-15) | |
1624 | * -3 if the number of labels given exceeds MPLS_MAX_LABELS | |
a22f3f5d | 1625 | */ |
d62a17ae | 1626 | int mpls_str2label(const char *label_str, u_int8_t *num_labels, |
1627 | mpls_label_t *labels) | |
1628 | { | |
1629 | char *ostr; // copy of label string (start) | |
1630 | char *lstr; // copy of label string | |
1631 | char *nump; // pointer to next segment | |
1632 | char *endp; // end pointer | |
1633 | int i; // for iterating label_str | |
1634 | int rc; // return code | |
1635 | mpls_label_t pl[MPLS_MAX_LABELS]; // parsed labels | |
1636 | ||
1637 | /* labels to zero until we have a successful parse */ | |
1638 | ostr = lstr = XSTRDUP(MTYPE_TMP, label_str); | |
1639 | *num_labels = 0; | |
1640 | rc = 0; | |
1641 | ||
1642 | for (i = 0; i < MPLS_MAX_LABELS && lstr && !rc; i++) { | |
1643 | nump = strsep(&lstr, "/"); | |
1644 | pl[i] = strtoul(nump, &endp, 10); | |
1645 | ||
1646 | /* format check */ | |
1647 | if (*endp != '\0') | |
1648 | rc = -1; | |
1649 | /* validity check */ | |
1650 | else if (!IS_MPLS_UNRESERVED_LABEL(pl[i])) | |
1651 | rc = -2; | |
1652 | } | |
a22f3f5d | 1653 | |
d62a17ae | 1654 | /* excess labels */ |
1655 | if (!rc && i == MPLS_MAX_LABELS && lstr) | |
1656 | rc = -3; | |
a22f3f5d | 1657 | |
d62a17ae | 1658 | if (!rc) { |
1659 | *num_labels = i; | |
1660 | memcpy(labels, pl, *num_labels * sizeof(mpls_label_t)); | |
1661 | } | |
a22f3f5d | 1662 | |
d62a17ae | 1663 | XFREE(MTYPE_TMP, ostr); |
8062bf1c | 1664 | |
d62a17ae | 1665 | return rc; |
a22f3f5d | 1666 | } |
1667 | ||
1668 | /* | |
1669 | * Label to string conversion, labels in string separated by '/'. | |
1670 | */ | |
d62a17ae | 1671 | char *mpls_label2str(u_int8_t num_labels, mpls_label_t *labels, char *buf, |
1672 | int len, int pretty) | |
1673 | { | |
5e8c8947 RW |
1674 | char label_buf[BUFSIZ]; |
1675 | int i; | |
d62a17ae | 1676 | |
5e8c8947 RW |
1677 | buf[0] = '\0'; |
1678 | for (i = 0; i < num_labels; i++) { | |
1679 | if (i != 0) | |
1680 | strlcat(buf, "/", len); | |
1681 | if (pretty) | |
1682 | label2str(labels[i], label_buf, sizeof(label_buf)); | |
1683 | else | |
1684 | snprintf(label_buf, sizeof(label_buf), "%u", labels[i]); | |
1685 | strlcat(buf, label_buf, len); | |
d62a17ae | 1686 | } |
5e8c8947 | 1687 | |
d62a17ae | 1688 | return buf; |
a22f3f5d | 1689 | } |
1690 | ||
a64448ba DS |
1691 | /* |
1692 | * Install dynamic LSP entry. | |
1693 | */ | |
d62a17ae | 1694 | int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn, |
1695 | struct route_entry *re) | |
a64448ba | 1696 | { |
d62a17ae | 1697 | struct route_table *table; |
1698 | zebra_fec_t *fec; | |
a64448ba | 1699 | |
d62a17ae | 1700 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; |
1701 | if (!table) | |
1702 | return -1; | |
a64448ba | 1703 | |
d62a17ae | 1704 | /* See if there is a configured label binding for this FEC. */ |
1705 | fec = fec_find(table, &rn->p); | |
1706 | if (!fec || fec->label == MPLS_INVALID_LABEL) | |
1707 | return 0; | |
a64448ba | 1708 | |
d62a17ae | 1709 | /* We cannot install a label forwarding entry if local label is the |
1710 | * implicit-null label. | |
1711 | */ | |
1712 | if (fec->label == MPLS_IMP_NULL_LABEL) | |
1713 | return 0; | |
a64448ba | 1714 | |
d62a17ae | 1715 | if (lsp_install(zvrf, fec->label, rn, re)) |
1716 | return -1; | |
a64448ba | 1717 | |
d62a17ae | 1718 | return 0; |
a64448ba DS |
1719 | } |
1720 | ||
1721 | /* | |
1722 | * Uninstall dynamic LSP entry, if any. | |
1723 | */ | |
d62a17ae | 1724 | int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, |
1725 | struct route_entry *re) | |
a64448ba | 1726 | { |
d62a17ae | 1727 | struct route_table *table; |
1728 | zebra_fec_t *fec; | |
a64448ba | 1729 | |
d62a17ae | 1730 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(&rn->p))]; |
1731 | if (!table) | |
1732 | return -1; | |
a64448ba | 1733 | |
d62a17ae | 1734 | /* See if there is a configured label binding for this FEC. */ |
1735 | fec = fec_find(table, &rn->p); | |
1736 | if (!fec || fec->label == MPLS_INVALID_LABEL) | |
1737 | return 0; | |
a64448ba | 1738 | |
d62a17ae | 1739 | /* Uninstall always removes all dynamic NHLFEs. */ |
1740 | return lsp_uninstall(zvrf, fec->label); | |
a64448ba DS |
1741 | } |
1742 | ||
5aba114a DS |
1743 | /* |
1744 | * Registration from a client for the label binding for a FEC. If a binding | |
1745 | * already exists, it is informed to the client. | |
28d58fd7 | 1746 | * NOTE: If there is a manually configured label binding, that is used. |
9bedbb1e | 1747 | * Otherwise, if a label index is specified, it means we have to allocate the |
28d58fd7 VV |
1748 | * label from a locally configured label block (SRGB), if one exists and index |
1749 | * is acceptable. | |
5aba114a | 1750 | */ |
d62a17ae | 1751 | int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, |
1752 | u_int32_t label_index, struct zserv *client) | |
1753 | { | |
1754 | struct route_table *table; | |
1755 | zebra_fec_t *fec; | |
1756 | char buf[BUFSIZ]; | |
1757 | int new_client; | |
1758 | int label_change = 0; | |
1759 | u_int32_t old_label; | |
1760 | ||
1761 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; | |
1762 | if (!table) | |
1763 | return -1; | |
1764 | ||
1765 | if (IS_ZEBRA_DEBUG_MPLS) | |
1766 | prefix2str(p, buf, BUFSIZ); | |
1767 | ||
1768 | /* Locate FEC */ | |
1769 | fec = fec_find(table, p); | |
1770 | if (!fec) { | |
1771 | fec = fec_add(table, p, MPLS_INVALID_LABEL, 0, label_index); | |
1772 | if (!fec) { | |
1773 | prefix2str(p, buf, BUFSIZ); | |
1774 | zlog_err( | |
1775 | "Failed to add FEC %s upon register, client %s", | |
1776 | buf, zebra_route_string(client->proto)); | |
1777 | return -1; | |
1778 | } | |
1779 | ||
1780 | old_label = MPLS_INVALID_LABEL; | |
1781 | new_client = 1; | |
1782 | } else { | |
1783 | /* Client may register same FEC with different label index. */ | |
1784 | new_client = | |
1785 | (listnode_lookup(fec->client_list, client) == NULL); | |
1786 | if (!new_client && fec->label_index == label_index) | |
1787 | /* Duplicate register */ | |
1788 | return 0; | |
1789 | ||
1790 | /* Save current label, update label index */ | |
1791 | old_label = fec->label; | |
1792 | fec->label_index = label_index; | |
1793 | } | |
1794 | ||
1795 | if (new_client) | |
1796 | listnode_add(fec->client_list, client); | |
1797 | ||
1798 | if (IS_ZEBRA_DEBUG_MPLS) | |
1799 | zlog_debug("FEC %s Label Index %u %s by client %s", buf, | |
1800 | label_index, new_client ? "registered" : "updated", | |
1801 | zebra_route_string(client->proto)); | |
1802 | ||
1803 | /* If not a configured FEC, derive the local label (from label index) | |
1804 | * or reset it. | |
1805 | */ | |
1806 | if (!(fec->flags & FEC_FLAG_CONFIGURED)) { | |
1807 | fec_derive_label_from_index(zvrf, fec); | |
1808 | ||
1809 | /* If no label change, exit. */ | |
1810 | if (fec->label == old_label) | |
1811 | return 0; | |
1812 | ||
1813 | label_change = 1; | |
1814 | } | |
1815 | ||
1816 | /* If new client or label change, update client and install or uninstall | |
1817 | * label forwarding entry as needed. | |
1818 | */ | |
1819 | /* Inform client of label, if needed. */ | |
1820 | if ((new_client && fec->label != MPLS_INVALID_LABEL) || label_change) { | |
1821 | if (IS_ZEBRA_DEBUG_MPLS) | |
1822 | zlog_debug("Update client label %u", fec->label); | |
1823 | fec_send(fec, client); | |
1824 | } | |
1825 | ||
1826 | if (new_client || label_change) | |
1827 | return fec_change_update_lsp(zvrf, fec, old_label); | |
1828 | ||
1829 | return 0; | |
5aba114a DS |
1830 | } |
1831 | ||
1832 | /* | |
1833 | * Deregistration from a client for the label binding for a FEC. The FEC | |
1834 | * itself is deleted if no other registered clients exist and there is no | |
1835 | * label bound to the FEC. | |
1836 | */ | |
d62a17ae | 1837 | int zebra_mpls_fec_unregister(struct zebra_vrf *zvrf, struct prefix *p, |
1838 | struct zserv *client) | |
5aba114a | 1839 | { |
d62a17ae | 1840 | struct route_table *table; |
1841 | zebra_fec_t *fec; | |
1842 | char buf[BUFSIZ]; | |
5aba114a | 1843 | |
d62a17ae | 1844 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; |
1845 | if (!table) | |
1846 | return -1; | |
5aba114a | 1847 | |
d62a17ae | 1848 | if (IS_ZEBRA_DEBUG_MPLS) |
1849 | prefix2str(p, buf, BUFSIZ); | |
5aba114a | 1850 | |
d62a17ae | 1851 | fec = fec_find(table, p); |
1852 | if (!fec) { | |
1853 | prefix2str(p, buf, BUFSIZ); | |
1854 | zlog_err("Failed to find FEC %s upon unregister, client %s", | |
1855 | buf, zebra_route_string(client->proto)); | |
1856 | return -1; | |
1857 | } | |
5aba114a | 1858 | |
d62a17ae | 1859 | listnode_delete(fec->client_list, client); |
1860 | ||
1861 | if (IS_ZEBRA_DEBUG_MPLS) | |
1862 | zlog_debug("FEC %s unregistered by client %s", buf, | |
1863 | zebra_route_string(client->proto)); | |
1864 | ||
1865 | /* If not a configured entry, delete the FEC if no other clients. Before | |
1866 | * deleting, see if any LSP needs to be uninstalled. | |
1867 | */ | |
1868 | if (!(fec->flags & FEC_FLAG_CONFIGURED) | |
1869 | && list_isempty(fec->client_list)) { | |
1870 | mpls_label_t old_label = fec->label; | |
1871 | fec->label = MPLS_INVALID_LABEL; /* reset */ | |
1872 | fec_change_update_lsp(zvrf, fec, old_label); | |
1873 | fec_del(fec); | |
1874 | } | |
5aba114a | 1875 | |
d62a17ae | 1876 | return 0; |
5aba114a DS |
1877 | } |
1878 | ||
1879 | /* | |
1880 | * Cleanup any FECs registered by this client. | |
1881 | */ | |
d62a17ae | 1882 | int zebra_mpls_cleanup_fecs_for_client(struct zebra_vrf *zvrf, |
1883 | struct zserv *client) | |
1884 | { | |
1885 | struct route_node *rn; | |
1886 | zebra_fec_t *fec; | |
1887 | struct listnode *node; | |
1888 | struct zserv *fec_client; | |
1889 | int af; | |
1890 | ||
1891 | for (af = AFI_IP; af < AFI_MAX; af++) { | |
1892 | if (zvrf->fec_table[af] == NULL) | |
1893 | continue; | |
1894 | ||
1895 | for (rn = route_top(zvrf->fec_table[af]); rn; | |
1896 | rn = route_next(rn)) { | |
1897 | fec = rn->info; | |
1898 | if (!fec || list_isempty(fec->client_list)) | |
1899 | continue; | |
1900 | ||
1901 | for (ALL_LIST_ELEMENTS_RO(fec->client_list, node, | |
1902 | fec_client)) { | |
1903 | if (fec_client == client) { | |
1904 | listnode_delete(fec->client_list, | |
1905 | fec_client); | |
1906 | if (!(fec->flags & FEC_FLAG_CONFIGURED) | |
1907 | && list_isempty(fec->client_list)) | |
1908 | fec_del(fec); | |
1909 | break; | |
1910 | } | |
1911 | } | |
1912 | } | |
1913 | } | |
5aba114a | 1914 | |
d62a17ae | 1915 | return 0; |
5aba114a DS |
1916 | } |
1917 | ||
f31e084c DS |
1918 | /* |
1919 | * Return FEC (if any) to which this label is bound. | |
1920 | * Note: Only works for per-prefix binding and when the label is not | |
1921 | * implicit-null. | |
1922 | * TODO: Currently walks entire table, can optimize later with another | |
1923 | * hash.. | |
1924 | */ | |
d62a17ae | 1925 | zebra_fec_t *zebra_mpls_fec_for_label(struct zebra_vrf *zvrf, |
1926 | mpls_label_t label) | |
1927 | { | |
1928 | struct route_node *rn; | |
1929 | zebra_fec_t *fec; | |
1930 | int af; | |
1931 | ||
1932 | for (af = AFI_IP; af < AFI_MAX; af++) { | |
1933 | if (zvrf->fec_table[af] == NULL) | |
1934 | continue; | |
1935 | ||
1936 | for (rn = route_top(zvrf->fec_table[af]); rn; | |
1937 | rn = route_next(rn)) { | |
1938 | if (!rn->info) | |
1939 | continue; | |
1940 | fec = rn->info; | |
1941 | if (fec->label == label) | |
1942 | return fec; | |
1943 | } | |
1944 | } | |
f31e084c | 1945 | |
d62a17ae | 1946 | return NULL; |
f31e084c DS |
1947 | } |
1948 | ||
1949 | /* | |
1950 | * Inform if specified label is currently bound to a FEC or not. | |
1951 | */ | |
d62a17ae | 1952 | int zebra_mpls_label_already_bound(struct zebra_vrf *zvrf, mpls_label_t label) |
f31e084c | 1953 | { |
d62a17ae | 1954 | return (zebra_mpls_fec_for_label(zvrf, label) ? 1 : 0); |
f31e084c DS |
1955 | } |
1956 | ||
1957 | /* | |
5aba114a | 1958 | * Add static FEC to label binding. If there are clients registered for this |
a64448ba DS |
1959 | * FEC, notify them. If there are labeled routes for this FEC, install the |
1960 | * label forwarding entry. | |
9d303b37 | 1961 | */ |
d62a17ae | 1962 | int zebra_mpls_static_fec_add(struct zebra_vrf *zvrf, struct prefix *p, |
1963 | mpls_label_t in_label) | |
1964 | { | |
1965 | struct route_table *table; | |
1966 | zebra_fec_t *fec; | |
1967 | char buf[BUFSIZ]; | |
1968 | mpls_label_t old_label; | |
1969 | int ret = 0; | |
1970 | ||
1971 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; | |
1972 | if (!table) | |
1973 | return -1; | |
1974 | ||
1975 | if (IS_ZEBRA_DEBUG_MPLS) | |
1976 | prefix2str(p, buf, BUFSIZ); | |
1977 | ||
1978 | /* Update existing FEC or create a new one. */ | |
1979 | fec = fec_find(table, p); | |
1980 | if (!fec) { | |
1981 | fec = fec_add(table, p, in_label, FEC_FLAG_CONFIGURED, | |
1982 | MPLS_INVALID_LABEL_INDEX); | |
1983 | if (!fec) { | |
1984 | prefix2str(p, buf, BUFSIZ); | |
1985 | zlog_err("Failed to add FEC %s upon config", buf); | |
1986 | return -1; | |
1987 | } | |
1988 | ||
1989 | if (IS_ZEBRA_DEBUG_MPLS) | |
1990 | zlog_debug("Add fec %s label %u", buf, in_label); | |
1991 | } else { | |
1992 | fec->flags |= FEC_FLAG_CONFIGURED; | |
1993 | if (fec->label == in_label) | |
1994 | /* Duplicate config */ | |
1995 | return 0; | |
1996 | ||
1997 | /* Label change, update clients. */ | |
1998 | old_label = fec->label; | |
1999 | if (IS_ZEBRA_DEBUG_MPLS) | |
2000 | zlog_debug("Update fec %s new label %u", buf, in_label); | |
2001 | ||
2002 | fec->label = in_label; | |
2003 | fec_update_clients(fec); | |
2004 | ||
2005 | /* Update label forwarding entries appropriately */ | |
2006 | ret = fec_change_update_lsp(zvrf, fec, old_label); | |
2007 | } | |
2008 | ||
2009 | return ret; | |
f31e084c DS |
2010 | } |
2011 | ||
2012 | /* | |
5aba114a DS |
2013 | * Remove static FEC to label binding. If there are no clients registered |
2014 | * for this FEC, delete the FEC; else notify clients | |
28d58fd7 VV |
2015 | * Note: Upon delete of static binding, if label index exists for this FEC, |
2016 | * client may need to be updated with derived label. | |
f31e084c | 2017 | */ |
d62a17ae | 2018 | int zebra_mpls_static_fec_del(struct zebra_vrf *zvrf, struct prefix *p) |
2019 | { | |
2020 | struct route_table *table; | |
2021 | zebra_fec_t *fec; | |
2022 | mpls_label_t old_label; | |
2023 | char buf[BUFSIZ]; | |
2024 | ||
2025 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; | |
2026 | if (!table) | |
2027 | return -1; | |
2028 | ||
2029 | fec = fec_find(table, p); | |
2030 | if (!fec) { | |
2031 | prefix2str(p, buf, BUFSIZ); | |
2032 | zlog_err("Failed to find FEC %s upon delete", buf); | |
2033 | return -1; | |
2034 | } | |
2035 | ||
2036 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2037 | prefix2str(p, buf, BUFSIZ); | |
2038 | zlog_debug("Delete fec %s label index %u", buf, | |
2039 | fec->label_index); | |
2040 | } | |
2041 | ||
2042 | old_label = fec->label; | |
2043 | fec->flags &= ~FEC_FLAG_CONFIGURED; | |
2044 | fec->label = MPLS_INVALID_LABEL; | |
2045 | ||
2046 | /* If no client exists, just delete the FEC. */ | |
2047 | if (list_isempty(fec->client_list)) { | |
2048 | fec_del(fec); | |
2049 | return 0; | |
2050 | } | |
2051 | ||
2052 | /* Derive the local label (from label index) or reset it. */ | |
2053 | fec_derive_label_from_index(zvrf, fec); | |
2054 | ||
2055 | /* If there is a label change, update clients. */ | |
2056 | if (fec->label == old_label) | |
2057 | return 0; | |
2058 | fec_update_clients(fec); | |
2059 | ||
2060 | /* Update label forwarding entries appropriately */ | |
2061 | return fec_change_update_lsp(zvrf, fec, old_label); | |
f31e084c DS |
2062 | } |
2063 | ||
2064 | /* | |
2065 | * Display MPLS FEC to label binding configuration (VTY command handler). | |
2066 | */ | |
d62a17ae | 2067 | int zebra_mpls_write_fec_config(struct vty *vty, struct zebra_vrf *zvrf) |
f31e084c | 2068 | { |
d62a17ae | 2069 | struct route_node *rn; |
2070 | int af; | |
2071 | zebra_fec_t *fec; | |
2072 | char buf[BUFSIZ]; | |
2073 | int write = 0; | |
f31e084c | 2074 | |
d62a17ae | 2075 | for (af = AFI_IP; af < AFI_MAX; af++) { |
2076 | if (zvrf->fec_table[af] == NULL) | |
2077 | continue; | |
f31e084c | 2078 | |
d62a17ae | 2079 | for (rn = route_top(zvrf->fec_table[af]); rn; |
2080 | rn = route_next(rn)) { | |
2081 | if (!rn->info) | |
2082 | continue; | |
f31e084c | 2083 | |
d62a17ae | 2084 | char lstr[BUFSIZ]; |
2085 | fec = rn->info; | |
f31e084c | 2086 | |
d62a17ae | 2087 | if (!(fec->flags & FEC_FLAG_CONFIGURED)) |
2088 | continue; | |
f31e084c | 2089 | |
d62a17ae | 2090 | write = 1; |
2091 | prefix2str(&rn->p, buf, BUFSIZ); | |
2092 | vty_out(vty, "mpls label bind %s %s\n", buf, | |
2093 | label2str(fec->label, lstr, BUFSIZ)); | |
2094 | } | |
2095 | } | |
f31e084c | 2096 | |
d62a17ae | 2097 | return write; |
f31e084c DS |
2098 | } |
2099 | ||
2100 | /* | |
2101 | * Display MPLS FEC to label binding (VTY command handler). | |
2102 | */ | |
d62a17ae | 2103 | void zebra_mpls_print_fec_table(struct vty *vty, struct zebra_vrf *zvrf) |
f31e084c | 2104 | { |
d62a17ae | 2105 | struct route_node *rn; |
2106 | int af; | |
f31e084c | 2107 | |
d62a17ae | 2108 | for (af = AFI_IP; af < AFI_MAX; af++) { |
2109 | if (zvrf->fec_table[af] == NULL) | |
2110 | continue; | |
f31e084c | 2111 | |
d62a17ae | 2112 | for (rn = route_top(zvrf->fec_table[af]); rn; |
2113 | rn = route_next(rn)) { | |
2114 | if (!rn->info) | |
2115 | continue; | |
2116 | fec_print(rn->info, vty); | |
2117 | } | |
2118 | } | |
f31e084c DS |
2119 | } |
2120 | ||
2121 | /* | |
2122 | * Display MPLS FEC to label binding for a specific FEC (VTY command handler). | |
2123 | */ | |
d62a17ae | 2124 | void zebra_mpls_print_fec(struct vty *vty, struct zebra_vrf *zvrf, |
2125 | struct prefix *p) | |
f31e084c | 2126 | { |
d62a17ae | 2127 | struct route_table *table; |
2128 | struct route_node *rn; | |
f31e084c | 2129 | |
d62a17ae | 2130 | table = zvrf->fec_table[family2afi(PREFIX_FAMILY(p))]; |
2131 | if (!table) | |
2132 | return; | |
f31e084c | 2133 | |
d62a17ae | 2134 | apply_mask(p); |
2135 | rn = route_node_lookup(table, p); | |
2136 | if (!rn) | |
2137 | return; | |
f31e084c | 2138 | |
d62a17ae | 2139 | route_unlock_node(rn); |
2140 | if (!rn->info) | |
2141 | return; | |
f31e084c | 2142 | |
d62a17ae | 2143 | fec_print(rn->info, vty); |
f31e084c DS |
2144 | } |
2145 | ||
ce549947 RW |
2146 | /* |
2147 | * Install/uninstall a FEC-To-NHLFE (FTN) binding. | |
2148 | */ | |
d62a17ae | 2149 | int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, |
2150 | struct prefix *prefix, enum nexthop_types_t gtype, | |
2151 | union g_addr *gate, ifindex_t ifindex, u_int8_t distance, | |
2152 | mpls_label_t out_label) | |
2153 | { | |
2154 | struct route_table *table; | |
2155 | struct route_node *rn; | |
2156 | struct route_entry *re; | |
2157 | struct nexthop *nexthop; | |
2158 | ||
2159 | /* Lookup table. */ | |
2160 | table = zebra_vrf_table(family2afi(prefix->family), SAFI_UNICAST, | |
2161 | zvrf_id(zvrf)); | |
2162 | if (!table) | |
2163 | return -1; | |
2164 | ||
2165 | /* Lookup existing route */ | |
2166 | rn = route_node_get(table, prefix); | |
2167 | RNODE_FOREACH_RE(rn, re) | |
88d88a9c | 2168 | { |
d62a17ae | 2169 | if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) |
2170 | continue; | |
2171 | if (re->distance == distance) | |
2172 | break; | |
88d88a9c | 2173 | } |
ce549947 | 2174 | |
d62a17ae | 2175 | if (re == NULL) |
2176 | return -1; | |
2177 | ||
2178 | for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) { | |
2179 | switch (nexthop->type) { | |
2180 | case NEXTHOP_TYPE_IPV4: | |
2181 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
2182 | if (gtype != NEXTHOP_TYPE_IPV4 | |
2183 | && gtype != NEXTHOP_TYPE_IPV4_IFINDEX) | |
2184 | continue; | |
2185 | if (!IPV4_ADDR_SAME(&nexthop->gate.ipv4, &gate->ipv4)) | |
2186 | continue; | |
2187 | if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX | |
2188 | && nexthop->ifindex != ifindex) | |
2189 | continue; | |
2190 | goto found; | |
2191 | case NEXTHOP_TYPE_IPV6: | |
2192 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
2193 | if (gtype != NEXTHOP_TYPE_IPV6 | |
2194 | && gtype != NEXTHOP_TYPE_IPV6_IFINDEX) | |
2195 | continue; | |
2196 | if (!IPV6_ADDR_SAME(&nexthop->gate.ipv6, &gate->ipv6)) | |
2197 | continue; | |
2198 | if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX | |
2199 | && nexthop->ifindex != ifindex) | |
2200 | continue; | |
2201 | goto found; | |
2202 | default: | |
2203 | break; | |
2204 | } | |
2205 | } | |
2206 | /* nexthop not found */ | |
2207 | return -1; | |
2208 | ||
2209 | found: | |
2210 | if (add && nexthop->nh_label_type == ZEBRA_LSP_NONE) | |
2211 | nexthop_add_labels(nexthop, type, 1, &out_label); | |
2212 | else if (!add && nexthop->nh_label_type == type) | |
2213 | nexthop_del_labels(nexthop); | |
2214 | else | |
2215 | return 0; | |
ce549947 | 2216 | |
d62a17ae | 2217 | SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); |
332ad713 | 2218 | SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); |
d62a17ae | 2219 | rib_queue_add(rn); |
ce549947 | 2220 | |
d62a17ae | 2221 | return 0; |
ce549947 RW |
2222 | } |
2223 | ||
2224 | /* | |
2225 | * Install/update a NHLFE for an LSP in the forwarding table. This may be | |
2226 | * a new LSP entry or a new NHLFE for an existing in-label or an update of | |
2227 | * the out-label for an existing NHLFE (update case). | |
2228 | */ | |
d62a17ae | 2229 | int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, |
2230 | mpls_label_t in_label, mpls_label_t out_label, | |
2231 | enum nexthop_types_t gtype, union g_addr *gate, | |
2232 | ifindex_t ifindex) | |
2233 | { | |
2234 | struct hash *lsp_table; | |
2235 | zebra_ile_t tmp_ile; | |
2236 | zebra_lsp_t *lsp; | |
2237 | zebra_nhlfe_t *nhlfe; | |
2238 | char buf[BUFSIZ]; | |
2239 | ||
2240 | /* Lookup table. */ | |
2241 | lsp_table = zvrf->lsp_table; | |
2242 | if (!lsp_table) | |
2243 | return -1; | |
2244 | ||
2245 | /* If entry is present, exit. */ | |
2246 | tmp_ile.in_label = in_label; | |
2247 | lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); | |
2248 | if (!lsp) | |
2249 | return -1; | |
2250 | nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); | |
2251 | if (nhlfe) { | |
2252 | struct nexthop *nh = nhlfe->nexthop; | |
2253 | ||
2254 | assert(nh); | |
2255 | assert(nh->nh_label); | |
2256 | ||
2257 | /* Clear deleted flag (in case it was set) */ | |
2258 | UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); | |
2259 | if (nh->nh_label->label[0] == out_label) | |
2260 | /* No change */ | |
2261 | return 0; | |
2262 | ||
2263 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2264 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
2265 | zlog_debug( | |
2266 | "LSP in-label %u type %d nexthop %s " | |
2267 | "out-label changed to %u (old %u)", | |
2268 | in_label, type, buf, out_label, | |
2269 | nh->nh_label->label[0]); | |
2270 | } | |
2271 | ||
2272 | /* Update out label, trigger processing. */ | |
2273 | nh->nh_label->label[0] = out_label; | |
2274 | } else { | |
2275 | /* Add LSP entry to this nexthop */ | |
2276 | nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, out_label); | |
2277 | if (!nhlfe) | |
2278 | return -1; | |
2279 | ||
2280 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2281 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
2282 | zlog_debug( | |
2283 | "Add LSP in-label %u type %d nexthop %s " | |
2284 | "out-label %u", | |
2285 | in_label, type, buf, out_label); | |
2286 | } | |
2287 | ||
2288 | lsp->addr_family = NHLFE_FAMILY(nhlfe); | |
2289 | } | |
2290 | ||
2291 | /* Mark NHLFE, queue LSP for processing. */ | |
2292 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
2293 | if (lsp_processq_add(lsp)) | |
2294 | return -1; | |
2295 | ||
2296 | return 0; | |
ce549947 RW |
2297 | } |
2298 | ||
2299 | /* | |
2300 | * Uninstall a particular NHLFE in the forwarding table. If this is | |
2301 | * the only NHLFE, the entire LSP forwarding entry has to be deleted. | |
2302 | */ | |
d62a17ae | 2303 | int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, |
2304 | mpls_label_t in_label, enum nexthop_types_t gtype, | |
2305 | union g_addr *gate, ifindex_t ifindex) | |
2306 | { | |
2307 | struct hash *lsp_table; | |
2308 | zebra_ile_t tmp_ile; | |
2309 | zebra_lsp_t *lsp; | |
2310 | zebra_nhlfe_t *nhlfe; | |
2311 | char buf[BUFSIZ]; | |
2312 | ||
2313 | /* Lookup table. */ | |
2314 | lsp_table = zvrf->lsp_table; | |
2315 | if (!lsp_table) | |
2316 | return -1; | |
2317 | ||
2318 | /* If entry is not present, exit. */ | |
2319 | tmp_ile.in_label = in_label; | |
2320 | lsp = hash_lookup(lsp_table, &tmp_ile); | |
2321 | if (!lsp) | |
2322 | return 0; | |
2323 | nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); | |
2324 | if (!nhlfe) | |
2325 | return 0; | |
2326 | ||
2327 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2328 | nhlfe2str(nhlfe, buf, BUFSIZ); | |
2329 | zlog_debug("Del LSP in-label %u type %d nexthop %s flags 0x%x", | |
2330 | in_label, type, buf, nhlfe->flags); | |
2331 | } | |
2332 | ||
2333 | /* Mark NHLFE for delete or directly delete, as appropriate. */ | |
2334 | if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { | |
2335 | UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); | |
2336 | SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); | |
2337 | if (lsp_processq_add(lsp)) | |
2338 | return -1; | |
2339 | } else { | |
2340 | nhlfe_del(nhlfe); | |
2341 | ||
2342 | /* Free LSP entry if no other NHLFEs and not scheduled. */ | |
2343 | if (!lsp->nhlfe_list | |
2344 | && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) { | |
2345 | if (IS_ZEBRA_DEBUG_MPLS) | |
2346 | zlog_debug("Free LSP in-label %u flags 0x%x", | |
2347 | lsp->ile.in_label, lsp->flags); | |
2348 | ||
2349 | lsp = hash_release(lsp_table, &lsp->ile); | |
2350 | if (lsp) | |
2351 | XFREE(MTYPE_LSP, lsp); | |
2352 | } | |
2353 | } | |
2354 | return 0; | |
ce549947 RW |
2355 | } |
2356 | ||
2357 | /* | |
2358 | * Uninstall all LDP NHLFEs for a particular LSP forwarding entry. | |
2359 | * If no other NHLFEs exist, the entry would be deleted. | |
2360 | */ | |
d62a17ae | 2361 | void mpls_ldp_lsp_uninstall_all(struct hash_backet *backet, void *ctxt) |
ce549947 | 2362 | { |
d62a17ae | 2363 | zebra_lsp_t *lsp; |
2364 | struct hash *lsp_table; | |
ce549947 | 2365 | |
d62a17ae | 2366 | lsp = (zebra_lsp_t *)backet->data; |
2367 | if (!lsp || !lsp->nhlfe_list) | |
2368 | return; | |
ce549947 | 2369 | |
d62a17ae | 2370 | lsp_table = ctxt; |
2371 | if (!lsp_table) | |
2372 | return; | |
ce549947 | 2373 | |
d62a17ae | 2374 | mpls_lsp_uninstall_all(lsp_table, lsp, ZEBRA_LSP_LDP); |
ce549947 RW |
2375 | } |
2376 | ||
2377 | /* | |
2378 | * Uninstall all LDP FEC-To-NHLFE (FTN) bindings of the given address-family. | |
2379 | */ | |
d62a17ae | 2380 | void mpls_ldp_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi) |
2381 | { | |
2382 | struct route_table *table; | |
2383 | struct route_node *rn; | |
2384 | struct route_entry *re; | |
2385 | struct nexthop *nexthop; | |
2386 | int update; | |
2387 | ||
2388 | /* Process routes of interested address-families. */ | |
2389 | table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); | |
2390 | if (!table) | |
2391 | return; | |
2392 | ||
2393 | for (rn = route_top(table); rn; rn = route_next(rn)) { | |
2394 | update = 0; | |
2395 | RNODE_FOREACH_RE(rn, re) | |
2396 | for (nexthop = re->nexthop; nexthop; nexthop = nexthop->next) | |
2397 | if (nexthop->nh_label_type == ZEBRA_LSP_LDP) { | |
2398 | nexthop_del_labels(nexthop); | |
2399 | SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); | |
2400 | SET_FLAG(re->status, | |
332ad713 | 2401 | ROUTE_ENTRY_LABELS_CHANGED); |
d62a17ae | 2402 | update = 1; |
2403 | } | |
2404 | ||
2405 | if (update) | |
2406 | rib_queue_add(rn); | |
2407 | } | |
ce549947 RW |
2408 | } |
2409 | ||
1c1cf002 | 2410 | #if defined(HAVE_CUMULUS) |
7758e3f3 | 2411 | /* |
2412 | * Check that the label values used in LSP creation are consistent. The | |
2413 | * main criteria is that if there is ECMP, the label operation must still | |
2414 | * be consistent - i.e., all paths either do a swap or do PHP. This is due | |
2415 | * to current HW restrictions. | |
2416 | */ | |
d62a17ae | 2417 | int zebra_mpls_lsp_label_consistent(struct zebra_vrf *zvrf, |
2418 | mpls_label_t in_label, | |
2419 | mpls_label_t out_label, | |
2420 | enum nexthop_types_t gtype, | |
2421 | union g_addr *gate, ifindex_t ifindex) | |
2422 | { | |
2423 | struct hash *slsp_table; | |
2424 | zebra_ile_t tmp_ile; | |
2425 | zebra_slsp_t *slsp; | |
2426 | zebra_snhlfe_t *snhlfe; | |
2427 | ||
2428 | /* Lookup table. */ | |
2429 | slsp_table = zvrf->slsp_table; | |
2430 | if (!slsp_table) | |
2431 | return 0; | |
2432 | ||
2433 | /* If entry is not present, exit. */ | |
2434 | tmp_ile.in_label = in_label; | |
2435 | slsp = hash_lookup(slsp_table, &tmp_ile); | |
2436 | if (!slsp) | |
2437 | return 1; | |
2438 | ||
2439 | snhlfe = snhlfe_find(slsp, gtype, gate, ifindex); | |
2440 | if (snhlfe) { | |
2441 | if (snhlfe->out_label == out_label) | |
2442 | return 1; | |
2443 | ||
2444 | /* If not only NHLFE, cannot allow label change. */ | |
2445 | if (snhlfe != slsp->snhlfe_list || snhlfe->next) | |
2446 | return 0; | |
2447 | } else { | |
2448 | /* If other NHLFEs exist, label operation must match. */ | |
2449 | if (slsp->snhlfe_list) { | |
2450 | int cur_op, new_op; | |
2451 | ||
2452 | cur_op = (slsp->snhlfe_list->out_label | |
2453 | == MPLS_IMP_NULL_LABEL); | |
2454 | new_op = (out_label == MPLS_IMP_NULL_LABEL); | |
2455 | if (cur_op != new_op) | |
2456 | return 0; | |
2457 | } | |
2458 | } | |
2459 | ||
2460 | /* Label values are good. */ | |
2461 | return 1; | |
7758e3f3 | 2462 | } |
1c1cf002 | 2463 | #endif /* HAVE_CUMULUS */ |
7758e3f3 | 2464 | |
2465 | /* | |
2466 | * Add static LSP entry. This may be the first entry for this incoming label | |
2467 | * or an additional nexthop; an existing entry may also have outgoing label | |
2468 | * changed. | |
2469 | * Note: The label operation (swap or PHP) is common for the LSP entry (all | |
2470 | * NHLFEs). | |
2471 | */ | |
d62a17ae | 2472 | int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, |
2473 | mpls_label_t out_label, | |
2474 | enum nexthop_types_t gtype, union g_addr *gate, | |
2475 | ifindex_t ifindex) | |
2476 | { | |
2477 | struct hash *slsp_table; | |
2478 | zebra_ile_t tmp_ile; | |
2479 | zebra_slsp_t *slsp; | |
2480 | zebra_snhlfe_t *snhlfe; | |
2481 | char buf[BUFSIZ]; | |
2482 | ||
2483 | /* Lookup table. */ | |
2484 | slsp_table = zvrf->slsp_table; | |
2485 | if (!slsp_table) | |
2486 | return -1; | |
2487 | ||
2488 | /* If entry is present, exit. */ | |
2489 | tmp_ile.in_label = in_label; | |
2490 | slsp = hash_get(slsp_table, &tmp_ile, slsp_alloc); | |
2491 | if (!slsp) | |
2492 | return -1; | |
2493 | snhlfe = snhlfe_find(slsp, gtype, gate, ifindex); | |
2494 | if (snhlfe) { | |
2495 | if (snhlfe->out_label == out_label) | |
2496 | /* No change */ | |
2497 | return 0; | |
2498 | ||
2499 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2500 | snhlfe2str(snhlfe, buf, BUFSIZ); | |
2501 | zlog_debug( | |
2502 | "Upd static LSP in-label %u nexthop %s " | |
2503 | "out-label %u (old %u)", | |
2504 | in_label, buf, out_label, snhlfe->out_label); | |
2505 | } | |
2506 | snhlfe->out_label = out_label; | |
2507 | } else { | |
2508 | /* Add static LSP entry to this nexthop */ | |
2509 | snhlfe = snhlfe_add(slsp, gtype, gate, ifindex, out_label); | |
2510 | if (!snhlfe) | |
2511 | return -1; | |
2512 | ||
2513 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2514 | snhlfe2str(snhlfe, buf, BUFSIZ); | |
2515 | zlog_debug( | |
2516 | "Add static LSP in-label %u nexthop %s out-label %u", | |
2517 | in_label, buf, out_label); | |
2518 | } | |
2519 | } | |
2520 | ||
2521 | /* (Re)Install LSP in the main table. */ | |
2522 | if (mpls_lsp_install(zvrf, ZEBRA_LSP_STATIC, in_label, out_label, gtype, | |
2523 | gate, ifindex)) | |
2524 | return -1; | |
2525 | ||
2526 | return 0; | |
7758e3f3 | 2527 | } |
2528 | ||
2529 | /* | |
2530 | * Delete static LSP entry. This may be the delete of one particular | |
2531 | * NHLFE for this incoming label or the delete of the entire entry (i.e., | |
2532 | * all NHLFEs). | |
2533 | * NOTE: Delete of the only NHLFE will also end up deleting the entire | |
2534 | * LSP configuration. | |
2535 | */ | |
d62a17ae | 2536 | int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label, |
2537 | enum nexthop_types_t gtype, union g_addr *gate, | |
2538 | ifindex_t ifindex) | |
2539 | { | |
2540 | struct hash *slsp_table; | |
2541 | zebra_ile_t tmp_ile; | |
2542 | zebra_slsp_t *slsp; | |
2543 | zebra_snhlfe_t *snhlfe; | |
2544 | ||
2545 | /* Lookup table. */ | |
2546 | slsp_table = zvrf->slsp_table; | |
2547 | if (!slsp_table) | |
2548 | return -1; | |
2549 | ||
2550 | /* If entry is not present, exit. */ | |
2551 | tmp_ile.in_label = in_label; | |
2552 | slsp = hash_lookup(slsp_table, &tmp_ile); | |
2553 | if (!slsp) | |
2554 | return 0; | |
2555 | ||
2556 | /* Is it delete of entire LSP or a specific NHLFE? */ | |
2557 | if (gtype == NEXTHOP_TYPE_BLACKHOLE) { | |
2558 | if (IS_ZEBRA_DEBUG_MPLS) | |
2559 | zlog_debug("Del static LSP in-label %u", in_label); | |
2560 | ||
2561 | /* Uninstall entire LSP from the main table. */ | |
2562 | mpls_static_lsp_uninstall_all(zvrf, in_label); | |
2563 | ||
2564 | /* Delete all static NHLFEs */ | |
2565 | snhlfe_del_all(slsp); | |
2566 | } else { | |
2567 | /* Find specific NHLFE, exit if not found. */ | |
2568 | snhlfe = snhlfe_find(slsp, gtype, gate, ifindex); | |
2569 | if (!snhlfe) | |
2570 | return 0; | |
2571 | ||
2572 | if (IS_ZEBRA_DEBUG_MPLS) { | |
2573 | char buf[BUFSIZ]; | |
2574 | snhlfe2str(snhlfe, buf, BUFSIZ); | |
2575 | zlog_debug("Del static LSP in-label %u nexthop %s", | |
2576 | in_label, buf); | |
2577 | } | |
2578 | ||
2579 | /* Uninstall LSP from the main table. */ | |
2580 | mpls_lsp_uninstall(zvrf, ZEBRA_LSP_STATIC, in_label, gtype, | |
2581 | gate, ifindex); | |
2582 | ||
2583 | /* Delete static LSP NHLFE */ | |
2584 | snhlfe_del(snhlfe); | |
2585 | } | |
2586 | ||
2587 | /* Remove entire static LSP entry if no NHLFE - valid in either case | |
2588 | * above. */ | |
2589 | if (!slsp->snhlfe_list) { | |
2590 | slsp = hash_release(slsp_table, &tmp_ile); | |
2591 | if (slsp) | |
2592 | XFREE(MTYPE_SLSP, slsp); | |
2593 | } | |
2594 | ||
2595 | return 0; | |
7758e3f3 | 2596 | } |
2597 | ||
40c7bdb0 | 2598 | /* |
2599 | * Schedule all MPLS label forwarding entries for processing. | |
2600 | * Called upon changes that may affect one or more of them such as | |
2601 | * interface or nexthop state changes. | |
2602 | */ | |
d62a17ae | 2603 | void zebra_mpls_lsp_schedule(struct zebra_vrf *zvrf) |
40c7bdb0 | 2604 | { |
d62a17ae | 2605 | if (!zvrf) |
2606 | return; | |
2607 | hash_iterate(zvrf->lsp_table, lsp_schedule, NULL); | |
40c7bdb0 | 2608 | } |
2609 | ||
3ab18ff2 | 2610 | /* |
2611 | * Display MPLS label forwarding table for a specific LSP | |
2612 | * (VTY command handler). | |
2613 | */ | |
d62a17ae | 2614 | void zebra_mpls_print_lsp(struct vty *vty, struct zebra_vrf *zvrf, |
2615 | mpls_label_t label, u_char use_json) | |
3ab18ff2 | 2616 | { |
d62a17ae | 2617 | struct hash *lsp_table; |
2618 | zebra_lsp_t *lsp; | |
2619 | zebra_ile_t tmp_ile; | |
2620 | json_object *json = NULL; | |
3ab18ff2 | 2621 | |
d62a17ae | 2622 | /* Lookup table. */ |
2623 | lsp_table = zvrf->lsp_table; | |
2624 | if (!lsp_table) | |
2625 | return; | |
3ab18ff2 | 2626 | |
d62a17ae | 2627 | /* If entry is not present, exit. */ |
2628 | tmp_ile.in_label = label; | |
2629 | lsp = hash_lookup(lsp_table, &tmp_ile); | |
2630 | if (!lsp) | |
2631 | return; | |
3ab18ff2 | 2632 | |
d62a17ae | 2633 | if (use_json) { |
2634 | json = lsp_json(lsp); | |
9d303b37 DL |
2635 | vty_out(vty, "%s\n", json_object_to_json_string_ext( |
2636 | json, JSON_C_TO_STRING_PRETTY)); | |
d62a17ae | 2637 | json_object_free(json); |
2638 | } else | |
2639 | lsp_print(lsp, (void *)vty); | |
3ab18ff2 | 2640 | } |
2641 | ||
2642 | /* | |
2643 | * Display MPLS label forwarding table (VTY command handler). | |
2644 | */ | |
d62a17ae | 2645 | void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, |
2646 | u_char use_json) | |
2647 | { | |
2648 | char buf[BUFSIZ]; | |
2649 | json_object *json = NULL; | |
2650 | zebra_lsp_t *lsp = NULL; | |
2651 | zebra_nhlfe_t *nhlfe = NULL; | |
2652 | struct nexthop *nexthop = NULL; | |
2653 | struct listnode *node = NULL; | |
2654 | struct list *lsp_list = hash_get_sorted_list(zvrf->lsp_table, lsp_cmp); | |
2655 | ||
2656 | if (use_json) { | |
2657 | json = json_object_new_object(); | |
2658 | ||
2659 | for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) | |
2660 | json_object_object_add( | |
2661 | json, label2str(lsp->ile.in_label, buf, BUFSIZ), | |
2662 | lsp_json(lsp)); | |
2663 | ||
9d303b37 DL |
2664 | vty_out(vty, "%s\n", json_object_to_json_string_ext( |
2665 | json, JSON_C_TO_STRING_PRETTY)); | |
d62a17ae | 2666 | json_object_free(json); |
2667 | } else { | |
2668 | vty_out(vty, " Inbound Outbound\n"); | |
2669 | vty_out(vty, " Label Type Nexthop Label\n"); | |
2670 | vty_out(vty, "-------- ------- --------------- --------\n"); | |
2671 | ||
2672 | for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) { | |
2673 | for (nhlfe = lsp->nhlfe_list; nhlfe; | |
2674 | nhlfe = nhlfe->next) { | |
2675 | vty_out(vty, "%8d %7s ", lsp->ile.in_label, | |
2676 | nhlfe_type2str(nhlfe->type)); | |
2677 | nexthop = nhlfe->nexthop; | |
2678 | ||
2679 | switch (nexthop->type) { | |
2680 | case NEXTHOP_TYPE_IPV4: | |
2681 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
2682 | vty_out(vty, "%15s", | |
2683 | inet_ntoa(nexthop->gate.ipv4)); | |
2684 | break; | |
2685 | case NEXTHOP_TYPE_IPV6: | |
2686 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
2687 | vty_out(vty, "%15s", | |
2688 | inet_ntop(AF_INET6, | |
2689 | &nexthop->gate.ipv6, | |
2690 | buf, BUFSIZ)); | |
2691 | break; | |
2692 | default: | |
2693 | break; | |
2694 | } | |
2695 | ||
2696 | vty_out(vty, " %8d\n", | |
2697 | nexthop->nh_label->label[0]); | |
2698 | } | |
2699 | } | |
2700 | ||
2701 | vty_out(vty, "\n"); | |
2702 | } | |
2703 | ||
2704 | list_delete(lsp_list); | |
3ab18ff2 | 2705 | } |
2706 | ||
7758e3f3 | 2707 | /* |
2708 | * Display MPLS LSP configuration of all static LSPs (VTY command handler). | |
2709 | */ | |
d62a17ae | 2710 | int zebra_mpls_write_lsp_config(struct vty *vty, struct zebra_vrf *zvrf) |
2711 | { | |
2712 | zebra_slsp_t *slsp; | |
2713 | zebra_snhlfe_t *snhlfe; | |
2714 | struct listnode *node; | |
2715 | struct list *slsp_list = | |
2716 | hash_get_sorted_list(zvrf->slsp_table, slsp_cmp); | |
2717 | ||
2718 | for (ALL_LIST_ELEMENTS_RO(slsp_list, node, slsp)) { | |
2719 | for (snhlfe = slsp->snhlfe_list; snhlfe; | |
2720 | snhlfe = snhlfe->next) { | |
2721 | char buf[INET6_ADDRSTRLEN]; | |
2722 | char lstr[30]; | |
2723 | ||
2724 | snhlfe2str(snhlfe, buf, BUFSIZ); | |
2725 | switch (snhlfe->out_label) { | |
2726 | case MPLS_V4_EXP_NULL_LABEL: | |
2727 | case MPLS_V6_EXP_NULL_LABEL: | |
2728 | strlcpy(lstr, "explicit-null", sizeof(lstr)); | |
2729 | break; | |
2730 | case MPLS_IMP_NULL_LABEL: | |
2731 | strlcpy(lstr, "implicit-null", sizeof(lstr)); | |
2732 | break; | |
2733 | default: | |
2734 | sprintf(lstr, "%u", snhlfe->out_label); | |
2735 | break; | |
2736 | } | |
2737 | ||
2738 | vty_out(vty, "mpls lsp %u %s %s\n", slsp->ile.in_label, | |
2739 | buf, lstr); | |
2740 | } | |
2741 | } | |
b78b820d | 2742 | |
d62a17ae | 2743 | list_delete(slsp_list); |
2744 | return (zvrf->slsp_table->count ? 1 : 0); | |
7758e3f3 | 2745 | } |
2746 | ||
1b6d5c7e VV |
2747 | /* |
2748 | * Add/update global label block. | |
2749 | */ | |
d62a17ae | 2750 | int zebra_mpls_label_block_add(struct zebra_vrf *zvrf, u_int32_t start_label, |
2751 | u_int32_t end_label) | |
1b6d5c7e | 2752 | { |
d62a17ae | 2753 | zvrf->mpls_srgb.start_label = start_label; |
2754 | zvrf->mpls_srgb.end_label = end_label; | |
28d58fd7 | 2755 | |
d62a17ae | 2756 | /* Evaluate registered FECs to see if any get a label or not. */ |
2757 | fec_evaluate(zvrf); | |
2758 | return 0; | |
1b6d5c7e VV |
2759 | } |
2760 | ||
2761 | /* | |
2762 | * Delete global label block. | |
2763 | */ | |
d62a17ae | 2764 | int zebra_mpls_label_block_del(struct zebra_vrf *zvrf) |
1b6d5c7e | 2765 | { |
d62a17ae | 2766 | zvrf->mpls_srgb.start_label = MPLS_DEFAULT_MIN_SRGB_LABEL; |
2767 | zvrf->mpls_srgb.end_label = MPLS_DEFAULT_MAX_SRGB_LABEL; | |
28d58fd7 | 2768 | |
d62a17ae | 2769 | /* Process registered FECs to clear their local label, if needed. */ |
2770 | fec_evaluate(zvrf); | |
2771 | return 0; | |
1b6d5c7e VV |
2772 | } |
2773 | ||
2774 | /* | |
2775 | * Display MPLS global label block configuration (VTY command handler). | |
2776 | */ | |
d62a17ae | 2777 | int zebra_mpls_write_label_block_config(struct vty *vty, struct zebra_vrf *zvrf) |
1b6d5c7e | 2778 | { |
d62a17ae | 2779 | if (zvrf->mpls_srgb.start_label == 0) |
2780 | return 0; | |
1b6d5c7e | 2781 | |
d62a17ae | 2782 | if ((zvrf->mpls_srgb.start_label != MPLS_DEFAULT_MIN_SRGB_LABEL) |
2783 | || (zvrf->mpls_srgb.end_label != MPLS_DEFAULT_MAX_SRGB_LABEL)) { | |
2784 | vty_out(vty, "mpls label global-block %u %u\n", | |
2785 | zvrf->mpls_srgb.start_label, zvrf->mpls_srgb.end_label); | |
2786 | } | |
1b6d5c7e | 2787 | |
d62a17ae | 2788 | return 1; |
1b6d5c7e VV |
2789 | } |
2790 | ||
40c7bdb0 | 2791 | /* |
2792 | * Called upon process exiting, need to delete LSP forwarding | |
2793 | * entries from the kernel. | |
2794 | * NOTE: Currently supported only for default VRF. | |
2795 | */ | |
d62a17ae | 2796 | void zebra_mpls_close_tables(struct zebra_vrf *zvrf) |
40c7bdb0 | 2797 | { |
d62a17ae | 2798 | hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); |
2799 | hash_clean(zvrf->lsp_table, NULL); | |
2800 | hash_free(zvrf->lsp_table); | |
2801 | hash_clean(zvrf->slsp_table, NULL); | |
2802 | hash_free(zvrf->slsp_table); | |
9b67b514 DS |
2803 | route_table_finish(zvrf->fec_table[AFI_IP]); |
2804 | route_table_finish(zvrf->fec_table[AFI_IP6]); | |
40c7bdb0 | 2805 | } |
2806 | ||
7758e3f3 | 2807 | /* |
2808 | * Allocate MPLS tables for this VRF and do other initialization. | |
2809 | * NOTE: Currently supported only for default VRF. | |
2810 | */ | |
d62a17ae | 2811 | void zebra_mpls_init_tables(struct zebra_vrf *zvrf) |
7758e3f3 | 2812 | { |
d62a17ae | 2813 | if (!zvrf) |
2814 | return; | |
2815 | zvrf->slsp_table = hash_create(label_hash, label_cmp, NULL); | |
2816 | zvrf->lsp_table = hash_create(label_hash, label_cmp, NULL); | |
2817 | zvrf->fec_table[AFI_IP] = route_table_init(); | |
2818 | zvrf->fec_table[AFI_IP6] = route_table_init(); | |
2819 | zvrf->mpls_flags = 0; | |
2820 | zvrf->mpls_srgb.start_label = MPLS_DEFAULT_MIN_SRGB_LABEL; | |
2821 | zvrf->mpls_srgb.end_label = MPLS_DEFAULT_MAX_SRGB_LABEL; | |
7758e3f3 | 2822 | } |
2823 | ||
2824 | /* | |
2825 | * Global MPLS initialization. | |
2826 | */ | |
d62a17ae | 2827 | void zebra_mpls_init(void) |
7758e3f3 | 2828 | { |
d62a17ae | 2829 | mpls_enabled = 0; |
33c32282 | 2830 | |
d62a17ae | 2831 | if (mpls_kernel_init() < 0) { |
2832 | zlog_warn("Disabling MPLS support (no kernel support)"); | |
2833 | return; | |
2834 | } | |
fe6c7157 | 2835 | |
d62a17ae | 2836 | if (!mpls_processq_init(&zebrad)) |
2837 | mpls_enabled = 1; | |
7758e3f3 | 2838 | } |