Skip to content

Commit 06ffb7a

Browse files
committed
aaa_diameter: Add Diameter server support
* add the E_DM_REQUEST event, for receiving arbitrary Diameter requests * add the dm_send_reply(<avps-json>) function, in order to reply back to the sender
1 parent 4da5b73 commit 06ffb7a

File tree

7 files changed

+534
-78
lines changed

7 files changed

+534
-78
lines changed

aaa/aaa.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
#define AAA_AUTH 4
5353
#define AAA_ACCT 5
5454
#define AAA_RECV 6
55-
#define AAA_CUSTOM 7
55+
#define AAA_CUSTOM_REQ 7
56+
#define AAA_CUSTOM_RPL 8
5657

5758
#define AAA_GET_FROM_START 8
5859
#define AAA_GET_FROM_CURRENT 9

modules/aaa_diameter/aaa_diameter.c

+121-8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "../../ut.h"
2727

2828
#include "aaa_impl.h"
29+
#include "aaa_evi.h"
2930
#include "peer.h"
3031

3132
static int mod_init(void);
@@ -40,6 +41,7 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
4041
str *avp_json, pv_spec_t *rpl_avps_pv);
4142
static int dm_send_request_async(struct sip_msg *msg, async_ctx *ctx,
4243
int *app_id, int *cmd_code, str *avp_json, pv_spec_t *rpl_avps_pv);
44+
static int dm_send_reply(struct sip_msg *msg, str *avp_json);
4345
static int dm_bind_api(aaa_prot *api);
4446

4547
int fd_log_level = FD_LOG_NOTICE;
@@ -56,6 +58,10 @@ static const cmd_export_t cmds[]= {
5658
{CMD_PARAM_VAR|CMD_PARAM_OPT,0,0}, {0,0,0}},
5759
ALL_ROUTES},
5860

61+
{"dm_send_reply", (cmd_function)dm_send_reply, {
62+
{CMD_PARAM_STR,0,0}, {0,0,0}},
63+
EVENT_ROUTE},
64+
5965
{"aaa_bind_api", (cmd_function) dm_bind_api, {{0, 0, 0}}, 0},
6066
{0,0,{{0,0,0}},0}
6167
};
@@ -143,6 +149,11 @@ int mod_init(void)
143149
return -1;
144150
}
145151

152+
if (dm_init_evi() != 0) {
153+
LM_ERR("failed to init the Diameter event\n");
154+
return -1;
155+
}
156+
146157
if (dm_init_peer() != 0) {
147158
LM_ERR("failed to init the local Diameter peer\n");
148159
return -1;
@@ -229,11 +240,11 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
229240
struct dict_object *req;
230241
cJSON *avps;
231242
int rc;
232-
char *rpl_avps;
243+
char *rpl_avps = NULL;
233244

234-
if (fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_CODE_R,
235-
cmd_code, &req, ENOENT) == ENOENT) {
236-
LM_ERR("unrecognized Request command code: %d\n", *cmd_code);
245+
if ((rc = fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND,CMD_BY_CODE_R,
246+
cmd_code, &req, ENOENT)) != 0) {
247+
LM_ERR("unrecognized Request command code: %d (errno: %d)\n", *cmd_code, rc);
237248
LM_ERR("to fix this, you can define the Request/Answer format in the "
238249
"'extra-avps-file' config file\n");
239250
return -1;
@@ -259,7 +270,7 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
259270
goto error;
260271
}
261272

262-
dmsg = _dm_create_message(NULL, AAA_CUSTOM, *app_id, *cmd_code);
273+
dmsg = _dm_create_message(NULL, AAA_CUSTOM_REQ, *app_id, *cmd_code, NULL);
263274
if (!dmsg) {
264275
LM_ERR("oom\n");
265276
goto error;
@@ -280,8 +291,8 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
280291
LM_ERR("failed to set output rpl_avps pv to: %s\n", rpl_avps);
281292
}
282293

283-
if (rc != 0) {
284-
LM_ERR("Diameter request failed\n");
294+
if (rc < 0) {
295+
LM_ERR("Diameter request failed (rc: %d)\n", rc);
285296
cJSON_Delete(avps);
286297
return rc;
287298
}
@@ -301,6 +312,108 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
301312
return -1;
302313
}
303314

