From 4672e35a13b22a686cf6e6cce989e7fdb49a12d3 Mon Sep 17 00:00:00 2001 From: Dennis Grunert Date: Mon, 29 Jan 2024 02:41:28 +0100 Subject: [PATCH] Fix: AOC-D unit information length; encode AOC-E according to real traces; Add option to disable seconds in time/date IE - Fix: AOC-D unit information length was set to 1 despite its actual length - Removed optional BillingID from AOC-E data - Cross-checked AOC-D and AOC-E IEs to other systems (Audiocodes Mediant 800; traces found in public sources) - Tested with ISDN TEs: Siemens Profiset 51, T-Concept P522, Philips SOPHO ErgoLine, sphairon NT1PLUS-split; K1297 ISDN protocol tester - Attach AOC-E to DISCONNECT or RELEASE; transmit AOC-E even if zero units have been charged - Send AOC-D with zero units to indicate free of charge call - New option "time-no-seconds" disables sending current seconds within the date/time IE --- src/isdn/dss1.c | 143 ++++++++++++++++++-------------- src/isdn/isdn.c | 5 +- src/isdn/isdn.h | 13 +-- src/isdn/main.c | 10 ++- src/libmisdnuser/suppserv/fac.c | 33 ++++---- 5 files changed, 115 insertions(+), 89 deletions(-) diff --git a/src/isdn/dss1.c b/src/isdn/dss1.c index 2586492..bed1dfd 100644 --- a/src/isdn/dss1.c +++ b/src/isdn/dss1.c @@ -184,9 +184,9 @@ static void split_3pty(call_t *call) } } -/* Generate AOC-S facility IE from metering information */ +/* Send AOC-S facility msg from metering information */ #define AOCS_CURRENCY_AMOUNT_PER_UNIT 5 -static void generate_aocs_fac(call_t *call) +static void snd_msg_fac_aocs(call_t *call) { struct l3_msg *l3m; uint8_t fac_ie[256]; @@ -260,33 +260,61 @@ static void generate_aocs_fac(call_t *call) } } -/* AOC-D timer callback */ -static void aocd_timer_cb(void *data) +/* Send AOC-D facility msg */ +static void snd_msg_fac_aocd(call_t *call) +{ + struct l3_msg *l3m; + uint8_t fac_ie[256]; + struct asn1_parm fac; + + if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received) { + LOGP(DISDN, LOGL_DEBUG, "Sending AOC-D facility, subTotal units: %d\n", call->metering_total_units); + + memset(&fac, 0, sizeof(fac)); + fac.Valid = 1; + fac.comp = CompInvoke; + fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ + fac.u.inv.operationValue = Fac_AOCDChargingUnit; + fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units; + encodeFac(fac_ie, &fac); + + // sending facility + l3m = create_l3msg(); + enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); + call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m); + } +} + +/* Encode AOC-E facility */ +static void enc_ie_fac_aoce(call_t *call, struct l3_msg *l3m) { uint8_t fac_ie[256]; struct asn1_parm fac; - struct l3_msg *l3m; + + if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received) { + LOGP(DISDN, LOGL_DEBUG, "Attaching AOC-E facility, total units: %d\n", call->metering_total_units); + + memset(&fac, 0, sizeof(fac)); + fac.Valid = 1; + fac.comp = CompInvoke; + fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ + fac.u.inv.operationValue = Fac_AOCEChargingUnit; + fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units; + encodeFac(fac_ie, &fac); + + // attach facility + enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); + } +} + +/* Metering unit timer callback */ +static void metering_unit_timer_cb(void *data) +{ call_t *call = data; call->metering_total_units++; - - LOGP(DISDN, LOGL_DEBUG, "Sending next AOC-D facility, total_units=%d\n", call->metering_total_units); - - osmo_timer_schedule(&call->aocd_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000); - - memset(&fac, 0, sizeof(fac)); - fac.Valid = 1; - fac.comp = CompInvoke; - fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ - fac.u.inv.operationValue = Fac_AOCDChargingUnit; - fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units; - - encodeFac(fac_ie, &fac); - - // sending facility - l3m = create_l3msg(); - enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); - call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m); + osmo_timer_schedule(&call->metering_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000); + snd_msg_fac_aocd(call); } /* @@ -461,7 +489,8 @@ void setup_ind(call_t *call, uint32_t pid, struct l3_msg *l3m) /* reset AOC information */ call->metering_info_received = 0; - call->aocd_unit_timer_started = 0; + call->metering_unit_timer_started = 0; + call->metering_total_units = 0; new_state(call, ISDN_STATE_IN_SETUP); @@ -876,11 +905,10 @@ void disconnect_ind(call_t *call, uint32_t pid, struct l3_msg *l3m) /* send message to osmo-cc */ osmo_cc_ll_msg(&call->isdn_ep->cc_ep, call->cc_callref, msg); - /* stop AOC-D timer */ - if(call->aocd_unit_timer_started) - { - osmo_timer_del(&call->aocd_unit_timer); - call->aocd_unit_timer_started = 0; + /* stop AOC-D timer and send AOC-E facility */ + if(call->metering_unit_timer_started) { + osmo_timer_del(&call->metering_unit_timer); + call->metering_unit_timer_started = 0; } } @@ -2110,7 +2138,7 @@ skip_ies: rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); if(rc >= 0) { call->metering_info_received = 1; - generate_aocs_fac(call); + snd_msg_fac_aocs(call); } } @@ -2173,7 +2201,7 @@ void alert_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); if(rc >= 0) { call->metering_info_received = 1; - generate_aocs_fac(call); + snd_msg_fac_aocs(call); } } @@ -2187,8 +2215,6 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) char connected[33]; char display[128]; int rc; - uint8_t fac_ie[256]; - struct asn1_parm fac; /* NT-MODE in setup state we must send PROCEEDING first */ if (!call->proceeding_sent && call->isdn_ep->ntmode) { @@ -2231,7 +2257,7 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) /* date & time, in NT mode only */ if (call->isdn_ep->ntmode) { time(¤t_time); - enc_ie_date(l3m, current_time, 0); + enc_ie_date(l3m, current_time, call->isdn_ep->time_no_sec); } /* connected number */ @@ -2253,34 +2279,19 @@ void setup_rsp(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); if(rc >= 0) { call->metering_info_received = 1; - generate_aocs_fac(call); + snd_msg_fac_aocs(call); } /* AOC-D handling on connect */ - if(call->isdn_ep->ntmode && call->isdn_ep->aocd && call->metering_info_received && call->metering_connect_units > 0) - { - LOGP(DISDN, LOGL_DEBUG, "Sending first AOC-D facility, units: %d\n", call->metering_connect_units); + if(call->metering_info_received) { call->metering_total_units = call->metering_connect_units; - - memset(&fac, 0, sizeof(fac)); - fac.Valid = 1; - fac.comp = CompInvoke; - fac.u.inv.invokeId = 2; /* doesn't matter since no response is expected */ - fac.u.inv.operationValue = Fac_AOCDChargingUnit; - - fac.u.inv.o.AOCchu.recordedUnits = call->metering_total_units; - encodeFac(fac_ie, &fac); - - // sending facility - l3m = create_l3msg(); - enc_ie_facility(l3m, fac_ie + 2, fac_ie[1]); - call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_FACILITY, call->l3_pid, l3m); + snd_msg_fac_aocd(call); if(call->metering_unit_period_decisecs > 0) { - LOGP(DISDN, LOGL_DEBUG, "Scheduling AOC-D unit information every %d deciseconds.\n", call->metering_unit_period_decisecs); - call->aocd_unit_timer_started = 1; - osmo_timer_setup(&call->aocd_unit_timer, aocd_timer_cb, call); - osmo_timer_schedule(&call->aocd_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000); + LOGP(DISDN, LOGL_DEBUG, "Scheduling metering unit timer every %d deciseconds.\n", call->metering_unit_period_decisecs); + call->metering_unit_timer_started = 1; + osmo_timer_setup(&call->metering_unit_timer, metering_unit_timer_cb, call); + osmo_timer_schedule(&call->metering_unit_timer, call->metering_unit_period_decisecs / 10, (call->metering_unit_period_decisecs % 10) * 100000); } } @@ -2389,7 +2400,7 @@ void progress_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) rc = osmo_cc_get_ie_metering(msg, 0, &(call->metering_connect_units), &(call->metering_unit_period_decisecs)); if(rc >= 0) { call->metering_info_received = 1; - generate_aocs_fac(call); + snd_msg_fac_aocs(call); } } @@ -2514,17 +2525,19 @@ void disc_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) enc_ie_display(l3m, display); } - /* stop AOC-D timer */ - if(call->aocd_unit_timer_started) - { - osmo_timer_del(&call->aocd_unit_timer); - call->aocd_unit_timer_started = 0; - } + /* AOC-E */ + enc_ie_fac_aoce(call, l3m); new_state(call, ISDN_STATE_OUT_DISCONNECT); /* send message to ISDN */ call->isdn_ep->ml3->to_layer3(call->isdn_ep->ml3, MT_DISCONNECT, call->l3_pid, l3m); + + /* stop AOC-D timer and send AOC-E facility */ + if(call->metering_unit_timer_started) { + osmo_timer_del(&call->metering_unit_timer); + call->metering_unit_timer_started = 0; + } } /* CC-RELEASE REQUEST */ @@ -2557,6 +2570,9 @@ void rel_req(call_t *call, uint32_t pid, osmo_cc_msg_t *msg) enc_ie_display(l3m, display); } + /* AOC-E */ + enc_ie_fac_aoce(call, l3m); + new_state(call, ISDN_STATE_OUT_RELEASE); /* send message to ISDN */ @@ -2729,4 +2745,3 @@ void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg) osmo_cc_free_msg(msg); } - diff --git a/src/isdn/isdn.c b/src/isdn/isdn.c index 45646bf..cd435b2 100644 --- a/src/isdn/isdn.c +++ b/src/isdn/isdn.c @@ -690,7 +690,7 @@ void isdn_destroy(isdn_t *isdn_ep) static void clock_timeout(void *data); /* initialization and configuration to isdn interface instance */ -int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs) +int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs, int time_no_sec) { int rc; void *mui; @@ -728,6 +728,7 @@ int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const cha isdn_ep->serving_location = serving_location; isdn_ep->aocd = aocd; isdn_ep->aocs = aocs; + isdn_ep->time_no_sec = time_no_sec; /* channel selection list */ if (channel_out) { @@ -853,7 +854,7 @@ void call_destroy(call_t *call) free((char *)call->sdp); /* remove metering timer, if still active */ - osmo_timer_del(&call->aocd_unit_timer); + osmo_timer_del(&call->metering_unit_timer); /* detach */ call_p = &call->isdn_ep->call_list; diff --git a/src/isdn/isdn.h b/src/isdn/isdn.h index 801d0df..d2c5bc4 100644 --- a/src/isdn/isdn.h +++ b/src/isdn/isdn.h @@ -105,10 +105,11 @@ typedef struct isdn { int l1hold; int l2hold; int l2sock; + int time_no_sec; void *l2inst; ph_socket_t *ph_socket; pthread_mutex_t upqueue_lock; - struct mqueue upqueue; + struct mqueue upqueue; int upqueue_initialized; int upqueue_pipe[2]; struct osmo_fd upqueue_ofd; @@ -127,7 +128,7 @@ typedef struct isdn { struct b_timer_inst b_timer_inst[128]; uint8_t b_buffer[128][160]; int b_buffer_pos[128]; - unsigned char l2mask[16]; /* 128 bits for each tei */ + unsigned char l2mask[16]; /* 128 bits for each tei */ /* bridging */ int bridge_possible; @@ -141,7 +142,7 @@ typedef struct isdn { typedef struct call_list { struct call_list *next; - isdn_t *isdn_ep; + isdn_t *isdn_ep; /* mISDN states */ uint32_t l3_pid; @@ -184,8 +185,8 @@ typedef struct call_list { int park_len; uint8_t park_callid[8]; /* metering/AOC-D/AOC-S */ - struct osmo_timer_list aocd_unit_timer; - int aocd_unit_timer_started; + struct osmo_timer_list metering_unit_timer; + int metering_unit_timer_started; int metering_info_received; uint16_t metering_connect_units; uint16_t metering_unit_period_decisecs; @@ -214,7 +215,7 @@ int open_bchannel_out(call_t *call, unsigned int cmd, int channel, int exclusive /* isdn instance */ isdn_t *isdn_create(void); void isdn_destroy(isdn_t *isdn_ep); -int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs); +int isdn_initialize(isdn_t *isdn_ep, ph_socket_t *ph_socket, char law, const char *portname, int ntmode, int ptp, int layer1hold, int layer2hold, const char *channel_out, const char *channel_in, const char *timeouts, int tx_delay, int local_tones, int serving_location, int aocd, int aocs, int time_no_sec); int isdn_open(isdn_t *isdn_ep); void isdn_close(isdn_t *isdn_ep); void isdn_add_msn(isdn_t *isdn_ep, const char *msn); diff --git a/src/isdn/main.c b/src/isdn/main.c index 1e966cd..a696b0d 100644 --- a/src/isdn/main.c +++ b/src/isdn/main.c @@ -48,6 +48,7 @@ static int layer1hold = 0; static int layer2hold = 0; static int aocd = 0; static int aocs = 0; +static int time_no_sec = 0; static const char *channel_out = NULL; static const char *channel_in = NULL; static const char *timeouts = NULL; @@ -131,6 +132,8 @@ static void print_help() printf(" Send AOC-D charging information\n"); printf(" --aocs\n"); printf(" Send AOC-S charging information\n"); + printf(" --time-no-seconds\n"); + printf(" Send date/time IE without seconds\n"); printf(" -T --local-tones german | oldgerman | american\n"); printf(" Send locally generated tones, if not provided by remote interface.\n"); printf(" -D --debug-misdn\n"); @@ -162,6 +165,7 @@ static void print_help() #define OPT_BR_ONLY 267 #define OPT_AOCD 268 #define OPT_AOCS 269 +#define OPT_TIME_NO_SEC 270 static void add_options(void) { @@ -182,6 +186,7 @@ static void add_options(void) option_add('T', "local-tones", 1); option_add(OPT_AOCD, "aocd", 0); option_add(OPT_AOCS, "aocs", 0); + option_add(OPT_TIME_NO_SEC, "time-no-seconds", 0); option_add('D', "debug-misdn", 0); option_add(OPT_SERVING, "serving-location", 1); option_add('B', "bridging", 1); @@ -253,6 +258,9 @@ static int handle_options(int short_option, int argi, char **argv) case OPT_AOCS: aocs = 1; break; + case OPT_TIME_NO_SEC: + time_no_sec = 1; + break; case 'T': if (!strcasecmp(argv[argi], "american")) local_tones = TONES_TYPE_AMERICAN; @@ -392,7 +400,7 @@ int main(int argc, char *argv[]) isdn_tone_generate_ulaw_samples(); /* init instance */ - rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs); + rc = isdn_initialize(isdn_ep, (misdn_user) ? &ph_drv.ph_socket : NULL, law, portname, ntmode, ptp, layer1hold, layer2hold, channel_out, channel_in, timeouts, tx_delay, local_tones, serving_location, aocd, aocs, time_no_sec); if (rc) { LOGP(DISDN, LOGL_ERROR, "mISDN initializing failed!\n"); goto error; diff --git a/src/libmisdnuser/suppserv/fac.c b/src/libmisdnuser/suppserv/fac.c index 6e3cd90..6e19955 100644 --- a/src/libmisdnuser/suppserv/fac.c +++ b/src/libmisdnuser/suppserv/fac.c @@ -423,14 +423,14 @@ static int encodeAOCDChargingUnitOperation(__u8 * dest, const struct asn1_parm * p[i++] = 0x02; // Operation Tag p[i++] = 0x01; // Tag Length p[i++] = Fac_AOCDChargingUnit; // Operation Value - p[i++] = 0x30; + p[i++] = 0x30; // specificChargingUnits p[i++] = (0x09 + len); // Length p[i++] = 0xa1; // APDU p[i++] = (0x04 + len); // Length - p[i++] = 0x30; - p[i++] = (0x02 + len); - p[i++] = 0x02; // Operation Tag - p[i++] = 0x01; + p[i++] = 0x30; // recordedUnits + p[i++] = (0x02 + len); // Length + p[i++] = 0x02; // recordedNumberOfUnits + p[i++] = len; // Recorded units could take up as much as 3 bytes (0xFFFFFF) p[i] = numberOfUnits & 0xFF; @@ -441,10 +441,10 @@ static int encodeAOCDChargingUnitOperation(__u8 * dest, const struct asn1_parm * i += len; p[i++] = 0x82; // Type of charging info - p[i++] = 0x01; + p[i++] = 0x01; // Length p[i++] = 0x00; // AOC-D so Sub-Total - p[1] = (AOCD_CHARGE_UNIT_IE_LENGTH + len); // IE Payload Length + p[1] = i - 2; // IE Payload Length p[4] = p[1] - 3; // Invoke Component Length result = p[1] + 2; // Total Length of IE } @@ -481,15 +481,16 @@ static int encodeAOCEChargingUnitOperation(__u8 * dest, const struct asn1_parm * p[i++] = 0x02; // Operation Tag p[i++] = 0x01; // Tag Length p[i++] = Fac_AOCEChargingUnit; // Operation Value - p[i++] = 0xa1; // APDU - p[i++] = 0x30; - p[i++] = (0x09 + len); // Length - p[i++] = 0xa1; // APDU + p[i++] = 0x30; // ChargingUnitInfo + p[i++] = (0x08 + len); // Length + p[i++] = 0x30; // specificChargingUnits + p[i++] = (0x06 + len); // Length + p[i++] = 0xa1; // Constructor p[i++] = (0x04 + len); // Length - p[i++] = 0x30; - p[i++] = (0x02 + len); - p[i++] = 0x02; - p[i++] = 0x02; // AOC-E so Total + p[i++] = 0x30; // recordedUnits + p[i++] = (0x02 + len); // Length + p[i++] = 0x02; // recordedNumberOfUnits + p[i++] = len; // Recorded units could take up as much as 3 bytes (0xFFFFFF) p[i] = numberOfUnits & 0xFF; @@ -499,7 +500,7 @@ static int encodeAOCEChargingUnitOperation(__u8 * dest, const struct asn1_parm * p[i+2] = (numberOfUnits >> 16) & 0xFF; i += len; - p[1] = (AOCE_CHARGE_UNIT_IE_LENGTH + len); // IE Payload Length + p[1] = i - 2; // IE Payload Length p[4] = p[1] - 3; // Invoke Component Length result = p[1] + 2; // Total Length of IE