Skip to content

Commit

Permalink
aaa_diameter: Add "reply_timeout" modparam and logic
Browse files Browse the repository at this point in the history
In case the Diameter Answer never arrives after invoking
dm_send_request(), the blocked SIP worker will now eventually time out
and return a -1 error code at script level.

Default timeout: 2000 ms
  • Loading branch information
liviuchircu committed May 17, 2022
1 parent eade9fd commit bb1a39a
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 27 deletions.
52 changes: 39 additions & 13 deletions modules/aaa_diameter/aaa_diameter.c
Expand Up @@ -36,18 +36,20 @@ char *dm_conf_filename = "freeDiameter.conf";
char *extra_avps_file;

static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
str *avp_json);
str *avp_json, pv_spec_t *res_code_pv);
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 */
int dm_reply_timeout = 2000; /* ms */

static cmd_export_t cmds[]= {
{"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}},
{CMD_PARAM_STR,0,0},
{CMD_PARAM_VAR|CMD_PARAM_OPT,0,0}, {0,0,0}},
ALL_ROUTES},

{"aaa_bind_api", (cmd_function) dm_bind_api, {{0, 0, 0}}, 0},
Expand All @@ -64,6 +66,7 @@ static param_export_t params[] =
{ "fd_log_level", INT_PARAM, &fd_log_level },
{ "realm", STR_PARAM, &dm_realm.s },
{ "peer_identity", STR_PARAM, &dm_peer_identity.s },
{ "reply_timeout", INT_PARAM, &dm_reply_timeout },
{ NULL, 0, NULL },
};

Expand Down Expand Up @@ -198,11 +201,12 @@ static int dm_bind_api(aaa_prot *api)


static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
str *avp_json)
str *avp_json, pv_spec_t *res_code_pv)
{
aaa_message *dmsg;
aaa_message *dmsg = NULL;
struct dict_object *req;
cJSON *avps, *_avp;
int rc, res_code;

if (fd_dict_search(fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_CODE_R,
cmd_code, &req, ENOENT) == ENOENT) {
Expand All @@ -229,21 +233,21 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
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;
goto error;
}

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

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;
goto error;
}

cJSON *avp = _avp->child; // only work with child #0
Expand All @@ -256,7 +260,7 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
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;
goto error;
}

if (_isdigit(avp->string[0])) {
Expand All @@ -266,7 +270,7 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
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;
goto error;
}

LM_DBG("AVP:: searching AVP by int: %d\n", code);
Expand All @@ -293,22 +297,44 @@ static int dm_send_request(struct sip_msg *msg, int *app_id, int *cmd_code,
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;
goto error;
}
} 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;
goto error;
}
}
}

