/* * 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 #include "ldp_struct.h" #include "ldp_assert.h" #include "ldp_outlabel.h" #include "ldp_session.h" #include "ldp_entity.h" #include "ldp_inlabel.h" #include "ldp_outlabel.h" #include "ldp_refcnt.h" #include "ldp_addr.h" #include "ldp_attr.h" #include "ldp_adj.h" #include "ldp_msg.h" #include "ldp_inet_addr.h" #include "ldp_global.h" #include "ldp_state_machine.h" #include "ldp_label_rel_with.h" #include "ldp_label_request.h" #include "ldp_label_mapping.h" #include "ldp_mm_impl.h" #include "ldp_timer_impl.h" #include "ldp_socket_impl.h" #include "ldp_mpls_impl.h" #include "ldp_trace_impl.h" #include "ldp_ifmgr_impl.h" static uint32_t _ldp_session_next_index = 1; ldp_session* ldp_session_create() { ldp_session* s = (ldp_session*)ldp_malloc(sizeof(ldp_session)); if(s) { memset(s,0,sizeof(ldp_session)); LDP_REFCNT_INIT(s,0); LDP_LIST_ELEM_INIT(s,_global); LDP_LIST_ELEM_INIT(s,_entity); LDP_LIST_INIT(&s->outlabel_root,ldp_outlabel); LDP_LIST_INIT(&s->inlabel_root,ldp_inlabel_elem); LDP_LIST_INIT(&s->addr_root,ldp_addr_elem); LDP_LIST_INIT(&s->attr_root,ldp_attr); s->on_global = LDP_FALSE; s->index = _ldp_session_get_next_index(); } return s; } ldp_session *ldp_session_create_active(ldp_global *g,ldp_adj *a) { uint32_t socket = 0; ldp_session* s = NULL; ldp_inet_addr *addr = NULL; uint8_t ret; LDP_ASSERT(g != NULL && a != NULL && a->entity != NULL); LDP_ENTER(g->user_data,"ldp_session_create_active"); s = ldp_session_create(); if(s == NULL) { goto ldp_session_create_active_end; } if(a->remote_transport_address.protocol != 0) { addr = &a->remote_transport_address; } else { addr = &a->remote_source_address; } memcpy(&s->remote_dest.addr,addr,sizeof(ldp_inet_addr)); s->remote_dest.port = a->entity->remote_tcp_port; socket = ldp_socket_create_tcp(g->socket_handle); s->socket = socket; if(ldp_socket_handle_verify(g->socket_handle,socket) == LDP_FALSE) { goto ldp_session_create_active_end; } if(ldp_socket_options(g->socket_handle,socket,LDP_SOCKET_NONBLOCK) == LDP_FAILURE) { goto ldp_session_create_active_end; } s->state = LDP_STATE_NON_EXIST; LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL,LDP_TRACE_FLAG_DEBUG, "ldp_session_create_active: (%d) changed to NON_EXIST\n",s->index); ldp_adj_add_session(a,s); ret = ldp_socket_tcp_connect(g->socket_handle,socket,&s->remote_dest); switch(ret) { case LDP_NON_BLOCKING: ldp_socket_writelist_add(g->socket_handle,socket,(void*)s, LDP_TCP_CONNECT); break; case LDP_SUCCESS: if(ldp_state_machine(g,s,a,a->entity,LDP_EVENT_CONNECT,NULL,NULL) == LDP_FAILURE) { goto ldp_session_create_active_end; } break; default: goto ldp_session_create_active_end; break; } LDP_EXIT(g->user_data,"ldp_session_create_active"); return s; ldp_session_create_active_end: /* only get here if we have a failure along this path */ if(s) { if(a->session == s) ldp_adj_del_session(a); ldp_session_delete(s); } LDP_EXIT(g->user_data,"ldp_session_create_active"); return NULL; } ldp_session *ldp_session_create_passive(ldp_global *g,ldp_adj *a, ldp_socket_handle socket) { ldp_session* s = ldp_session_create(); LDP_ASSERT(g != NULL); LDP_ENTER(g->user_data,"ldp_session_create_passive"); if(s == NULL) return NULL; if(a != NULL) { ldp_adj_add_session(a,s); } s->socket = socket; s->state = LDP_STATE_NON_EXIST; LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL,LDP_TRACE_FLAG_DEBUG, "ldp_session_create_passive: (%d) changed to NON_EXIST\n",s->index); LDP_EXIT(g->user_data,"ldp_session_create_passive"); return s; } void ldp_session_delete(ldp_session* s) { // LDP_PRINT(g->user_data,"session delete\n"); LDP_REFCNT_ASSERT(s,0); ldp_free(s); } void ldp_session_startup(ldp_global *g,ldp_session* s) { ldp_entity* e = NULL; ldp_adj* a = NULL; ldp_if iff; ldp_if_handle handle; void (*callback)(ldp_timer_handle timer,void* extra,ldp_cfg_handle g); LDP_ASSERT(s != NULL && g != NULL && (e = s->entity) != NULL && (a = s->adj) != NULL); LDP_ENTER(g->user_data,"ldp_session_startup"); /* when we make it to operational, get rid of any backoff timers */ ldp_adj_backoff_stop(g,a); s->state = LDP_STATE_OPERATIONAL; LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL,LDP_TRACE_FLAG_DEBUG, "ldp_session_startup: (%d) changed to OPERATIONAL\n",s->index); _ldp_global_add_session(g,s); /* * if configured to distribute addr messages walk the if table * and send an addr message for each */ if(g->send_address_messages) { handle = ldp_ifmgr_getfirst(g->ifmgr_handle); while(ldp_if_handle_verify(g->ifmgr_handle,handle) == LDP_TRUE) { iff.handle = handle; if(ldp_ifmgr_get_address(g->ifmgr_handle,handle, &iff.local_source_address,NULL,NULL) == LDP_SUCCESS) { ldp_addr_send(g,s,&iff); } handle = ldp_ifmgr_getnext(g->ifmgr_handle,handle); } } /* depending on the mode, grab a pointer to the correct callback */ switch(s->oper_distribution_mode) { case LDP_DISTRIBUTION_ONDEMAND: callback = ldp_label_request_initial_callback; break; case LDP_DISTRIBUTION_UNSOLICITED: callback = ldp_label_mapping_initial_callback; break; default: LDP_ASSERT(0); } /* * create a timer which will go about "chunking" the initial * set of requests or mappings */ LDP_REFCNT_HOLD(s); s->initial_distribution_timer = ldp_timer_create(g->timer_handle, LDP_SEC,LDP_REQUEST_CHUNK,(void*)s,g,callback); if(ldp_timer_handle_verify(g->timer_handle, s->initial_distribution_timer) == LDP_FALSE) { LDP_REFCNT_RELEASE(s,ldp_session_delete); LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL,LDP_TRACE_FLAG_DEBUG, "ldp_session_startup: initial distrib error(%d)\n",s->index); /* timer error, we might as well shutdown the session, it's usless */ ldp_session_shutdown(g,s,1); return; } ldp_timer_start(g->timer_handle,s->initial_distribution_timer, LDP_ONESHOT); LDP_EXIT(g->user_data,"ldp_session_startup"); return; } void ldp_session_shutdown(ldp_global *g,ldp_session* s,int flag) { ldp_addr_elem* e = NULL; ldp_attr *attr = NULL; ldp_attr *temp_attr = NULL; LDP_ASSERT(s != NULL); LDP_ENTER(g->user_data,"ldp_session_shutdown"); /* * hold a refcount so this session doesn't disappear on us * while cleaning up */ LDP_REFCNT_HOLD(s); s->state = LDP_STATE_NONE; LDP_TRACE_LOG(g->user_data,LDP_TRACE_STATE_ALL,LDP_TRACE_FLAG_DEBUG, "ldp_session_shutdown: (%d) changed to NONE\n",s->index); /* * kill the timers for the session */ if(ldp_timer_handle_verify(g->timer_handle,s->keepalive_recv_timer) == LDP_TRUE) { ldp_timer_stop(g->timer_handle,s->keepalive_recv_timer); ldp_timer_delete(g->timer_handle,s->keepalive_recv_timer); LDP_REFCNT_RELEASE(s,ldp_session_delete); s->keepalive_recv_timer = (ldp_timer_handle)0; } if(ldp_timer_handle_verify(g->timer_handle,s->keepalive_send_timer) == LDP_TRUE) { ldp_timer_stop(g->timer_handle,s->keepalive_send_timer); ldp_timer_delete(g->timer_handle,s->keepalive_send_timer); LDP_REFCNT_RELEASE(s,ldp_session_delete); s->keepalive_send_timer = (ldp_timer_handle)0; } if(ldp_timer_handle_verify(g->timer_handle,s->initial_distribution_timer) == LDP_TRUE) { ldp_timer_stop(g->timer_handle,s->initial_distribution_timer); ldp_timer_delete(g->timer_handle,s->initial_distribution_timer); LDP_REFCNT_RELEASE(s,ldp_session_delete); s->initial_distribution_timer = (ldp_timer_handle)0; } /* * get rid of the socket */ if(ldp_socket_handle_verify(g->socket_handle,s->socket) == LDP_TRUE) { ldp_socket_readlist_del(g->socket_handle,s->socket); ldp_socket_close(g->socket_handle,s->socket); } /* * get rid of out cached keepalive */ if(s->keepalive != NULL) { ldp_msg_delete(s->keepalive); s->keepalive = NULL; } attr = LDP_LIST_HEAD(&g->attr); while(attr != NULL) { if(attr->session && attr->session->index == s->index) { temp_attr = attr; LDP_REFCNT_HOLD(temp_attr); /* * ldp_attr_remove_complete removed everythig associated with the attr. * in and out labels, and cross connects as well */ ldp_attr_remove_complete(g,attr); } else { temp_attr = NULL; } attr = LDP_LIST_NEXT(&g->attr,attr,_global); if(temp_attr) LDP_REFCNT_RELEASE(temp_attr,ldp_attr_delete); } /* * clean up the addrs we created */ e = LDP_LIST_HEAD(&s->addr_root); while(e != NULL) { ldp_session_del_addr(s,e->addr); e = LDP_LIST_HEAD(&s->addr_root); } /* * if we have an adj AND we are shuting down for a protocol reason, start a * backoff timer, so we can try again in the near future */ if(s->adj != NULL) { if(flag && s->adj->role == LDP_ACTIVE) { ldp_adj_backoff_start(g,s->adj); } /* this takes care of the entity reference as well */ ldp_adj_del_session(s->adj); } if(s->on_global == LDP_TRUE) { _ldp_global_del_session(g,s); } /* * it is safe to release this refcnt now, if it is the last one, the * session will be deleted */ LDP_REFCNT_RELEASE(s,ldp_session_delete); LDP_EXIT(g->user_data,"ldp_session_shutdown"); } ldp_return_enum ldp_session_maintain_timer(ldp_global* g,ldp_session* s, int flag) { ldp_return_enum result = LDP_FAILURE; LDP_ENTER(g->user_data,"ldp_session_maintain_timer"); /* * all session keepalive maintainance comes through here (SEND and RECV) */ if(flag == LDP_KEEPALIVE_RECV) { ldp_timer_stop(g->timer_handle,s->keepalive_recv_timer); result = ldp_timer_start(g->timer_handle,s->keepalive_recv_timer, LDP_ONESHOT); } else { ldp_timer_stop(g->timer_handle,s->keepalive_send_timer); result = ldp_timer_start(g->timer_handle,s->keepalive_send_timer, LDP_REOCCURRING); } LDP_EXIT(g->user_data,"ldp_session_maintain_timer"); return result; } ldp_return_enum ldp_session_add_outlabel(ldp_session* s,ldp_outlabel* o) { if(s && o) { LDP_REFCNT_HOLD(o); LDP_LIST_ADD_HEAD(&s->outlabel_root,o,_session,ldp_outlabel); _ldp_outlabel_add_session(o,s); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_session_del_outlabel(ldp_session* s,ldp_outlabel* o) { if(s && o) { LDP_LIST_REMOVE(&s->outlabel_root,o,_session); _ldp_outlabel_del_session(o); LDP_REFCNT_RELEASE(o,ldp_outlabel_delete); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_session_add_inlabel(ldp_session* s,ldp_inlabel* i) { if(s && i) { ldp_inlabel_elem* ie = (ldp_inlabel_elem*)ldp_malloc(sizeof(ldp_inlabel_elem)); if(ie != NULL) { LDP_LIST_ELEM_INIT(ie,_elem); LDP_REFCNT_HOLD(i); ie->inlabel = i; LDP_LIST_ADD_HEAD(&s->inlabel_root,ie,_elem,ldp_inlabel_elem); return _ldp_inlabel_add_session(i,s); } } return LDP_FAILURE; } ldp_return_enum ldp_session_del_inlabel(ldp_session* s,ldp_inlabel* i) { if(s && i) { ldp_inlabel_elem* ie = LDP_LIST_HEAD(&s->inlabel_root); while(ie) { if(ie->inlabel->index == i->index) { LDP_LIST_REMOVE(&s->inlabel_root,ie,_elem); _ldp_inlabel_del_session(ie->inlabel,s); LDP_REFCNT_RELEASE(ie->inlabel,ldp_inlabel_delete) ldp_free(ie); return LDP_SUCCESS; } ie = LDP_LIST_NEXT(&s->inlabel_root,ie,_elem); } } return LDP_FAILURE; } ldp_return_enum _ldp_session_add_attr(ldp_session* s,ldp_attr* a) { if(s && a) { LDP_REFCNT_HOLD(a); LDP_LIST_ADD_HEAD(&s->attr_root,a,_session,ldp_attr); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum _ldp_session_del_attr(ldp_session* s,ldp_attr* a) { if(s && a) { LDP_LIST_REMOVE(&s->attr_root,a,_session); LDP_REFCNT_RELEASE(a,ldp_attr_delete); return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum ldp_session_add_addr(ldp_session* s,ldp_addr* a) { if(s && a) { struct ldp_addr_elem* e = ldp_addr_elem_create(a); if(e) { LDP_LIST_ADD_HEAD(&s->addr_root,e,_elem,ldp_addr_elem); _ldp_addr_add_session(a,s); return LDP_SUCCESS; } } return LDP_FAILURE; } ldp_return_enum ldp_session_del_addr(ldp_session* s,ldp_addr* a) { struct ldp_addr_elem* e = NULL; if(s && a) { e = LDP_LIST_HEAD(&s->addr_root); while(e != NULL) { if((e->addr != NULL) && (ldp_inet_addr_is_equal(&e->addr->address, &a->address) == LDP_TRUE)) { LDP_LIST_REMOVE(&s->addr_root,e,_elem); _ldp_addr_del_session(e->addr,s); ldp_addr_elem_delete(e); return LDP_SUCCESS; } e = LDP_LIST_NEXT(&s->addr_root,e,_elem); } } return LDP_FAILURE; } ldp_return_enum _ldp_session_add_entity(ldp_session* s,ldp_entity* e) { if(s && e) { LDP_REFCNT_HOLD(e); s->entity = e; s->oper_max_pdu = e->max_pdu; s->oper_keepalive = e->keepalive_timer; s->oper_path_vector_limit = e->path_vector_limit; s->oper_distribution_mode = e->label_distribution_mode; s->oper_loop_detection = e->loop_detection_mode; return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum _ldp_session_del_entity(ldp_session* s) { if(s && s->entity) { LDP_REFCNT_RELEASE(s->entity,ldp_entity_delete); s->entity = NULL; return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum _ldp_session_add_adj(ldp_session* s,ldp_adj* a) { if(s && a) { LDP_REFCNT_HOLD(a); s->adj = a; return LDP_SUCCESS; } return LDP_FAILURE; } ldp_return_enum _ldp_session_del_adj(ldp_session* s) { if(s && s->adj) { LDP_REFCNT_RELEASE(s->adj,ldp_adj_delete); s->adj = NULL; return LDP_SUCCESS; } return LDP_FAILURE; } uint32_t _ldp_session_get_next_index() { uint32_t retval = _ldp_session_next_index; _ldp_session_next_index++; if(retval > _ldp_session_next_index++) { _ldp_session_next_index = 1; } return retval; } struct ldp_session_elem* ldp_session_elem_create(ldp_session* session) { struct ldp_session_elem* e = (struct ldp_session_elem*)ldp_malloc(sizeof(struct ldp_session_elem)); if(e != NULL) { LDP_LIST_ELEM_INIT(e,_elem); LDP_REFCNT_HOLD(session); e->session = session; } return e; } ldp_return_enum ldp_session_elem_delete(struct ldp_session_elem* e) { if(e == NULL) return LDP_FAILURE; LDP_REFCNT_RELEASE(e->session,ldp_session_delete); ldp_free(e); return LDP_SUCCESS; }