/* Mobie Network Call Control (MNCC) cross connecting mobiles * * (C) 2017 by Andreas Eversberg * 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 . */ #include #include #include #include #include #include "../libsample/sample.h" #include "../common/debug.h" #include "../common/call.h" #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"); }