Skip to content

Commit

Permalink
aaa_diameter: Fix acc_extra / acc_leg accounting
Browse files Browse the repository at this point in the history
This commit adds support for dynamically configured AVPs in both
OpenSIPS (client-side) and freeDiameter (server-side), such that they
can be added to the sent message and parsed from the received message.

The syntax is identical to the RADIUS configuration file, e.g.:

    ATTRIBUTE out_gw     231 string
    ATTRIBUTE trunk_id   232 string
    ...

(cherry picked from commit ba7077a)
  • Loading branch information
liviuchircu committed Jun 28, 2021
1 parent df5bee8 commit 329527b
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 32 deletions.
1 change: 1 addition & 0 deletions modules/aaa_diameter/aaa_diameter.c
Expand Up @@ -32,6 +32,7 @@ static int dm_check_config(void);
static void mod_destroy(void);

char *dm_conf_filename = "freeDiameter.conf";
char *extra_avps_file;

int aaa_diameter_bind_api(aaa_prot *api);

Expand Down
67 changes: 65 additions & 2 deletions modules/aaa_diameter/aaa_impl.c
Expand Up @@ -22,6 +22,7 @@

#include "../../ut.h"
#include "../../lib/list.h"
#include "../../lib/csv.h"
#include "../../lib/hash.h"

#include "aaa_impl.h"
Expand Down Expand Up @@ -959,23 +960,85 @@ void dm_destroy(void)
}


static int parse_config_string(const char *cfgstr,
char **cfg_filename, char **extra_avps_file)
{
csv_record *items, *it;
int have_conf = 0;

items = __parse_csv_record(_str(cfgstr), 0, ';');
for (it = items; it; it = it->next) {
str dup;

if (!have_conf) {
if (pkg_nt_str_dup(&dup, &it->s) != 0) {
LM_ERR("oom\n");
return -1;
}

*cfg_filename = dup.s;
have_conf = 1;
} else {
csv_record *kv;

kv = __parse_csv_record(&it->s, 0, ':');
if (str_match(&kv->s, const_str("extra-avps-file"))) {
if (pkg_nt_str_dup(&dup, &kv->next->s) != 0) {
LM_ERR("oom\n");
return -1;
}

*extra_avps_file = dup.s;
}
}
}

LM_DBG("freeDiameter cfg file: '%s'\n", *cfg_filename);
LM_DBG("freeDiameter extra-avps-file: '%s'\n", *extra_avps_file);

free_csv_record(items);
return 0;
}


aaa_conn *dm_init_prot(str *aaa_url)
{
static str previous_url;
aaa_prot_config parsed;

if (previous_url.s && !str_match(&previous_url, aaa_url)) {
LM_ERR("please use the same Diameter URL for all modules\n");
return NULL;
}

if (!previous_url.s && pkg_str_dup(&previous_url, aaa_url) != 0) {
LM_ERR("oom\n");
return NULL;
}

if (aaa_parse_url(aaa_url, &parsed) != 0) {
LM_ERR("bad AAA URL\n");
return NULL;
}

if (strlen((char *)parsed.rest))
dm_conf_filename = (char *)parsed.rest;
if (strlen((char *)parsed.rest)) {
if (parse_config_string((char *)parsed.rest,
&dm_conf_filename, &extra_avps_file) != 0) {
LM_ERR("failed to parse config string\n");
return NULL;
}
}

if (dm_init_minimal() != 0) {
LM_ERR("failed to init freeDiameter global dictionary\n");
return NULL;
}

if (parse_extra_avps(extra_avps_file) != 0) {
LM_ERR("failed to load the 'extra-avps-file'\n");
return NULL;
}

return DM_DUMMY_HANDLE;
}

