From 18fa4716a502b9ce8330755aaacd617917791f5d Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Tue, 14 Apr 2009 15:05:21 +0000 Subject: [PATCH] Add service maintenance message support This adds support for two new message types: Service and Service Acknowledge. When a channel receives a service message it will either take the channel in or out of service and then send a service acknowledgment. Although not enforced here (enforced in chan_dahdi), the service messages are only supported with switch types 4ess/5ess. The required Asterisk changes will be coming next. (issue #3450) Reported by: cmaj git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@732 2fbb986a-6c06-0410-b554-c9c1f0a7f128 --- libpri.h | 22 ++++ pri.c | 17 +++ pri_internal.h | 5 + pri_q921.h | 4 + pri_q931.h | 12 +++ q921.c | 5 +- q931.c | 288 ++++++++++++++++++++++++++++++++++++++++--------- 7 files changed, 301 insertions(+), 52 deletions(-) diff --git a/libpri.h b/libpri.h index 685e347..533244a 100644 --- a/libpri.h +++ b/libpri.h @@ -81,6 +81,8 @@ #define PRI_EVENT_NOTIFY 16 /* Notification received (NOTIFY) */ #define PRI_EVENT_PROGRESS 17 /* When we get PROGRESS */ #define PRI_EVENT_KEYPAD_DIGIT 18 /* When we receive during ACTIVE state (INFORMATION) */ +#define PRI_EVENT_SERVICE 19 /* SERVICE maintenance message */ +#define PRI_EVENT_SERVICE_ACK 20 /* SERVICE maintenance acknowledgement message */ /* Simple states */ #define PRI_STATE_DOWN 0 @@ -430,6 +432,18 @@ typedef struct pri_event_keypad_digit { char digits[64]; } pri_event_keypad_digit; +typedef struct pri_event_service { + int e; + int channel; + int changestatus; +} pri_event_service; + +typedef struct pri_event_service_ack { + int e; + int channel; + int changestatus; +} pri_event_service_ack; + typedef union { int e; pri_event_generic gen; /* Generic view */ @@ -445,6 +459,8 @@ typedef union { pri_event_setup_ack setup_ack; /* SETUP_ACKNOWLEDGE structure */ pri_event_notify notify; /* Notification */ pri_event_keypad_digit digit; /* Digits that come during a call */ + pri_event_service service; /* service message */ + pri_event_service_ack service_ack; /* service acknowledgement message */ } pri_event; struct pri; @@ -556,6 +572,9 @@ int pri_restart(struct pri *pri); int pri_reset(struct pri *pri, int channel); +/* handle b-channel maintenance messages */ +extern int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus); + /* Create a new call */ q931_call *pri_new_call(struct pri *pri); @@ -601,6 +620,9 @@ int pri_mwi_activate(struct pri *pri, q931_call *c, char *caller, int callerplan /* Send an MWI deactivate request to a remote location */ int pri_mwi_deactivate(struct pri *pri, q931_call *c, char *caller, int callerplan, char *callername, int callerpres, char *called, int calledplan); +/* Set service message support flag */ +int pri_set_service_message_support(struct pri *pri, int supportflag); + #define PRI_2BCT /* Attempt to pass the channels back to the NET side if compatable and * suscribed. Sometimes called 2 bchannel transfer (2BCT) */ diff --git a/pri.c b/pri.c index 88cbbca..afc376a 100644 --- a/pri.c +++ b/pri.c @@ -110,6 +110,15 @@ int pri_get_timer(struct pri *pri, int timer) return pri->timers[timer]; } +int pri_set_service_message_support(struct pri *pri, int supportflag) +{ + if (!pri) { + return -1; + } + pri->service_message_support = supportflag; + return 0; +} + int pri_timer2idx(char *timer) { if (!strcasecmp(timer, "N200")) @@ -630,6 +639,14 @@ int pri_reset(struct pri *pri, int channel) return q931_restart(pri, channel); } +int pri_maintenance_service(struct pri *pri, int span, int channel, int changestatus) +{ + if (!pri) { + return -1; + } + return maintenance_service(pri, span, channel, changestatus); +} + q931_call *pri_new_call(struct pri *pri) { if (!pri) diff --git a/pri_internal.h b/pri_internal.h index 4bcbffb..c3435f6 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -114,6 +114,9 @@ struct pri { /* do we do overlap dialing */ int overlapdial; + /* do we support SERVICE messages */ + int service_message_support; + /* do not skip channel 16 */ int chan_mapping_logical; @@ -264,6 +267,8 @@ struct q931_call { /* Bridged call info */ q931_call *bridged_call; /* Pointer to other leg of bridged call */ + + int changestatus; /* SERVICE message changestatus */ }; extern int pri_schedule_event(struct pri *pri, int ms, void (*function)(void *data), void *data); diff --git a/pri_q921.h b/pri_q921.h index fcb81b8..2dec4c4 100644 --- a/pri_q921.h +++ b/pri_q921.h @@ -192,4 +192,8 @@ extern pri_event *q921_receive(struct pri *pri, q921_h *h, int len); extern int q921_transmit_iframe(struct pri *pri, void *buf, int len, int cr); +extern pri_event *q921_dchannel_up(struct pri *pri); + +extern pri_event *q921_dchannel_down(struct pri *pri); + #endif diff --git a/pri_q931.h b/pri_q931.h index f8b70e2..c6c2bb5 100644 --- a/pri_q931.h +++ b/pri_q931.h @@ -113,6 +113,8 @@ typedef struct q931_ie { #define Q931_PROTOCOL_DISCRIMINATOR 0x08 #define GR303_PROTOCOL_DISCRIMINATOR 0x4f +#define MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 0x03 +#define MAINTENANCE_PROTOCOL_DISCRIMINATOR_2 0x43 /* Q.931 / National ISDN Message Types */ @@ -160,6 +162,12 @@ typedef struct q931_ie { #define NATIONAL_SERVICE 0x0f #define NATIONAL_SERVICE_ACKNOWLEDGE 0x07 +#define SERVICE_CHANGE_STATUS_INSERVICE 0 +#define SERVICE_CHANGE_STATUS_LOOPBACK 1 /* not supported */ +#define SERVICE_CHANGE_STATUS_OUTOFSERVICE 2 +#define SERVICE_CHANGE_STATUS_REQCONTINUITYCHECK 3 /* not supported */ +#define SERVICE_CHANGE_STATUS_SHUTDOWN 4 /* not supported */ + /* Special codeset 0 IE */ #define NATIONAL_CHANGE_STATUS 0x1 @@ -248,6 +256,10 @@ typedef struct q931_ie { /* EuroISDN */ #define Q931_SENDING_COMPLETE 0xa1 +extern int maintenance_service(struct pri *pri, int span, int channel, int changestatus); + +extern int maintenance_service_ack(struct pri *pri, q931_call *call); + /* Q.SIG specific */ #define QSIG_IE_TRANSIT_COUNT 0x31 diff --git a/q921.c b/q921.c index 704c858..f85be2a 100644 --- a/q921.c +++ b/q921.c @@ -248,7 +248,6 @@ static int q921_ack_packet(struct pri *pri, int num) static void t203_expire(void *); static void t200_expire(void *); -static pri_event *q921_dchannel_down(struct pri *pri); static void reschedule_t200(struct pri *pri) { @@ -785,7 +784,7 @@ void q921_dump(struct pri *pri, q921_h *h, int len, int showraw, int txrx) } } -static pri_event *q921_dchannel_up(struct pri *pri) +pri_event *q921_dchannel_up(struct pri *pri) { /* Stop any SABME retransmissions */ if (pri->sabme_timer) { @@ -814,7 +813,7 @@ static pri_event *q921_dchannel_up(struct pri *pri) return &pri->ev; } -static pri_event *q921_dchannel_down(struct pri *pri) +pri_event *q921_dchannel_down(struct pri *pri) { /* Reset counters, reset sabme timer etc */ q921_reset(pri); diff --git a/q931.c b/q931.c index 59c5440..3d4d067 100644 --- a/q931.c +++ b/q931.c @@ -89,11 +89,14 @@ static struct msgtype msgs[] = { { Q931_SUSPEND, "SUSPEND" }, { Q931_SUSPEND_ACKNOWLEDGE, "SUSPEND ACKNOWLEDGE" }, { Q931_SUSPEND_REJECT, "SUSPEND REJECT" }, - - /* Maintenance */ - { NATIONAL_SERVICE, "SERVICE" }, - { NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE" }, }; +static int post_handle_q931_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c, int missingmand); + +struct msgtype maintenance_msgs[] = { + { NATIONAL_SERVICE, "SERVICE", { Q931_CHANNEL_IDENT } }, + { NATIONAL_SERVICE_ACKNOWLEDGE, "SERVICE ACKNOWLEDGE", { Q931_CHANNEL_IDENT } }, +}; +static int post_handle_maintenance_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c); static struct msgtype causes[] = { { PRI_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" }, @@ -258,6 +261,20 @@ static char *code2str(int code, struct msgtype *codes, int max) return "Unknown"; } +static char *pritype(int type) +{ + switch (type) { + case PRI_CPE: + return "CPE"; + break; + case PRI_NETWORK: + return "NET"; + break; + default: + return "UNKNOWN"; + } +} + static void call_init(struct q931_call *c) { c->forceinvert = -1; @@ -458,8 +475,8 @@ static FUNC_DUMP(dump_channel_id) if (!(ie->data[pos] & 0x10)) { /* Number specified */ pos++; - pri_message(pri, "%c Ext: %d Channel: %d ]\n", prefix, (ie->data[pos] & 0x80) >> 7, - (ie->data[pos]) & 0x7f); + pri_message(pri, "%c Ext: %d Channel: %d Type: %s]\n", prefix, (ie->data[pos] & 0x80) >> 7, + (ie->data[pos]) & 0x7f, pritype(pri->localtype)); } else { pos++; /* Map specified */ @@ -1180,6 +1197,29 @@ static FUNC_SEND(transmit_user_user) return 0; } +static FUNC_DUMP(dump_change_status) +{ + int x; + + pri_message(pri, "%c Change Status Information (len=%2d) [", prefix, len); + for (x=0; xlen; x++) { + pri_message(pri, " %02x", ie->data[x] & 0x7f); + } + pri_message(pri, " ]\n"); +} + +static FUNC_RECV(receive_change_status) +{ + call->changestatus = ie->data[0] & 0x0f; + return 0; +} + +static FUNC_SEND(transmit_change_status) +{ + ie->data[0] = 0xc0 | call->changestatus; + return 3; +} + static char *prog2str(int prog) { static struct msgtype progs[] = { @@ -2143,7 +2183,7 @@ static FUNC_DUMP(dump_transit_count) static struct ie ies[] = { /* Codeset 0 - Common */ - { 1, NATIONAL_CHANGE_STATUS, "Change Status" }, + { 1, NATIONAL_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status }, { 0, Q931_LOCKING_SHIFT, "Locking Shift", dump_shift }, { 0, Q931_BEARER_CAPABILITY, "Bearer Capability", dump_bearer_capability, receive_bearer_capability, transmit_bearer_capability }, { 0, Q931_CAUSE, "Cause", dump_cause, receive_cause, transmit_cause }, @@ -2187,7 +2227,7 @@ static struct ie ies[] = { { 1, Q931_IE_USER_USER, "User-User", dump_user_user, receive_user_user, transmit_user_user }, { 1, Q931_IE_ESCAPE_FOR_EXT, "Escape for Extension" }, { 1, Q931_IE_CALL_STATUS, "Call Status" }, - { 1, Q931_IE_CHANGE_STATUS, "Change Status" }, + { 1, Q931_IE_CHANGE_STATUS, "Change Status", dump_change_status, receive_change_status, transmit_change_status }, { 1, Q931_IE_CONNECTED_ADDR, "Connected Number", dump_connected_number }, { 1, Q931_IE_CONNECTED_NUM, "Connected Number", dump_connected_number }, { 1, Q931_IE_ORIGINAL_CALLED_NUMBER, "Original Called Number", dump_redirecting_number, receive_redirecting_number, transmit_redirecting_number }, @@ -2273,6 +2313,16 @@ static char *msg2str(int msg) return "Unknown Message Type"; } +static char *maintenance_msg2str(int msg) +{ + unsigned int x; + for (x=0; xcrlen, q931_cr(h) & 0x7FFF, q931_cr(h) & 0x7FFF, (h->crv[0] & 0x80) ? "Terminator" : "Originator"); /* Message header begins at the end of the call reference number */ mh = (q931_mh *)(h->contents + h->crlen); - pri_message(pri, "%c Message type: %s (%d)\n", c, msg2str(mh->msg), mh->msg); + if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + pri_message(pri, "%c Message Type: %s (%d)\n", c, maintenance_msg2str(mh->msg), mh->msg); + } else { + pri_message(pri, "%c Message Type: %s (%d)\n", c, msg2str(mh->msg), mh->msg); + } /* Drop length of header, including call reference */ len -= (h->crlen + 3); codeset = cur_codeset = 0; @@ -2571,12 +2625,16 @@ static int q931_handle_ie(int codeset, struct pri *pri, q931_call *c, int msg, q return -1; } -static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len) +static void init_header(struct pri *pri, q931_call *call, unsigned char *buf, q931_h **hb, q931_mh **mhb, int *len, int protodisc) { /* Returns header and message header and modifies length in place */ q931_h *h = (q931_h *)buf; q931_mh * mh; - h->pd = pri->protodisc; + if (protodisc) { + h->pd = protodisc; + } else { + h->pd = pri->protodisc; + } h->x0 = 0; /* Reserved 0 */ if (!pri->bri) { h->crlen = 2; /* Two bytes of Call Reference. Invert the top bit to make it from our sense */ @@ -2639,8 +2697,8 @@ static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[]) memset(buf, 0, sizeof(buf)); len = sizeof(buf); - init_header(pri, c, buf, &h, &mh, &len); - mh->msg = msgtype; + init_header(pri, c, buf, &h, &mh, &len, (msgtype >> 8)); + mh->msg = msgtype & 0x00ff; x=0; codeset = 0; while(ies[x] > -1) { @@ -2662,6 +2720,30 @@ static int send_message(struct pri *pri, q931_call *c, int msgtype, int ies[]) return 0; } +static int maintenance_service_ies[] = { Q931_IE_CHANGE_STATUS, Q931_CHANNEL_IDENT, -1 }; + +int maintenance_service_ack(struct pri *pri, q931_call *c) +{ + return send_message(pri, c, (MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 << 8) | NATIONAL_SERVICE_ACKNOWLEDGE, maintenance_service_ies); +} + +int maintenance_service(struct pri *pri, int span, int channel, int changestatus) +{ + struct q931_call *c; + c = q931_getcall(pri, 0x8000, 0); + if (!c) { + return -1; + } + if (channel > -1) { + channel &= 0xff; + } + c->ds1no = span; + c->channelno = channel; + c->chanflags |= FLAG_EXCLUSIVE; + c->changestatus = changestatus; + return send_message(pri, c, (MAINTENANCE_PROTOCOL_DISCRIMINATOR_1 << 8) | NATIONAL_SERVICE, maintenance_service_ies); +} + static int status_ies[] = { Q931_CAUSE, Q931_CALL_STATE, -1 }; static int q931_status(struct pri *pri, q931_call *c, int cause) @@ -3266,45 +3348,36 @@ int q931_hangup(struct pri *pri, q931_call *c, int cause) return 0; } -int q931_receive(struct pri *pri, q931_h *h, int len) +static int prepare_to_handle_maintenance_message(struct pri *pri, q931_mh *mh, q931_call *c) { - q931_mh *mh; - q931_call *c; - q931_ie *ie; - unsigned int x; - int y; - int res; - int r; - int mandies[MAX_MAND_IES]; - int missingmand; - int codeset, cur_codeset; - int last_ie[8]; - struct apdu_event *cur = NULL; - - memset(last_ie, 0, sizeof(last_ie)); - if (pri->debug & PRI_DEBUG_Q931_DUMP) - q931_dump(pri, h, len, 0); -#ifdef LIBPRI_COUNTERS - pri->q931_rxcount++; -#endif - mh = (q931_mh *)(h->contents + h->crlen); - if ((h->pd == 0x3) || (h->pd == 0x43)) { - /* This is the weird maintenance stuff. We majorly - KLUDGE this by changing byte 4 from a 0xf (SERVICE) - to a 0x7 (SERVICE ACKNOWLEDGE) */ - h->raw[h->crlen + 2] -= 0x8; - q931_xmit(pri, h, len, 1); - return 0; - } else if (h->pd != pri->protodisc) { - pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); - return 0; - } - c = q931_getcall(pri, q931_cr(h), 0); - if (!c) { - pri_error(pri, "Unable to locate call %d\n", q931_cr(h)); + if ((!pri) || (!mh) || (!c)) { return -1; } - /* Preliminary handling */ + /* SERVICE messages are a superset of messages that can take b-channels + * or entire d-channels in and out of service */ + switch(mh->msg) { + case NATIONAL_SERVICE: + case NATIONAL_SERVICE_ACKNOWLEDGE: + c->channelno = -1; + c->slotmap = -1; + c->chanflags = 0; + c->ds1no = 0; + c->ri = -1; + c->changestatus = -1; + break; + default: + pri_error(pri, "!! Don't know how to pre-handle maintenance message type '%s' (%d)\n", maintenance_msg2str(mh->msg), mh->msg); + return -1; + } + return 0; +} + +static int prepare_to_handle_q931_message(struct pri *pri, q931_mh *mh, q931_call *c) +{ + if ((!pri) || (!mh) || (!c)) { + return -1; + } + switch(mh->msg) { case Q931_RESTART: if (pri->debug & PRI_DEBUG_Q931_STATE) @@ -3433,6 +3506,57 @@ int q931_receive(struct pri *pri, q931_h *h, int len) q931_destroycall(pri,c->cr); return -1; } + return 0; +} + +int q931_receive(struct pri *pri, q931_h *h, int len) +{ + q931_mh *mh; + q931_call *c; + q931_ie *ie; + unsigned int x; + int y; + int res; + int r; + int mandies[MAX_MAND_IES]; + int missingmand; + int codeset, cur_codeset; + int last_ie[8]; + + memset(last_ie, 0, sizeof(last_ie)); + if (pri->debug & PRI_DEBUG_Q931_DUMP) + q931_dump(pri, h, len, 0); +#ifdef LIBPRI_COUNTERS + pri->q931_rxcount++; +#endif + mh = (q931_mh *)(h->contents + h->crlen); + if ((h->pd != pri->protodisc) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + pri_error(pri, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); + return 0; + } + if (((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) && (!pri->service_message_support)) { + /* Real service message support has not been enabled (and is OFF in libpri by default), + * so we have to revert to the 'traditional' KLUDGE of changing byte 4 from a 0xf (SERVICE) + * to a 0x7 (SERVICE ACKNOWLEDGE) */ + /* This is the weird maintenance stuff. We majorly + KLUDGE this by changing byte 4 from a 0xf (SERVICE) + to a 0x7 (SERVICE ACKNOWLEDGE) */ + h->raw[h->crlen + 2] -= 0x8; + q931_xmit(pri, h, len, 1); + return 0; + } + c = q931_getcall(pri, q931_cr(h), 0); + if (!c) { + pri_error(pri, "Unable to locate call %d\n", q931_cr(h)); + return -1; + } + /* Preliminary handling */ + if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + prepare_to_handle_maintenance_message(pri, mh, c); + } else { + prepare_to_handle_q931_message(pri, mh, c); + } + /* Handle IEs */ memset(mandies, 0, sizeof(mandies)); missingmand = 0; @@ -3521,6 +3645,72 @@ int q931_receive(struct pri *pri, q931_h *h, int len) } /* Post handling */ + if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { + res = post_handle_maintenance_message(pri, mh, c); + } else { + res = post_handle_q931_message(pri, mh, c, missingmand); + } + return res; +} + +static int post_handle_maintenance_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c) +{ + /* Do some maintenance stuff */ + switch (mh->msg) { + case NATIONAL_SERVICE: + if (c->channelno > 0) { + pri->ev.e = PRI_EVENT_SERVICE; + pri->ev.service.channel = c->channelno | (c->ds1no << 8); + pri->ev.service.changestatus = 0x0f & c->changestatus; + } else { + switch (0x0f & c->changestatus) { + case SERVICE_CHANGE_STATUS_INSERVICE: + pri->ev.e = PRI_EVENT_DCHAN_UP; + q921_dchannel_up(pri); + break; + case SERVICE_CHANGE_STATUS_OUTOFSERVICE: + pri->ev.e = PRI_EVENT_DCHAN_DOWN; + q921_dchannel_down(pri); + break; + default: + pri_error(pri, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus)); + return -1; + } + } + maintenance_service_ack(pri, c); + return Q931_RES_HAVEEVENT; + case NATIONAL_SERVICE_ACKNOWLEDGE: + if (c->channelno > 0) { + pri->ev.e = PRI_EVENT_SERVICE_ACK; + pri->ev.service_ack.channel = c->channelno | (c->ds1no << 8); + pri->ev.service_ack.changestatus = 0x0f & c->changestatus; + } else { + switch (0x0f & c->changestatus) { + case SERVICE_CHANGE_STATUS_INSERVICE: + pri->ev.e = PRI_EVENT_DCHAN_UP; + q921_dchannel_up(pri); + break; + case SERVICE_CHANGE_STATUS_OUTOFSERVICE: + pri->ev.e = PRI_EVENT_DCHAN_DOWN; + q921_dchannel_down(pri); + break; + default: + pri_error(pri, "!! Don't know how to handle span service change status '%d'\n", (0x0f & c->changestatus)); + return -1; + } + } + return Q931_RES_HAVEEVENT; + default: + pri_error(pri, "!! Don't know how to post-handle maintenance message type %s (%d)\n", maintenance_msg2str(mh->msg), mh->msg); + } + return -1; +} + +static int post_handle_q931_message(struct pri *pri, struct q931_mh *mh, struct q931_call *c, int missingmand) +{ + int res; + struct apdu_event *cur = NULL; + switch(mh->msg) { case Q931_RESTART: if (missingmand) {