1 /*
2 * $Id$
3 *
4 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
5 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
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
46 #include <time.h>
47 #include "sem.h"
48 #include "../ims_usrloc_pcscf/usrloc.h"
49 #include "../ims_dialog/dlg_load.h"
50 #include "../cdp/session.h"
51 #include "ims_qos_mod.h"
52 #include "cdpeventprocessor.h"
53 #include "rx_str.h"
54 #include "ims_qos_stats.h"
55
56 cdp_cb_event_list_t *cdp_event_list = 0;
57 extern usrloc_api_t ul;
58 extern ims_dlg_api_t dlgb;
59 extern int cdp_event_latency;
60 extern int cdp_event_threshold;
61 extern int cdp_event_latency_loglevel;
62 extern int cdp_event_list_size_threshold;
63
64 extern struct ims_qos_counters_h ims_qos_cnts_h;
65
66 extern int terminate_dialog_on_rx_failure;
67 extern int delete_contact_on_rx_failure;
68
init_cdp_cb_event_list()69 int init_cdp_cb_event_list()
70 {
71 cdp_event_list = shm_malloc(sizeof(cdp_cb_event_list_t));
72 if (!cdp_event_list) {
73 LM_ERR("No more SHM mem\n");
74 return 0;
75 }
76 memset(cdp_event_list, 0, sizeof(cdp_cb_event_list_t));
77 cdp_event_list->lock = lock_alloc();
78 if (!cdp_event_list->lock) {
79 LM_ERR("failed to create cdp event list lock\n");
80 return 0;
81 }
82 cdp_event_list->lock = lock_init(cdp_event_list->lock);
83 cdp_event_list->size = 0;
84
85 sem_new(cdp_event_list->empty, 0); //pre-locked - as we assume list is empty at start
86
87 return 1;
88 }
89
destroy_cdp_cb_event_list()90 void destroy_cdp_cb_event_list()
91 {
92 cdp_cb_event_t *ev, *tmp;
93
94 lock_get(cdp_event_list->lock);
95 ev = cdp_event_list->head;
96 while (ev) {
97 tmp = ev->next;
98 free_cdp_cb_event(ev);
99 ev = tmp;
100 }
101 lock_destroy(cdp_event_list->lock);
102 lock_dealloc(cdp_event_list->lock);
103 shm_free(cdp_event_list);
104 }
105
new_cdp_cb_event(int event,str * rx_session_id,rx_authsessiondata_t * session_data)106 cdp_cb_event_t* new_cdp_cb_event(int event, str *rx_session_id, rx_authsessiondata_t *session_data)
107 {
108 cdp_cb_event_t *new_event = shm_malloc(sizeof(cdp_cb_event_t));
109 if (!new_event) {
110 LM_ERR("no more shm mem\n");
111 return NULL;
112 }
113 memset(new_event, 0, sizeof(cdp_cb_event_t));
114
115 //we have to make a copy of the rx session id because it is not in shm mem
116 if ((rx_session_id->len > 0) && rx_session_id->s) {
117 LM_DBG("Creating new event for rx session [%.*s]\n", rx_session_id->len, rx_session_id->s);
118 new_event->rx_session_id.s = (char*) shm_malloc(rx_session_id->len);
119 if (!new_event->rx_session_id.s) {
120 LM_ERR("no more shm memory\n");
121 shm_free(new_event);
122 return NULL;
123 }
124 memcpy(new_event->rx_session_id.s, rx_session_id->s, rx_session_id->len);
125 new_event->rx_session_id.len = rx_session_id->len;
126 }
127
128 new_event->event = event;
129 new_event->registered = time(NULL);
130 new_event->session_data = session_data; //session_data is already in shm mem
131
132 return new_event;
133 }
134 //add to tail
135
push_cdp_cb_event(cdp_cb_event_t * event)136 void push_cdp_cb_event(cdp_cb_event_t* event)
137 {
138 lock_get(cdp_event_list->lock);
139 if (cdp_event_list->head == 0) { //empty list
140 cdp_event_list->head = cdp_event_list->tail = event;
141 } else {
142 cdp_event_list->tail->next = event;
143 cdp_event_list->tail = event;
144 }
145 cdp_event_list->size++;
146 if (cdp_event_list_size_threshold > 0 && cdp_event_list->size > cdp_event_list_size_threshold) {
147 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);
148 }
149 sem_release(cdp_event_list->empty);
150 lock_release(cdp_event_list->lock);
151 }
152
153 //pop from head
154
pop_cdp_cb_event()155 cdp_cb_event_t* pop_cdp_cb_event()
156 {
157 cdp_cb_event_t *ev;
158
159 lock_get(cdp_event_list->lock);
160 while (cdp_event_list->head == 0) {
161 lock_release(cdp_event_list->lock);
162 sem_get(cdp_event_list->empty);
163 lock_get(cdp_event_list->lock);
164 }
165
166 ev = cdp_event_list->head;
167 cdp_event_list->head = ev->next;
168
169 if (ev == cdp_event_list->tail) { //list now empty
170 cdp_event_list->tail = 0;
171 }
172 ev->next = 0; //make sure whoever gets this cant access our list
173 cdp_event_list->size--;
174 lock_release(cdp_event_list->lock);
175
176 return ev;
177 }
178
179 /*main event process function*/
cdp_cb_event_process()180 void cdp_cb_event_process()
181 {
182 cdp_cb_event_t *ev;
183 udomain_t* domain;
184 pcontact_t* pcontact;
185 pcontact_info_t contact_info;
186
187 struct pcontact_info ci;
188 memset(&ci, 0, sizeof(struct pcontact_info));
189
190 for (;;) {
191 ev = pop_cdp_cb_event();
192
193 if (cdp_event_latency) { //track delays
194 unsigned int diff = time(NULL) - ev->registered;
195 if (diff > cdp_event_threshold) {
196 switch (cdp_event_latency_loglevel) {
197 case 0:
198 LM_ERR("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
199 break;
200 case 1:
201 LM_WARN("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
202 break;
203 case 2:
204 LM_INFO("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
205 break;
206 case 3:
207 LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
208 break;
209 default:
210 LM_DBG("Unknown log level....printing as debug\n");
211 LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
212 break;
213 }
214 }
215 }
216 LM_DBG("processing event [%d]\n", ev->event);
217 rx_authsessiondata_t *p_session_data = ev->session_data;
218 str *rx_session_id = &ev->rx_session_id;
219
220 switch (ev->event) {
221 case AUTH_EV_SESSION_TIMEOUT:
222 case AUTH_EV_SESSION_GRACE_TIMEOUT:
223 case AUTH_EV_RECV_ASR:
224 LM_DBG("Received notification of ASR from transport plane or CDP timeout for CDP session with Rx session ID: [%.*s] and associated contact [%.*s]"
225 " and domain [%.*s]\n",
226 rx_session_id->len, rx_session_id->s,
227 p_session_data->registration_aor.len, p_session_data->registration_aor.s,
228 p_session_data->domain.len, p_session_data->domain.s);
229
230
231 if (p_session_data->subscribed_to_signaling_path_status) {
232 LM_DBG("This is a subscription to signalling bearer session");
233 //nothing to do here - just wait for AUTH_EV_SERVICE_TERMINATED event
234 } else {
235 LM_DBG("This is a media bearer session session");
236 //this is a media bearer session that was terminated from the transport plane - we need to terminate the associated dialog
237 //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
238 //we only terminate the dialog if terminate_dialog_on_rx_failure is set
239 if (terminate_dialog_on_rx_failure) {
240 p_session_data->must_terminate_dialog = 1;
241 }
242 }
243 break;
244
245 case AUTH_EV_SERVICE_TERMINATED:
246 LM_DBG("Received notification of CDP TERMINATE of CDP session with Rx session ID: [%.*s] and associated contact [%.*s]"
247 " and domain [%.*s]\n",
248 rx_session_id->len, rx_session_id->s,
249 p_session_data->registration_aor.len, p_session_data->registration_aor.s,
250 p_session_data->domain.len, p_session_data->domain.s);
251
252 if (p_session_data->subscribed_to_signaling_path_status) {
253 LM_DBG("This is a subscription to signalling bearer session");
254 //instead of removing the contact from usrloc_pcscf we just change the state of the contact to TERMINATE_PENDING_NOTIFY
255 //pcscf_registrar sees this, sends a SIP PUBLISH and on SIP NOTIFY the contact is deleted
256 //note we only send SIP PUBLISH if the session has been successfully opened
257
258 if (p_session_data->session_has_been_opened) {
259 if (delete_contact_on_rx_failure) {
260 if (ul.register_udomain(p_session_data->domain.s, &domain)
261 < 0) {
262 LM_DBG("Unable to register usrloc domain....aborting\n");
263 return;
264 }
265 ul.lock_udomain(domain, &p_session_data->via_host, p_session_data->via_port, p_session_data->via_proto);
266
267 contact_info.received_host = p_session_data->ip;
268 contact_info.received_port = p_session_data->recv_port;
269 contact_info.received_proto = p_session_data->recv_proto;
270 contact_info.searchflag = (1 << SEARCH_RECEIVED);
271
272 contact_info.via_host = p_session_data->via_host;
273 contact_info.via_port = p_session_data->via_port;
274 contact_info.via_prot = p_session_data->via_proto;
275 contact_info.aor = p_session_data->registration_aor;
276 contact_info.reg_state = PCONTACT_ANY;
277
278 if (ul.get_pcontact(domain, &contact_info, &pcontact) != 0) {
279 LM_DBG("no contact found for terminated Rx reg session..... ignoring\n");
280 } else {
281 LM_DBG("Updating contact [%.*s] after Rx reg session terminated, setting state to PCONTACT_DEREG_PENDING_PUBLISH\n", pcontact->aor.len, pcontact->aor.s);
282 ci.reg_state = PCONTACT_DEREG_PENDING_PUBLISH;
283 ci.num_service_routes = 0;
284 ul.update_pcontact(domain, &ci, pcontact);
285 }
286 ul.unlock_udomain(domain, &p_session_data->via_host, p_session_data->via_port, p_session_data->via_proto);
287 }
288 counter_add(ims_qos_cnts_h.active_registration_rx_sessions, -1);
289 }
290 } else {
291 LM_DBG("This is a media bearer session session");
292 if (p_session_data->session_has_been_opened) {
293 LM_DBG("Session was opened so decrementing active_media_rx_sessions\n");
294 counter_add(ims_qos_cnts_h.active_media_rx_sessions, -1);
295 }
296
297 //we only terminate the dialog if this was triggered from the transport plane or timeout - i.e. if must_terminate_dialog is set
298 //if this was triggered from the signalling plane (i.e. someone hanging up) then we don'y need to terminate the dialog
299 if (p_session_data->must_terminate_dialog) {
300 LM_DBG("Terminating dialog with callid, ftag, ttag: [%.*s], [%.*s], [%.*s]\n",
301 p_session_data->callid.len, p_session_data->callid.s,
302 p_session_data->ftag.len, p_session_data->ftag.s,
303 p_session_data->ttag.len, p_session_data->ttag.s);
304 dlgb.terminate_dlg(&p_session_data->callid,
305 &p_session_data->ftag, &p_session_data->ttag, &confirmed_qosrelease_headers,
306 &early_qosrelease_reason);
307 }
308 }
309
310 //free callback data
311 if (p_session_data) {
312 free_callsessiondata(p_session_data);
313 }
314 break;
315 default:
316 break;
317 }
318
319 free_cdp_cb_event(ev);
320 }
321 }
322
free_cdp_cb_event(cdp_cb_event_t * ev)323 void free_cdp_cb_event(cdp_cb_event_t *ev)
324 {
325 if (ev) {
326 LM_DBG("Freeing cdpb CB event structure\n");
327 if (ev->rx_session_id.len > 0 && ev->rx_session_id.s) {
328 LM_DBG("about to free string from cdp CB event [%.*s]\n", ev->rx_session_id.len, ev->rx_session_id.s);
329 shm_free(ev->rx_session_id.s);
330 }
331 shm_free(ev);
332 }
333 }
334
335
336