Skip to content

Commit

Permalink
aaa_diameter: Add support for generic Diameter requests
Browse files Browse the repository at this point in the history
Using a JSON Array containing the required AVPs as input, script writers
may now send generic Diameter requests from script and also fully
define them in the "dictionary.opensips" file, covering any possible
Diameter application.  Usage example:

    $var(payload) = "[
            { \"Origin-Host\": \"client.diameter.test\" },
            { \"Origin-Realm\": \"diameter.test\" },
            { \"Destination-Realm\": \"diameter.test\" },
            { \"Sip-From-Tag\": \"dc93-4fba-91db\" },
            { \"Sip-To-Tag\": \"ae12-47d6-816a\" },
            { \"Acct-Session-Id\": \"a59c-dff0d9efd167\" },
            { \"Sip-Call-Duration\": 6 },
            { \"Sip-Call-Setuptime\": 1 },
            { \"Sip-Call-Created\": 1652372541 },
            { \"Sip-Call-MSDuration\": 5850 },
            { \"out_gw\": \"GW-774\" },
            { \"cost\": \"10.84\" }
	]";

$var(rc) = dm_send_request(3, 271, $var(payload), $var(res_code),
			$var(res_status));
xlog("rc: $var(rc), Result-Code: $var(res_code) ($var(res_status))\n");
  • Loading branch information
liviuchircu committed May 17, 2022
1 parent eceb1e1 commit ba0fa49
Show file tree
Hide file tree
Showing 9 changed files with 667 additions and 23 deletions.
8 changes: 6 additions & 2 deletions aaa/aaa.h
Expand Up @@ -47,11 +47,15 @@
#define AAA_DICT_FIND_VAL 1
#define AAA_DICT_FIND_ATTR 2
#define AAA_DICT_FIND_VEND 3

/* message types */
#define AAA_AUTH 4
#define AAA_ACCT 5
#define AAA_RECV 6
#define AAA_GET_FROM_START 7
#define AAA_GET_FROM_CURRENT 8
#define AAA_CUSTOM 7

#define AAA_GET_FROM_START 8
#define AAA_GET_FROM_CURRENT 9

#define AAA_APP_ACCOUNTING 3
#define AAA_APP_SIP 6
Expand Down
1 change: 1 addition & 0 deletions etc/dictionary.opensips
Expand Up @@ -23,6 +23,7 @@ ATTRIBUTE Sip-Call-Duration 227 integer
ATTRIBUTE Sip-Call-Setuptime 228 integer
ATTRIBUTE Sip-Call-Created 229 integer
ATTRIBUTE Sip-Call-MSDuration 230 integer
ATTRIBUTE Transaction-Id 231 string

### Acct-Status-Type Values ###
VALUE Acct-Status-Type Start 1 # RFC2866, RADIUS acc
Expand Down
132 changes: 129 additions & 3 deletions modules/aaa_diameter/aaa_diameter.c
Expand Up @@ -22,6 +22,7 @@

#include "../../sr_module.h"
#include "../../lib/list.h"
#include "../../ut.h"

#include "aaa_impl.h"
#include "peer.h"
Expand All @@ -34,14 +35,22 @@ static void mod_destroy(void);
char *dm_conf_filename = "freeDiameter.conf";
char *extra_avps_file;

int aaa_diameter_bind_api(aaa_prot *api);
static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
str *avp_json);
static int dm_bind_api(aaa_prot *api);

int fd_log_level = FD_LOG_NOTICE;
str dm_realm = str_init("diameter.test");
str dm_peer_identity = str_init("server"); /* a.k.a. server.diameter.test */

static cmd_export_t cmds[]= {
{"aaa_bind_api", (cmd_function) aaa_diameter_bind_api, {{0, 0, 0}}, 0},
{"dm_send_request", (cmd_function)dm_send_request, {
{CMD_PARAM_INT,0,0},
{CMD_PARAM_INT,0,0},
{CMD_PARAM_STR,0,0}, {0,0,0}},
ALL_ROUTES},

{"aaa_bind_api", (cmd_function) dm_bind_api, {{0, 0, 0}}, 0},
{0,0,{{0,0,0}},0}
};

Expand Down Expand Up @@ -169,7 +178,7 @@ static void mod_destroy(void)
}


int aaa_diameter_bind_api(aaa_prot *api)
static int dm_bind_api(aaa_prot *api)
{
if (!api)
return -1;
Expand All @@ -186,3 +195,120 @@ int aaa_diameter_bind_api(aaa_prot *api)

return 0;
}


static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
str *avp_json)
{
aaa_message *dmsg;
struct dict_object *req;
cJSON *avps, *_avp;

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);
LM_ERR("to fix this, you can define the Request/Answer format in the "
"'extra-avps-file' config file\n");
return -1;
}

LM_DBG("found a matching dict entry for command code %d\n", *cmd_code);

if (!avp_json || !avp_json->s) {
LM_ERR("NULL 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);
return -1;
}

dmsg = _dm_create_message(NULL, AAA_CUSTOM, *app_id, *cmd_code);
if (!dmsg) {
LM_ERR("oom\n");
return -1;
}

for (_avp = avps->child; _avp; _avp = _avp->next) {
if (_avp->type != cJSON_Object) {
LM_ERR("bad JSON type in Array: AVPs must be Objects ('%.*s' "
"..., total: %d)\n", avp_json->len > 512 ? 512 : avp_json->len,
avp_json->s, avp_json->len);
return -1;
}

cJSON *avp = _avp->child; // only work with child #0
struct dict_avp_data dm_avp;
struct dict_object *obj;
char *name;
unsigned int code;

// TODO: allow dict too
if (!(avp->type & (cJSON_String|cJSON_Number))) {
LM_ERR("bad AVP value: only String allowed ('%.*s' ..., key: %s)\n",
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp->string);
return -1;
}

if (_isdigit(avp->string[0])) {
str st;

init_str(&st, avp->string);
if (str2int(&st, &code) != 0) {
LM_ERR("bad AVP key: cannot start with a digit ('%.*s' ..., key: %s)\n",
avp_json->len > 512 ? 512 : avp_json->len, avp_json->s, avp->string);
return -1;
}

LM_DBG("AVP:: searching AVP by int: %d\n", code);
FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE,
&code, &obj, ENOENT));
FD_CHECK(fd_dict_getval(obj, &dm_avp));

name = dm_avp.avp_name;
} else {
LM_DBG("AVP:: searching AVP by string: %s\n", avp->string);

FD_CHECK(fd_dict_search(fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME,
avp->string, &obj, ENOENT));
FD_CHECK(fd_dict_getval(obj, &dm_avp));

name = avp->string;
code = dm_avp.avp_code;
}

aaa_map my_avp = {.name = name};

if (avp->type & cJSON_String) {
LM_DBG("dbg::: AVP %d (name: '%s', str-val: %s)\n", code, name, avp->valuestring);
if (dm_avp_add(NULL, dmsg, &my_avp, avp->valuestring,
strlen(avp->valuestring), 0) != 0) {
LM_ERR("failed to add AVP %d, aborting request\n", code);
return -1;
}
} else {
LM_DBG("dbg::: AVP %d (name: '%s', int-val: %d)\n", code, name, avp->valueint);
if (dm_avp_add(NULL, dmsg, &my_avp, &avp->valueint, -1, 0) != 0) {
LM_ERR("failed to add AVP %d, aborting request\n", code);
return -1;
}
}
}

if (dm_send_message(NULL, dmsg, NULL) != 0) {
LM_ERR("failed to queue Diameter request for sending\n");
return -1;
}

cJSON_Delete(avps);
return 1;
}
113 changes: 104 additions & 9 deletions modules/aaa_diameter/aaa_impl.c
Expand Up @@ -204,6 +204,70 @@ static int dm_auth_reply(struct msg **_msg, struct avp * avp, struct session * s
}


static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct session * sess, void * data, enum disp_action * act)
{
struct msg_hdr *hdr = NULL;
struct msg *msg = *_msg;
struct avp *a = NULL;
struct avp_hdr * h = NULL;
int rc;
str tid;
struct dm_cond **prpl_cond, *rpl_cond;

FD_CHECK(fd_msg_hdr(msg, &hdr));

if (hdr->msg_flags & CMD_FLAG_REQUEST) {
LM_INFO("received a request?! discarding...\n");
goto out;
}

FD_CHECK(fd_msg_search_avp(msg, dm_dict.Result_Code, &a));
FD_CHECK(fd_msg_avp_hdr(a, &h));
rc = h->avp_value->u32;

FD_CHECK(fd_msg_search_avp(msg, dm_dict.Transaction_Id, &a));
FD_CHECK(fd_msg_avp_hdr(a, &h));
tid.s = (char *)h->avp_value->os.data;
tid.len = (int)h->avp_value->os.len;

LM_DBG("%d/%d reply %d, Transaction-Id: %.*s\n", hdr->msg_appl,
hdr->msg_code, rc, tid.len, tid.s);

prpl_cond = (struct dm_cond **)hash_find_key(pending_replies, tid);
if (!prpl_cond) {
LM_ERR("failed to match Transaction_Id %.*s to a pending request\n",
tid.len, tid.s);
goto out;
}
rpl_cond = *prpl_cond;
rpl_cond->rc = rc;

hash_remove_key(pending_replies, tid);

FD_CHECK(fd_msg_search_avp(msg, dm_dict.Error_Message, &a));
if (a) {
rpl_cond->is_error = 1;
FD_CHECK(fd_msg_avp_hdr(a, &h));
LM_DBG("transaction failed, rc: %d (%.*s)\n",
rc, (int)h->avp_value->os.len, h->avp_value->os.data);
} else {
rpl_cond->is_error = 0;
}

/* signal the blocked SIP worker that the auth result is available! */
pthread_mutex_lock(&rpl_cond->mutex);
if (rpl_cond->count == 0)
pthread_cond_signal(&rpl_cond->cond);
rpl_cond->count += 1;
pthread_mutex_unlock(&rpl_cond->mutex);

out:
FD_CHECK(fd_msg_free(msg));
*_msg = NULL;
return 0;
}


int dm_register_callbacks(void)
{
struct disp_when data;
Expand Down Expand Up @@ -240,6 +304,28 @@ int dm_register_callbacks(void)
FD_CHECK(fd_disp_app_support(data.app, NULL, 0, 1 ));
}

/* custom commands */
{
int i;

memset(&data, 0, sizeof data);

for (i = 0; i < n_app_ids; i++) {
/* Initialize the dictionary objects we use */
FD_CHECK_dict_search(DICT_APPLICATION, APPLICATION_BY_ID,
&app_ids[i], &data.app);

/* Register the dispatch callback */
FD_CHECK(fd_disp_register(dm_custom_cmd_reply,
DISP_HOW_APPID, &data, NULL, NULL));

/* Advertise support for the respective app */
FD_CHECK(fd_disp_app_support(data.app, NULL, 0, 1 ));

LM_DBG("registered a reply callback for App ID %d ...\n", app_ids[i]);
}
}

return 0;
}

Expand Down Expand Up @@ -1074,12 +1160,12 @@ int freeDiameter_init(void)
}


int dm_find(aaa_conn *con, aaa_map *map, int op)
int dm_find(aaa_conn *_, aaa_map *map, int op)
{
struct dict_object *obj;

if (!con || !map) {
LM_ERR("invalid arguments (%p %p)\n", con, map);
if (!map) {
LM_ERR("NULL map argument\n");
return -1;
}

Expand Down Expand Up @@ -1137,7 +1223,8 @@ int dm_find(aaa_conn *con, aaa_map *map, int op)
}


aaa_message *dm_create_message(aaa_conn *con, int msg_type)
aaa_message *_dm_create_message(aaa_conn *_, int msg_type,
unsigned int app_id, unsigned int cmd_code)
{
aaa_message *m;
struct dm_message *dm;
Expand All @@ -1162,12 +1249,20 @@ aaa_message *dm_create_message(aaa_conn *con, int msg_type)
memset(dm, 0, sizeof *dm);
INIT_LIST_HEAD(&dm->avps);
dm->am = m;
dm->app_id = app_id;
dm->cmd_code = cmd_code;

return m;
}


int dm_avp_add(aaa_conn *con, aaa_message *msg, aaa_map *avp, void *val,
aaa_message *dm_create_message(aaa_conn *_, int msg_type)
{
return _dm_create_message(_, msg_type, 0, 0);
}


int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val,
int val_length, int vendor)
{
struct {
Expand Down Expand Up @@ -1213,11 +1308,11 @@ int dm_avp_add(aaa_conn *con, aaa_message *msg, aaa_map *avp, void *val,
}


int dm_send_message(aaa_conn *con, aaa_message *req, aaa_message **reply)
int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply)
{
struct dm_message *dm;

if (!con || !req || !my_reply_cond)
if (!req || !my_reply_cond)
return -1;

dm = (struct dm_message *)(req->avpair);
Expand All @@ -1238,7 +1333,7 @@ int dm_send_message(aaa_conn *con, aaa_message *req, aaa_message **reply)

LM_DBG("message queued for sending\n");

if (req->type == AAA_AUTH) {
if (req->type == AAA_AUTH || req->type == AAA_CUSTOM) {
LM_DBG("awaiting auth reply...\n");

pthread_mutex_lock(&my_reply_cond->mutex);
Expand Down Expand Up @@ -1275,7 +1370,7 @@ void _dm_destroy_message(aaa_message *msg)
shm_free(msg);
}

int dm_destroy_message(aaa_conn *conn, aaa_message *msg)
int dm_destroy_message(aaa_conn *_, aaa_message *msg)
{
if (!msg)
return 0;
Expand Down

0 comments on commit ba0fa49

Please sign in to comment.