Skip to content

Commit

Permalink
aaa_diameter: Add Diameter server support
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
liviuchircu committed Feb 26, 2024
1 parent 4da5b73 commit 06ffb7a
Show file tree
Hide file tree
Showing 7 changed files with 534 additions and 78 deletions.
3 changes: 2 additions & 1 deletion aaa/aaa.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
#define AAA_AUTH 4
#define AAA_ACCT 5
#define AAA_RECV 6
#define AAA_CUSTOM 7
#define AAA_CUSTOM_REQ 7
#define AAA_CUSTOM_RPL 8

#define AAA_GET_FROM_START 8
#define AAA_GET_FROM_CURRENT 9
Expand Down
129 changes: 121 additions & 8 deletions modules/aaa_diameter/aaa_diameter.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "../../ut.h"

#include "aaa_impl.h"
#include "aaa_evi.h"
#include "peer.h"

static int mod_init(void);
Expand All @@ -40,6 +41,7 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
str *avp_json, pv_spec_t *rpl_avps_pv);
static int dm_send_request_async(struct sip_msg *msg, async_ctx *ctx,
int *app_id, int *cmd_code, str *avp_json, pv_spec_t *rpl_avps_pv);
static int dm_send_reply(struct sip_msg *msg, str *avp_json);
static int dm_bind_api(aaa_prot *api);

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

{"dm_send_reply", (cmd_function)dm_send_reply, {
{CMD_PARAM_STR,0,0}, {0,0,0}},
EVENT_ROUTE},

{"aaa_bind_api", (cmd_function) dm_bind_api, {{0, 0, 0}}, 0},
{0,0,{{0,0,0}},0}
};
Expand Down Expand Up @@ -143,6 +149,11 @@ int mod_init(void)
return -1;
}

if (dm_init_evi() != 0) {
LM_ERR("failed to init the Diameter event\n");
return -1;
}

