Remove the usage of fprintf from ldp-portable
[mpls-ldp-portable.git] / ldp / ldp_fec.c
blob5a71069b2f8e1cd7cc370b08f3e12060d723cd83
2 /*
3 * Copyright (C) James R. Leu 2000
4 * jleu@mindspring.com
6 * This software is covered under the LGPL, for more
7 * info check out http://www.gnu.org/copyleft/lgpl.html
8 */
10 #include "ldp_struct.h"
11 #include "ldp_fec.h"
12 #include "ldp_if.h"
13 #include "ldp_attr.h"
14 #include "ldp_addr.h"
15 #include "ldp_nexthop.h"
16 #include "ldp_session.h"
17 #include "ldp_inlabel.h"
18 #include "ldp_outlabel.h"
19 #include "ldp_global.h"
20 #include "ldp_label_mapping.h"
21 #include "ldp_label_request.h"
22 #include "ldp_label_abort.h"
23 #include "ldp_label_rel_with.h"
24 #include "mpls_assert.h"
25 #include "mpls_compare.h"
26 #include "mpls_mm_impl.h"
27 #include "mpls_tree_impl.h"
28 #include "mpls_policy_impl.h"
29 #include "mpls_trace_impl.h"
31 #if MPLS_USE_LSR
32 #include "lsr_cfg.h"
33 #else
34 #include "mpls_mpls_impl.h"
35 #endif
37 static uint32_t _ldp_fec_next_index = 1;
39 static mpls_return_enum ldp_fec_insert(ldp_global *g, ldp_fec * fec)
41 mpls_return_enum retval = MPLS_SUCCESS;
42 uint32_t key;
43 uint8_t len;
45 MPLS_ASSERT(g && fec);
46 LDP_ENTER(g->user_data, "ldp_fec_insert");
48 switch(fec->info.type) {
49 case MPLS_FEC_PREFIX:
50 key = fec->info.u.prefix.network.u.ipv4;
51 len = fec->info.u.prefix.length;
52 break;
53 case MPLS_FEC_HOST:
54 key = fec->info.u.host.u.ipv4;
55 len = 32;
56 break;
57 case MPLS_FEC_L2CC:
58 /* they had better insert it into the global list */
59 LDP_EXIT(g->user_data, "ldp_fec_insert: l2cc");
60 return MPLS_SUCCESS;
61 default:
62 MPLS_ASSERT(0);
65 LDP_PRINT(g->user_data, "ldp_fec_insert: 0x%08x/%d\n", key, len);
67 if (mpls_tree_insert(g->fec_tree, key, len, (void *)fec) != MPLS_SUCCESS) {
68 LDP_PRINT(g->user_data, "ldp_fec_insert: error adding fec\n");
69 retval = MPLS_FATAL;
72 LDP_EXIT(g->user_data, "ldp_fec_insert");
73 return retval;
76 static void ldp_fec_remove(ldp_global *g, mpls_fec *fec)
78 ldp_fec *f = NULL;
79 uint32_t key;
80 uint8_t len;
82 MPLS_ASSERT(g && fec);
83 LDP_ENTER(g->user_data, "ldp_fec_remove");
85 switch(fec->type) {
86 case MPLS_FEC_PREFIX:
87 key = fec->u.prefix.network.u.ipv4;
88 len = fec->u.prefix.length;
89 break;
90 case MPLS_FEC_HOST:
91 key = fec->u.host.u.ipv4;
92 len = 32;
93 break;
94 case MPLS_FEC_L2CC:
95 /* they had better remove it from the global list */
96 LDP_EXIT(g->user_data, "ldp_fec_remove");
97 return;
98 default:
99 MPLS_ASSERT(0);
102 LDP_PRINT(g->user_data, "ldp_fec_remove: 0x%08x/%d\n", key, len);
103 mpls_tree_remove(g->fec_tree, key, len, (void **)&f);
105 MPLS_ASSERT(f);
107 LDP_EXIT(g->user_data, "ldp_fec_remove");
110 static uint32_t _ldp_fec_get_next_index()
112 uint32_t retval = _ldp_fec_next_index;
114 _ldp_fec_next_index++;
115 if (retval > _ldp_fec_next_index) {
116 _ldp_fec_next_index = 1;
118 return retval;
121 ldp_fec *ldp_fec_create(ldp_global *g, mpls_fec *f)
123 ldp_fec *fec = (ldp_fec *) mpls_malloc(sizeof(ldp_fec));
125 if (fec != NULL) {
126 memset(fec, 0, sizeof(ldp_fec));
128 * note: this is init to 1 for a reason!
129 * We're placing it in the global list, so this is our refcnt
130 * when this refcnt gets to zero, it will be removed from the
131 * global list and deleted
134 * TESTING: jleu 6/7/2004, since I want the FEC to be cleaned up
135 * when it no longer has a nexthop, addr, or label, the only things that
136 * should increment the ref are those (nh, addr, label etc), not global
137 * nor inserting into the tree. I also added this comment in
138 * _ldp_global_add_fec()
139 MPLS_REFCNT_INIT(fec, 1);
141 MPLS_REFCNT_INIT(fec, 0);
142 MPLS_LIST_ELEM_INIT(fec, _global);
143 MPLS_LIST_ELEM_INIT(fec, _inlabel);
144 MPLS_LIST_ELEM_INIT(fec, _outlabel);
145 MPLS_LIST_ELEM_INIT(fec, _fec);
146 MPLS_LIST_INIT(&fec->nh_root, ldp_nexthop);
147 MPLS_LIST_INIT(&fec->fs_root_us, ldp_fs);
148 MPLS_LIST_INIT(&fec->fs_root_ds, ldp_fs);
149 fec->index = _ldp_fec_get_next_index();
150 fec->is_route = MPLS_BOOL_FALSE;
151 mpls_fec2ldp_fec(f,fec);
153 _ldp_global_add_fec(g, fec);
154 ldp_fec_insert(g, fec);
156 return fec;
159 void ldp_fec_delete(ldp_global *g, ldp_fec * fec)
161 LDP_PRINT(g->user_data, "fec delete: %08x/%d",
162 fec->info.u.prefix.network.u.ipv4, fec->info.u.prefix.length);
163 ldp_fec_remove(g, &fec->info);
164 _ldp_global_del_fec(g, fec);
165 mpls_free(fec);
168 ldp_fec *ldp_fec_find(ldp_global *g, mpls_fec *fec)
170 ldp_fec *f = NULL;
171 uint32_t key;
172 uint8_t len;
174 switch(fec->type) {
175 case MPLS_FEC_PREFIX:
176 key = fec->u.prefix.network.u.ipv4;
177 len = fec->u.prefix.length;
178 break;
179 case MPLS_FEC_HOST:
180 key = fec->u.host.u.ipv4;
181 len = 32;
182 break;
183 case MPLS_FEC_L2CC:
184 if (ldp_global_find_fec(g, fec, &f) == MPLS_SUCCESS) {
185 return f;
187 return NULL;
188 default:
189 MPLS_ASSERT(0);
192 LDP_PRINT(g->user_data, "ldp_fec_find: 0x%08x/%d\n", key, len);
193 if (mpls_tree_get(g->fec_tree, key, len, (void **)&f) != MPLS_SUCCESS) {
194 return NULL;
196 return f;
199 ldp_fec *ldp_fec_find2(ldp_global *g, mpls_fec *fec)
201 ldp_fec *f = NULL;
202 f = ldp_fec_find(g, fec);
203 if (!f) {
204 f = ldp_fec_create(g, fec);
206 return f;
209 ldp_nexthop *ldp_fec_nexthop_find(ldp_fec *f, mpls_nexthop *n)
211 ldp_nexthop *nh = NULL;
213 MPLS_ASSERT(f && n);
215 nh = MPLS_LIST_HEAD(&f->nh_root);
216 while (nh) {
217 if (!mpls_nexthop_compare(&nh->info, n)) {
218 return nh;
220 nh = MPLS_LIST_NEXT(&f->nh_root, nh, _fec);
223 return NULL;
226 mpls_return_enum ldp_fec_find_nexthop_index(ldp_fec *f, int index,
227 ldp_nexthop **n)
229 ldp_nexthop *nh = NULL;
231 MPLS_ASSERT(f);
233 if (index > 0) {
235 /* because we sort our inserts by index, this lets us know
236 if we've "walked" past the end of the list */
238 nh = MPLS_LIST_TAIL(&f->nh_root);
239 if (!nh || nh->index < index) {
240 *n = NULL;
241 return MPLS_END_OF_LIST;
244 nh = MPLS_LIST_HEAD(&f->nh_root);
245 do {
246 if (nh->index == index) {
247 *n = nh;
248 return MPLS_SUCCESS;
250 } while((nh = MPLS_LIST_NEXT(&f->nh_root, nh, _fec)));
252 *n = NULL;
253 return MPLS_FAILURE;
256 mpls_return_enum ldp_fec_add_nexthop(ldp_global *g, ldp_fec * f,
257 ldp_nexthop * nh)
259 MPLS_ASSERT(f && nh);
261 LDP_ENTER(g->user_data, "ldp_fec_add_nexthop");
263 MPLS_REFCNT_HOLD(nh);
264 MPLS_LIST_ADD_HEAD(&f->nh_root, nh, _fec, ldp_nexthop);
266 ldp_nexthop_add_fec(nh, f);
268 LDP_EXIT(g->user_data, "ldp_fec_add_nexthop: success");
269 return MPLS_SUCCESS;
271 ldp_fec_add_nexthop_error:
273 ldp_fec_del_nexthop(g, f, nh);
274 LDP_EXIT(g->user_data, "ldp_fec_add_nexthop: fail");
275 return MPLS_FATAL;
278 void ldp_fec_del_nexthop(ldp_global *g, ldp_fec * f, ldp_nexthop *nh)
280 MPLS_ASSERT(f && nh);
282 MPLS_LIST_REMOVE(&f->nh_root, nh, _fec);
283 ldp_nexthop_del_fec(g, nh);
284 MPLS_REFCNT_RELEASE2(g, nh, ldp_nexthop_delete);
287 mpls_return_enum ldp_fec_process_add(ldp_global * g, ldp_fec * f,
288 ldp_nexthop *nh, ldp_session *nh_session)
290 ldp_session *peer = NULL;
291 ldp_attr *ds_attr = NULL;
292 ldp_attr *us_attr = NULL;
293 mpls_bool egress;
294 ldp_outlabel *out;
296 LDP_ENTER(g->user_data, "ldp_fec_process_add");
299 * find the info about the next hop for this FEC
301 if (!nh_session) {
302 nh_session = ldp_get_next_hop_session_for_fec2(f, nh);
305 if (nh_session) {
306 /* check if we've received and retainted a label from nh_session for f */
307 ds_attr = ldp_attr_find_downstream_state2(g, nh_session, f,
308 LDP_LSP_STATE_MAP_RECV);
309 if (ds_attr && !ds_attr->outlabel) {
310 /* if so, and we have not installed that label, install it now */
311 out = ldp_outlabel_create_complete(g, nh_session, ds_attr, nh);
312 if (!out) {
313 return MPLS_FAILURE;
315 ds_attr->outlabel = out;
319 /* are we willing to egress for this FEC */
320 egress = mpls_policy_egress_check(g->user_data, &f->info, &nh->info);
323 * for every peer except the nh hop peer, check to see if we need to
324 * send a mapping
326 peer = MPLS_LIST_HEAD(&g->session);
327 while (peer != NULL) { /* FEC.1 */
328 if ((peer->state != LDP_STATE_OPERATIONAL) ||
329 (nh_session && peer->index == nh_session->index)) {
330 goto next_peer;
332 /* have I already sent a mapping for FEC to peer */
333 if ((us_attr = ldp_attr_find_upstream_state2(g, peer, f,
334 LDP_LSP_STATE_MAP_SENT))) {
335 /* yep, don't send another */
336 if (ds_attr) {
337 /* we need to XC the label we sent with the one we already have */
338 if (ldp_inlabel_add_outlabel(g, us_attr->inlabel,
339 ds_attr->outlabel) != MPLS_SUCCESS) {
340 return MPLS_FAILURE;
343 goto next_peer;
346 /* we need to send a label */
347 if (peer->oper_distribution_mode == LDP_DISTRIBUTION_UNSOLICITED) {
348 if (g->lsp_control_mode == LDP_CONTROL_INDEPENDENT) {
349 /* if we have received a pending request, give that as input to
350 * ldp_label_mapping_with_xc */
351 us_attr =
352 ldp_attr_find_upstream_state2(g, peer, f, LDP_LSP_STATE_REQ_RECV);
354 /* FEC.1.DUI3,4 */
355 if (ldp_label_mapping_with_xc(g, peer, f, &us_attr, ds_attr) !=
356 MPLS_SUCCESS) {
357 if (us_attr->in_tree) {
358 ldp_attr_remove_complete(g, us_attr, MPLS_BOOL_FALSE);
360 goto next_peer;
362 } else {
364 *LDP_CONTROL_ORDERED
367 if (ds_attr || (egress == MPLS_BOOL_TRUE)) { /* FEC.1.DUO2 */
368 /* FEC.1.DUO3-4 */
371 * ldp_label_mapping_with_xc will create a us_attr if
372 * one does not exist yet
374 if (ldp_label_mapping_with_xc(g, peer, f, &us_attr, ds_attr) !=
375 MPLS_SUCCESS) {
376 return MPLS_FAILURE;
381 next_peer:
382 peer = MPLS_LIST_NEXT(&g->session, peer, _global);
385 if (ds_attr) { /* FEC.2 */
386 /* does this send an updated mapping? */
387 if (ldp_label_mapping_process(g, nh_session, NULL, NULL, ds_attr, f) ==
388 MPLS_FAILURE) { /* FEC.5 */
389 return MPLS_FAILURE;
391 return MPLS_SUCCESS;
395 * LDP_DISTRIBUTION_ONDEMAND
397 /* FEC.3 */
398 if (nh_session &&
399 nh_session->oper_distribution_mode == LDP_DISTRIBUTION_ONDEMAND) {
400 /* assume we're always "request when needed" */
401 ds_attr = NULL;
402 if (ldp_label_request_for_xc(g, nh_session, &f->info, NULL, &ds_attr) ==
403 MPLS_FAILURE) { /* FEC.4 */
404 return MPLS_FAILURE;
408 LDP_EXIT(g->user_data, "ldp_fec_process_add");
410 return MPLS_SUCCESS; /* FEC.6 */
413 mpls_return_enum ldp_fec_process_change(ldp_global * g, ldp_fec * f,
414 ldp_nexthop *nh, ldp_nexthop *nh_old, ldp_session *nh_session_old) {
415 ldp_session *peer = NULL;
416 ldp_attr *us_attr = NULL;
417 ldp_attr *ds_attr = NULL;
418 ldp_session *nh_session = NULL;
420 LDP_ENTER(g->user_data,
421 "ldp_fec_process_change: fec %p nh %p nh_old %p nh_session_old %p",
422 f, nh, nh_old, nh_session_old);
424 if (!nh_session_old) {
425 nh_session_old = ldp_session_for_nexthop(nh_old);
429 * NH 1-5 decide if we need to release an existing mapping
431 ds_attr = ldp_attr_find_downstream_state2(g, nh_session_old, f,
432 LDP_LSP_STATE_MAP_RECV);
433 if (!ds_attr) { /* NH.1 */
434 goto Detect_Change_Fec_Next_Hop_6;
437 if (ds_attr->ingress == MPLS_BOOL_TRUE) {
439 #if MPLS_USE_LSR
440 lsr_ftn ftn;
441 ftn.outsegment_index = ds_attr->outlabel->info.handle;
442 memcpy(&ftn.fec, &f->info, sizeof(mpls_fec));
443 lsr_cfg_ftn_set2(g->lsr_handle, &ftn, LSR_CFG_DEL);
444 #else
445 mpls_mpls_fec2out_del(g->mpls_handle, &f->info, &ds_attr->outlabel->info);
446 #endif
447 ds_attr->ingress = MPLS_BOOL_FALSE;
448 ds_attr->outlabel->merge_count--;
451 if (g->label_retention_mode == LDP_RETENTION_LIBERAL) { /* NH.3 */
452 ldp_attr *us_temp;
453 us_attr = MPLS_LIST_HEAD(&ds_attr->us_attr_root);
454 while (us_attr) {
455 /* need to walk the list in such a way as not to
456 * "pull the rug out from under me self"
458 us_temp = MPLS_LIST_NEXT(&ds_attr->us_attr_root, us_attr, _ds_attr);
459 if (us_attr->state == LDP_LSP_STATE_MAP_SENT) {
460 ldp_inlabel_del_outlabel(g, us_attr->inlabel); /* NH.2 */
461 ldp_attr_del_us2ds(g, us_attr, ds_attr);
463 us_attr = us_temp;
465 goto Detect_Change_Fec_Next_Hop_6;
468 ldp_label_release_send(g, nh_session_old, ds_attr, LDP_NOTIF_NONE); /* NH.4 */
469 ldp_attr_remove_complete(g, ds_attr, MPLS_BOOL_FALSE); /* NH.2,5 */
471 Detect_Change_Fec_Next_Hop_6:
474 * NH 6-9 decides is we need to send a label request abort
476 ds_attr = ldp_attr_find_downstream_state2(g, nh_session_old, f,
477 LDP_LSP_STATE_REQ_SENT);
478 if (ds_attr) { /* NH.6 */
479 if (g->label_retention_mode != LDP_RETENTION_CONSERVATIVE) { /* NH.7 */
480 /* NH.8,9 */
481 if (ldp_label_abort_send(g, nh_session_old, ds_attr) != MPLS_SUCCESS) {
482 return MPLS_FAILURE;
488 * NH 10-12 decides if we can use a mapping from our database
490 if ((!nh) || (!(nh_session = ldp_get_next_hop_session_for_fec2(f,nh)))) {
491 goto Detect_Change_Fec_Next_Hop_16;
494 ds_attr = ldp_attr_find_downstream_state2(g, nh_session, f,
495 LDP_LSP_STATE_MAP_RECV);
496 if (!ds_attr) { /* NH.11 */
497 goto Detect_Change_Fec_Next_Hop_13;
500 if (ldp_label_mapping_process(g, nh_session, NULL, NULL, ds_attr, f) !=
501 MPLS_SUCCESS) { /* NH.12 */
502 return MPLS_FAILURE;
504 goto Detect_Change_Fec_Next_Hop_20;
506 Detect_Change_Fec_Next_Hop_13:
509 * NH 13-15 decides if we need to make a label request
511 if (nh_session->oper_distribution_mode == LDP_DISTRIBUTION_ONDEMAND &&
512 g->label_retention_mode == LDP_RETENTION_CONSERVATIVE) {
513 /* NH.14-15 */
514 if (ldp_label_request_for_xc(g, nh_session, &f->info, NULL, &ds_attr) !=
515 MPLS_SUCCESS) {
516 return MPLS_FAILURE;
519 goto Detect_Change_Fec_Next_Hop_20;
521 Detect_Change_Fec_Next_Hop_16:
523 peer = MPLS_LIST_HEAD(&g->session);
524 while (peer) {
525 if (peer->state == LDP_STATE_OPERATIONAL) {
526 us_attr = ldp_attr_find_upstream_state2(g, peer, f,
527 LDP_LSP_STATE_MAP_SENT);
528 if (us_attr) { /* NH.17 */
529 mpls_return_enum retval;
530 MPLS_REFCNT_HOLD(us_attr);
531 ldp_attr_remove_complete(g, us_attr, MPLS_BOOL_FALSE);
532 retval = ldp_label_withdraw_send(g, peer, us_attr, LDP_NOTIF_NONE);
533 MPLS_REFCNT_RELEASE2(g, us_attr, ldp_attr_delete);
534 if (retval != MPLS_SUCCESS) { /* NH.18 */
536 * I think it is best to exit out immediatly with an error
537 * and let the caller do something about it, the only real
538 * option is a global reset of LDP
540 return MPLS_FAILURE;
544 peer = MPLS_LIST_NEXT(&g->session, peer, _global);
547 Detect_Change_Fec_Next_Hop_20:
549 LDP_EXIT(g->user_data, "ldp_fec_process_change");
551 return MPLS_SUCCESS;
554 void mpls_fec2ldp_fec(mpls_fec * a, ldp_fec * b)
556 memcpy(&b->info, a, sizeof(mpls_fec));
559 void mpls_fec2fec_tlv(mpls_fec * lf, mplsLdpFecTlv_t * tlv, int i)
561 tlv->fecElArray[i].addressEl.addressFam = 1;
563 switch (lf->type) {
564 case MPLS_FEC_PREFIX:
565 tlv->fecElArray[i].addressEl.type = MPLS_PREFIX_FEC;
566 tlv->fecElArray[i].addressEl.preLen = lf->u.prefix.length;
567 tlv->fecElArray[i].addressEl.address = lf->u.prefix.network.u.ipv4;
568 tlv->fecElemTypes[i] = MPLS_PREFIX_FEC;
569 break;
570 case MPLS_FEC_HOST:
571 tlv->fecElArray[i].addressEl.type = MPLS_HOSTADR_FEC;
572 tlv->fecElArray[i].addressEl.preLen = MPLS_IPv4LEN;
573 tlv->fecElArray[i].addressEl.address = lf->u.host.u.ipv4;
574 tlv->fecElemTypes[i] = MPLS_HOSTADR_FEC;
575 break;
576 default:
577 MPLS_ASSERT(0);
581 void fec_tlv2mpls_fec(mplsLdpFecTlv_t * tlv, int i, mpls_fec * lf) {
582 switch (tlv->fecElemTypes[i]) {
583 case MPLS_PREFIX_FEC:
584 lf->type = MPLS_FEC_PREFIX;
585 lf->u.prefix.length = tlv->fecElArray[i].addressEl.preLen;
586 lf->u.prefix.network.u.ipv4 = tlv->fecElArray[i].addressEl.address;
587 lf->u.prefix.network.type = MPLS_FAMILY_IPV4;
588 break;
589 case MPLS_HOSTADR_FEC:
590 lf->type = MPLS_FEC_HOST;
591 lf->u.host.u.ipv4 = tlv->fecElArray[i].addressEl.address;
592 lf->u.host.type = MPLS_FAMILY_IPV4;
593 break;
594 default:
595 MPLS_ASSERT(0);
599 mpls_bool ldp_fec_empty(ldp_fec *fec)
601 if (MPLS_LIST_EMPTY(&fec->fs_root_us) &&
602 MPLS_LIST_EMPTY(&fec->nh_root) &&
603 MPLS_LIST_EMPTY(&fec->fs_root_ds)) {
604 return MPLS_BOOL_TRUE;
606 return MPLS_BOOL_FALSE;