2017-10-29 06:58:20 +00:00
|
|
|
/* Mobie Network Call Control (MNCC) cross connecting mobiles
|
|
|
|
*
|
|
|
|
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2017-11-17 08:01:44 +00:00
|
|
|
#include "../common/sample.h"
|
|
|
|
#include "../common/debug.h"
|
|
|
|
#include "../common/call.h"
|
2017-10-29 06:58:20 +00:00
|
|
|
#include "cause.h"
|
|
|
|
#include "mncc.h"
|
|
|
|
#include "mncc_cross.h"
|
|
|
|
|
|
|
|
int new_callref = 0; /* toward mobile */
|
|
|
|
|
|
|
|
typedef struct cross {
|
|
|
|
struct cross *next;
|
|
|
|
int callref1;
|
|
|
|
int callref2;
|
|
|
|
} cross_t;
|
|
|
|
|
|
|
|
static cross_t *cross_head = NULL;
|
|
|
|
|
|
|
|
static int create_cross(int callref)
|
|
|
|
{
|
|
|
|
cross_t *cross;
|
|
|
|
|
|
|
|
cross = calloc(1, sizeof(*cross));
|
|
|
|
if (!cross) {
|
|
|
|
PDEBUG(DMNCC, DEBUG_ERROR, "No memory!\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
cross->callref1 = callref;
|
|
|
|
cross->callref2 = ++new_callref;
|
|
|
|
|
|
|
|
/* attach to list */
|
|
|
|
cross->next = cross_head;
|
|
|
|
cross_head = cross;
|
|
|
|
|
|
|
|
PDEBUG(DMNCC, DEBUG_INFO, "Cross connection created\n");
|
|
|
|
|
|
|
|
return cross->callref2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void destroy_cross(cross_t *cross)
|
|
|
|
{
|
|
|
|
cross_t **crossp;
|
|
|
|
|
|
|
|
/* detach from list */
|
|
|
|
crossp = &cross_head;
|
|
|
|
while (*crossp && *crossp != cross)
|
|
|
|
crossp = &((*crossp)->next);
|
|
|
|
if (!(*crossp)) {
|
|
|
|
PDEBUG(DMNCC, DEBUG_ERROR, "Transaction not in list, please fix!!\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
*crossp = cross->next;
|
|
|
|
|
|
|
|
free(cross);
|
|
|
|
|
|
|
|
PDEBUG(DMNCC, DEBUG_INFO, "Cross connection destroyed\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct queue {
|
|
|
|
struct queue *next;
|
|
|
|
int length;
|
|
|
|
uint8_t buf[0];
|
|
|
|
} queue_t;
|
|
|
|
|
|
|
|
static queue_t *queue_head;
|
|
|
|
|
|
|
|
static void cross_mncc_up(uint8_t *buf, int length);
|
|
|
|
|
|
|
|
static int cross_mncc_up_queue(uint8_t *buf, int length)
|
|
|
|
{
|
|
|
|
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
|
|
|
|
queue_t *queue, **queuep;
|
|
|
|
|
|
|
|
/* directly forward voice */
|
|
|
|
if (mncc->msg_type == ANALOG_8000HZ) {
|
|
|
|
cross_mncc_up(buf, length);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* queue all other messages */
|
|
|
|
queue = calloc(1, sizeof(*queue) + length);
|
|
|
|
if (!queue) {
|
|
|
|
PDEBUG(DMNCC, DEBUG_ERROR, "No memory!\n");
|
|
|
|
return -CAUSE_TEMPFAIL;
|
|
|
|
}
|
|
|
|
queue->length = length;
|
|
|
|
memcpy(queue->buf, buf, length);
|
|
|
|
|
|
|
|
/* add tail */
|
|
|
|
queuep = &queue_head;
|
|
|
|
while (*queuep)
|
|
|
|
queuep = &((*queuep)->next);
|
|
|
|
*queuep = queue;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cross_mncc_up(uint8_t *buf, int length)
|
|
|
|
{
|
|
|
|
struct gsm_mncc *mncc = (struct gsm_mncc *)buf;
|
|
|
|
cross_t *cross = NULL;
|
|
|
|
int callref = mncc->callref, remote = 0;
|
|
|
|
|
|
|
|
/* find cross instance */
|
|
|
|
for (cross = cross_head; cross; cross = cross->next) {
|
|
|
|
if (cross->callref1 == callref) {
|
|
|
|
remote = cross->callref2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cross->callref2 == callref) {
|
|
|
|
remote = cross->callref1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mncc->msg_type == MNCC_REL_CNF) {
|
|
|
|
if (cross)
|
|
|
|
destroy_cross(cross);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!remote && mncc->msg_type != MNCC_SETUP_IND) {
|
|
|
|
PDEBUG(DMNCC, DEBUG_ERROR, "invalid call ref.\n");
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_REL_REQ;
|
|
|
|
mncc->fields |= MNCC_F_CAUSE;
|
|
|
|
mncc->cause.location = LOCATION_USER;
|
|
|
|
mncc->cause.value = CAUSE_INVALCALLREF;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mncc->msg_type) {
|
|
|
|
case ANALOG_8000HZ:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_SETUP_IND:
|
|
|
|
remote = create_cross(callref);
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_SETUP_REQ;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_CALL_CONF_IND:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_CALL_PROC_REQ;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_ALERT_IND:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_ALERT_REQ;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_SETUP_CNF:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_SETUP_RSP;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_SETUP_COMPL_IND:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_SETUP_COMPL_REQ;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_DISC_IND:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_DISC_REQ;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
break;
|
|
|
|
case MNCC_REL_IND:
|
|
|
|
/* send down reused MNCC */
|
|
|
|
mncc->msg_type = MNCC_REL_REQ;
|
|
|
|
mncc->callref = remote;
|
|
|
|
mncc_down(buf, length);
|
|
|
|
destroy_cross(cross);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mncc_cross_handle(void)
|
|
|
|
{
|
|
|
|
queue_t *queue;
|
|
|
|
|
|
|
|
while (queue_head) {
|
|
|
|
/* remove from head */
|
|
|
|
queue = queue_head;
|
|
|
|
queue_head = queue->next;
|
|
|
|
|
|
|
|
cross_mncc_up(queue->buf, queue->length);
|
|
|
|
free(queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int mncc_cross_init(void)
|
|
|
|
{
|
|
|
|
mncc_up = cross_mncc_up_queue;
|
|
|
|
|
|
|
|
PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC crossconnect initialized, waiting for connection.\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mncc_cross_exit(void)
|
|
|
|
{
|
|
|
|
while (cross_head)
|
|
|
|
destroy_cross(cross_head);
|
|
|
|
|
|
|
|
PDEBUG(DMNCC, DEBUG_DEBUG, "MNCC crossconnect removed.\n");
|
|
|
|
}
|
|
|
|
|