if (dm_init_peer() != 0) {
LM_ERR("failed to init the local Diameter peer\n");
return -1;
Expand Down Expand Up @@ -229,11 +240,11 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
struct dict_object *req;
cJSON *avps;
int rc;
char *rpl_avps;
char *rpl_avps = NULL;

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

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

if (rc != 0) {
LM_ERR("Diameter request failed\n");
if (rc < 0) {
LM_ERR("Diameter request failed (rc: %d)\n", rc);
cJSON_Delete(avps);
return rc;
}
Expand All @@ -301,6 +312,108 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
return -1;
}


static int dm_send_reply(struct sip_msg *msg, str *avp_json)
{
aaa_message *dmsg = NULL;
cJSON *avps;
pv_param_t evp;
pv_value_t res;
str sessid;
int appid, cmdcode, rc;
unsigned long fd_req;

if (ZSTRP(avp_json)) {
LM_ERR("unable to build reply (NULL 'avps_json' input)\n");
return -1;
}

avps = cJSON_Parse(avp_json->s);
if (!avps) {
LM_ERR("failed to parse input JSON ('%.*s' ..., total: %d)\n",
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len);
return -1;
}

if (avps->type != cJSON_Array) {
LM_ERR("bad JSON type: must be Array ('%.*s' ..., total: %d)\n",
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len);
goto error;
}

/* Here, we know 100% that we're inside an event_route, so we can pull the
* Diameter request info (Session-ID, App, Code) using the "params" API */
memset(&evp, 0, sizeof evp);
evp.pvn.type = PV_NAME_INTSTR;
evp.pvn.u.isname.type = AVP_NAME_STR;

evp.pvn.u.isname.name.s = dmev_req_pname_sessid;
route_params_run(msg, &evp, &res);
if (ZSTR(res.rs) || !pvv_is_str(&res)) {
LM_ERR("failed to fetch unique session ID\n");
sessid = STR_NULL;
} else {
sessid = res.rs;
}

evp.pvn.u.isname.name.s = dmev_req_pname_appid;
route_params_run(msg, &evp, &res);
if (!pvv_is_int(&res)) {
LM_ERR("failed to fetch Application ID\n");
appid = 0;
} else {
appid = res.ri;
}

evp.pvn.u.isname.name.s = dmev_req_pname_cmdcode;
route_params_run(msg, &evp, &res);
if (!pvv_is_int(&res)) {
LM_ERR("failed to fetch Command Code\n");
cmdcode = 0;
} else {
cmdcode = res.ri;
}

evp.pvn.u.isname.name.s = dmev_req_pname_fdmsg;
route_params_run(msg, &evp, &res);
if (!pvv_is_str(&res)) {
LM_ERR("failed to fetch FD Message\n");
goto error;
} else {
reverse_hex2int64(res.rs.s, res.rs.len, 1, &fd_req);
}

dmsg = _dm_create_message(NULL, AAA_CUSTOM_RPL, appid, cmdcode, (void *)fd_req);
if (!dmsg) {
LM_ERR("oom\n");
goto error;
}

if (dm_build_avps(&((struct dm_message *)(dmsg->avpair))->avps,
avps->child) != 0) {
LM_ERR("failed to unpack JSON ('%.*s' ..., total: %d)\n",
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp_json->len);
goto error;
}

rc = _dm_send_message(NULL, dmsg, NULL, NULL);
if (rc < 0) {
LM_ERR("failed to send Diameter reply (sess: %.*s, app: %d, cmd: %d)\n",
sessid.len, sessid.s, appid, cmdcode);
cJSON_Delete(avps);
return rc;
}

cJSON_Delete(avps);
return 0;

error:
_dm_destroy_message(dmsg);
cJSON_Delete(avps);
return -1;
}


struct dm_async_msg {
pv_spec_p ret;
struct dm_cond *cond;
Expand Down Expand Up @@ -403,7 +516,7 @@ static int dm_send_request_async(struct sip_msg *msg, async_ctx *ctx,
goto error;
}

dmsg = _dm_create_message(NULL, AAA_CUSTOM, *app_id, *cmd_code);
dmsg = _dm_create_message(NULL, AAA_CUSTOM_REQ, *app_id, *cmd_code, NULL);
if (!dmsg) {
LM_ERR("oom\n");
goto error;
Expand Down
174 changes: 174 additions & 0 deletions modules/aaa_diameter/aaa_evi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2024 OpenSIPS Solutions
*
* This file is part of opensips, a free SIP server.
*
* opensips is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* opensips is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/

#include "aaa_evi.h"

#include "../../dprint.h"
#include "../../ut.h"
#include "../../ipc.h"

ipc_handler_type dmev_req_ipc;

static event_id_t dmev_req_id = EVI_ERROR; /* E_DM_REQUEST */
static evi_params_p dmev_req_params;

static evi_param_p dmev_req_param_sessid;
static evi_param_p dmev_req_param_appid;
static evi_param_p dmev_req_param_cmdcode;
static evi_param_p dmev_req_param_avpsjson;
static evi_param_p dmev_req_param_fdmsg;

str dmev_req_pname_sessid = str_init("sess_id");
str dmev_req_pname_appid = str_init("app_id");
str dmev_req_pname_cmdcode = str_init("cmd_code");
str dmev_req_pname_avpsjson = str_init("avps_json");
str dmev_req_pname_fdmsg = str_init("_fdmsg_");


static int dm_init_ipc(void)
{
dmev_req_ipc = ipc_register_handler(dm_raise_event_request,
"DM Request Dispatch");
if (ipc_bad_handler_type(dmev_req_ipc)) {
LM_ERR("failed to register 'DM Request Dispatch' IPC handler\n");
return -1;
}

return 0;
}


int dm_init_evi(void)
{
if (dm_init_ipc() != 0) {
LM_ERR("failed to init IPC\n");
return -1;
}

/* First publish the events */
dmev_req_id = evi_publish_event(str_init("E_DM_REQUEST"));
if (dmev_req_id == EVI_ERROR) {
LM_ERR("cannot register 'request' event\n");
return -1;
}

dmev_req_params = pkg_malloc(sizeof *dmev_req_params);
if (!dmev_req_params) {
LM_ERR("oom\n");
return -1;
}
memset(dmev_req_params, 0, sizeof *dmev_req_params);

dmev_req_param_sessid = evi_param_create(dmev_req_params, &dmev_req_pname_sessid);
dmev_req_param_appid = evi_param_create(dmev_req_params, &dmev_req_pname_appid);
dmev_req_param_cmdcode = evi_param_create(dmev_req_params, &dmev_req_pname_cmdcode);
dmev_req_param_avpsjson = evi_param_create(dmev_req_params, &dmev_req_pname_avpsjson);
dmev_req_param_fdmsg = evi_param_create(dmev_req_params, &dmev_req_pname_fdmsg);
if (!dmev_req_param_sessid || !dmev_req_param_appid
|| !dmev_req_param_cmdcode || !dmev_req_param_avpsjson
|| !dmev_req_param_fdmsg) {
LM_ERR("failed to create EVI params\n");
return -1;
}

return 0;
}


int dm_dispatch_event_req(struct msg *msg, const str *sessid, int app_id,
int cmd_code, const str *avps_json)
{
dm_ipc_event_req *job;

job = shm_malloc(sizeof *job);
if (!job)
goto out_oom;
memset(job, 0, sizeof *job);

job->fd_msg = msg;
job->app_id = app_id;
job->cmd_code = cmd_code;

if (shm_nt_str_dup(&job->sessid, sessid)
|| shm_nt_str_dup(&job->avps_json, avps_json))
goto out_oom;

return ipc_dispatch_job(dmev_req_ipc, job);

out_oom:
if (job) {
shm_free(job->sessid.s);
shm_free(job->avps_json.s);
shm_free(job);
}
LM_ERR("oom\n");
return -1;
}


void dm_raise_event_request(int sender, void *dm_req)
{
char buf[sizeof(long)*2 + 1], *p = buf;
int sz = sizeof(buf);
str ptr;

dm_ipc_event_req *job = (dm_ipc_event_req *)dm_req;

LM_DBG("received Diameter request via IPC, tid: %.*s\n",
job->sessid.len, job->sessid.s);

/* raise the event to script (dispatch) */
if (evi_param_set_str(dmev_req_param_sessid, &job->sessid) < 0) {
LM_ERR("failed to set 'sess_id'\n");
goto out;
}

if (evi_param_set_int(dmev_req_param_appid, &job->app_id) < 0) {
LM_ERR("failed to set 'app_id'\n");
goto out;
}

if (evi_param_set_int(dmev_req_param_cmdcode, &job->cmd_code) < 0) {
LM_ERR("failed to set 'cmd_code'\n");
goto out;
}

if (evi_param_set_str(dmev_req_param_avpsjson, &job->avps_json) < 0) {
LM_ERR("failed to set 'avps_json'\n");
goto out;
}

int64_2reverse_hex(&p, &sz, (unsigned long)job->fd_msg);
*p = '\0';
init_str(&ptr, buf);

if (evi_param_set_str(dmev_req_param_fdmsg, &ptr) < 0) {
LM_ERR("failed to set '_fdmsg_'\n");
goto out;
}

if (evi_raise_event(dmev_req_id, dmev_req_params) < 0)
LM_ERR("failed to raise 'E_DM_REQUEST' event\n");

out:
shm_free(job->sessid.s);
shm_free(job->avps_json.s);
shm_free(job);
}

0 comments on commit 06ffb7a

Please sign in to comment.