/* * Copyright (C) James R. Leu 2000 * jleu@mindspring.com * * This software is covered under the LGPL, for more * info check out http://www.gnu.org/copyleft/lgpl.html */ #include "ldp_struct.h" #include "ldp_label_mapping.h" #include "ldp_attr.h" #include "ldp_fec.h" #include "ldp_refcnt.h" #include "ldp_global.h" #include "ldp_inlabel.h" #include "ldp_outlabel.h" #include "ldp_session.h" #include "ldp_mm_impl.h" #include "ldp_mpls_impl.h" #include "ldp_tree_impl.h" #include "ldp_trace_impl.h" static ldp_fec* _ldp_attr_get_fec2(ldp_global* g,ldp_fec* f,ldp_bool flag); static ldp_fec* _ldp_attr_get_fec(ldp_global* g,ldp_attr* a,ldp_bool flag); static ldp_fs* _ldp_fec_add_fs_ds(ldp_fec* fec,ldp_session* s); static ldp_fs* _ldp_fec_add_fs_us(ldp_fec* fec,ldp_session* s); static ldp_fs* _ldp_fec_find_fs_us(ldp_fec* fec,ldp_session* s,ldp_bool flag); static ldp_fs* _ldp_fec_find_fs_ds(ldp_fec* fec,ldp_session* s,ldp_bool flag); static void _ldp_fec_del_fs_us(ldp_fec* fec,ldp_fs* fs); static void _ldp_fec_del_fs_ds(ldp_fec* fec,ldp_fs* fs); static ldp_fs* _ldp_fs_create(ldp_session* s); static void _ldp_fs_delete(ldp_fs* fs); static ldp_attr* _ldp_fs_find_attr(ldp_fs* fs,ldp_attr* a); static ldp_return_enum _ldp_fs_add_attr(ldp_fs* fs,ldp_attr* a); static ldp_bool _ldp_fs_del_attr(ldp_fs* fs,ldp_attr* a); static uint32_t _ldp_attr_get_next_index(); static uint32_t _ldp_attr_next_index = 1; ldp_attr* ldp_attr_find_downstream_state(ldp_global* g,ldp_session* s, ldp_fec* f,ldp_lsp_state state) { ldp_attr_list* ds_list = ldp_attr_find_downstream_all(g,s,f); if(ds_list != NULL) { ldp_attr* ds_attr = LDP_LIST_HEAD(ds_list); while(ds_attr != NULL) { if(ds_attr->state == state) { return ds_attr; } ds_attr = LDP_LIST_NEXT(ds_list,ds_attr,_fs); } } return NULL; } ldp_attr* ldp_attr_find_upstream_state(ldp_global* g,ldp_session* s, ldp_fec* f,ldp_lsp_state state) { ldp_attr_list* us_list = ldp_attr_find_upstream_all(g,s,f); if(us_list != NULL) { ldp_attr* us_attr = LDP_LIST_HEAD(us_list); while(us_attr != NULL) { if(us_attr->state == state) { return us_attr; } us_attr = LDP_LIST_NEXT(us_list,us_attr,_fs); } } return NULL; } void ldp_attr_remove_complete(ldp_global* g,ldp_attr* attr) { ldp_session* session = attr->session; ldp_outlabel* out = NULL; ldp_inlabel* in = NULL; ldp_fec fec; int i; switch(attr->state) { case LDP_LSP_STATE_MAP_RECV: if(attr->installed == LDP_TRUE) { out = attr->outlabel; LDP_ASSERT(out != NULL); while((in = LDP_LIST_HEAD(&out->inlabel_root)) != NULL) { ldp_mpls_in2out_del(g->mpls_handle,in,out); ldp_inlabel_del_outlabel(in); } if(out->merge_count > 0) { for(i = 0;i < attr->fecTlv.numberFecElements;i++) { fec_tlv2ldp_fec(&attr->fecTlv,i,&fec); out->merge_count--; ldp_mpls_fec2out_del(g->mpls_handle,&fec,out); } } LDP_ASSERT(out->merge_count == 0); ldp_mpls_outlabel_del(g->mpls_handle,out); ldp_attr_del_outlabel(attr); ldp_session_del_outlabel(session,out); _ldp_global_del_outlabel(g,out); } ldp_attr_delete_downstream(g,session,attr); break; case LDP_LSP_STATE_MAP_SENT: in = attr->inlabel; out = in->outlabel; if(out != NULL) { if(in->reuse_count == 1) { ldp_mpls_in2out_del(g->mpls_handle,in,out); ldp_inlabel_del_outlabel(in); } } ldp_attr_del_inlabel(attr); ldp_attr_delete_upstream(g,session,attr); ldp_session_del_inlabel(session,in); if(in->reuse_count == 0) { ldp_mpls_inlabel_del(g->mpls_handle,in); _ldp_global_del_inlabel(g,in); } break; case LDP_LSP_STATE_REQ_RECV: case LDP_LSP_STATE_ABORT_SENT: case LDP_LSP_STATE_WITH_SENT: case LDP_LSP_STATE_NO_LABEL_RESOURCE_SENT: ldp_attr_delete_upstream(g,session,attr); break; case LDP_LSP_STATE_REQ_SENT: case LDP_LSP_STATE_WITH_RECV: case LDP_LSP_STATE_NO_LABEL_RESOURCE_RECV: ldp_attr_delete_downstream(g,session,attr); break; } } ldp_attr* ldp_attr_create(ldp_fec* fec) { ldp_attr* a = (ldp_attr*)ldp_malloc(sizeof(ldp_attr)); if(a != NULL) { memset(a,0,sizeof(ldp_attr)); LDP_LIST_ELEM_INIT(a,_session); LDP_LIST_ELEM_INIT(a,_global); LDP_LIST_ELEM_INIT(a,_fs); LDP_REFCNT_INIT(a,0); a->index = _ldp_attr_get_next_index(); a->in_tree = LDP_FALSE; a->installed = LDP_FALSE; a->filtered = LDP_FALSE; if(fec != NULL) { ldp_fec2fec_tlv(fec,&a->fecTlv,0); a->fecTlv.numberFecElements = 1; a->fecTlvExists = 1; } } return a; } void ldp_attr_delete(ldp_attr* a) { // LDP_PRINT(g->user_data,"attr delete\n"); LDP_ASSERT(a->in_tree == LDP_FALSE); ldp_free(a); } void ldp_attr2ldp_attr(ldp_attr* a,ldp_attr* b,uint32_t flag) { if(a->fecTlvExists && flag & LDP_ATTR_FEC) { memcpy(&b->fecTlv,&a->fecTlv,sizeof(mplsLdpFecTlv_t)); b->fecTlvExists = 1; } if(a->genLblTlvExists && flag & LDP_ATTR_LABEL) { memcpy(&b->genLblTlv,&a->genLblTlv,sizeof(mplsLdpGenLblTlv_t)); b->genLblTlvExists = 1; } else if(a->atmLblTlvExists && flag & LDP_ATTR_LABEL) { memcpy(&b->atmLblTlv,&a->atmLblTlv,sizeof(mplsLdpAtmLblTlv_t)); b->atmLblTlvExists = 1; } else if(a->frLblTlvExists && flag & LDP_ATTR_LABEL) { memcpy(&b->frLblTlv,&a->frLblTlv,sizeof(mplsLdpFrLblTlv_t)); b->frLblTlvExists = 1; } if(a->hopCountTlvExists && flag & LDP_ATTR_HOPCOUNT) { memcpy(&b->hopCountTlv,&a->hopCountTlv,sizeof(mplsLdpHopTlv_t)); b->hopCountTlvExists = 1; } if(a->pathVecTlvExists && flag & LDP_ATTR_PATH) { memcpy(&b->pathVecTlv,&a->pathVecTlv,sizeof(mplsLdpPathTlv_t)); b->pathVecTlvExists = 1; } if(a->lblMsgIdTlvExists && flag & LDP_ATTR_MSGID) { memcpy(&b->lblMsgIdTlv,&a->lblMsgIdTlv,sizeof(mplsLdpLblMsgIdTlv_t)); b->lblMsgIdTlvExists = 1; } if(a->lspidTlvExists && flag & LDP_ATTR_LSPID) { memcpy(&b->lspidTlv,&a->lspidTlv,sizeof(mplsLdpLspIdTlv_t)); b->lspidTlvExists = 1; } if(a->trafficTlvExists && flag & LDP_ATTR_TRAFFIC) { memcpy(&b->trafficTlv,&a->trafficTlv,sizeof(mplsLdpTrafficTlv_t)); b->trafficTlvExists = 1; } } ldp_return_enum ldp_attr_add_inlabel(ldp_attr* a,ldp_inlabel* i) { if(a && i) { LDP_REFCNT_HOLD(i); a->inlabel = i; _ldp_inlabel_add_attr(i,a); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_attr_del_inlabel(ldp_attr* a) { if(a && a->inlabel) { _ldp_inlabel_del_attr(a->inlabel,a); LDP_REFCNT_RELEASE(a->inlabel,ldp_inlabel_delete); a->inlabel = NULL; return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_attr_add_outlabel(ldp_attr* a,ldp_outlabel* o) { if(a && o) { LDP_REFCNT_HOLD(o); a->outlabel = o; _ldp_outlabel_add_attr(o,a); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_attr_del_outlabel(ldp_attr* a) { if(a && a->outlabel) { _ldp_outlabel_del_attr(a->outlabel); LDP_REFCNT_RELEASE(a->outlabel,ldp_outlabel_delete); a->outlabel = NULL; return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_attr_add_session(ldp_attr* a,ldp_session* s) { if(a && s) { LDP_REFCNT_HOLD(s); a->session = s; _ldp_session_add_attr(s,a); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_attr_del_session(ldp_attr* a) { if(a && a->session) { _ldp_session_del_attr(a->session,a); LDP_REFCNT_RELEASE(a->session,ldp_session_delete); a->session = NULL; return LDP_SUCCESS; } return LDP_FAILURE; } ldp_bool ldp_attr_is_equal(ldp_attr* a,ldp_attr* b,uint32_t flag) { if(flag & LDP_ATTR_LABEL) { if(a->genLblTlvExists && b->genLblTlvExists) { if(a->genLblTlv.label != b->genLblTlv.label) { return LDP_FALSE; } } else if(a->atmLblTlvExists && b->atmLblTlvExists) { if(a->atmLblTlv.flags.flags.vpi != b->atmLblTlv.flags.flags.vpi || a->atmLblTlv.vci != b->atmLblTlv.vci) { return LDP_FALSE; } } else if(a->frLblTlvExists && b->frLblTlvExists) { if(a->frLblTlv.flags.flags.len != b->frLblTlv.flags.flags.len || a->frLblTlv.flags.flags.dlci != b->frLblTlv.flags.flags.dlci) { return LDP_FALSE; } } else { return LDP_FALSE; } } if(flag & LDP_ATTR_HOPCOUNT) { if(a->hopCountTlvExists && b->hopCountTlvExists) { if(a->hopCountTlv.hcValue != b->hopCountTlv.hcValue) { return LDP_FALSE; } } else { return LDP_FALSE; } } if(flag & LDP_ATTR_PATH) { int i; if(a->pathVecTlvExists && b->pathVecTlvExists) { for(i = 0;i < MPLS_MAXHOPSNUMBER;i++) { if(a->pathVecTlv.lsrId[i] != b->pathVecTlv.lsrId[i]) { return LDP_FALSE; } } } else { return LDP_FALSE; } } if(flag & LDP_ATTR_FEC) { int i; if(a->fecTlvExists && b->fecTlvExists) { if(a->fecTlv.numberFecElements != b->fecTlv.numberFecElements) { return LDP_FALSE; } for(i = 0;i < a->fecTlv.numberFecElements;i++) { if(a->fecTlv.fecElemTypes[i] != b->fecTlv.fecElemTypes[i]) { return LDP_FALSE; } switch(a->fecTlv.fecElemTypes[i]) { case MPLS_CRLSP_FEC: case MPLS_WC_FEC: /* nothing of interest to compare */ break; case MPLS_PREFIX_FEC: case MPLS_HOSTADR_FEC: if(a->fecTlv.fecElArray[i].addressEl.addressFam != b->fecTlv.fecElArray[i].addressEl.addressFam || a->fecTlv.fecElArray[i].addressEl.preLen != b->fecTlv.fecElArray[i].addressEl.preLen || a->fecTlv.fecElArray[i].addressEl.address != b->fecTlv.fecElArray[i].addressEl.address) { return LDP_FALSE; } break; default: LDP_ASSERT(0); } } } else { return LDP_FALSE; } } if(flag & LDP_ATTR_MSGID) { if(a->lblMsgIdTlvExists && b->lblMsgIdTlvExists) { if(a->lblMsgIdTlv.msgId != b->lblMsgIdTlv.msgId) { return LDP_FALSE; } } else { return LDP_FALSE; } } if(flag & LDP_ATTR_LSPID) { if(a->lspidTlvExists && b->lspidTlvExists) { if(a->lspidTlv.localCrlspId != b->lspidTlv.localCrlspId || a->lspidTlv.routerId != b->lspidTlv.routerId) { return LDP_FALSE; } } else { return LDP_FALSE; } } if(flag & LDP_ATTR_TRAFFIC) { } return LDP_TRUE; } ldp_return_enum ldp_attr_insert_upstream(ldp_global* g,ldp_session* s, ldp_attr* a) { ldp_fec* fnode = NULL; ldp_fs* fs = NULL; ldp_return_enum retval; LDP_ASSERT(a->in_tree == LDP_FALSE); if((fnode = _ldp_attr_get_fec(g,a,LDP_TRUE)) == NULL) { /* we couldn't get/add a node from/to the tree! */ return LDP_FAILURE; } /* find the upstream fs for this session */ if((fs = _ldp_fec_find_fs_us(fnode,s,LDP_TRUE)) == NULL) { /* this session isn't in the list and cannot be added */ return LDP_FAILURE; } ldp_attr_add_session(a,s); retval = _ldp_fs_add_attr(fs,a); _ldp_global_add_attr(g,a); a->in_tree = LDP_TRUE; return retval; } ldp_return_enum ldp_attr_insert_downstream(ldp_global* g,ldp_session* s, ldp_attr* a) { ldp_fec* fnode = NULL; ldp_fs* fs = NULL; ldp_return_enum retval; LDP_ASSERT(a->in_tree == LDP_FALSE); if((fnode = _ldp_attr_get_fec(g,a,LDP_TRUE)) == NULL) { /* we couldn't get/add a node from/to the tree! */ return LDP_FAILURE; } /* find the downstream fs for this session */ if((fs = _ldp_fec_find_fs_ds(fnode,s,LDP_TRUE)) == NULL) { /* this session isn't in the list and cannot be added */ return LDP_FAILURE; } ldp_attr_add_session(a,s); retval = _ldp_fs_add_attr(fs,a); _ldp_global_add_attr(g,a); a->in_tree = LDP_TRUE; return retval; } ldp_attr* ldp_attr_find_upstream(ldp_global* g,ldp_session* s,ldp_fec* f) { ldp_attr_list* l = ldp_attr_find_upstream_all(g,s,f); if(l == NULL) { return NULL; } return LDP_LIST_HEAD(l); } ldp_attr_list* ldp_attr_find_upstream_all(ldp_global* g,ldp_session* s, ldp_fec* f) { ldp_fec* fnode = NULL; ldp_fs* fs = NULL; if((fnode = _ldp_attr_get_fec2(g,f,LDP_FALSE)) == NULL) { /* we couldn't get the node from the tree! */ return NULL; } /* find the upstream fs for this session */ if((fs = _ldp_fec_find_fs_us(fnode,s,LDP_FALSE)) == NULL) { /* this session isn't in the list */ return NULL; } return &fs->attr_root; } ldp_attr* ldp_attr_find_downstream(ldp_global* g,ldp_session* s,ldp_fec* f) { ldp_attr_list* l = ldp_attr_find_downstream_all(g,s,f); if(l == NULL) { return NULL; } return LDP_LIST_HEAD(l); } ldp_attr_list* ldp_attr_find_downstream_all(ldp_global* g,ldp_session* s, ldp_fec* f) { ldp_fec* fnode = NULL; ldp_fs* fs = NULL; if((fnode = _ldp_attr_get_fec2(g,f,LDP_FALSE)) == NULL) { /* we couldn't get the node from the tree! */ return NULL; } if(s != NULL) { /* find the downstream fs for this session */ if((fs = _ldp_fec_find_fs_ds(fnode,s,LDP_FALSE)) == NULL) { /* this session isn't in the list */ return NULL; } } else { /* there has to be a better way to do this */ ldp_attr *attr = NULL; LDP_PRINT(g->user_data,"Gratuitous search!!\n"); fs = LDP_LIST_HEAD(&fnode->fs_root_ds); while(fs != NULL) { attr = LDP_LIST_HEAD(&fs->attr_root); while(attr != NULL) { if(attr->state == LDP_LSP_STATE_MAP_RECV) { return &fs->attr_root; } attr = LDP_LIST_NEXT(&fs->attr_root,attr,_fs); } fs = LDP_LIST_NEXT(&fnode->fs_root_ds,fs,_fec); } } return &fs->attr_root; } void ldp_attr_delete_upstream(ldp_global* g,ldp_session* s,ldp_attr* a) { ldp_fec* fnode = NULL; ldp_fs* fs = NULL; if((fnode = _ldp_attr_get_fec(g,a,LDP_FALSE)) == NULL) { /* we couldn't get the node from the tree! */ return; } /* find the upstream fs for this session */ if((fs = _ldp_fec_find_fs_us(fnode,s,LDP_FALSE)) == NULL) { /* this session isn't in the list */ return; } ldp_attr_del_session(a); if(_ldp_fs_del_attr(fs,a) == LDP_TRUE) { _ldp_fec_del_fs_us(fnode,fs); } a->in_tree = LDP_FALSE; _ldp_global_del_attr(g,a); } void ldp_attr_delete_downstream(ldp_global* g,ldp_session* s, ldp_attr* a) { ldp_fec* fnode = NULL; ldp_fs* fs = NULL; if((fnode = _ldp_attr_get_fec(g,a,LDP_FALSE)) == NULL) { /* we couldn't get the node from the tree! */ return; } /* find the downstream fs for this session */ if((fs = _ldp_fec_find_fs_ds(fnode,s,LDP_FALSE)) == NULL) { /* this session isn't in the list */ return; } ldp_attr_del_session(a); if(_ldp_fs_del_attr(fs,a) == LDP_TRUE) { _ldp_fec_del_fs_ds(fnode,fs); } a->in_tree = LDP_FALSE; _ldp_global_del_attr(g,a); } void ldp_attr2ldp_label(ldp_attr* a,ldp_label* l) { if(a->genLblTlvExists) { l->type = LDP_LABEL_GEN; l->u.gen = a->genLblTlv.label; } else if(a->atmLblTlvExists) { l->type = LDP_LABEL_ATM; l->u.atm.vpi = a->atmLblTlv.flags.flags.vpi; l->u.atm.vci = a->atmLblTlv.vci; } else if(a->frLblTlvExists) { l->type = LDP_LABEL_FR; l->u.fr.len = a->frLblTlv.flags.flags.len; l->u.fr.dlci = a->frLblTlv.flags.flags.dlci; } else { LDP_ASSERT(0); } } void ldp_label2ldp_attr(ldp_label* l,ldp_attr* a) { switch(l->type) { case LDP_LABEL_GEN: a->genLblTlvExists = 1; a->atmLblTlvExists = 0; a->frLblTlvExists = 0; a->genLblTlv.label = l->u.gen; break; case LDP_LABEL_ATM: a->genLblTlvExists = 0; a->atmLblTlvExists = 1; a->frLblTlvExists = 0; a->atmLblTlv.flags.flags.vpi = l->u.atm.vpi; a->atmLblTlv.vci = l->u.atm.vci; case LDP_LABEL_FR: a->genLblTlvExists = 0; a->atmLblTlvExists = 0; a->frLblTlvExists = 1; a->frLblTlv.flags.flags.len = l->u.fr.len; a->frLblTlv.flags.flags.dlci = l->u.fr.dlci; default: LDP_ASSERT(0); } } static ldp_fec* _ldp_attr_get_fec2(ldp_global* g,ldp_fec* f,ldp_bool flag) { ldp_fec* fnode = NULL; if(ldp_tree_get(g->fec_tree,f->prefix.u.ipv4,f->prefix_len, (void**)&fnode) == LDP_FAILURE || fnode == NULL) { if(flag == LDP_FALSE) { return NULL; } /* this FEC doesn't exist in the tree yet, create one ... */ fnode = ldp_fec_create(); ldp_fec2ldp_fec(f,fnode); LDP_REFCNT_HOLD(fnode); /* ... add it to the tree ... */ if(ldp_tree_insert(g->fec_tree,f->prefix.u.ipv4,f->prefix_len, (void*)fnode) == LDP_FAILURE) { /* insert failed, by releasing our refcnt it will be deleted */ LDP_REFCNT_RELEASE(fnode,ldp_fec_delete); return NULL; } } return fnode; } static ldp_fec* _ldp_attr_get_fec(ldp_global* g,ldp_attr* a,ldp_bool flag) { ldp_fec fec; /* get FEC from attr */ fec_tlv2ldp_fec(&a->fecTlv,0,&fec); return _ldp_attr_get_fec2(g,&fec,flag); } static ldp_fs* _ldp_fec_add_fs_ds(ldp_fec* fec,ldp_session* s) { ldp_fs* fs = _ldp_fec_find_fs_ds(fec,s,LDP_FALSE); if(fs == NULL) { fs = _ldp_fs_create(s); if(fs == NULL) { return NULL; } LDP_LIST_ADD_HEAD(&fec->fs_root_ds,fs,_fec,ldp_fs); } return fs; } static ldp_fs* _ldp_fec_add_fs_us(ldp_fec* fec,ldp_session* s) { ldp_fs* fs = _ldp_fec_find_fs_us(fec,s,LDP_FALSE); if(fs == NULL) { fs = _ldp_fs_create(s); if(fs == NULL) { return NULL; } LDP_LIST_ADD_HEAD(&fec->fs_root_us,fs,_fec,ldp_fs); } return fs; } static ldp_fs* _ldp_fec_find_fs_us(ldp_fec* fec,ldp_session* s,ldp_bool flag) { ldp_fs* fs = LDP_LIST_HEAD(&fec->fs_root_us); while(fs != NULL) { if(fs->session->index == s->index) { return fs; } fs = LDP_LIST_NEXT(&fec->fs_root_us,fs,_fec); } if(flag == LDP_FALSE) { return NULL; } return _ldp_fec_add_fs_us(fec,s); } static ldp_fs* _ldp_fec_find_fs_ds(ldp_fec* fec,ldp_session* s,ldp_bool flag) { ldp_fs* fs = LDP_LIST_HEAD(&fec->fs_root_ds); while(fs != NULL) { if(fs->session->index == s->index) { return fs; } fs = LDP_LIST_NEXT(&fec->fs_root_ds,fs,_fec); } if(flag == LDP_FALSE) { return NULL; } return _ldp_fec_add_fs_ds(fec,s); } static void _ldp_fec_del_fs_us(ldp_fec* fec,ldp_fs* fs) { if(fs == NULL) { return; } LDP_LIST_REMOVE(&fec->fs_root_us,fs,_fec); _ldp_fs_delete(fs); } static void _ldp_fec_del_fs_ds(ldp_fec* fec,ldp_fs* fs) { if(fs == NULL) { return; } LDP_LIST_REMOVE(&fec->fs_root_ds,fs,_fec); _ldp_fs_delete(fs); } static ldp_fs* _ldp_fs_create(ldp_session* s) { ldp_fs* fs = (ldp_fs*)ldp_malloc(sizeof(ldp_fs)); if(fs != NULL) { memset(fs,0,sizeof(ldp_fs)); LDP_LIST_INIT(&fs->attr_root,ldp_attr); LDP_LIST_ELEM_INIT(fs,_fec); if(s != NULL) { LDP_REFCNT_HOLD(s); fs->session = s; } } return fs; } static void _ldp_fs_delete(ldp_fs* fs) { // LDP_PRINT(g->user_data,"fs delete\n"); if(fs->session != NULL) { LDP_REFCNT_RELEASE(fs->session,ldp_session_delete); } ldp_free(fs); } static ldp_attr* _ldp_fs_find_attr(ldp_fs* fs,ldp_attr* a) { ldp_attr* ptr = LDP_LIST_HEAD(&fs->attr_root); while(ptr != NULL) { if(ldp_attr_is_equal(a,ptr,LDP_ATTR_LABEL|LDP_ATTR_FEC) == LDP_TRUE) { return ptr; } ptr = LDP_LIST_NEXT(&fs->attr_root,ptr,_fs); } return NULL; } static ldp_return_enum _ldp_fs_add_attr(ldp_fs* fs,ldp_attr* a) { ldp_attr *ptr = _ldp_fs_find_attr(fs,a); LDP_ASSERT(ptr == NULL); LDP_REFCNT_HOLD(a); LDP_LIST_ADD_HEAD(&fs->attr_root,a,_fs,ldp_attr); return LDP_SUCCESS; } static ldp_bool _ldp_fs_del_attr(ldp_fs* fs,ldp_attr* a) { ldp_attr *ptr = _ldp_fs_find_attr(fs,a); if(ptr != NULL) { LDP_LIST_REMOVE(&fs->attr_root,ptr,_fs); LDP_REFCNT_RELEASE(ptr,ldp_attr_delete); } if(LDP_LIST_HEAD(&fs->attr_root) == NULL) return LDP_TRUE; return LDP_FALSE; } static uint32_t _ldp_attr_get_next_index() { uint32_t retval = _ldp_attr_next_index; _ldp_attr_next_index++; if(retval > _ldp_attr_next_index++) { _ldp_attr_next_index = 1; } return retval; }