# HG changeset patch # User Mychaela Falconia # Date 1717888332 0 # Node ID 053f0468710646631c9255a2ccf02cda1fbb3d16 # Parent b161dbfffdaa832cc49895ca2fcb70ff60c855f2 mncc: initial import from old ThemWi diff -r b161dbfffdaa -r 053f04687106 mncc/call_setup.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/call_setup.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,276 @@ +/* + * 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 "../include/number_db_v2.h" +#include "../libnumdb2/lookup_func.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; + struct owned_number_rec *own; + struct short_number_rec *snum; + int 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)) { + syslog(LOG_ERR, "rejecting MO call 0x%x: no called number", + msg->callref); + 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) { + syslog(LOG_ERR, "rejecting MO call 0x%x: no memory for call", + msg->callref); + 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; + snum = numdb_lookup_short(msg->called.number); + if (!snum) { + syslog(LOG_ERR, + "rejecting MO call 0x%x: unassigned short number", + msg->callref); + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); + call->gc_flag = 1; + return; + } + switch (snum->short_num_type) { + case SHORT_NUM_TYPE_ABBREV: + is_nanp = 1; + msg->called.type = GSM48_TON_INTERNATIONAL; + msg->called.plan = GSM48_NPI_ISDN_E164; + sprintf(msg->called.number, "1%03u%03u%04u", + snum->fullnum_prefix[0], + snum->fullnum_prefix[1], snum->short_num); + break; + case SHORT_NUM_TYPE_ITN: + is_itn = 1; + msg->called.type = GSM48_TON_NET_SPEC; + msg->called.plan = GSM48_NPI_PRIVATE; + break; + case SHORT_NUM_TYPE_TEST_SINK: + syslog(LOG_ERR, + "rejecting MO call 0x%x: test sink not implemented", + msg->callref); + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + call->gc_flag = 1; + return; + default: + syslog(LOG_ERR, + "rejecting MO call 0x%x: unknown short number type 0x%02X", + msg->callref, snum->short_num_type); + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + call->gc_flag = 1; + return; + } + 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)) { + syslog(LOG_ERR, + "rejecting MO call 0x%x: invalid NANP number", + msg->callref); + 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)) { + syslog(LOG_ERR, + "rejecting MO call 0x%x: invalid NANP number", + msg->callref); + 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 && (own = numdb_lookup_nanp(msg->called.number+1))) { + is_local = 1; + switch (own->usage & NUMBER_USAGE_MASK) { + case NUMBER_USAGE_TYPE_GSM_SUB: + break; + case NUMBER_USAGE_TYPE_ALIAS: + sprintf(msg->called.number, "1%03u%03u%04u", + own->remap[0], own->remap[1], own->remap[2]); + break; + default: + syslog(LOG_ERR, + "rejecting MO call 0x%x: unassigned owned NANP", + msg->callref); + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_UNASSIGNED_NR); + call->gc_flag = 1; + return; + } + } + /* weed out attempts to call yourself */ + if (is_local && !strcmp(msg->calling.number, msg->called.number)) { + syslog(LOG_ERR, "rejecting MO call 0x%x: call to self", + msg->callref); + 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); + else + outbound_mo_setup(call, msg); +} + +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]) { + syslog(LOG_ERR, "rejecting ext MT: no called number"); + 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) { + syslog(LOG_ERR, "rejecting ext MT: no memory for 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; + conn->ncalls++; + syslog(LOG_DEBUG, "mapped socket callref 0x%x to GSM callref 0x%x", + msg->callref, call->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 b161dbfffdaa -r 053f04687106 mncc/extsock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/extsock.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,243 @@ +/* + * 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" + +extern char *mncc_msg_name(); + +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) { + syslog(LOG_WARNING, + "clearing broken socket call: GSM callref 0x%x, socket ref 0x%x", + call->callref, call->socket_ref); + 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; + } + syslog(LOG_DEBUG, "Rx MNCC_SETUP_REQ from socket, callref=0x%x", + msg->callref); + 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; + } + syslog(LOG_DEBUG, "Rx %s from socket, callref=0x%x", + mncc_msg_name(msg->msg_type), msg->callref); + 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; + } + syslog(LOG_DEBUG, "Rx %s from socket, callref=0x%x", + mncc_msg_name(msg->msg_type), msg->callref); + 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) { + syslog(LOG_ERR, "ext socket broken with calls present"); + broken_socket_clear_calls(conn); + } else + syslog(LOG_INFO, "normal closing of ext socket"); + 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 b161dbfffdaa -r 053f04687106 mncc/gsm_call.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/gsm_call.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,102 @@ +/* + * 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; + } + syslog(LOG_DEBUG, "creating new MT callref 0x%x", callref); + 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 b161dbfffdaa -r 053f04687106 mncc/gsm_call.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/gsm_call.h Sat Jun 08 23:12:12 2024 +0000 @@ -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 b161dbfffdaa -r 053f04687106 mncc/intswitch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/intswitch.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,233 @@ +/* + * 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" + +extern char *mncc_msg_name(); + +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)) { + syslog(LOG_ERR, "rejecting intsw call 0x%x: no bearer cap", + msg->callref); + 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) { + syslog(LOG_ERR, "rejecting intsw call 0x%x: bad bearer cap", + msg->callref); + 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) { + syslog(LOG_ERR, + "rejecting intsw call 0x%x: no memory for MT call", + msg->callref); + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_RESOURCE_UNAVAIL); + call->gc_flag = 1; + return; + } + syslog(LOG_DEBUG, "intsw: MO=0x%x MT=0x%x", call->callref, mt->callref); + 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) { + syslog(LOG_ERR, "intsw: missing other leg for MNCC_SETUP_CNF"); + return; + } + syslog(LOG_DEBUG, "MNCC_SETUP_CNF from 0x%x to 0x%x", call->callref, + call->other_leg->callref); + 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) { + syslog(LOG_ERR, "intsw: missing other leg for msg forwarding"); + /* 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) { + syslog(LOG_ERR, "intsw: missing other leg for MNCC_DISC_IND"); + return; + } + syslog(LOG_DEBUG, "MNCC_DISC_IND from 0x%x to 0x%x", call->callref, + remote->callref); + 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; + syslog(LOG_DEBUG, "release with remote: from 0x%x to 0x%x", + call->callref, remote->callref); + /* 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; + 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_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, "%s unhandled for internal switch", + mncc_msg_name(msg->msg_type)); + } +} diff -r b161dbfffdaa -r 053f04687106 mncc/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/main.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,87 @@ +/* + * 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; + +struct socket_conn outcall_conn; + +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); + outcall_conn.fd = -1; + /* 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; + } + if (outcall_conn.fd >= 0) + FD_SET(outcall_conn.fd, &fds); + 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); + if (outcall_conn.fd >= 0 && FD_ISSET(outcall_conn.fd, &fds)) + extsock_read_select(&outcall_conn); + gc_call_list(); + } +} diff -r b161dbfffdaa -r 053f04687106 mncc/mncc_recv.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/mncc_recv.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,198 @@ +/* + * 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" + +extern char *mncc_msg_name(); + +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; + } + syslog(LOG_DEBUG, "Rx MNCC_SETUP_IND from GSM, callref=0x%x", + msg->callref); + 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; + } + syslog(LOG_DEBUG, "Rx %s from GSM, callref=0x%x", + mncc_msg_name(msg->msg_type), msg->callref); + call = find_gsm_callref(msg->callref); + if (!call) { + syslog(LOG_ERR, "%s from GSM: callref 0x%x not found", + mncc_msg_name(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; + } + syslog(LOG_DEBUG, "Rx %s from GSM, callref=0x%x", + mncc_msg_name(msg->msg_type), msg->callref); + call = find_gsm_callref(msg->callref); + if (!call) { + syslog(LOG_ERR, "%s from GSM: callref 0x%x not found", + mncc_msg_name(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); + syslog(LOG_DEBUG, + "clearing socket call: GSM callref 0x%x, socket ref 0x%x", + call->callref, call->socket_ref); + 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; + } + syslog(LOG_DEBUG, "Rx %s from GSM, callref=0x%x", + mncc_msg_name(msg->msg_type), msg->callref); + call = find_gsm_callref(msg->callref); + if (!call) { + syslog(LOG_ERR, "%s from GSM: callref 0x%x not found", + mncc_msg_name(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 b161dbfffdaa -r 053f04687106 mncc/mncc_sock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/mncc_sock.c Sat Jun 08 23:12:12 2024 +0000 @@ -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 b161dbfffdaa -r 053f04687106 mncc/mtsock.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/mtsock.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,80 @@ +/* + * 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 +#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); + } + chmod(mtcall_socket_pathname, 0775); + 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; + + sa_len = sizeof sa; + 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; + } + syslog(LOG_INFO, "new MT call socket connection"); + bzero(conn, sizeof(struct socket_conn)); + conn->fd = fd; + conn->next = mtcall_socket_head; + mtcall_socket_head = conn; + update_max_fd(fd); +} diff -r b161dbfffdaa -r 053f04687106 mncc/outcall.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/outcall.c Sat Jun 08 23:12:12 2024 +0000 @@ -0,0 +1,69 @@ +/* + * In this module we handle routing of outbound MO calls + * to the outcall socket provided by themwi-sip-out. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/mncc.h" +#include "../include/gsm48_const.h" +#include "struct.h" + +extern struct socket_conn outcall_conn; + +static char outcall_socket_pathname[] = "/var/gsm/outcall_socket"; + +connect_outcall_socket() +{ + struct sockaddr_un sa; + unsigned sa_len; + int fd, rc; + + if (outcall_conn.fd >= 0) + return(0); + fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); + if (fd < 0) { + syslog(LOG_CRIT, "socket(AF_UNIX, SOCK_SEQPACKET, 0): %m"); + return(-1); + } + fill_sockaddr_un(outcall_socket_pathname, &sa, &sa_len); + rc = connect(fd, (struct sockaddr *) &sa, sa_len); + if (rc < 0) { + syslog(LOG_ERR, "connect to %s: %m", outcall_socket_pathname); + close(fd); + return(-1); + } + update_max_fd(fd); + outcall_conn.fd = fd; + return(0); +} + +void +outbound_mo_setup(call, msg) + struct gsm_call *call; + struct gsm_mncc *msg; +{ + int rc; + + rc = connect_outcall_socket(); + if (rc < 0) { + syslog(LOG_ERR, "rejecting MO call 0x%x: outbound gateway down", + msg->callref); + reject_mo_call(msg->callref, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_DEST_OOO); + call->gc_flag = 1; + return; + } + call->socket = &outcall_conn; + call->socket_ref = msg->callref; + outcall_conn.ncalls++; + mncc_signal_to_socket(call, msg); +} diff -r b161dbfffdaa -r 053f04687106 mncc/struct.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mncc/struct.h Sat Jun 08 23:12:12 2024 +0000 @@ -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 */