cdpeventprocessor.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * $Id$
  3. *
  4. * Copyright (C) 2012 Smile Communications, [email protected]
  5. * Copyright (C) 2012 Smile Communications, [email protected]
  6. *
  7. * The initial version of this code was written by Dragos Vingarzan
  8. * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
  9. * Fruanhofer Institute. It was and still is maintained in a separate
  10. * branch of the original SER. We are therefore migrating it to
  11. * Kamailio/SR and look forward to maintaining it from here on out.
  12. * 2011/2012 Smile Communications, Pty. Ltd.
  13. * ported/maintained/improved by
  14. * Jason Penton (jason(dot)penton(at)smilecoms.com and
  15. * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
  16. * effort to add full IMS support to Kamailio/SR using a new and
  17. * improved architecture
  18. *
  19. * NB: Alot of this code was originally part of OpenIMSCore,
  20. * FhG Fokus.
  21. * Copyright (C) 2004-2006 FhG Fokus
  22. * Thanks for great work! This is an effort to
  23. * break apart the various CSCF functions into logically separate
  24. * components. We hope this will drive wider use. We also feel
  25. * that in this way the architecture is more complete and thereby easier
  26. * to manage in the Kamailio/SR environment
  27. *
  28. * This file is part of Kamailio, a free SIP server.
  29. *
  30. * Kamailio is free software; you can redistribute it and/or modify
  31. * it under the terms of the GNU General Public License as published by
  32. * the Free Software Foundation; either version 2 of the License, or
  33. * (at your option) any later version
  34. *
  35. * Kamailio is distributed in the hope that it will be useful,
  36. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  37. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  38. * GNU General Public License for more details.
  39. *
  40. * You should have received a copy of the GNU General Public License
  41. * along with this program; if not, write to the Free Software
  42. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  43. *
  44. */
  45. #include <time.h>
  46. #include "sem.h"
  47. #include "../ims_usrloc_pcscf/usrloc.h"
  48. #include "../dialog_ng/dlg_load.h"
  49. #include "../cdp/session.h"
  50. #include "mod.h"
  51. #include "cdpeventprocessor.h"
  52. #include "rx_str.h"
  53. #include "ims_qos_stats.h"
  54. cdp_cb_event_list_t *cdp_event_list = 0;
  55. extern usrloc_api_t ul;
  56. extern struct dlg_binds dlgb;
  57. extern int cdp_event_latency;
  58. extern int cdp_event_threshold;
  59. extern int cdp_event_latency_loglevel;
  60. extern int cdp_event_list_size_threshold;
  61. extern struct ims_qos_counters_h ims_qos_cnts_h;
  62. int init_cdp_cb_event_list() {
  63. cdp_event_list = shm_malloc(sizeof (cdp_cb_event_list_t));
  64. if (!cdp_event_list) {
  65. LM_ERR("No more SHM mem\n");
  66. return 0;
  67. }
  68. memset(cdp_event_list, 0, sizeof (cdp_cb_event_list_t));
  69. cdp_event_list->lock = lock_alloc();
  70. if (!cdp_event_list->lock) {
  71. LM_ERR("failed to create cdp event list lock\n");
  72. return 0;
  73. }
  74. cdp_event_list->lock = lock_init(cdp_event_list->lock);
  75. cdp_event_list->size = 0;
  76. sem_new(cdp_event_list->empty, 0); //pre-locked - as we assume list is empty at start
  77. return 1;
  78. }
  79. void destroy_cdp_cb_event_list() {
  80. cdp_cb_event_t *ev, *tmp;
  81. lock_get(cdp_event_list->lock);
  82. ev = cdp_event_list->head;
  83. while (ev) {
  84. tmp = ev->next;
  85. free_cdp_cb_event(ev);
  86. ev = tmp;
  87. }
  88. lock_destroy(cdp_event_list->lock);
  89. lock_dealloc(cdp_event_list->lock);
  90. shm_free(cdp_event_list);
  91. }
  92. cdp_cb_event_t* new_cdp_cb_event(int event, str *rx_session_id, rx_authsessiondata_t *session_data) {
  93. cdp_cb_event_t *new_event = shm_malloc(sizeof (cdp_cb_event_t));
  94. if (!new_event) {
  95. LM_ERR("no more shm mem\n");
  96. return NULL;
  97. }
  98. memset(new_event, 0, sizeof (cdp_cb_event_t));
  99. //we have to make a copy of the rx session id because it is not in shm mem
  100. if ((rx_session_id->len > 0) && rx_session_id->s) {
  101. LM_DBG("Creating new event for rx session [%.*s]\n", rx_session_id->len, rx_session_id->s);
  102. new_event->rx_session_id.s = (char*) shm_malloc(rx_session_id->len);
  103. if (!new_event->rx_session_id.s) {
  104. LM_ERR("no more shm memory\n");
  105. shm_free(new_event);
  106. return NULL;
  107. }
  108. memcpy(new_event->rx_session_id.s, rx_session_id->s, rx_session_id->len);
  109. new_event->rx_session_id.len = rx_session_id->len;
  110. }
  111. new_event->event = event;
  112. new_event->registered = time(NULL);
  113. new_event->session_data = session_data; //session_data is already in shm mem
  114. return new_event;
  115. }
  116. //add to tail
  117. void push_cdp_cb_event(cdp_cb_event_t* event) {
  118. lock_get(cdp_event_list->lock);
  119. if (cdp_event_list->head == 0) { //empty list
  120. cdp_event_list->head = cdp_event_list->tail = event;
  121. } else {
  122. cdp_event_list->tail->next = event;
  123. cdp_event_list->tail = event;
  124. }
  125. cdp_event_list->size++;
  126. if(cdp_event_list_size_threshold > 0 && cdp_event_list->size > cdp_event_list_size_threshold) {
  127. LM_WARN("cdp_event_list is size [%d] and has exceed cdp_event_list_size_threshold of [%d]", cdp_event_list->size, cdp_event_list_size_threshold);
  128. }
  129. sem_release(cdp_event_list->empty);
  130. lock_release(cdp_event_list->lock);
  131. }
  132. //pop from head
  133. cdp_cb_event_t* pop_cdp_cb_event() {
  134. cdp_cb_event_t *ev;
  135. lock_get(cdp_event_list->lock);
  136. while (cdp_event_list->head == 0) {
  137. lock_release(cdp_event_list->lock);
  138. sem_get(cdp_event_list->empty);
  139. lock_get(cdp_event_list->lock);
  140. }
  141. ev = cdp_event_list->head;
  142. cdp_event_list->head = ev->next;
  143. if (ev == cdp_event_list->tail) { //list now empty
  144. cdp_event_list->tail = 0;
  145. }
  146. ev->next = 0; //make sure whoever gets this cant access our list
  147. cdp_event_list->size--;
  148. lock_release(cdp_event_list->lock);
  149. return ev;
  150. }
  151. /*main event process function*/
  152. void cdp_cb_event_process() {
  153. cdp_cb_event_t *ev;
  154. udomain_t* domain;
  155. pcontact_t* pcontact;
  156. str release_reason = {"QoS released", 12}; /* TODO: This could be a module parameter */
  157. struct pcontact_info ci;
  158. memset(&ci, 0, sizeof (struct pcontact_info));
  159. for (;;) {
  160. ev = pop_cdp_cb_event();
  161. if (cdp_event_latency) { //track delays
  162. unsigned int diff = time(NULL) - ev->registered;
  163. if (diff > cdp_event_threshold) {
  164. switch (cdp_event_latency_loglevel) {
  165. case 0:
  166. LM_ERR("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
  167. break;
  168. case 1:
  169. LM_WARN("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
  170. break;
  171. case 2:
  172. LM_INFO("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
  173. break;
  174. case 3:
  175. LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
  176. break;
  177. default:
  178. LM_DBG("Unknown log level....printing as debug\n");
  179. LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
  180. break;
  181. }
  182. }
  183. }
  184. LM_DBG("processing event [%d]\n", ev->event);
  185. rx_authsessiondata_t *p_session_data = ev->session_data;
  186. str *rx_session_id = &ev->rx_session_id;
  187. switch (ev->event) {
  188. case AUTH_EV_SESSION_TIMEOUT:
  189. case AUTH_EV_SESSION_GRACE_TIMEOUT:
  190. case AUTH_EV_RECV_ASR:
  191. LM_DBG("Received notification of ASR from transport plane or CDP timeout for CDP session with Rx session ID: [%.*s] and associated contact [%.*s]"
  192. " and domain [%.*s]\n",
  193. rx_session_id->len, rx_session_id->s,
  194. p_session_data->registration_aor.len, p_session_data->registration_aor.s,
  195. p_session_data->domain.len, p_session_data->domain.s);
  196. if (p_session_data->subscribed_to_signaling_path_status) {
  197. LM_DBG("This is a subscription to signalling bearer session");
  198. //nothing to do here - just wait for AUTH_EV_SERVICE_TERMINATED event
  199. } else {
  200. LM_DBG("This is a media bearer session session");
  201. //this is a media bearer session that was terminated from the transport plane - we need to terminate the associated dialog
  202. //so we set p_session_data->must_terminate_dialog to 1 and when we receive AUTH_EV_SERVICE_TERMINATED event we will terminate the dialog
  203. p_session_data->must_terminate_dialog = 1;
  204. }
  205. break;
  206. case AUTH_EV_SERVICE_TERMINATED:
  207. LM_DBG("Received notification of CDP TERMINATE of CDP session with Rx session ID: [%.*s] and associated contact [%.*s]"
  208. " and domain [%.*s]\n",
  209. rx_session_id->len, rx_session_id->s,
  210. p_session_data->registration_aor.len, p_session_data->registration_aor.s,
  211. p_session_data->domain.len, p_session_data->domain.s);
  212. if (p_session_data->subscribed_to_signaling_path_status) {
  213. LM_DBG("This is a subscription to signalling bearer session");
  214. //instead of removing the contact from usrloc_pcscf we just change the state of the contact to TERMINATE_PENDING_NOTIFY
  215. //pcscf_registrar sees this, sends a SIP PUBLISH and on SIP NOTIFY the contact is deleted
  216. //note we only send SIP PUBLISH if the session has been successfully opened
  217. if(p_session_data->session_has_been_opened) {
  218. if (ul.register_udomain(p_session_data->domain.s, &domain)
  219. < 0) {
  220. LM_DBG("Unable to register usrloc domain....aborting\n");
  221. return;
  222. }
  223. ul.lock_udomain(domain, &p_session_data->registration_aor, &p_session_data->ip, p_session_data->recv_port);
  224. if (ul.get_pcontact(domain, &p_session_data->registration_aor, &p_session_data->ip, p_session_data->recv_port, &pcontact) != 0) {
  225. LM_DBG("no contact found for terminated Rx reg session..... ignoring\n");
  226. } else {
  227. LM_DBG("Updating contact [%.*s] after Rx reg session terminated, setting state to PCONTACT_DEREG_PENDING_PUBLISH\n", pcontact->aor.len, pcontact->aor.s);
  228. ci.reg_state = PCONTACT_DEREG_PENDING_PUBLISH;
  229. ci.num_service_routes = 0;
  230. ul.update_pcontact(domain, &ci, pcontact);
  231. }
  232. ul.unlock_udomain(domain, &p_session_data->registration_aor, &p_session_data->ip, p_session_data->recv_port);
  233. counter_add(ims_qos_cnts_h.active_registration_rx_sessions, -1);
  234. }
  235. } else {
  236. LM_DBG("This is a media bearer session session");
  237. if(p_session_data->session_has_been_opened) {
  238. LM_DBG("Session was opened so decrementing active_media_rx_sessions\n");
  239. counter_add(ims_qos_cnts_h.active_media_rx_sessions, -1);
  240. }
  241. //we only terminate the dialog if this was triggered from the transport plane or timeout - i.e. if must_terminate_dialog is set
  242. //if this was triggered from the signalling plane (i.e. someone hanging up) then we don'y need to terminate the dialog
  243. if (p_session_data->must_terminate_dialog) {
  244. LM_DBG("Terminating dialog with callid, ftag, ttag: [%.*s], [%.*s], [%.*s]\n",
  245. p_session_data->callid.len, p_session_data->callid.s,
  246. p_session_data->ftag.len, p_session_data->ftag.s,
  247. p_session_data->ttag.len, p_session_data->ttag.s);
  248. dlgb.terminate_dlg(&p_session_data->callid,
  249. &p_session_data->ftag, &p_session_data->ttag, NULL,
  250. &release_reason);
  251. }
  252. }
  253. //free callback data
  254. if (p_session_data) {
  255. free_callsessiondata(p_session_data);
  256. }
  257. break;
  258. default:
  259. break;
  260. }
  261. free_cdp_cb_event(ev);
  262. }
  263. }
  264. void free_cdp_cb_event(cdp_cb_event_t *ev) {
  265. if (ev) {
  266. LM_DBG("Freeing cdpb CB event structure\n");
  267. if (ev->rx_session_id.len > 0 && ev->rx_session_id.s) {
  268. LM_DBG("about to free string from cdp CB event [%.*s]\n", ev->rx_session_id.len, ev->rx_session_id.s);
  269. shm_free(ev->rx_session_id.s);
  270. }
  271. shm_free(ev);
  272. }
  273. }