3 * Copyright (C) James R. Leu 2000
6 * This software is covered under the LGPL, for more
7 * info check out http://www.gnu.org/copyleft/lgpl.html
10 #include "ldp_struct.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"
34 #include "mpls_mpls_impl.h"
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
;
45 MPLS_ASSERT(g
&& fec
);
46 LDP_ENTER(g
->user_data
, "ldp_fec_insert");
48 switch(fec
->info
.type
) {
50 key
= fec
->info
.u
.prefix
.network
.u
.ipv4
;
51 len
= fec
->info
.u
.prefix
.length
;
54 key
= fec
->info
.u
.host
.u
.ipv4
;
58 /* they had better insert it into the global list */
59 LDP_EXIT(g
->user_data
, "ldp_fec_insert: l2cc");
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");
72 LDP_EXIT(g
->user_data
, "ldp_fec_insert");
76 static void ldp_fec_remove(ldp_global
*g
, mpls_fec
*fec
)
82 MPLS_ASSERT(g
&& fec
);
83 LDP_ENTER(g
->user_data
, "ldp_fec_remove");
87 key
= fec
->u
.prefix
.network
.u
.ipv4
;
88 len
= fec
->u
.prefix
.length
;
91 key
= fec
->u
.host
.u
.ipv4
;
95 /* they had better remove it from the global list */
96 LDP_EXIT(g
->user_data
, "ldp_fec_remove");
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
);
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;
121 ldp_fec
*ldp_fec_create(ldp_global
*g
, mpls_fec
*f
)
123 ldp_fec
*fec
= (ldp_fec
*) mpls_malloc(sizeof(ldp_fec
));
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
);
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
);
168 ldp_fec
*ldp_fec_find(ldp_global
*g
, mpls_fec
*fec
)
175 case MPLS_FEC_PREFIX
:
176 key
= fec
->u
.prefix
.network
.u
.ipv4
;
177 len
= fec
->u
.prefix
.length
;
180 key
= fec
->u
.host
.u
.ipv4
;
184 if (ldp_global_find_fec(g
, fec
, &f
) == MPLS_SUCCESS
) {
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
) {
199 ldp_fec
*ldp_fec_find2(ldp_global
*g
, mpls_fec
*fec
)
202 f
= ldp_fec_find(g
, fec
);
204 f
= ldp_fec_create(g
, fec
);
209 ldp_nexthop
*ldp_fec_nexthop_find(ldp_fec
*f
, mpls_nexthop
*n
)
211 ldp_nexthop
*nh
= NULL
;
215 nh
= MPLS_LIST_HEAD(&f
->nh_root
);
217 if (!mpls_nexthop_compare(&nh
->info
, n
)) {
220 nh
= MPLS_LIST_NEXT(&f
->nh_root
, nh
, _fec
);
226 mpls_return_enum
ldp_fec_find_nexthop_index(ldp_fec
*f
, int index
,
229 ldp_nexthop
*nh
= NULL
;
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
) {
241 return MPLS_END_OF_LIST
;
244 nh
= MPLS_LIST_HEAD(&f
->nh_root
);
246 if (nh
->index
== index
) {
250 } while((nh
= MPLS_LIST_NEXT(&f
->nh_root
, nh
, _fec
)));
256 mpls_return_enum
ldp_fec_add_nexthop(ldp_global
*g
, ldp_fec
* f
,
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");
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");
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
;
296 LDP_ENTER(g
->user_data
, "ldp_fec_process_add");
299 * find the info about the next hop for this FEC
302 nh_session
= ldp_get_next_hop_session_for_fec2(f
, nh
);
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
);
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
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
)) {
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 */
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
) {
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 */
352 ldp_attr_find_upstream_state2(g
, peer
, f
, LDP_LSP_STATE_REQ_RECV
);
355 if (ldp_label_mapping_with_xc(g
, peer
, f
, &us_attr
, ds_attr
) !=
357 if (us_attr
->in_tree
) {
358 ldp_attr_remove_complete(g
, us_attr
, MPLS_BOOL_FALSE
);
367 if (ds_attr
|| (egress
== MPLS_BOOL_TRUE
)) { /* FEC.1.DUO2 */
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
) !=
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 */
395 * LDP_DISTRIBUTION_ONDEMAND
399 nh_session
->oper_distribution_mode
== LDP_DISTRIBUTION_ONDEMAND
) {
400 /* assume we're always "request when needed" */
402 if (ldp_label_request_for_xc(g
, nh_session
, &f
->info
, NULL
, &ds_attr
) ==
403 MPLS_FAILURE
) { /* FEC.4 */
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
) {
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
);
445 mpls_mpls_fec2out_del(g
->mpls_handle
, &f
->info
, &ds_attr
->outlabel
->info
);
447 ds_attr
->ingress
= MPLS_BOOL_FALSE
;
448 ds_attr
->outlabel
->merge_count
--;
451 if (g
->label_retention_mode
== LDP_RETENTION_LIBERAL
) { /* NH.3 */
453 us_attr
= MPLS_LIST_HEAD(&ds_attr
->us_attr_root
);
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
);
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 */
481 if (ldp_label_abort_send(g
, nh_session_old
, ds_attr
) != MPLS_SUCCESS
) {
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 */
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
) {
514 if (ldp_label_request_for_xc(g
, nh_session
, &f
->info
, NULL
, &ds_attr
) !=
519 goto Detect_Change_Fec_Next_Hop_20
;
521 Detect_Change_Fec_Next_Hop_16
:
523 peer
= MPLS_LIST_HEAD(&g
->session
);
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
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");
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;
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
;
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
;
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
;
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
;
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
;