Expand Down
1 change: 1 addition & 0 deletions modules/aaa_diameter/aaa_impl.h
Expand Up @@ -98,6 +98,7 @@ struct dm_cond {
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;

int freeDiameter_init(void);
Expand Down
2 changes: 1 addition & 1 deletion modules/aaa_diameter/app_opensips/CMakeLists.txt
@@ -1,5 +1,5 @@
# The app_opensips extension
PROJECT("OpenSIPS Diameter Accounting" C)
PROJECT("OpenSIPS Diameter Applications" C)

# Find MySQL
FIND_PACKAGE(MySQL REQUIRED)
Expand Down
103 changes: 84 additions & 19 deletions modules/aaa_diameter/app_opensips/app_opensips.c
Expand Up @@ -50,6 +50,7 @@

#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>

#include "avps.h"

Expand Down Expand Up @@ -271,24 +272,40 @@ static int acc_request( struct msg ** msg, struct avp * avp, struct session * se
CHECK_FCT( fd_msg_avp_add( ans, MSG_BRW_LAST_CHILD, a ) );
}

a = NULL;
CHECK_FCT( fd_msg_search_avp ( qry, dm_dict.Event_Timestamp, &a) );
if (a) {
time_t ts;
unsigned char *bytes;
struct avp * nextavp;
CHECK_FCT( fd_msg_browse(qry, MSG_BRW_FIRST_CHILD, (void *)&nextavp, NULL) );
while (nextavp) {
CHECK_FCT( fd_msg_avp_hdr( nextavp, &h ) );

CHECK_FCT( fd_msg_avp_hdr( a, &h ) );
bytes = h->avp_value->os.data;
/* special handling for Event-Timestamp, which needs decoding */
if (h->avp_code == 55) {
time_t ts;
unsigned char *bytes;

ts = ((time_t)bytes[0] << 24) | ((time_t)bytes[1] << 16) |
((time_t)bytes[2] << 8) | (time_t)bytes[3];
bytes = h->avp_value->os.data;

ts -= 2208988800UL;
fd_log_debug("[ACC] Event-Timestamp (UNIX ts): %lu", ts);
IOV_ADD_NUMBER(ts);
}
ts = ((time_t)bytes[0] << 24) | ((time_t)bytes[1] << 16) |
((time_t)bytes[2] << 8) | (time_t)bytes[3];

/* We may also dump other data from the message, such as Accounting session Id, number of packets, ... */
ts -= 2208988800UL;
fd_log_debug("[ACC] Event-Timestamp (UNIX ts): %lu", ts);
IOV_ADD_NUMBER(ts);
goto next;
}

if (h->avp_value->os.len) {
IOV_ADD_STRING(h->avp_value->os.data, h->avp_value->os.len);
fd_log_debug("[ACC] adding AVP %d (string, '%.*s')",
h->avp_code, h->avp_value->os.len, h->avp_value->os.data);
} else {
IOV_ADD_NUMBER(h->avp_value->u32);
fd_log_debug("[ACC] adding AVP %d (integer, %d)",
h->avp_code, h->avp_value->u32);
}

next:
CHECK_FCT( fd_msg_browse(nextavp, MSG_BRW_NEXT, (void *)&nextavp, NULL) );
}

if (acc_log_cdrs && n) {
FILE *f = get_acc_log();
Expand All @@ -307,8 +324,6 @@ static int acc_request( struct msg ** msg, struct avp * avp, struct session * se
IOV_CLEANUP();
}

/* TODO: acc extra iteration */

fd_log_debug("----------------------------------------------------------------------");

/* Send the answer */
Expand Down Expand Up @@ -632,14 +647,64 @@ static int auth_request( struct msg ** msg, struct avp * avp, struct session * s
}


static int parse_conf_string(const char *confstring,
char **extra_avps_file, int *lib_mode)
{
char *p;

*extra_avps_file = NULL;
*lib_mode = 0;

if (!confstring)
goto out;

p = strcasestr(confstring, "extra-avps-file");
if (p) {
p += strlen("extra-avps-file");
while (*p != '/' && *p != '\0')
p++;

if (*p != '/') {
fd_log_error("'extra-avps-file' requires an absolute file path\n");
} else {
char *e = p;
while (!isspace(*e) && *e != ';' && *e != '\0')
e++;
*extra_avps_file = malloc(e - p + 1);
memcpy(*extra_avps_file, p, e - p);
(*extra_avps_file)[e - p] = '\0';
}
}

p = strcasestr(confstring, "library-mode");
if (p) {
p += strlen("library-mode");
while (!isdigit(*p) && *p != ';' && *p != '\0')
p++;
if (isdigit(*p) && *p != '0')
*lib_mode = 1;
}

out:
fd_log_debug("[INIT] extra-avps-file: %s", *extra_avps_file);
fd_log_debug("[INIT] library-mode: %d", *lib_mode);
return 0;
}


/* entry point: register handler for Base Accounting messages in the daemon */
static int os_entry(char * conffile)
static int os_entry(char *confstring)
{
struct disp_when data;
char *extra_avps_file;
int lib_mode;

fd_log_debug("opensips entry");
CHECK_FCT(parse_conf_string(confstring, &extra_avps_file, &lib_mode));
CHECK_FCT(parse_extra_avps(extra_avps_file));
free(extra_avps_file);

TRACE_ENTRY("%p", conffile);
if (lib_mode)
return 0;

CHECK_FCT(register_osips_avps());

Expand Down
121 changes: 121 additions & 0 deletions modules/aaa_diameter/app_opensips/avps.c
Expand Up @@ -35,6 +35,8 @@

#include <freeDiameter/extension.h>

#include "ctype.h"

#include "../peer.h"
#include "avps.h"

Expand All @@ -46,6 +48,12 @@
extern int dm_store_enumval(const char *name, int value);
#endif

#ifdef PKG_MALLOC
#include "../../../dprint.h"
#define LOG_DBG LM_DBG
#else
#define LOG_DBG fd_log_debug
#endif

static int dm_register_radius_avps(void)
{
Expand Down Expand Up @@ -423,3 +431,116 @@ int register_osips_avps(void)

return 0;
}


int parse_attr_line(char *line, ssize_t len)
{
int attr_len = strlen("ATTRIBUTE"), avp_len, avp_code;
char *avp_name, *newp, *p = line, *end = p + len;
enum dict_avp_basetype avp_type;

if (len < attr_len || strncasecmp(p, "ATTRIBUTE", attr_len))
goto error;

p += attr_len;
len -= attr_len;

while (isspace(*p)) { p++; len--; }
if (p >= end)
goto error;

avp_name = p; avp_len = 0;
while (!isspace(*p)) { p++; len--; avp_len++; }
if (p >= end)
goto error;

while (isspace(*p)) { p++; len--; }
if (p >= end)
goto error;

avp_code = strtol(p, &newp, 10);
if (avp_code == 0)
goto error;

len -= newp - p;
p = newp;

while (isspace(*p)) { p++; len--; }
if (p >= end) {
avp_type = AVP_TYPE_OCTETSTRING;
} else {
if ((len >= strlen("integer")
&& !strncasecmp(p, "integer", strlen("integer"))) ||
(len >= strlen("unsigned32")
&& !strncasecmp(p, "unsigned32", strlen("unsigned32"))))
avp_type = AVP_TYPE_UNSIGNED32;
else if ((len >= strlen("string")
&& !strncasecmp(p, "string", strlen("string"))) ||
(len >= strlen("utf8string")
&& !strncasecmp(p, "utf8string", strlen("utf8string"))))
avp_type = AVP_TYPE_OCTETSTRING;
else
goto error;
}

char *nt_name = malloc(avp_len + 1);
memcpy(nt_name, avp_name, avp_len);
nt_name[avp_len] = '\0';

struct dict_avp_data data = {
avp_code, /* Code */
0, /* Vendor */
nt_name, /* Name */
AVP_FLAG_VENDOR | AVP_FLAG_MANDATORY, /* Fixed flags */
AVP_FLAG_MANDATORY, /* Fixed flag values */
avp_type /* base type of data */
};
FD_CHECK_dict_new(DICT_AVP, &data, NULL, NULL);

LOG_DBG("registered custom AVP (%s, code %d, type %s)\n",
nt_name, avp_code, avp_type == AVP_TYPE_UNSIGNED32 ?
"integer" : "string");

free(nt_name);
return 0;
error:
printf("ERROR: failed to parse line: %s\n", line);
return -1;
}


int parse_extra_avps(const char *extra_avps_file)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;

if (!extra_avps_file)
return 0;

fp = fopen(extra_avps_file, "r");
if (!fp)
return -1;

while ((read = getline(&line, &len, fp)) != -1) {
char *p = line;

while (isspace(*p))
p++;

// comment or empty line
if (*p == '#' || p - line >= read)
continue;

if (parse_attr_line(p, read - (p - line)) == 0)
continue;

// unknown line... ignoring
}

fclose(fp);
free(line);

return 0;
}

0 comments on commit 329527b

Please sign in to comment.