315+
316+
static int dm_send_reply(struct sip_msg *msg, str *avp_json)
317+
{
318+
aaa_message *dmsg = NULL;
319+
cJSON *avps;
320+
pv_param_t evp;
321+
pv_value_t res;
322+
str sessid;
323+
int appid, cmdcode, rc;
324+
unsigned long fd_req;
325+
326+
if (ZSTRP(avp_json)) {
327+
LM_ERR("unable to build reply (NULL 'avps_json' input)\n");
328+
return -1;
329+
}
330+
331+
avps = cJSON_Parse(avp_json->s);
332+
if (!avps) {
333+
LM_ERR("failed to parse input JSON ('%.*s' ..., total: %d)\n",
334+
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len);
335+
return -1;
336+
}
337+
338+
if (avps->type != cJSON_Array) {
339+
LM_ERR("bad JSON type: must be Array ('%.*s' ..., total: %d)\n",
340+
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len);
341+
goto error;
342+
}
343+
344+
/* Here, we know 100% that we're inside an event_route, so we can pull the
345+
* Diameter request info (Session-ID, App, Code) using the "params" API */
346+
memset(&evp, 0, sizeof evp);
347+
evp.pvn.type = PV_NAME_INTSTR;
348+
evp.pvn.u.isname.type = AVP_NAME_STR;
349+
350+
evp.pvn.u.isname.name.s = dmev_req_pname_sessid;
351+
route_params_run(msg, &evp, &res);
352+
if (ZSTR(res.rs) || !pvv_is_str(&res)) {
353+
LM_ERR("failed to fetch unique session ID\n");
354+
sessid = STR_NULL;
355+
} else {
356+
sessid = res.rs;
357+
}
358+
359+
evp.pvn.u.isname.name.s = dmev_req_pname_appid;
360+
route_params_run(msg, &evp, &res);
361+
if (!pvv_is_int(&res)) {
362+
LM_ERR("failed to fetch Application ID\n");
363+
appid = 0;
364+
} else {
365+
appid = res.ri;
366+
}
367+
368+
evp.pvn.u.isname.name.s = dmev_req_pname_cmdcode;
369+
route_params_run(msg, &evp, &res);
370+
if (!pvv_is_int(&res)) {
371+
LM_ERR("failed to fetch Command Code\n");
372+
cmdcode = 0;
373+
} else {
374+
cmdcode = res.ri;
375+
}
376+
377+
evp.pvn.u.isname.name.s = dmev_req_pname_fdmsg;
378+
route_params_run(msg, &evp, &res);
379+
if (!pvv_is_str(&res)) {
380+
LM_ERR("failed to fetch FD Message\n");
381+
goto error;
382+
} else {
383+
reverse_hex2int64(res.rs.s, res.rs.len, 1, &fd_req);
384+
}
385+
386+
dmsg = _dm_create_message(NULL, AAA_CUSTOM_RPL, appid, cmdcode, (void *)fd_req);
387+
if (!dmsg) {
388+
LM_ERR("oom\n");
389+
goto error;
390+
}
391+
392+
if (dm_build_avps(&((struct dm_message *)(dmsg->avpair))->avps,
393+
avps->child) != 0) {
394+
LM_ERR("failed to unpack JSON ('%.*s' ..., total: %d)\n",
395+
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len);
396+
goto error;
397+
}
398+
399+
rc = _dm_send_message(NULL, dmsg, NULL, NULL);
400+
if (rc < 0) {
401+
LM_ERR("failed to send Diameter reply (sess: %.*s, app: %d, cmd: %d)\n",
402+
sessid.len, sessid.s, appid, cmdcode);
403+
cJSON_Delete(avps);
404+
return rc;
405+
}
406+
407+
cJSON_Delete(avps);
408+
return 0;
409+
410+
error:
411+
_dm_destroy_message(dmsg);
412+
cJSON_Delete(avps);
413+
return -1;
414+
}
415+
416+
304417
struct dm_async_msg {
305418
pv_spec_p ret;
306419
struct dm_cond *cond;
@@ -403,7 +516,7 @@ static int dm_send_request_async(struct sip_msg *msg, async_ctx *ctx,
403516
goto error;
404517
}
405518

406-
dmsg = _dm_create_message(NULL, AAA_CUSTOM, *app_id, *cmd_code);
519+
dmsg = _dm_create_message(NULL, AAA_CUSTOM_REQ, *app_id, *cmd_code, NULL);
407520
if (!dmsg) {
408521
LM_ERR("oom\n");
409522
goto error;

modules/aaa_diameter/aaa_evi.c

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
* Copyright (C) 2024 OpenSIPS Solutions
3+
*
4+
* This file is part of opensips, a free SIP server.
5+
*
6+
* opensips is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version
10+
*
11+
* opensips is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
19+
*/
20+
21+
#include "aaa_evi.h"
22+
23+
#include "../../dprint.h"
24+
#include "../../ut.h"
25+
#include "../../ipc.h"
26+
27+
ipc_handler_type dmev_req_ipc;
28+
29+
static event_id_t dmev_req_id = EVI_ERROR; /* E_DM_REQUEST */
30+
static evi_params_p dmev_req_params;
31+
32+
static evi_param_p dmev_req_param_sessid;
33+
static evi_param_p dmev_req_param_appid;
34+
static evi_param_p dmev_req_param_cmdcode;
35+
static evi_param_p dmev_req_param_avpsjson;
36+
static evi_param_p dmev_req_param_fdmsg;
37+
38+
str dmev_req_pname_sessid = str_init("sess_id");
39+
str dmev_req_pname_appid = str_init("app_id");
40+
str dmev_req_pname_cmdcode = str_init("cmd_code");
41+
str dmev_req_pname_avpsjson = str_init("avps_json");
42+
str dmev_req_pname_fdmsg = str_init("_fdmsg_");
43+
44+
45+
static int dm_init_ipc(void)
46+
{
47+
dmev_req_ipc = ipc_register_handler(dm_raise_event_request,
48+
"DM Request Dispatch");
49+
if (ipc_bad_handler_type(dmev_req_ipc)) {
50+
LM_ERR("failed to register 'DM Request Dispatch' IPC handler\n");
51+
return -1;
52+
}
53+
54+
return 0;
55+
}
56+
57+
58+
int dm_init_evi(void)
59+
{
60+
if (dm_init_ipc() != 0) {
61+
LM_ERR("failed to init IPC\n");
62+
return -1;
63+
}
64+
65+
/* First publish the events */
66+
dmev_req_id = evi_publish_event(str_init("E_DM_REQUEST"));
67+
if (dmev_req_id == EVI_ERROR) {
68+
LM_ERR("cannot register 'request' event\n");
69+
return -1;
70+
}
71+
72+
dmev_req_params = pkg_malloc(sizeof *dmev_req_params);
73+
if (!dmev_req_params) {
74+
LM_ERR("oom\n");
75+
return -1;
76+
}
77+
memset(dmev_req_params, 0, sizeof *dmev_req_params);
78+
79+
dmev_req_param_sessid = evi_param_create(dmev_req_params, &dmev_req_pname_sessid);
80+
dmev_req_param_appid = evi_param_create(dmev_req_params, &dmev_req_pname_appid);
81+
dmev_req_param_cmdcode = evi_param_create(dmev_req_params, &dmev_req_pname_cmdcode);
82+
dmev_req_param_avpsjson = evi_param_create(dmev_req_params, &dmev_req_pname_avpsjson);
83+
dmev_req_param_fdmsg = evi_param_create(dmev_req_params, &dmev_req_pname_fdmsg);
84+
if (!dmev_req_param_sessid || !dmev_req_param_appid
85+
|| !dmev_req_param_cmdcode || !dmev_req_param_avpsjson
86+
|| !dmev_req_param_fdmsg) {
87+
LM_ERR("failed to create EVI params\n");
88+
return -1;
89+
}
90+
91+
return 0;
92+
}
93+
94+
95+
int dm_dispatch_event_req(struct msg *msg, const str *sessid, int app_id,
96+
int cmd_code, const str *avps_json)
97+
{
98+
dm_ipc_event_req *job;
99+
100+
job = shm_malloc(sizeof *job);
101+
if (!job)
102+
goto out_oom;
103+
memset(job, 0, sizeof *job);
104+
105+
job->fd_msg = msg;
106+
job->app_id = app_id;
107+
job->cmd_code = cmd_code;
108+
109+
if (shm_nt_str_dup(&job->sessid, sessid)
110+
|| shm_nt_str_dup(&job->avps_json, avps_json))
111+
goto out_oom;
112+
113+
return ipc_dispatch_job(dmev_req_ipc, job);
114+
115+
out_oom:
116+
if (job) {
117+
shm_free(job->sessid.s);
118+
shm_free(job->avps_json.s);
119+
shm_free(job);
120+
}
121+
LM_ERR("oom\n");
122+
return -1;
123+
}
124+
125+
126+
void dm_raise_event_request(int sender, void *dm_req)
127+
{
128+
char buf[sizeof(long)*2 + 1], *p = buf;
129+
int sz = sizeof(buf);
130+
str ptr;
131+
132+
dm_ipc_event_req *job = (dm_ipc_event_req *)dm_req;
133+
134+
LM_DBG("received Diameter request via IPC, tid: %.*s\n",
135+
job->sessid.len, job->sessid.s);
136+
137+
/* raise the event to script (dispatch) */
138+
if (evi_param_set_str(dmev_req_param_sessid, &job->sessid) < 0) {
139+
LM_ERR("failed to set 'sess_id'\n");
140+
goto out;
141+
}
142+
143+
if (evi_param_set_int(dmev_req_param_appid, &job->app_id) < 0) {
144+
LM_ERR("failed to set 'app_id'\n");
145+
goto out;
146+
}
147+
148+
if (evi_param_set_int(dmev_req_param_cmdcode, &job->cmd_code) < 0) {
149+
LM_ERR("failed to set 'cmd_code'\n");
150+
goto out;
151+
}
152+
153+
if (evi_param_set_str(dmev_req_param_avpsjson, &job->avps_json) < 0) {
154+
LM_ERR("failed to set 'avps_json'\n");
155+
goto out;
156+
}
157+
158+
int64_2reverse_hex(&p, &sz, (unsigned long)job->fd_msg);
159+
*p = '\0';
160+
init_str(&ptr, buf);
161+
162+
if (evi_param_set_str(dmev_req_param_fdmsg, &ptr) < 0) {
163+
LM_ERR("failed to set '_fdmsg_'\n");
164+
goto out;
165+
}
166+
167+
if (evi_raise_event(dmev_req_id, dmev_req_params) < 0)
168+
LM_ERR("failed to raise 'E_DM_REQUEST' event\n");
169+
170+
out:
171+
shm_free(job->sessid.s);
172+
shm_free(job->avps_json.s);
173+
shm_free(job);
174+
}

0 commit comments

Comments
 (0)