Don't allow the user to delete a fec that still has nexthops
[mpls-ldp-portable.git] / ldp / ldp_fec.c
blob162332fe5458b45808bd04a130ed1d3f45ee312c
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 void walk_tree(ldp_global *g) {
40 LDP_ENTER(g->user_data, "walk_tree");
41 route_dump_node(g->fec_tree);
42 LDP_EXIT(g->user_data, "walk_tree");
45 static mpls_return_enum ldp_fec_insert(ldp_global *g, ldp_fec * fec)
47 mpls_return_enum retval = MPLS_SUCCESS;
48 uint32_t key;
49 uint8_t len;
51 MPLS_ASSERT(g && fec);
52 LDP_ENTER(g->user_data, "ldp_fec_insert");
54 switch(fec->info.type) {
55 case MPLS_FEC_PREFIX:
56 key = fec->info.u.prefix.network.u.ipv4;
57 len = fec->info.u.prefix.length;
58 break;
59 case MPLS_FEC_HOST:
60 key = fec->info.u.host.u.ipv4;
61 len = 32;
62 break;
63 case MPLS_FEC_L2CC:
64 /* they had better insert it into the global list */
65 LDP_EXIT(g->user_data, "ldp_fec_insert: l2cc");
66 return MPLS_SUCCESS;
67 default:
68 MPLS_ASSERT(0);
71 LDP_PRINT(g->user_data, "ldp_fec_insert: 0x%08x/%d\n", key, len);
73 if (mpls_tree_insert(g->fec_tree, key, len, (void *)fec) != MPLS_SUCCESS) {
74 LDP_PRINT(g->user_data, "ldp_fec_insert: error adding fec\n");
75 retval = MPLS_FATAL;
78 LDP_EXIT(g->user_data, "ldp_fec_insert");
79 return retval;
82 static void ldp_fec_remove(ldp_global *g, mpls_fec *fec)
84 ldp_fec *f = NULL;
85 uint32_t key;
86 uint8_t len;
88 MPLS_ASSERT(g && fec);
89 LDP_ENTER(g->user_data, "ldp_fec_remove");
91 switch(fec->type) {
92 case MPLS_FEC_PREFIX:
93 key = fec->u.prefix.network.u.ipv4;
94 len = fec->u.prefix.length;
95 break;
96 case MPLS_FEC_HOST:
97 key = fec->u.host.u.ipv4;
98 len = 32;
99 break;
100 case MPLS_FEC_L2CC:
101 /* they had better remove it from the global list */
102 LDP_EXIT(g->user_data, "ldp_fec_remove");
103 return;
104 default:
105 MPLS_ASSERT(0);
108 LDP_PRINT(g->user_data, "ldp_fec_remove: 0x%08x/%d\n", key, len);
109 mpls_tree_remove(g->fec_tree, key, len, (void **)&f);
111 MPLS_ASSERT(f);
113 LDP_EXIT(g->user_data, "ldp_fec_remove");
116 static uint32_t _ldp_fec_get_next_index()
118 uint32_t retval = _ldp_fec_next_index;
120 _ldp_fec_next_index++;
121 if (retval > _ldp_fec_next_index) {
122 _ldp_fec_next_index = 1;
124 return retval;
127 ldp_fec *ldp_fec_create(ldp_global *g, mpls_fec *f)
129 ldp_fec *fec = (ldp_fec *) mpls_malloc(sizeof(ldp_fec));
131 if (fec != NULL) {
132 memset(fec, 0, sizeof(ldp_fec));
134 * note: this is init to 1 for a reason!
135 * We're placing it in the global list, so this is our refcnt
136 * when this refcnt gets to zero, it will be removed from the
137 * global list and deleted
140 * TESTING: jleu 6/7/2004, since I want the FEC to be cleaned up
141 * when it no longer has a nexthop, addr, or label, the only things that
142 * should increment the ref are those (nh, addr, label etc), not global
143 * nor inserting into the tree. I also added this comment in
144 * _ldp_global_add_fec()
145 MPLS_REFCNT_INIT(fec, 1);
147 MPLS_REFCNT_INIT(fec, 0);
148 MPLS_LIST_ELEM_INIT(fec, _global);
149 MPLS_LIST_ELEM_INIT(fec, _inlabel);
150 MPLS_LIST_ELEM_INIT(fec, _outlabel);
151 MPLS_LIST_ELEM_INIT(fec, _fec);
152 MPLS_LIST_INIT(&fec->nh_root, ldp_nexthop);
153 MPLS_LIST_INIT(&fec->fs_root_us, ldp_fs);
154 MPLS_LIST_INIT(&fec->fs_root_ds, ldp_fs);
155 fec->index = _ldp_fec_get_next_index();
156 mpls_fec2ldp_fec(f,fec);
158 _ldp_global_add_fec(g, fec);
159 ldp_fec_insert(g, fec);
161 return fec;
164 void ldp_fec_delete(ldp_global *g, ldp_fec * fec)
166 fprintf(stderr, "fec delete: %08x/%d\n", fec->info.u.prefix.network.u.ipv4,
167 fec->info.u.prefix.length);
168 ldp_fec_remove(g, &fec->info);
169 _ldp_global_del_fec(g, fec);
170 mpls_free(fec);
173 ldp_fec *ldp_fec_find(ldp_global *g, mpls_fec *fec)
175 ldp_fec *f = NULL;
176 uint32_t key;
177 uint8_t len;
179 switch(fec->type) {
180 case MPLS_FEC_PREFIX:
181 key = fec->u.prefix.network.u.ipv4;
182 len = fec->u.prefix.length;
183 break;
184 case MPLS_FEC_HOST:
185 key = fec->u.host.u.ipv4;
186 len = 32;
187 break;
188 case MPLS_FEC_L2CC:
189 if (ldp_global_find_fec(g, fec, &f) == MPLS_SUCCESS) {
190 return f;
192 return NULL;
193 default:
194 MPLS_ASSERT(0);
197 LDP_PRINT(g->user_data, "ldp_fec_find: 0x%08x/%d\n", key, len);
198 if (mpls_tree_get(g->fec_tree, key, len, (void **)&f) != MPLS_SUCCESS) {
199 return NULL;
201 return f;
204 ldp_fec *ldp_fec_find2(ldp_global *g, mpls_fec *fec)
206 ldp_fec *f = NULL;
207 f = ldp_fec_find(g, fec);
208 if (!f) {
209 f = ldp_fec_create(g, fec);
211 return f;
214 ldp_nexthop *ldp_fec_nexthop_find(ldp_fec *f, mpls_nexthop *n)
216 ldp_nexthop *nh = NULL;
218 MPLS_ASSERT(f && n);
220 nh = MPLS_LIST_HEAD(&f->nh_root);
221 while (nh) {
222 if (!mpls_nexthop_compare(&nh->info, n)) {
223 return nh;
225 nh = MPLS_LIST_NEXT(&f->nh_root, nh, _fec);
228 return NULL;
231 mpls_return_enum ldp_fec_find_nexthop_index(ldp_fec *f, int index,
232 ldp_nexthop **n)
234 ldp_nexthop *nh = NULL;
236 MPLS_ASSERT(f);
238 if (index > 0) {
240 /* because we sort our inserts by index, this lets us know
241 if we've "walked" past the end of the list */
243 nh = MPLS_LIST_TAIL(&f->nh_root);
244 if (!nh || nh->index < index) {
245 *n = NULL;
246 return MPLS_END_OF_LIST;
249 nh = MPLS_LIST_HEAD(&f->nh_root);
250 do {
251 if (nh->index == index) {
252 *n = nh;
253 return MPLS_SUCCESS;
255 } while((nh = MPLS_LIST_NEXT(&f->nh_root, nh, _fec)));
257 *n = NULL;
258 return MPLS_FAILURE;
261 mpls_return_enum ldp_fec_add_nexthop(ldp_global *g, ldp_fec * f,
262 ldp_nexthop * nh)
264 MPLS_ASSERT(f && nh);
266 LDP_ENTER(g->user_data, "ldp_fec_add_nexthop");
268 MPLS_REFCNT_HOLD(nh);
269 MPLS_LIST_ADD_HEAD(&f->nh_root, nh, _fec, ldp_nexthop);
271 ldp_nexthop_add_fec(nh, f);
273 LDP_EXIT(g->user_data, "ldp_fec_add_nexthop: success");
274 return MPLS_SUCCESS;
276 ldp_fec_add_nexthop_error:
278 ldp_fec_del_nexthop(g, f, nh);
279 LDP_EXIT(g->user_data, "ldp_fec_add_nexthop: fail");
280 return MPLS_FATAL;
283 void ldp_fec_del_nexthop(ldp_global *g, ldp_fec * f, ldp_nexthop *nh)
285 MPLS_ASSERT(f && nh);
287 MPLS_LIST_REMOVE(&f->nh_root, nh, _fec);
288 ldp_nexthop_del_fec(g, nh);
290 MPLS_REFCNT_RELEASE2(g, nh, ldp_nexthop_delete);
293 mpls_return_enum ldp_fec_process_add(ldp_global * g, ldp_fec * f,
294 ldp_nexthop *nh, ldp_session *nh_session)
296 ldp_session *peer = NULL;
297 ldp_attr *ds_attr = NULL;
298 ldp_attr *us_attr = NULL;
299 mpls_bool egress = MPLS_BOOL_FALSE;
300 ldp_outlabel *out;
302 LDP_ENTER(g->user_data, "ldp_fec_process_add");
305 * find the info about the next hop for this FEC
307 if (!nh_session) {
308 nh_session = ldp_session_for_nexthop(nh);
311 if (nh_session) {
312 ds_attr = ldp_attr_find_downstream_state2(g, nh_session, f,
313 LDP_LSP_STATE_MAP_RECV);
314 if (ds_attr && !ds_attr->outlabel) {
315 out = ldp_outlabel_create_complete(g, nh_session, ds_attr, nh);
316 if (!out) {
317 return MPLS_FAILURE;
319 ds_attr->outlabel = out;
324 * for every peer except the nh hop peer, check to see if we need to
325 * send a mapping
327 peer = MPLS_LIST_HEAD(&g->session);
328 while (peer != NULL) { /* FEC.1 */
329 if ((peer->state != LDP_STATE_OPERATIONAL) ||
330 (nh_session && peer->index == nh_session->index)) {
331 goto next_peer;
333 /* have I already sent a mapping for FEC to peer */
334 if ((us_attr = ldp_attr_find_upstream_state2(g, peer, f,
335 LDP_LSP_STATE_MAP_SENT))) {
336 /* yep, don't send another */
337 if (ds_attr) {
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 if (peer->oper_distribution_mode == LDP_DISTRIBUTION_UNSOLICITED) {
347 if (g->lsp_control_mode == LDP_CONTROL_INDEPENDENT) {
348 us_attr =
349 ldp_attr_find_upstream_state2(g, peer, f, LDP_LSP_STATE_REQ_RECV);
351 /* FEC.1.DUI3,4 */
352 if (ldp_label_mapping_with_xc(g, peer, f, &us_attr, ds_attr) !=
353 MPLS_SUCCESS) {
354 if (!us_attr->in_tree) {
355 ldp_attr_remove_complete(g, us_attr, MPLS_BOOL_FALSE);
357 goto next_peer;
359 } else {
361 *LDP_CONTROL_ORDERED
364 if (ds_attr || egress == MPLS_BOOL_TRUE) { /* FEC.1.DUO2 */
365 if (!(us_attr = ldp_attr_create(&f->info))) {
366 return MPLS_FAILURE;
368 /* FEC.1.DUO3-4 */
369 if ((egress == MPLS_BOOL_TRUE) && (mpls_policy_egress_check(
370 g->user_data, &f->info, &nh->info) == MPLS_BOOL_TRUE)) {
371 goto next_peer;
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 if (ldp_label_mapping_process(g, nh_session, NULL, NULL, ds_attr, f) ==
387 MPLS_FAILURE) { /* FEC.5 */
388 return MPLS_FAILURE;
390 return MPLS_SUCCESS;
394 * LDP_DISTRIBUTION_ONDEMAND
396 /* FEC.3 */
397 if (nh_session &&
398 nh_session->oper_distribution_mode == LDP_DISTRIBUTION_ONDEMAND) {
399 /* assume we're always "request when needed" */
400 ds_attr = NULL;
401 if (ldp_label_request_for_xc(g, nh_session, &f->info, NULL, &ds_attr) ==
402 MPLS_FAILURE) { /* FEC.4 */
403 return MPLS_FAILURE;
407 LDP_EXIT(g->user_data, "ldp_fec_process_add");
409 return MPLS_SUCCESS; /* FEC.6 */
412 mpls_return_enum ldp_fec_process_change(ldp_global * g, ldp_fec * f,
413 ldp_nexthop *nh, ldp_nexthop *nh_old, ldp_session *nh_session_old) {
414 ldp_session *peer = NULL;
415 ldp_attr *us_attr = NULL;
416 ldp_attr *ds_attr = NULL;
417 ldp_session *nh_session = NULL;
419 LDP_ENTER(g->user_data,
420 "ldp_fec_process_change: fec %p nh %p nh_old %p nh_session_old %p",
421 f, nh, nh_old, nh_session_old);
423 if (!nh_session_old) {
424 nh_session_old = ldp_session_for_nexthop(nh_old);
428 * NH 1-5 decide if we need to release an existing mapping
430 ds_attr = ldp_attr_find_downstream_state2(g, nh_session_old, f,
431 LDP_LSP_STATE_MAP_RECV);
432 if (!ds_attr) { /* NH.1 */
433 goto Detect_Change_Fec_Next_Hop_6;
436 if (ds_attr->ingress == MPLS_BOOL_TRUE) {
438 #if MPLS_USE_LSR
439 lsr_ftn ftn;
440 ftn.outsegment_index = ds_attr->outlabel->info.handle;
441 memcpy(&ftn.fec, &f->info, sizeof(mpls_fec));
442 lsr_cfg_ftn_set2(g->lsr_handle, &ftn, LSR_CFG_DEL);
443 #else
444 mpls_mpls_fec2out_del(g->mpls_handle, &f->info, &ds_attr->outlabel->info);
445 #endif
446 ds_attr->ingress = MPLS_BOOL_FALSE;
447 ds_attr->outlabel->merge_count--;
450 if (g->label_retention_mode == LDP_RETENTION_LIBERAL) { /* NH.3 */
451 ldp_attr *us_temp;
452 us_attr = MPLS_LIST_HEAD(&ds_attr->us_attr_root);
453 while (us_attr) {
454 /* need to walk the list in such a way as not to
455 * "pull the rug out from under me self"
457 us_temp = MPLS_LIST_NEXT(&ds_attr->us_attr_root, us_attr, _ds_attr);
458 if (us_attr->state == LDP_LSP_STATE_MAP_SENT) {
459 ldp_inlabel_del_outlabel(g, us_attr->inlabel); /* NH.2 */
460 ldp_attr_del_us2ds(us_attr, ds_attr);
462 us_attr = us_temp;
464 goto Detect_Change_Fec_Next_Hop_6;
467 ldp_label_release_send(g, nh_session_old, ds_attr, LDP_NOTIF_NONE); /* NH.4 */
468 ldp_attr_remove_complete(g, ds_attr, MPLS_BOOL_FALSE); /* NH.2,5 */
470 Detect_Change_Fec_Next_Hop_6:
473 * NH 6-9 decides is we need to send a label request abort
475 ds_attr = ldp_attr_find_downstream_state2(g, nh_session_old, f,
476 LDP_LSP_STATE_REQ_SENT);
477 if (ds_attr) { /* NH.6 */
478 if (g->label_retention_mode != LDP_RETENTION_CONSERVATIVE) { /* NH.7 */
479 /* NH.8,9 */
480 if (ldp_label_abort_send(g, nh_session_old, ds_attr) != MPLS_SUCCESS) {
481 return MPLS_FAILURE;
487 * NH 10-12 decides if we can use a mapping from our database
489 if ((!nh) || (!(nh_session = ldp_get_next_hop_session_for_fec2(f,nh)))) {
490 goto Detect_Change_Fec_Next_Hop_16;
493 ds_attr = ldp_attr_find_downstream_state2(g, nh_session, f,
494 LDP_LSP_STATE_MAP_RECV);
495 if (!ds_attr) { /* NH.11 */
496 goto Detect_Change_Fec_Next_Hop_13;
499 if (ldp_label_mapping_process(g, nh_session, NULL, NULL, ds_attr, f) !=
500 MPLS_SUCCESS) { /* NH.12 */
501 return MPLS_FAILURE;
503 goto Detect_Change_Fec_Next_Hop_20;
505 Detect_Change_Fec_Next_Hop_13:
508 * NH 13-15 decides if we need to make a label request
510 if (nh_session->oper_distribution_mode == LDP_DISTRIBUTION_ONDEMAND &&
511 g->label_retention_mode == LDP_RETENTION_CONSERVATIVE) {
512 /* NH.14-15 */
513 if (ldp_label_request_for_xc(g, nh_session, &f->info, NULL, &ds_attr) !=
514 MPLS_SUCCESS) {
515 return MPLS_FAILURE;
518 goto Detect_Change_Fec_Next_Hop_20;
520 Detect_Change_Fec_Next_Hop_16:
522 peer = MPLS_LIST_HEAD(&g->session);
523 while (peer) {
524 if (peer->state == LDP_STATE_OPERATIONAL) {
525 us_attr = ldp_attr_find_upstream_state2(g, peer, f,
526 LDP_LSP_STATE_MAP_SENT);
527 if (us_attr) { /* NH.17 */
528 if (ldp_label_withdraw_send(g, peer, us_attr, LDP_NOTIF_NONE) !=
529 MPLS_SUCCESS) { /* NH.18 */
530 ldp_attr_remove_complete(g, us_attr, MPLS_BOOL_FALSE);
531 return MPLS_FAILURE;
535 peer = MPLS_LIST_NEXT(&g->session, peer, _global);
538 Detect_Change_Fec_Next_Hop_20:
540 LDP_EXIT(g->user_data, "ldp_fec_process_change");
542 return MPLS_SUCCESS;
545 void mpls_fec2ldp_fec(mpls_fec * a, ldp_fec * b)
547 memcpy(&b->info, a, sizeof(mpls_fec));
550 void mpls_fec2fec_tlv(mpls_fec * lf, mplsLdpFecTlv_t * tlv, int i)
552 tlv->fecElArray[i].addressEl.addressFam = 1;
554 switch (lf->type) {
555 case MPLS_FEC_PREFIX:
556 tlv->fecElArray[i].addressEl.type = MPLS_PREFIX_FEC;
557 tlv->fecElArray[i].addressEl.preLen = lf->u.prefix.length;
558 tlv->fecElArray[i].addressEl.address = lf->u.prefix.network.u.ipv4;
559 tlv->fecElemTypes[i] = MPLS_PREFIX_FEC;
560 break;
561 case MPLS_FEC_HOST:
562 tlv->fecElArray[i].addressEl.type = MPLS_HOSTADR_FEC;
563 tlv->fecElArray[i].addressEl.preLen = MPLS_IPv4LEN;
564 tlv->fecElArray[i].addressEl.address = lf->u.host.u.ipv4;
565 tlv->fecElemTypes[i] = MPLS_HOSTADR_FEC;
566 break;
567 default:
568 MPLS_ASSERT(0);
572 void fec_tlv2mpls_fec(mplsLdpFecTlv_t * tlv, int i, mpls_fec * lf) {
573 switch (tlv->fecElemTypes[i]) {
574 case MPLS_PREFIX_FEC:
575 lf->type = MPLS_FEC_PREFIX;
576 lf->u.prefix.length = tlv->fecElArray[i].addressEl.preLen;
577 lf->u.prefix.network.u.ipv4 = tlv->fecElArray[i].addressEl.address;
578 lf->u.prefix.network.type = MPLS_FAMILY_IPV4;
579 break;
580 case MPLS_HOSTADR_FEC:
581 lf->type = MPLS_FEC_HOST;
582 lf->u.host.u.ipv4 = tlv->fecElArray[i].addressEl.address;
583 lf->u.host.type = MPLS_FAMILY_IPV4;
584 break;
585 default:
586 MPLS_ASSERT(0);
590 mpls_bool ldp_fec_empty(ldp_fec *fec)
592 if (MPLS_LIST_EMPTY(&fec->fs_root_us) &&
593 MPLS_LIST_EMPTY(&fec->nh_root) &&
594 MPLS_LIST_EMPTY(&fec->fs_root_ds)) {
595 return MPLS_BOOL_TRUE;
597 return MPLS_BOOL_FALSE;