Mercurial > hg > freeDiameter
annotate extensions/rt_rewrite/rt_rewrite.c @ 1388:61e693afccc6
rt_rewrite: improve locking
author | Thomas Klausner <tk@giga.or.at> |
---|---|
date | Tue, 15 Oct 2019 16:26:03 +0200 |
parents | b0401251d8c0 |
children | c8057892e56b |
rev | line source |
---|---|
1341 | 1 /********************************************************************************************************* |
2 * Software License Agreement (BSD License) * | |
3 * Author: Thomas Klausner <tk@giga.or.at> * | |
4 * * | |
5 * Copyright (c) 2018, Thomas Klausner * | |
6 * All rights reserved. * | |
7 * * | |
8 * Written under contract by Effortel Technologies SA, http://effortel.com/ * | |
9 * * | |
10 * Redistribution and use of this software in source and binary forms, with or without modification, are * | |
11 * permitted provided that the following conditions are met: * | |
12 * * | |
13 * * Redistributions of source code must retain the above * | |
14 * copyright notice, this list of conditions and the * | |
15 * following disclaimer. * | |
16 * * | |
17 * * Redistributions in binary form must reproduce the above * | |
18 * copyright notice, this list of conditions and the * | |
19 * following disclaimer in the documentation and/or other * | |
20 * materials provided with the distribution. * | |
21 * * | |
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * | |
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * | |
24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * | |
25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * | |
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * | |
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * | |
28 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * | |
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * | |
30 *********************************************************************************************************/ | |
31 | |
32 #include <freeDiameter/extension.h> | |
33 #include "rt_rewrite.h" | |
34 | |
35 #include <pthread.h> | |
36 #include <signal.h> | |
37 | |
38 /* | |
39 * Replace AVPs: put their values into other AVPs | |
40 * Remove AVPs: drop them from a message | |
41 */ | |
42 | |
43 /* handler */ | |
44 static struct fd_rt_fwd_hdl * rt_rewrite_handle = NULL; | |
45 | |
46 static char *config_file; | |
47 | |
48 struct avp_match *avp_match_start = NULL; | |
49 | |
50 static pthread_rwlock_t rt_rewrite_lock; | |
51 | |
52 #define MODULE_NAME "rt_rewrite" | |
53 | |
54 struct store { | |
55 struct avp *avp; | |
56 struct avp_target *target; | |
57 struct store *next; | |
58 }; | |
59 | |
60 static struct store *store_new(void) { | |
61 struct store *ret; | |
62 | |
63 if ((ret=malloc(sizeof(*ret))) == NULL) { | |
64 fd_log_error("%s: malloc failure", MODULE_NAME); | |
65 return NULL; | |
66 } | |
67 ret->avp = NULL; | |
68 ret->target = NULL; | |
69 ret->next = NULL; | |
70 | |
71 return ret; | |
72 } | |
73 | |
74 static void store_free(struct store *store) | |
75 { | |
76 while (store) { | |
77 struct store *next; | |
78 if (store->avp) { | |
79 fd_msg_free((msg_or_avp *)store->avp); | |
80 } | |
81 store->target = NULL; | |
82 next = store->next; | |
83 free(store); | |
84 store = next; | |
85 } | |
86 return; | |
87 } | |
88 | |
89 static int fd_avp_search_avp(msg_or_avp *where, struct dict_object *what, struct avp **avp) | |
90 { | |
91 struct avp *nextavp; | |
92 struct dict_avp_data dictdata; | |
93 enum dict_object_type dicttype; | |
94 | |
95 CHECK_PARAMS((fd_dict_gettype(what, &dicttype) == 0) && (dicttype == DICT_AVP)); | |
96 CHECK_FCT(fd_dict_getval(what, &dictdata)); | |
97 | |
98 /* Loop on all top AVPs */ | |
99 CHECK_FCT(fd_msg_browse(where, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL)); | |
100 while (nextavp) { | |
101 struct avp_hdr *header = NULL; | |
102 struct dict_object *model = NULL; | |
103 if (fd_msg_avp_hdr(nextavp, &header) != 0) { | |
104 fd_log_notice("%s: unable to get header for AVP", MODULE_NAME); | |
105 return -1; | |
106 } | |
107 if (fd_msg_model(nextavp, &model) != 0) { | |
108 fd_log_notice("%s: unable to get model for AVP (%d, vendor %d)", MODULE_NAME, header->avp_code, header->avp_vendor); | |
109 return 0; | |
110 } | |
111 if (model == NULL) { | |
112 fd_log_notice("%s: unknown AVP (%d, vendor %d) (not in dictionary), skipping", MODULE_NAME, header->avp_code, header->avp_vendor); | |
113 } else { | |
114 if ((header->avp_code == dictdata.avp_code) && (header->avp_vendor == dictdata.avp_vendor)) { | |
115 break; | |
116 } | |
117 } | |
118 | |
119 /* Otherwise move to next AVP in the message */ | |
120 CHECK_FCT(fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) ); | |
121 } | |
122 | |
123 if (avp) | |
124 *avp = nextavp; | |
125 | |
126 if (*avp || nextavp) { | |
127 return 0; | |
128 } else { | |
129 return ENOENT; | |
130 } | |
131 } | |
132 | |
133 | |
134 | |
135 static msg_or_avp *find_container(msg_or_avp *msg, struct avp_target *target) | |
136 { | |
137 msg_or_avp *location = msg; | |
138 while (target->child) { | |
139 struct dict_object *avp_do; | |
140 msg_or_avp *new_location = NULL; | |
141 int ret; | |
142 | |
143 fd_log_debug("%s: looking for '%s'", MODULE_NAME, target->name); | |
144 if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, target->name, &avp_do, ENOENT)) != 0) { | |
145 fd_log_error("%s: target AVP '%s' not in dictionary: %s", MODULE_NAME, target->name, strerror(ret)); | |
146 return NULL; | |
147 } | |
148 if ((ret=fd_avp_search_avp(location, avp_do, (struct avp **)&new_location)) != 0) { | |
149 fd_log_debug("%s: did not find '%s', adding it", MODULE_NAME, target->name); | |
150 struct avp *avp = NULL; | |
151 if ((ret = fd_msg_avp_new(avp_do, 0, &avp)) != 0) { | |
152 fd_log_notice("%s: unable to create new '%s' AVP", MODULE_NAME, target->name); | |
153 return NULL; | |
154 } | |
155 if ((ret=fd_msg_avp_add(location, MSG_BRW_LAST_CHILD, avp)) != 0) { | |
156 fd_log_error("%s: cannot add AVP '%s' to message: %s", MODULE_NAME, target->name, strerror(ret)); | |
157 return NULL; | |
158 } | |
159 fd_log_debug("%s: '%s' added", MODULE_NAME, target->name); | |
160 /* now find it in the new place */ | |
161 continue; | |
162 } else { | |
163 location = new_location; | |
164 fd_log_debug("%s: found '%s'", MODULE_NAME, target->name); | |
165 } | |
166 target = target->child; | |
167 } | |
168 fd_log_debug("%s: returning AVP for '%s'", MODULE_NAME, target->name); | |
169 return location; | |
170 } | |
171 | |
172 static int store_apply(msg_or_avp *msg, struct store **store) | |
173 { | |
174 while (*store) { | |
175 msg_or_avp *container; | |
176 struct store *next; | |
177 if ((*store)->avp) { | |
178 int ret; | |
179 if ((container=find_container(msg, (*store)->target)) == NULL) { | |
180 fd_log_error("%s: internal error, container not found", MODULE_NAME); | |
181 return -1; | |
182 } | |
183 | |
184 if ((ret=fd_msg_avp_add(container, MSG_BRW_LAST_CHILD, (*store)->avp)) != 0) { | |
185 fd_log_error("%s: cannot add AVP '%s' to message: %s", MODULE_NAME, (*store)->target->name, strerror(ret)); | |
186 return -1; | |
187 } | |
188 } | |
189 next = (*store)->next; | |
190 free(*store); | |
191 *store = next; | |
192 } | |
193 return 0; | |
194 } | |
195 | |
196 static int schedule_for_adding(struct avp *avp, struct avp_target *target, struct store *store) | |
197 { | |
198 if (store->avp) { | |
199 struct store *new; | |
200 if ((new=store_new()) == NULL) { | |
201 return -1; | |
202 } | |
203 new->avp = avp; | |
204 new->target = target; | |
205 new->next = store->next; | |
206 store->next = new; | |
207 } else { | |
208 store->avp = avp; | |
209 store->target = target; | |
210 } | |
211 fd_log_debug("%s: noted %s for later adding", MODULE_NAME, target->name); | |
212 return 0; | |
213 } | |
214 | |
215 static void move_avp_to_target(union avp_value *avp_value, struct avp_target *target, struct store *store) | |
216 { | |
217 struct dict_object *avp_do; | |
218 struct avp *avp; | |
219 int ret; | |
220 struct avp_target *final = target; | |
221 | |
222 while (final->child) { | |
223 final = final->child; | |
224 } | |
225 if ((ret=fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME_ALL_VENDORS, final->name, &avp_do, ENOENT)) != 0) { | |
226 fd_log_error("internal error, target AVP '%s' not in dictionary: %s", final->name, strerror(ret)); | |
227 return; | |
228 } | |
229 if ((ret=fd_msg_avp_new(avp_do, 0, &avp)) != 0) { | |
230 fd_log_error("internal error, error creating structure for AVP '%s': %s", final->name, strerror(ret)); | |
231 return; | |
232 } | |
233 if ((ret=fd_msg_avp_setvalue(avp, avp_value)) != 0) { | |
234 fd_log_error("internal error, cannot set value for AVP '%s': %s", final->name, strerror(ret)); | |
235 return; | |
236 } | |
237 if (schedule_for_adding(avp, target, store) != 0) { | |
238 fd_log_error("internal error, cannot add AVP '%s' to message", final->name); | |
239 return; | |
240 } | |
241 | |
242 return; | |
243 } | |
244 | |
245 | |
246 static struct avp_match *avp_to_be_replaced(const char *avp_name, struct avp_match *subtree) | |
247 { | |
248 struct avp_match *ret; | |
249 for (ret=subtree; ret != NULL; ret=ret->next) { | |
250 if (strcmp(ret->name, avp_name) == 0) { | |
251 return ret; | |
252 } | |
253 } | |
254 return NULL; | |
255 } | |
256 | |
257 /* | |
258 * msg: whole message | |
259 * avp: current AVP in message | |
260 * subtree: comparison subtree | |
261 */ | |
262 static int replace_avps(struct msg *msg, msg_or_avp *avp, struct avp_match *subtree, struct store *store) | |
263 { | |
264 int nothing_left = 1; | |
265 /* for each AVP in message */ | |
266 while (avp) { | |
267 struct avp_hdr *header = NULL; | |
268 struct dict_object *model = NULL; | |
269 const char *avp_name = NULL; | |
270 msg_or_avp *next; | |
271 int delete = 0; | |
272 | |
273 if (fd_msg_avp_hdr(avp, &header) != 0) { | |
274 fd_log_notice("internal error: unable to get header for AVP"); | |
275 return 0; | |
276 } | |
277 if (fd_msg_model(avp, &model) != 0) { | |
278 fd_log_notice("internal error: unable to get model for AVP (%d, vendor %d)", header->avp_code, header->avp_vendor); | |
279 return 0; | |
280 } | |
281 if (model == NULL) { | |
282 fd_log_notice("unknown AVP (%d, vendor %d) (not in dictionary), skipping", header->avp_code, header->avp_vendor); | |
283 | |
284 } else { | |
285 struct dict_avp_data dictdata; | |
286 struct avp_match *subtree_match; | |
287 enum dict_avp_basetype basetype = AVP_TYPE_OCTETSTRING; | |
288 | |
289 fd_dict_getval(model, &dictdata); | |
290 avp_name = dictdata.avp_name; | |
291 basetype = dictdata.avp_basetype; | |
292 /* check if it exists in the subtree */ | |
293 if ((subtree_match = avp_to_be_replaced(avp_name, subtree))) { | |
294 /* if this should be deleted, mark as such */ | |
295 if (subtree_match->drop) { | |
296 fd_log_notice("%s: dropping AVP '%s'", MODULE_NAME, avp_name); | |
297 delete = 1; | |
298 } | |
299 /* if grouped, dive down */ | |
300 if (basetype == AVP_TYPE_GROUPED) { | |
301 msg_or_avp *child = NULL; | |
302 | |
303 fd_log_debug("%s: grouped AVP '%s'", MODULE_NAME, avp_name); | |
304 if (fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &child, NULL) != 0) { | |
305 fd_log_notice("internal error: unable to browse message at AVP (%d, vendor %d)", header->avp_code, header->avp_vendor); | |
306 return 0; | |
307 } | |
308 | |
309 /* replace_avps returns if the AVP was emptied out */ | |
310 if (replace_avps(msg, child, subtree_match->children, store)) { | |
311 fd_log_notice("%s: removing empty grouped AVP '%s'", MODULE_NAME, avp_name); | |
312 delete = 1; | |
313 } | |
314 } | |
315 else { | |
316 /* if single, remove it and add replacement AVP in target structure */ | |
317 if (subtree_match->target) { | |
318 struct avp_target *final = subtree_match->target; | |
319 while (final->child) { | |
320 final = final->child; | |
321 } | |
322 fd_log_notice("%s: moving AVP '%s' to '%s'", MODULE_NAME, avp_name, final->name); | |
323 move_avp_to_target(header->avp_value, subtree_match->target, store); | |
324 delete = 1; | |
325 } | |
326 } | |
327 } | |
328 } | |
329 fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL); | |
330 if (delete) { | |
331 /* remove AVP from message */ | |
332 fd_msg_free((msg_or_avp *)avp); | |
333 } else { | |
334 nothing_left = 0; | |
335 } | |
336 | |
337 avp = next; | |
338 } | |
339 | |
340 return nothing_left; | |
341 } | |
342 | |
343 static volatile int in_signal_handler = 0; | |
344 | |
345 /* signal handler */ | |
346 static void sig_hdlr(void) | |
347 { | |
348 struct avp_match *old_config; | |
349 | |
350 if (in_signal_handler) { | |
351 fd_log_error("%s: already handling a signal, ignoring new one", MODULE_NAME); | |
352 return; | |
353 } | |
354 in_signal_handler = 1; | |
355 | |
356 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { | |
357 fd_log_error("%s: locking failed, aborting config reload", MODULE_NAME); | |
358 return; | |
359 } | |
360 | |
361 /* save old config in case reload goes wrong */ | |
362 old_config = avp_match_start; | |
363 avp_match_start = NULL; | |
364 | |
365 if (rt_rewrite_conf_handle(config_file) != 0) { | |
366 fd_log_notice("%s: error reloading configuration, restoring previous configuration", MODULE_NAME); | |
367 avp_match_free(avp_match_start); | |
368 avp_match_start = old_config; | |
369 } else { | |
370 avp_match_free(old_config); | |
371 } | |
372 | |
373 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { | |
374 fd_log_error("%s: unlocking failed after config reload, exiting", MODULE_NAME); | |
375 exit(1); | |
376 } | |
377 | |
378 fd_log_notice("%s: reloaded configuration", MODULE_NAME); | |
379 | |
380 in_signal_handler = 0; | |
381 } | |
382 | |
383 static int rt_rewrite(void * cbdata, struct msg **msg) | |
384 { | |
385 int ret; | |
386 msg_or_avp *avp = NULL; | |
387 struct store *store = NULL; | |
388 | |
389 /* nothing configured */ | |
390 if (avp_match_start == NULL) { | |
391 return 0; | |
392 } | |
393 | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
394 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
395 fd_log_error("%s: locking failed, aborting message rewrite", MODULE_NAME); |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
396 return errno; |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
397 } |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
398 |
1341 | 399 if ((store=store_new()) == NULL) { |
400 fd_log_error("%s: malloc failure"); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
401 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 402 return ENOMEM; |
403 } | |
404 if ((ret = fd_msg_parse_dict(*msg, fd_g_config->cnf_dict, NULL)) != 0) { | |
405 fd_log_notice("%s: error parsing message", MODULE_NAME); | |
406 free(store); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
407 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 408 return ret; |
409 } | |
410 if ((ret=fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL)) != 0) { | |
411 fd_log_notice("internal error: message has no child"); | |
412 free(store); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
413 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 414 return ret; |
415 } | |
416 if (replace_avps(*msg, avp, avp_match_start->children, store) != 0) { | |
417 fd_log_error("%s: replace AVP function failed", MODULE_NAME); | |
418 store_free(store); | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
419 pthread_rwlock_unlock(&rt_rewrite_lock); |
1341 | 420 return -1; |
421 } | |
1388
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
422 ret = store_apply(*msg, &store); |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
423 |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
424 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
425 fd_log_error("%s: unlocking failed, returning error", MODULE_NAME); |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
426 return errno; |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
427 } |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
428 |
61e693afccc6
rt_rewrite: improve locking
Thomas Klausner <tk@giga.or.at>
parents:
1341
diff
changeset
|
429 return ret; |
1341 | 430 } |
431 | |
432 /* entry point */ | |
433 static int rt_rewrite_entry(char * conffile) | |
434 { | |
435 int ret; | |
436 config_file = conffile; | |
437 | |
438 pthread_rwlock_init(&rt_rewrite_lock, NULL); | |
439 | |
440 if (pthread_rwlock_wrlock(&rt_rewrite_lock) != 0) { | |
441 fd_log_notice("%s: write-lock failed, aborting", MODULE_NAME); | |
442 return EDEADLK; | |
443 } | |
444 | |
445 /* Parse the configuration file */ | |
446 if ((ret=rt_rewrite_conf_handle(config_file)) != 0) { | |
447 pthread_rwlock_unlock(&rt_rewrite_lock); | |
448 return ret; | |
449 } | |
450 | |
451 if (pthread_rwlock_unlock(&rt_rewrite_lock) != 0) { | |
452 fd_log_notice("%s: write-unlock failed, aborting", MODULE_NAME); | |
453 return EDEADLK; | |
454 } | |
455 | |
456 /* Register reload callback */ | |
457 CHECK_FCT(fd_event_trig_regcb(SIGUSR1, MODULE_NAME, sig_hdlr)); | |
458 | |
459 /* Register the callback */ | |
460 if ((ret=fd_rt_fwd_register(rt_rewrite, NULL, RT_FWD_ALL, &rt_rewrite_handle)) != 0) { | |
461 fd_log_error("Cannot register callback handler"); | |
462 return ret; | |
463 } | |
464 | |
465 fd_log_notice("Extension 'Rewrite' initialized"); | |
466 return 0; | |
467 } | |
468 | |
469 /* Unload */ | |
470 void fd_ext_fini(void) | |
471 { | |
472 /* Unregister the callbacks */ | |
473 fd_rt_fwd_unregister(rt_rewrite_handle, NULL); | |
474 return ; | |
475 } | |
476 | |
477 EXTENSION_ENTRY("rt_rewrite", rt_rewrite_entry); |