if (dm_send_message(NULL, dmsg, NULL) != 0) {
LM_ERR("failed to queue Diameter request for sending\n");
rc = _dm_send_message(NULL, dmsg, NULL, &res_code);

if (res_code_pv) {
pv_value_t val = {STR_NULL, 0, PV_VAL_INT|PV_TYPE_INT};
val.ri = res_code;
if (pv_set_value(msg, res_code_pv, 0, &val) != 0)
LM_ERR("failed to set output res_code pv to %d\n", res_code);
}

if (rc != 0) {
LM_ERR("Diameter request failed, Result-Code: %d\n", res_code);
cJSON_Delete(avps);
return -1;
}

cJSON_Delete(avps);
return 1;

error:
if (res_code_pv) {
pv_value_t val = {STR_NULL, 0, PV_VAL_INT|PV_TYPE_INT};
val.ri = -1;
if (pv_set_value(msg, res_code_pv, 0, &val) != 0)
LM_ERR("failed to set output res_code pv to %d\n", res_code);
}

_dm_destroy_message(dmsg);
cJSON_Delete(avps);
return -1;
}
60 changes: 47 additions & 13 deletions modules/aaa_diameter/aaa_impl.c
Expand Up @@ -192,9 +192,7 @@ static int dm_auth_reply(struct msg **_msg, struct avp * avp, struct session * s

/* 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_cond_signal(&rpl_cond->cond);
pthread_mutex_unlock(&rpl_cond->mutex);

out:
Expand Down Expand Up @@ -233,18 +231,27 @@ static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct sessi
LM_DBG("%d/%d reply %d, Transaction-Id: %.*s\n", hdr->msg_appl,
hdr->msg_code, rc, tid.len, tid.s);

FD_CHECK(fd_msg_search_avp(msg, dm_dict.Error_Message, &a));

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;

pthread_mutex_lock(&rpl_cond->mutex);
if (!hash_find_key(pending_replies, tid)) {
pthread_mutex_unlock(&rpl_cond->mutex);
LM_ERR("Transaction_Id %.*s already processed!\n", tid.len, tid.s);
goto out;
}

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));
Expand All @@ -255,10 +262,7 @@ static int dm_custom_cmd_reply(struct msg **_msg, struct avp * avp, struct sessi
}

/* 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_cond_signal(&rpl_cond->cond);
pthread_mutex_unlock(&rpl_cond->mutex);

out:
Expand Down Expand Up @@ -1308,7 +1312,8 @@ int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val,
}


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

Expand All @@ -1334,16 +1339,40 @@ int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply)
LM_DBG("message queued for sending\n");

if (req->type == AAA_AUTH || req->type == AAA_CUSTOM) {
LM_DBG("awaiting auth reply...\n");
struct timespec wait_until;
struct timeval now, wait_time, res;
int rc;

gettimeofday(&now, NULL);
wait_time.tv_sec = dm_reply_timeout / 1000;
wait_time.tv_usec = dm_reply_timeout % 1000 * 1000UL;
LM_DBG("awaiting auth reply (%ld s, %ld us)...\n", wait_time.tv_sec, wait_time.tv_usec);

timeradd(&now, &wait_time, &res);

wait_until.tv_sec = res.tv_sec;
wait_until.tv_nsec = res.tv_usec * 1000UL;

pthread_mutex_lock(&my_reply_cond->mutex);
while (my_reply_cond->count == 0)
pthread_cond_wait(&my_reply_cond->cond, &my_reply_cond->mutex);
my_reply_cond->count -= 1;
rc = pthread_cond_timedwait(&my_reply_cond->cond,
&my_reply_cond->mutex, &wait_until);
if (rc != 0) {
LM_ERR("timeout (errno: %d '%s') while awaiting Diameter "
"reply\n", rc, strerror(rc));
pthread_mutex_unlock(&my_reply_cond->mutex);

if (res_code)
*res_code = -1;
return -1;
}

pthread_mutex_unlock(&my_reply_cond->mutex);

LM_DBG("reply received, Result-Code: %d (%s)\n", my_reply_cond->rc,
my_reply_cond->is_error ? "FAILURE" : "SUCCESS");

if (res_code)
*res_code = my_reply_cond->rc;
if (my_reply_cond->is_error)
return -1;
}
Expand All @@ -1352,6 +1381,11 @@ int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply)
}


int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply)
{
return _dm_send_message(_, req, reply, NULL);
}

void _dm_destroy_message(aaa_message *msg)
{
struct list_head *it, *aux;
Expand Down
4 changes: 3 additions & 1 deletion modules/aaa_diameter/aaa_impl.h
Expand Up @@ -95,7 +95,6 @@ struct dm_avp {
struct dm_cond {
pthread_mutex_t mutex;
pthread_cond_t cond;
int count; /* helps solve signal/wait race conditions */

int rc; /* the Diameter Result-Code AVP value */
int is_error;
Expand All @@ -105,6 +104,7 @@ int init_mutex_cond(pthread_mutex_t *mutex, pthread_cond_t *cond);
extern char *dm_conf_filename;
extern char *extra_avps_file;
extern struct _dm_dict dm_dict;
extern int dm_reply_timeout;

int freeDiameter_init(void);

Expand All @@ -121,6 +121,8 @@ aaa_message *_dm_create_message(aaa_conn *_, int msg_type,
int dm_avp_add(aaa_conn *_, aaa_message *msg, aaa_map *avp, void *val,
int val_length, int vendor);
int dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **__);
int _dm_send_message(aaa_conn *_, aaa_message *req, aaa_message **reply,
int *res_code);
int dm_destroy_message(aaa_conn *con, aaa_message *msg);
void _dm_destroy_message(aaa_message *msg);

Expand Down

0 comments on commit bb1a39a

Please sign in to comment.