# HG changeset patch # User Mychaela Falconia # Date 1656289907 28800 # Node ID ccc5ab6d8388477658c20cbe74ac70086057fcf6 # Parent aea422af79ddf63c0f2ffd2d804617d7f105d57a first version of themwi-mncc for ThemWi2 diff -r aea422af79dd -r ccc5ab6d8388 .hgignore --- a/.hgignore Sun Jun 26 14:42:33 2022 -0800 +++ b/.hgignore Sun Jun 26 16:31:47 2022 -0800 @@ -2,6 +2,8 @@ \.[oa]$ +^mncc/themwi-mncc$ + ^utils/themwi-check-own$ ^utils/themwi-dump-numdb$ ^utils/themwi-short-dial$ diff -r aea422af79dd -r ccc5ab6d8388 mncc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/Makefile Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,18 @@ +CC= gcc +CFLAGS= -O2 +PROG= themwi-mncc +OBJS= call_setup.o extsock.o gsm_call.o intswitch.o main.o mncc_recv.o \ + mncc_sock.o mtsock.o +LIBS= ../libnumdb/libnumdb.a ../libutil/libutil.a +INSTBIN=/usr/local/bin + +all: ${PROG} + +${PROG}: ${OBJS} ${LIBS} + ${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} + +install: + install -c -o bin -g bin -m 755 ${PROG} ${INSTBIN} + +clean: + rm -f *.o ${PROG} errs diff -r aea422af79dd -r ccc5ab6d8388 mncc/call_setup.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/call_setup.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,220 @@ +/* + * In this module we implement setup of new calls: either new MO calls + * coming from GSM or new MT calls coming from a ThemWi call socket. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "struct.h" +#include "gsm_call.h" + +preen_msc_provided_number(nums) + struct gsm_mncc_number *nums; +{ + int len; + + len = grok_number_string(nums->number, 0); + switch (len) { + case 4: + nums->type = GSM48_TON_NET_SPEC; + nums->plan = GSM48_NPI_PRIVATE; + break; + case 11: + if (nums->number[0] != '1') + return(0); + nums->type = GSM48_TON_INTERNATIONAL; + nums->plan = GSM48_NPI_ISDN_E164; + break; + default: + return(0); + } + nums->screen = GSM48_SCRN_NETWORK; + return(1); +} + +void +reject_mo_call(callref, cause_loc, cause_val) + uint32_t callref; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_REJ_REQ; + msg.callref = callref; + mncc_set_cause(&msg, cause_loc, cause_val); + send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc)); +} + +void +process_mo_call_setup(msg) + struct gsm_mncc *msg; +{ + struct gsm_call *call; + char nanp[11]; + int res, is_nanp, is_itn, is_local; + + if (preen_msc_provided_number(&msg->calling)) + msg->fields |= MNCC_F_CALLING; + else + msg->fields &= ~MNCC_F_CALLING; + if (!(msg->fields & MNCC_F_CALLED)) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_MAND_INF); + return; + } + call = create_gsm_call(msg->callref); + if (!call) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + return; + } + /* route based on destination address */ + refresh_number_db(); + is_nanp = is_itn = 0; + switch (grok_number_string(msg->called.number, 0)) { + case 4: + if (msg->called.type != GSM48_TON_UNKNOWN && + msg->called.type != GSM48_TON_NET_SPEC && + msg->called.type != GSM48_TON_SHORT_CODE) + break; + if (msg->called.plan != GSM48_NPI_UNKNOWN && + msg->called.plan != GSM48_NPI_ISDN_E164 && + msg->called.plan != GSM48_NPI_PRIVATE) + break; + res = lookup_short_dial_number(msg->called.number, nanp); + if (!res) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); + call->gc_flag = 1; + return; + } + if (nanp[0]) { + is_nanp = 1; + msg->called.type = GSM48_TON_INTERNATIONAL; + msg->called.plan = GSM48_NPI_ISDN_E164; + msg->called.number[0] = '1'; + strcpy(msg->called.number+1, nanp); + } else { + is_itn = 1; + msg->called.type = GSM48_TON_NET_SPEC; + msg->called.plan = GSM48_NPI_PRIVATE; + } + break; + case 10: + if (msg->called.type != GSM48_TON_UNKNOWN && + msg->called.type != GSM48_TON_NATIONAL) + break; + if (msg->called.plan != GSM48_NPI_UNKNOWN && + msg->called.plan != GSM48_NPI_ISDN_E164 && + msg->called.plan != GSM48_NPI_NATIONAL) + break; + if (!is_nanp_valid_prefix(msg->called.number)) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INV_NR_FORMAT); + call->gc_flag = 1; + return; + } + is_nanp = 1; + /* canonicalize to international format */ + bcopy(msg->called.number, msg->called.number+1, 11); + msg->called.number[0] = '1'; + msg->called.type = GSM48_TON_INTERNATIONAL; + msg->called.plan = GSM48_NPI_ISDN_E164; + break; + case 11: + if (msg->called.type != GSM48_TON_UNKNOWN && + msg->called.type != GSM48_TON_INTERNATIONAL) + break; + if (msg->called.plan != GSM48_NPI_UNKNOWN && + msg->called.plan != GSM48_NPI_ISDN_E164) + break; + if (msg->called.number[0] != '1') + break; + if (!is_nanp_valid_prefix(msg->called.number+1)) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INV_NR_FORMAT); + call->gc_flag = 1; + return; + } + is_nanp = 1; + /* canonicalize to international format */ + msg->called.type = GSM48_TON_INTERNATIONAL; + msg->called.plan = GSM48_NPI_ISDN_E164; + break; + } + is_local = is_itn; + if (is_nanp && is_nanp_locally_owned(msg->called.number+1)) + is_local = 1; + /* weed out attempts to call yourself */ + if (is_local && !strcmp(msg->calling.number, msg->called.number)) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INCOMPAT_DEST); + call->gc_flag = 1; + return; + } + /* actually route the call */ + if (is_local) { + internal_switch_mo_setup(call, msg); + return; + } + /* outbound calls remain to be implemented */ + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_NO_ROUTE); + call->gc_flag = 1; +} + +static void +reject_mt_call(conn, callref, cause_loc, cause_val) + struct socket_conn *conn; + uint32_t callref; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_REJ_REQ; + msg.callref = callref; + mncc_set_cause(&msg, cause_loc, cause_val); + mncc_signal_to_socket_nocall(conn, &msg); +} + +void +process_ext_mtcall_setup(conn, msg) + struct socket_conn *conn; + struct gsm_mncc *msg; +{ + struct gsm_call *call; + + if (!(msg->fields & MNCC_F_CALLED) && !msg->imsi[0]) { + reject_mt_call(conn, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_MAND_INF); + return; + } + call = create_new_mt_call(); + if (!call) { + reject_mt_call(conn, msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + return; + } + call->socket = conn; + call->socket_ref = msg->callref; + /* forward to GSM MNCC interface */ + msg->callref = call->callref; + send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc)); +} + +preen_connected_number(msg) + struct gsm_mncc *msg; +{ + if (preen_msc_provided_number(&msg->connected)) + msg->fields |= MNCC_F_CONNECTED; + else + msg->fields &= ~MNCC_F_CONNECTED; +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/extsock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/extsock.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,229 @@ +/* + * In this module we gather functions that deal with external + * socket connections, both externally-originating MT calls + * and outbound MO calls. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "struct.h" +#include "gsm_call.h" + +void +extsock_dec_refcount(conn) + struct socket_conn *conn; +{ + if (!conn->ncalls) { + syslog(LOG_CRIT, "FATAL BUG: ncalls=0 on socket call clearing"); + exit(1); + } + conn->ncalls--; +} + +static void +send_rel_on_broken_socket(call) + struct gsm_call *call; +{ + struct gsm_mncc msg; + + bzero(&msg, sizeof(struct gsm_mncc)); + msg.msg_type = MNCC_REL_REQ; + msg.callref = call->callref; + mncc_set_cause(&msg, GSM48_CAUSE_LOC_PRN_S_LU, GSM48_CC_CAUSE_DEST_OOO); + send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc)); +} + +static void +broken_socket_clear_calls(conn) + struct socket_conn *conn; +{ + extern struct gsm_call *call_list_head; + struct gsm_call *call; + + for (call = call_list_head; call; call = call->next) { + if (call->gc_flag) + continue; + if (call->socket == conn) { + send_rel_on_broken_socket(call); + extsock_dec_refcount(conn); + call->gc_flag = 1; + } + } + if (conn->ncalls) { + syslog(LOG_CRIT, + "FATAL BUG: ncalls!=0 after broken socket call clearing"); + exit(1); + } +} + +static void +report_runt(msg) + union mncc_msg *msg; +{ + syslog(LOG_CRIT, + "MNCC message type 0x%x from ThemWi call socket is too short!", + msg->msg_type); +} + +static void +handle_setup_req(conn, msg, msglen) + struct socket_conn *conn; + struct gsm_mncc *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc)) { + report_runt(msg); + return; + } + call = find_socket_call(conn, msg->callref); + if (call) { + syslog(LOG_ERR, + "duplicate MNCC_SETUP_REQ from socket for callref 0x%x", + msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + /* further processing */ + process_ext_mtcall_setup(conn, msg); +} + +static void +handle_signaling_msg(conn, msg, msglen) + struct socket_conn *conn; + struct gsm_mncc *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc)) { + report_runt(msg); + return; + } + call = find_socket_call(conn, msg->callref); + if (!call) { + syslog(LOG_ERR, + "MNCC message from ThemWi call socket: callref 0x%x not found", + msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + /* forward to GSM MNCC interface */ + msg->callref = call->callref; + send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc)); + if (msg->msg_type == MNCC_REJ_REQ) { + extsock_dec_refcount(conn); + call->gc_flag = 1; + } +} + +static void +handle_rtp_msg(conn, msg, msglen) + struct socket_conn *conn; + struct gsm_mncc_rtp *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc_rtp)) { + report_runt(msg); + return; + } + call = find_socket_call(conn, msg->callref); + if (!call) { + syslog(LOG_ERR, + "MNCC message from ThemWi call socket: callref 0x%x not found", + msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + /* forward to GSM MNCC interface */ + msg->callref = call->callref; + send_mncc_to_gsm(&msg, sizeof(struct gsm_mncc_rtp)); +} + +void +extsock_read_select(conn) + struct socket_conn *conn; +{ + union mncc_msg msg; + int rc; + + rc = recv(conn->fd, &msg, sizeof msg, 0); + if (rc < 4) { + if (conn->ncalls) + broken_socket_clear_calls(conn); + close(conn->fd); + conn->fd = -1; + return; + } + switch (msg.msg_type) { + case MNCC_SETUP_REQ: + handle_setup_req(conn, &msg, rc); + return; + case MNCC_SETUP_RSP: + case MNCC_SETUP_COMPL_REQ: + case MNCC_CALL_PROC_REQ: + case MNCC_PROGRESS_REQ: + case MNCC_ALERT_REQ: + case MNCC_NOTIFY_REQ: + case MNCC_DISC_REQ: + case MNCC_REL_REQ: + case MNCC_FACILITY_REQ: + case MNCC_START_DTMF_RSP: + case MNCC_START_DTMF_REJ: + case MNCC_STOP_DTMF_RSP: + case MNCC_MODIFY_REQ: + case MNCC_MODIFY_RSP: + case MNCC_MODIFY_REJ: + case MNCC_HOLD_CNF: + case MNCC_HOLD_REJ: + case MNCC_RETRIEVE_CNF: + case MNCC_RETRIEVE_REJ: + case MNCC_USERINFO_REQ: + case MNCC_REJ_REQ: + handle_signaling_msg(conn, &msg, rc); + return; + case MNCC_RTP_CREATE: + case MNCC_RTP_CONNECT: + case MNCC_RTP_FREE: + handle_rtp_msg(conn, &msg, rc); + return; + default: + syslog(LOG_CRIT, + "unknown MNCC message type 0x%x from ThemWi call socket", + msg.msg_type); + } +} + +mncc_signal_to_socket(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + msg->callref = call->socket_ref; + return send(call->socket->fd, msg, sizeof(struct gsm_mncc), 0); +} + +mncc_signal_to_socket_nocall(conn, msg) + struct socket_conn *conn; + struct gsm_mncc *msg; +{ + return send(conn->fd, msg, sizeof(struct gsm_mncc), 0); +} + +mncc_rtp_to_socket(call, msg) + struct gsm_call *call; + struct gsm_mncc_rtp *msg; +{ + msg->callref = call->socket_ref; + return send(call->socket->fd, msg, sizeof(struct gsm_mncc_rtp), 0); +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/gsm_call.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/gsm_call.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,101 @@ +/* + * In this module we implement allocation, freeing and retrieval + * of gsm_call structures. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "struct.h" +#include "gsm_call.h" + +struct gsm_call *call_list_head; + +static uint32_t mt_callref; + +struct gsm_call * +find_gsm_callref(callref) + uint32_t callref; +{ + struct gsm_call *call; + + for (call = call_list_head; call; call = call->next) { + if (call->gc_flag) + continue; + if (call->callref == callref) + return call; + } + return 0; +} + +struct gsm_call * +find_socket_call(conn, ref) + struct socket_conn *conn; + uint32_t ref; +{ + struct gsm_call *call; + + for (call = call_list_head; call; call = call->next) { + if (call->gc_flag) + continue; + if (call->socket == conn && call->socket_ref == ref) + return call; + } + return 0; +} + +struct gsm_call * +create_gsm_call(callref) + uint32_t callref; +{ + struct gsm_call *call; + + call = malloc(sizeof(struct gsm_call)); + if (call) { + bzero(call, sizeof(struct gsm_call)); + call->callref = callref; + call->next = call_list_head; + call_list_head = call; + } + return call; +} + +uint32_t +alloc_mt_callref() +{ + mt_callref++; + if (mt_callref > 0x7FFFFFFF) + mt_callref = 1; + return mt_callref; +} + +struct gsm_call * +create_new_mt_call() +{ + uint32_t callref; + + for (;;) { + callref = alloc_mt_callref(); + if (!find_gsm_callref(callref)) + break; + } + return create_gsm_call(callref); +} + +gc_call_list() +{ + struct gsm_call *call, **cp; + + for (cp = &call_list_head; call = *cp; ) { + if (call->gc_flag) { + *cp = call->next; + free(call); + continue; + } + cp = &call->next; + } +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/gsm_call.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/gsm_call.h Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,7 @@ +/* extern declarations for functions in gsm_call.c module */ + +extern struct gsm_call *find_gsm_callref(); +extern struct gsm_call *find_socket_call(); +extern struct gsm_call *create_gsm_call(); +extern struct gsm_call *create_new_mt_call(); +extern uint32_t alloc_mt_callref(); diff -r aea422af79dd -r ccc5ab6d8388 mncc/intswitch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/intswitch.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,211 @@ +/* + * In this module we implement internally switched calls, + * going from one GSM subscriber to another. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "struct.h" +#include "gsm_call.h" + +void +internal_switch_mo_setup(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + struct gsm_call *mt; + struct gsm_mncc callproc; + + if (!(msg->fields & MNCC_F_BEARER_CAP)) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_INVAL_MAND_INF); + call->gc_flag = 1; + return; + } + /* same speech-only restriction as in OsmoMSC's mncc_builtin */ + if (msg->bearer_cap.transfer != GSM48_BCAP_ITCAP_SPEECH || + msg->bearer_cap.mode != GSM48_BCAP_TMOD_CIRCUIT) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + call->gc_flag = 1; + return; + } + mt = create_new_mt_call(); + if (!mt) { + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + call->gc_flag = 1; + return; + } + call->other_leg = mt; + mt->other_leg = call; + /* send call proceeding */ + bzero(&callproc, sizeof(struct gsm_mncc)); + callproc.msg_type = MNCC_CALL_PROC_REQ; + callproc.callref = call->callref; + send_mncc_to_gsm(&callproc, sizeof(struct gsm_mncc)); + /* turn MNCC_SETUP_IND into MNCC_SETUP_REQ for MT */ + msg->msg_type = MNCC_SETUP_REQ; + msg->callref = mt->callref; + msg->imsi[0] = '\0'; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); +} + +static void +handle_setup_cnf(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + struct gsm_mncc ack; + struct gsm_mncc_bridge bridge; + + /* acknowledge connect */ + bzero(&ack, sizeof(struct gsm_mncc)); + ack.msg_type = MNCC_SETUP_COMPL_REQ; + ack.callref = call->callref; + send_mncc_to_gsm(&ack, sizeof(struct gsm_mncc)); + /* do we have the far end? */ + if (!call->other_leg) + return; + msg->msg_type = MNCC_SETUP_RSP; + msg->callref = call->other_leg->callref; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + /* bridge TCH */ + bzero(&bridge, sizeof(struct gsm_mncc_bridge)); + bridge.msg_type = MNCC_BRIDGE; + bridge.callref[0] = call->callref; + bridge.callref[1] = call->other_leg->callref; + send_mncc_to_gsm(&bridge, sizeof(struct gsm_mncc_bridge)); +} + +static void +forward_to_remote(call, msg, new_msg_type) + struct gsm_call *call; + struct gsm_mncc *msg; + uint32_t new_msg_type; +{ + if (!call->other_leg) { + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + msg->msg_type = new_msg_type; + msg->callref = call->other_leg->callref; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); +} + +static void +handle_disconnect(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + struct gsm_call *remote; + + /* release on near end */ + msg->msg_type = MNCC_REL_REQ; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + /* disconnect on far end */ + remote = call->other_leg; + if (!remote) + return; + msg->msg_type = MNCC_DISC_REQ; + msg->callref = remote->callref; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + /* sever the end-to-end association */ + call->other_leg = 0; + remote->other_leg = 0; +} + +static void +handle_release(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + struct gsm_call *remote; + + /* do we have a far end? */ + remote = call->other_leg; + /* free the near end */ + call->gc_flag = 1; + /* if no remote, nothing more to do */ + if (!remote) + return; + /* send them a release request */ + msg->msg_type = MNCC_REL_REQ; + msg->callref = remote->callref; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + /* sever the end-to-end association */ + remote->other_leg = 0; +} + +void +internal_switch_mncc(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + switch (msg->msg_type) { + case MNCC_SETUP_CNF: + handle_setup_cnf(call, msg); + return; + case MNCC_ALERT_IND: + forward_to_remote(call, msg, MNCC_ALERT_REQ); + return; + case MNCC_NOTIFY_IND: + forward_to_remote(call, msg, MNCC_NOTIFY_REQ); + return; + case MNCC_USERINFO_IND: + forward_to_remote(call, msg, MNCC_USERINFO_REQ); + return; + case MNCC_DISC_IND: + handle_disconnect(call, msg); + return; + case MNCC_START_DTMF_IND: + msg->msg_type = MNCC_START_DTMF_REJ; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + return; + case MNCC_STOP_DTMF_IND: + msg->msg_type = MNCC_STOP_DTMF_RSP; + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + return; + case MNCC_MODIFY_IND: + msg->msg_type = MNCC_MODIFY_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + return; + case MNCC_HOLD_IND: + msg->msg_type = MNCC_HOLD_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + return; + case MNCC_RETRIEVE_IND: + msg->msg_type = MNCC_RETRIEVE_REJ; + mncc_set_cause(msg, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + send_mncc_to_gsm(msg, sizeof(struct gsm_mncc)); + return; + case MNCC_SETUP_COMPL_IND: + case MNCC_CALL_CONF_IND: + /* no handling needed */ + return; + case MNCC_REL_IND: + case MNCC_REJ_IND: + handle_release(call, msg); + return; + case MNCC_REL_CNF: + call->gc_flag = 1; + return; + default: + syslog(LOG_ERR, + "MNCC message type 0x%x unhandled for internal switch", + msg->msg_type); + } +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/main.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,80 @@ +/* + * Main module for ThemWi MNCC daemon. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "struct.h" + +extern int mncc_socket; +extern int mtcall_listener; +extern struct socket_conn *mtcall_socket_head; + +static int max_fd; + +update_max_fd(newfd) +{ + if (newfd > max_fd) + max_fd = newfd; +} + +main(argc, argv) + char **argv; +{ + fd_set fds; + struct socket_conn *conn, **connp; + int c; + + openlog("themwi-mncc", 0, LOG_LOCAL5); + if (read_number_db() < 0) { + fprintf(stderr, "error reading number database\n"); + exit(1); + } + if (open_mncc_socket() < 0) { + fprintf(stderr, "error connecting to GSM MNCC socket\n"); + exit(1); + } + if (create_mtcall_socket() < 0) { + fprintf(stderr, "error creating MT call socket\n"); + exit(1); + } + signal(SIGPIPE, SIG_IGN); + /* main select loop */ + for (;;) { + FD_ZERO(&fds); + FD_SET(mncc_socket, &fds); + FD_SET(mtcall_listener, &fds); + for (connp = &mtcall_socket_head; conn = *connp; ) { + if (conn->fd < 0) { + *connp = conn->next; + free(conn); + continue; + } + FD_SET(conn->fd, &fds); + connp = &conn->next; + } + c = select(max_fd+1, &fds, 0, 0, 0); + if (c < 0) { + if (errno == EINTR) + continue; + syslog(LOG_CRIT, "select: %m"); + exit(1); + } + if (FD_ISSET(mncc_socket, &fds)) + mncc_socket_select(); + if (FD_ISSET(mtcall_listener, &fds)) + mtsock_accept_handler(); + for (conn = mtcall_socket_head; conn; conn = conn->next) + if (FD_ISSET(conn->fd, &fds)) + extsock_read_select(conn); + gc_call_list(); + } +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/mncc_recv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/mncc_recv.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,188 @@ +/* + * In this module we implement initial handling of MNCC messages + * coming from OsmoMSC, dispatching them further as appropriate. + */ + +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "struct.h" +#include "gsm_call.h" + +static void +report_runt(msg) + union mncc_msg *msg; +{ + syslog(LOG_CRIT, "MNCC message type 0x%x from GSM is too short!", + msg->msg_type); +} + +static void +handle_setup_ind(msg, msglen) + struct gsm_mncc *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc)) { + report_runt(msg); + return; + } + call = find_gsm_callref(msg->callref); + if (call) { + syslog(LOG_ERR, "duplicate MNCC_SETUP_IND for callref 0x%x", + msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + /* further processing */ + process_mo_call_setup(msg); +} + +static void +handle_signaling_msg(msg, msglen) + struct gsm_mncc *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc)) { + report_runt(msg); + return; + } + call = find_gsm_callref(msg->callref); + if (!call) { + syslog(LOG_ERR, + "MNCC message type 0x%x: callref 0x%x not found", + msg->msg_type, msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + if (msg->msg_type == MNCC_SETUP_CNF) + preen_connected_number(msg); + /* dispatch according to internal switch or socket */ + if (call->socket) + mncc_signal_to_socket(call, msg); + else + internal_switch_mncc(call, msg); +} + +static void +handle_release_msg(msg, msglen) + struct gsm_mncc *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc)) { + report_runt(msg); + return; + } + call = find_gsm_callref(msg->callref); + if (!call) { + syslog(LOG_ERR, + "MNCC message type 0x%x: callref 0x%x not found", + msg->msg_type, msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + /* dispatch according to internal switch or socket */ + if (call->socket) { + mncc_signal_to_socket(call, msg); + extsock_dec_refcount(call->socket); + call->gc_flag = 1; + } else + internal_switch_mncc(call, msg); +} + +static void +handle_rtp_msg(msg, msglen) + struct gsm_mncc_rtp *msg; + unsigned msglen; +{ + struct gsm_call *call; + + if (msglen < sizeof(struct gsm_mncc_rtp)) { + report_runt(msg); + return; + } + call = find_gsm_callref(msg->callref); + if (!call) { + syslog(LOG_ERR, + "MNCC message type 0x%x: callref 0x%x not found", + msg->msg_type, msg->callref); + /* drop it like OsmoMSC's mncc_builtin does */ + return; + } + /* only for socket connections - no RTP handling for internal */ + if (call->socket) + mncc_rtp_to_socket(call, msg); +} + +static void +handle_mncc_hello(msg, msglen) + struct gsm_mncc_hello *msg; + unsigned msglen; +{ + if (msglen < sizeof(struct gsm_mncc_hello)) { + syslog(LOG_CRIT, "MNCC_SOCKET_HELLO message is too short!"); + exit(1); + } + if (msg->version != MNCC_SOCK_VERSION) { + syslog(LOG_CRIT, "MNCC hello error: version number mismatch"); + exit(1); + } + if (msg->mncc_size != sizeof(struct gsm_mncc)) { + syslog(LOG_CRIT, "MNCC hello error: mncc_size mismatch"); + exit(1); + } +} + +void +mncc_msg_from_gsm(msg, msglen) + union mncc_msg *msg; + unsigned msglen; +{ + switch (msg->msg_type) { + case MNCC_SETUP_IND: + handle_setup_ind(msg, msglen); + return; + case MNCC_SETUP_CNF: + case MNCC_SETUP_COMPL_IND: + case MNCC_CALL_CONF_IND: + case MNCC_ALERT_IND: + case MNCC_NOTIFY_IND: + case MNCC_DISC_IND: + case MNCC_FACILITY_IND: + case MNCC_START_DTMF_IND: + case MNCC_STOP_DTMF_IND: + case MNCC_MODIFY_IND: + case MNCC_MODIFY_CNF: + case MNCC_MODIFY_REJ: + case MNCC_HOLD_IND: + case MNCC_RETRIEVE_IND: + case MNCC_USERINFO_IND: + handle_signaling_msg(msg, msglen); + return; + case MNCC_REL_IND: + case MNCC_REL_CNF: + case MNCC_REJ_IND: + handle_release_msg(msg, msglen); + return; + case MNCC_RTP_CREATE: + case MNCC_RTP_CONNECT: + case MNCC_RTP_FREE: + handle_rtp_msg(msg, msglen); + return; + case MNCC_SOCKET_HELLO: + handle_mncc_hello(msg, msglen); + return; + default: + syslog(LOG_CRIT, "unknown MNCC message type 0x%x from GSM", + msg->msg_type); + } +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/mncc_sock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/mncc_sock.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,63 @@ +/* + * In this module we implement low-level handling + * of the main MNCC socket that connects to OsmoMSC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" + +static char mncc_socket_pathname[] = "/var/gsm/mncc_socket"; + +int mncc_socket; + +open_mncc_socket() +{ + struct sockaddr_un sa; + unsigned sa_len; + int rc; + + mncc_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (mncc_socket < 0) { + syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m"); + return(-1); + } + fill_sockaddr_un(mncc_socket_pathname, &sa, &sa_len); + rc = connect(mncc_socket, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + syslog(LOG_ERR, "connect to %s: %m", mncc_socket_pathname); + return(-1); + } + update_max_fd(mncc_socket); + return(0); +} + +void +mncc_socket_select() +{ + union mncc_msg msg; + int rc; + + rc = recv(mncc_socket, &msg, sizeof msg, 0); + if (rc < 0) { + syslog(LOG_CRIT, "error reading from MNCC socket: %m"); + exit(1); + } + if (rc < 4) { + syslog(LOG_CRIT, "short read from MNCC socket: %d bytes", rc); + exit(1); + } + mncc_msg_from_gsm(&msg, rc); +} + +send_mncc_to_gsm(msg, msglen) + union mncc_msg *msg; + unsigned msglen; +{ + return send(mncc_socket, msg, msglen, 0); +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/mtsock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/mtsock.c Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,75 @@ +/* + * In this module we implement the MT call socket + * to which other ThemWi system sw components connect + * in order to send MT calls toward the GSM network. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "struct.h" + +static char mtcall_socket_pathname[] = "/var/gsm/mtcall_socket"; + +int mtcall_listener; +struct socket_conn *mtcall_socket_head; + +create_mtcall_socket() +{ + struct sockaddr_un sa; + unsigned sa_len; + int rc; + + mtcall_listener = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (mtcall_listener < 0) { + syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m"); + return(-1); + } + unlink(mtcall_socket_pathname); + fill_sockaddr_un(mtcall_socket_pathname, &sa, &sa_len); + rc = bind(mtcall_listener, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + syslog(LOG_ERR, "bind to %s: %m", mtcall_socket_pathname); + return(-1); + } + rc = listen(mtcall_listener, 3); + if (rc < 0) { + syslog(LOG_CRIT, "listen on UNIX socket: %m"); + return(-1); + } + update_max_fd(mtcall_listener); + return(0); +} + +void +mtsock_accept_handler() +{ + struct sockaddr_un sa; + socklen_t sa_len; + int fd; + struct socket_conn *conn; + + fd = accept(mtcall_listener, (struct sockaddr *) &sa, &sa_len); + if (fd < 0) { + syslog(LOG_CRIT, "accept on UNIX socket: %m"); + exit(1); + } + conn = malloc(sizeof(struct socket_conn)); + if (!conn) { + syslog(LOG_CRIT, "malloc for mtcall socket conn: %m"); + close(fd); + return; + } + bzero(conn, sizeof(struct socket_conn)); + conn->fd = fd; + conn->next = mtcall_socket_head; + mtcall_socket_head = conn; + update_max_fd(fd); +} diff -r aea422af79dd -r ccc5ab6d8388 mncc/struct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/struct.h Sun Jun 26 16:31:47 2022 -0800 @@ -0,0 +1,29 @@ +/* + * This header file defines internal data structures + * for ThemWi MNCC daemon, talking to OsmoMSC. + */ + +#ifndef __STRUCT_H +#define __STRUCT_H + +struct socket_conn { + int fd; + unsigned ncalls; + struct socket_conn *next; +}; + +/* GSM call leg on MNCC-MSC side, either MO or MT */ +struct gsm_call { + /* always present */ + uint32_t callref; + /* only for internal switching */ + struct gsm_call *other_leg; + /* only for external calls */ + struct socket_conn *socket; + uint32_t socket_ref; + /* linked list management */ + int gc_flag; + struct gsm_call *next; +}; + +#endif /* include guard */