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