FreeCalypso > hg > themwi-system-sw
changeset 60:02761f1ae5e5
sip-in INVITE processing: got as far as CRCX completion
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 11 Sep 2022 15:42:54 -0800 |
parents | bea761629c5b |
children | e12036337412 |
files | sip-in/Makefile sip-in/call.h sip-in/call_list.c sip-in/invite.c sip-in/mgw_ops.c sip-in/mgw_sock.c sip-in/sip_uas.c |
diffstat | 7 files changed, 518 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/sip-in/Makefile Thu Sep 08 14:53:18 2022 -0800 +++ b/sip-in/Makefile Sun Sep 11 15:42:54 2022 -0800 @@ -1,8 +1,8 @@ CC= gcc CFLAGS= -O2 PROG= themwi-sip-in -OBJS= invite.o main.o mgw_sock.o mncc_sock.o readconf.o sip_log.o sip_uas.o \ - sip_udp.o +OBJS= call_list.o invite.o main.o mgw_ops.o mgw_sock.o mncc_sock.o readconf.o\ + sip_log.o sip_uas.o sip_udp.o LIBS= ../libnumdb/libnumdb.a ../libsip/libsip.a ../libutil/libutil.a INSTBIN=/usr/local/bin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-in/call.h Sun Sep 11 15:42:54 2022 -0800 @@ -0,0 +1,65 @@ +/* + * struct call defined in this header file is the big daddy: + * it is the main call state structure for themwi-sip-in. + */ + +struct call { + /* call list management */ + char *sip_call_id; + struct call *next; + /* filled from initial INVITE */ + struct sockaddr_in udp_sin; + char *invite_from; + char *invite_to; + char *invite_via; + unsigned invite_cseq; + char called_nanp[11]; + char *from_uri; + unsigned from_uri_len; + char *from_user; + unsigned from_user_len; + int use_100rel; + /* PSTN side RTP info */ + struct sockaddr_in pstn_rtp_local; + struct sockaddr_in pstn_rtp_remote; + int use_pcma; + /* GSM side RTP info */ + struct sockaddr_storage gsm_rtp_osmo; + struct sockaddr_storage gsm_rtp_tmgw; + uint32_t gsm_payload_type; + uint32_t gsm_payload_msg_type; + /* state machines */ + uint32_t overall_state; + uint32_t sip_state; + uint32_t mgw_state; + uint32_t mgw_ep_id; + uint32_t mgw_xact; + uint32_t mgw_xact_id; + uint32_t sdp_addend; + char invite_fail[80]; + unsigned sip_tx_count; +}; + +#define OVERALL_STATE_CRCX 1 +#define OVERALL_STATE_CALL_GSM 2 +#define OVERALL_STATE_ALERTING 3 +#define OVERALL_STATE_ANSWERED 4 +#define OVERALL_STATE_CONNECTED 5 +#define OVERALL_STATE_TEARDOWN 6 +#define OVERALL_STATE_DEAD_SIP 7 + +#define SIP_STATE_INVITE_PROC 1 +#define SIP_STATE_RINGING 2 +#define SIP_STATE_RINGING_PRACK 3 +#define SIP_STATE_INVITE_200 4 +#define SIP_STATE_CONNECTED 5 +#define SIP_STATE_BYE_SENT 6 +#define SIP_STATE_INVITE_ERR 7 +#define SIP_STATE_ENDED 8 +#define SIP_STATE_MSG_SIZE_ERR 9 + +#define MGW_STATE_NO_EXIST 0 +#define MGW_STATE_ALLOCATED 1 +#define MGW_STATE_CONNECTING 2 +#define MGW_STATE_COMPLETE 3 +#define MGW_STATE_DELETING 4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-in/call_list.c Sun Sep 11 15:42:54 2022 -0800 @@ -0,0 +1,28 @@ +/* + * In this module we implement call list management for themwi-sip-in. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <syslog.h> +#include "call.h" + +struct call *call_list; + +struct call * +find_call_by_sip_id(sought_id) + char *sought_id; +{ + struct call *call; + + for (call = call_list; call; call = call->next) + if (!strcmp(call->sip_call_id, sought_id)) + return call; + return 0; +}
--- a/sip-in/invite.c Thu Sep 08 14:53:18 2022 -0800 +++ b/sip-in/invite.c Sun Sep 11 15:42:54 2022 -0800 @@ -5,26 +5,84 @@ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> +#include <arpa/inet.h> #include <stdio.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <syslog.h> -#include <unistd.h> #include "../libsip/parse.h" #include "../libsip/uas_basic.h" #include "../libsip/grok_from.h" #include "../libsip/req_supp.h" #include "../libsip/sdp.h" #include "../libsip/out_msg.h" +#include "call.h" extern struct in_addr sip_bind_ip; +extern unsigned sip_bind_port; extern int cfg_use_100rel; +extern struct call *call_list; +extern struct call *find_call_by_sip_id(); extern char *get_single_header(); -void -handle_sip_invite(req, ess, sin) +fill_invite_resp_from_call(msg, call) + struct sip_msg_out *msg; + struct call *call; +{ + char cseq_str[32]; + int rc; + + rc = out_msg_add_header(msg, "From", call->invite_from); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "To", call->invite_to); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Call-ID", call->sip_call_id); + if (rc < 0) + return rc; + sprintf(cseq_str, "%u INVITE", call->invite_cseq); + rc = out_msg_add_header(msg, "CSeq", cseq_str); + if (rc < 0) + return rc; + return out_msg_add_header(msg, "Via", call->invite_via); +} + +fill_invite_200_resp(msg, call) + struct sip_msg_out *msg; + struct call *call; +{ + char contact_str[80]; + struct sdp_gen sdp; + int rc; + + start_response_out_msg(msg, "200 CONNECT"); + rc = fill_invite_resp_from_call(msg, call); + if (rc < 0) + return rc; + sprintf(contact_str, "<sip:%s:%u;transport=udp>", + inet_ntoa(sip_bind_ip), sip_bind_port); + rc = out_msg_add_header(msg, "Contact", contact_str); + if (rc < 0) + return rc; + rc = out_msg_add_header(msg, "Content-Type", "application/sdp"); + if (rc < 0) + return rc; + bzero(&sdp, sizeof sdp); + sdp.conn_ip = call->pstn_rtp_local.sin_addr; + sdp.conn_port = ntohs(call->pstn_rtp_local.sin_port); + sdp.codec_mask = call->use_pcma ? SDP_CODEC_MASK_PCMA + : SDP_CODEC_MASK_PCMU; + sdp.session_id = (sdp.conn_port << 16) | call->sdp_addend; + sdp.owner_ip = sip_bind_ip; + return out_msg_finish_sdp(msg, &sdp); +} + +static void +invite_new_call(req, ess, sin) struct sip_pkt_rx *req; struct uas_parse_hdrs *ess; struct sockaddr_in *sin; @@ -37,9 +95,11 @@ int ext_100rel_req, ext_100rel_sup, use_100rel, use_pcma; struct sdp_parse sdp_parse; struct sdp_gen sdp_gen; + struct call *call; + char *dp; + unsigned copylen; int rc; - /* check for existing Call-ID will go here */ /* extract called number from Request-URI */ rc = user_from_sip_uri(req->req_uri, uri_user, 12); if (rc < 0) { @@ -48,12 +108,12 @@ error_resp: rc = add_resp_basic_headers(&resp, ess, req->req_method); if (rc < 0) { error_resp_toolong: syslog(LOG_ERR, - "INVITE error response length exceeded"); + "INVITE early error response length exceeded"); return; } out_msg_finish(&resp); sip_tx_packet(&resp, sin); - return; + return; } if (uri_user[0] == '+') { if (grok_number_string(uri_user+1, 0) != 11 || @@ -156,7 +216,7 @@ if (rc < 0) goto error_resp_toolong; sip_tx_packet(&resp, sin); - return; + return; } /* SIP INVITE validation done - check if GSM service is up */ rc = connect_gsm_mtcall(); @@ -165,4 +225,149 @@ goto error_resp; } /* stateful processing begins */ + call = malloc(sizeof(struct call) + strlen(ess->call_id) + + strlen(ess->from) + strlen(ess->to) + strlen(ess->via) + + 4); + if (!call) { + syslog(LOG_CRIT, "failed malloc for incoming call!"); + start_response_out_msg(&resp, + "503 Gateway resource allocation failure"); + goto error_resp; + } + bzero(call, sizeof(struct call)); + dp = (char *)(call + 1); + copylen = strlen(ess->call_id) + 1; + call->sip_call_id = dp; + bcopy(ess->call_id, dp, copylen); + dp += copylen; + copylen = strlen(ess->from) + 1; + call->invite_from = dp; + bcopy(ess->from, dp, copylen); + dp += copylen; + copylen = strlen(ess->to) + 1; + call->invite_to = dp; + bcopy(ess->to, dp, copylen); + dp += copylen; + copylen = strlen(ess->via) + 1; + call->invite_via = dp; + bcopy(ess->via, dp, copylen); + call->invite_cseq = ess->cseq_num; + bcopy(sin, &call->udp_sin, sizeof(struct sockaddr_in)); + bcopy(called_nanp, call->called_nanp, 11); + call->from_uri = call->invite_from + (gfrom.uri - ess->from); + call->from_uri_len = gfrom.uri_len; + call->from_user = call->invite_from + (gfrom.user - ess->from); + call->from_user_len = gfrom.user_len; + call->use_100rel = use_100rel; + call->pstn_rtp_remote.sin_family = AF_INET; + call->pstn_rtp_remote.sin_addr = sdp_parse.ip_addr; + call->pstn_rtp_remote.sin_port = htons(sdp_parse.audio_port); + call->use_pcma = use_pcma; + /* generate 100 response */ + start_response_out_msg(&resp, "100 Proceeding"); + rc = fill_invite_resp_from_call(&resp, call); + if (rc < 0) { + syslog(LOG_ERR, "INVITE 100 response length exceeded"); + free(call); + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); + /* add to call list */ + call->next = call_list; + call_list = call; + /* send CRCX to TMGW */ + tmgw_send_crcx(call); + call->overall_state = OVERALL_STATE_CRCX; + call->sip_state = SIP_STATE_INVITE_PROC; } + +static void +invite_existing_call(req, ess, sin, call) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; + struct call *call; +{ + struct sip_msg_out resp; + int rc; + + if (ess->cseq_num != call->invite_cseq) { + start_response_out_msg(&resp, "501 Re-INVITE not supported"); + rc = add_resp_basic_headers(&resp, ess, req->req_method); + if (rc < 0) { + syslog(LOG_ERR, + "sending 501 Re-INVITE error: response length exceeded"); + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); + return; + } + /* it's a retransmission, not a re-INVITE */ + switch (call->sip_state) { + case SIP_STATE_INVITE_PROC: + start_response_out_msg(&resp, "100 Proceeding"); + fill_invite_resp_from_call(&resp, call); + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); + return; + case SIP_STATE_RINGING: + case SIP_STATE_RINGING_PRACK: + start_response_out_msg(&resp, "180 Ringing"); + fill_invite_resp_from_call(&resp, call); + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); + return; + case SIP_STATE_INVITE_200: + case SIP_STATE_CONNECTED: + fill_invite_200_resp(&resp, call); + sip_tx_packet(&resp, sin); + return; + case SIP_STATE_INVITE_ERR: + start_response_out_msg(&resp, call->invite_fail); + fill_invite_resp_from_call(&resp, call); + out_msg_finish(&resp); + sip_tx_packet(&resp, sin); + return; + default: + /* silently discard */ + return; + } +} + +void +handle_sip_invite(req, ess, sin) + struct sip_pkt_rx *req; + struct uas_parse_hdrs *ess; + struct sockaddr_in *sin; +{ + struct call *call; + + call = find_call_by_sip_id(ess->call_id); + if (call) + invite_existing_call(req, ess, sin, call); + else + invite_new_call(req, ess, sin); +} + +void +signal_invite_error(call) + struct call *call; +{ + struct sip_msg_out resp; + int rc; + + start_response_out_msg(&resp, call->invite_fail); + rc = fill_invite_resp_from_call(&resp, call); + if (rc < 0) { + syslog(LOG_ERR, "INVITE late error response length exceeded"); + call->sip_state = SIP_STATE_MSG_SIZE_ERR; + /* TODO: transition from TEARDOWN to DEAD_SIP */ + return; + } + out_msg_finish(&resp); + sip_tx_packet(&resp, &call->udp_sin); + call->sip_state = SIP_STATE_INVITE_ERR; + call->sip_tx_count = 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sip-in/mgw_ops.c Sun Sep 11 15:42:54 2022 -0800 @@ -0,0 +1,204 @@ +/* + * In this module we implement all transactions from themwi-sip-in + * toward themwi-mgw. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <syslog.h> +#include "../include/tmgw_ctrl.h" +#include "../include/tmgw_const.h" +#include "call.h" + +extern struct call *call_list; + +struct call * +find_call_with_mgw_xact(xact_id) + uint32_t xact_id; +{ + struct call *call; + + for (call = call_list; call; call = call->next) + if (call->mgw_xact && call->mgw_xact_id == xact_id) + return call; + return 0; +} + +uint32_t +get_new_tmgw_xact_id() +{ + static uint32_t next_xact_id; + + for (;;) { + next_xact_id++; + if (!find_call_with_mgw_xact(next_xact_id)) + return next_xact_id; + } +} + +void +tmgw_send_crcx(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_CRCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = TMGW_EP_TYPE_GATEWAY; + req.setup_mask = TMGW_CTRL_MASK_PSTN_CONN; + bcopy(&call->pstn_rtp_remote, &req.pstn_addr, + sizeof(struct sockaddr_in)); + req.pstn_payload_type = + call->use_pcma ? PSTN_CODEC_PCMA : PSTN_CODEC_PCMU; + send_req_to_tmgw(&req); + call->mgw_xact = TMGW_CTRL_OP_CRCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_mdcx_gsm_rtp(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_MDCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id;; + req.setup_mask = TMGW_CTRL_MASK_GSM_CONN; + bcopy(&call->gsm_rtp_osmo, &req.gsm_addr, + sizeof(struct sockaddr_storage)); + req.gsm_payload_type = call->gsm_payload_type; + req.gsm_payload_msg_type = call->gsm_payload_msg_type; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_CONNECTING; + call->mgw_xact = TMGW_CTRL_OP_MDCX; + call->mgw_xact_id = req.transact_ref; +} + +void +tmgw_send_dlcx(call) + struct call *call; +{ + struct tmgw_ctrl_req req; + + bzero(&req, sizeof req); + req.opcode = TMGW_CTRL_OP_DLCX; + req.transact_ref = get_new_tmgw_xact_id(); + req.ep_id = call->mgw_ep_id;; + send_req_to_tmgw(&req); + call->mgw_state = MGW_STATE_DELETING; + call->mgw_xact = TMGW_CTRL_OP_DLCX; + call->mgw_xact_id = req.transact_ref; +} + +static void +handle_crcx_fail(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + call->overall_state = OVERALL_STATE_TEARDOWN; + strcpy(call->invite_fail, "503 Gateway resource allocation failure"); + signal_invite_error(call); +} + +static void +crcx_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + if (msg->res == TMGW_RESP_OK) { + call->mgw_state = MGW_STATE_ALLOCATED; + call->mgw_ep_id = msg->ep_id; + bcopy(&msg->gsm_addr, &call->gsm_rtp_tmgw, + sizeof(struct sockaddr_storage)); + bcopy(&msg->pstn_addr, &call->pstn_rtp_local, + sizeof(struct sockaddr_in)); + switch (call->overall_state) { + case OVERALL_STATE_CRCX: + /* proceed_with_call_setup(call); */ + return; + case OVERALL_STATE_TEARDOWN: + tmgw_send_dlcx(call); + return; + default: + bad_state: + syslog(LOG_CRIT, + "FATAL: invalid overall state 0x%x on CRCX response", + call->overall_state); + exit(1); + } + } else { + switch (call->overall_state) { + case OVERALL_STATE_CRCX: + handle_crcx_fail(call, msg); + return; + case OVERALL_STATE_TEARDOWN: + return; + default: + goto bad_state; + } + } +} + +static void +mdcx_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + /* will be handled later */ +} + +static void +dlcx_response(call, msg) + struct call *call; + struct tmgw_ctrl_resp *msg; +{ + if (msg->res != TMGW_RESP_OK) { + syslog(LOG_CRIT, "FATAL: TMGW DLCX failed with code 0x%x", + msg->res); + exit(1); + } + call->mgw_state = MGW_STATE_NO_EXIST; + /* TODO: transition from TEARDOWN to DEAD_SIP */ +} + +void +process_tmgw_response(msg) + struct tmgw_ctrl_resp *msg; +{ + struct call *call; + unsigned opc; + + call = find_call_with_mgw_xact(msg->transact_ref); + if (!call) { + syslog(LOG_CRIT, + "FATAL: response from TMGW xact 0x%x does not match any call", + msg->transact_ref); + exit(1); + } + opc = call->mgw_xact; + call->mgw_xact = 0; + switch (opc) { + case TMGW_CTRL_OP_CRCX: + crcx_response(call, msg); + return; + case TMGW_CTRL_OP_MDCX: + mdcx_response(call, msg); + return; + case TMGW_CTRL_OP_DLCX: + dlcx_response(call, msg); + return; + default: + syslog(LOG_CRIT, + "FATAL: invalid opcode 0x%x in call->msg_xact", opc); + exit(1); + } +}
--- a/sip-in/mgw_sock.c Thu Sep 08 14:53:18 2022 -0800 +++ b/sip-in/mgw_sock.c Sun Sep 11 15:42:54 2022 -0800 @@ -53,5 +53,11 @@ rc); exit(1); } - /* processing to be implemented */ + process_tmgw_response(&msg); } + +send_req_to_tmgw(msg) + struct tmgw_ctrl_req *msg; +{ + return send(mgw_socket, msg, sizeof(struct tmgw_ctrl_req), 0); +}
--- a/sip-in/sip_uas.c Thu Sep 08 14:53:18 2022 -0800 +++ b/sip-in/sip_uas.c Sun Sep 11 15:42:54 2022 -0800 @@ -10,7 +10,6 @@ #include <string.h> #include <strings.h> #include <syslog.h> -#include <unistd.h> #include "../libsip/parse.h" #include "../libsip/uas_basic.h" #include "../libsip/out_msg.h"