* the GNU General Public License
*/
+/* SC: Changes
+ -- packet retransmit mechanism (simplistic)
+ -- per endpoint/subchannel mgcp command sequencing.
+ -- better transaction handling
+ -- fixed some mem leaks
+ -- run-time configuration reload
+ -- distinguish CA and GW default MGCP ports
+ -- prevent clipping of DTMF tones in an established call
+ -- fixed a few crash scenarios in 3-way
+ -- fix for a few cases where asterisk and MGW end-up in conflicting ep states
+ -- enclose numeric IP in [] for outgoing requests
+*/
+
+/* SC: TODO
+ -- piggyback support
+ -- responseAck support
+ -- enhance retransmit mechanism (RTO calc. etc.)
+ -- embedded command support
+*/
+
#include <stdio.h>
#include <pthread.h>
#include <string.h>
static char *tdesc = "Media Gateway Control Protocol (MGCP)";
static char *config = "mgcp.conf";
-#define DEFAULT_MGCP_PORT 2427/* From RFC 2705 */
+#define DEFAULT_MGCP_GW_PORT 2427/* From RFC 2705 */
+#define DEFAULT_MGCP_CA_PORT 2727/* From RFC 2705 */
#define MGCP_MAX_PACKET 1500 /* Also from RFC 2543, should sub headers tho */
+#define DEFAULT_RETRANS 1000 /* How frequently to retransmit */
+#define MAX_RETRANS 5 /* Try only 5 times for retransmissions */
/* MGCP rtp stream modes */
#define MGCP_CX_SENDONLY 0
"inactive"
};
+/* SC: MGCP commands */
+#define MGCP_CMD_EPCF 0
+#define MGCP_CMD_CRCX 1
+#define MGCP_CMD_MDCX 2
+#define MGCP_CMD_DLCX 3
+#define MGCP_CMD_RQNT 4
+#define MGCP_CMD_NTFY 5
+#define MGCP_CMD_AUEP 6
+#define MGCP_CMD_AUCX 7
+#define MGCP_CMD_RSIP 8
+
static char context[AST_MAX_EXTENSION] = "default";
static char language[MAX_LANGUAGE] = "";
int lines; /* SDP Content */
char *line[MGCP_MAX_LINES];
char data[MGCP_MAX_PACKET];
+ int cmd; /* SC: int version of verb = command */
+ int trid; /* SC: int version of identifier = transaction id */
+ struct mgcp_request *next; /* SC: next in the queue */
};
+/* SC: obsolete
static struct mgcp_pkt {
int retrans;
struct mgcp_endpoint *owner;
char data[MGCP_MAX_PACKET];
struct mgcp_pkt *next;
} *packets = NULL;
+*/
/* MGCP message for queuing up */
struct mgcp_message {
+ struct mgcp_endpoint *owner_ep;
+ struct mgcp_subchannel *owner_sub;
+ int retrans;
+ unsigned long expire;
unsigned int seqno;
int len;
struct mgcp_message *next;
#define SUB_ALT 1
struct mgcp_subchannel {
+ /* SC: subchannel magic string.
+ Needed to prove that any subchannel pointer passed by asterisk
+ really points to a valid subchannel memory area.
+ Ugly.. But serves the purpose for the time being.
+ */
+#define MGCP_SUBCHANNEL_MAGIC "!978!"
+ char magic[6];
ast_mutex_t lock;
int id;
struct ast_channel *owner;
struct mgcp_endpoint *parent;
struct ast_rtp *rtp;
struct sockaddr_in tmpdest;
- char txident[80];
+ char txident[80]; /* FIXME SC: txident is replaced by rqnt_ident in endpoint.
+ This should be obsoleted */
char cxident[80];
char callid[80];
+/* SC: obsolete
time_t lastouttime;
int lastout;
+*/
int cxmode;
+ struct mgcp_request *cx_queue; /* SC: pending CX commands */
+ ast_mutex_t cx_queue_lock; /* SC: CX queue lock */
int nat;
int iseq; /* Not used? RTP? */
int outgoing;
int alreadygone;
+/* SC: obsolete
int messagepending;
- struct mgcp_message *msgs; /* Message queue */
+ struct mgcp_message *msgs;
+*/
struct mgcp_subchannel *next; /* for out circular linked list */
};
int immediate;
int hookstate;
int adsi;
+ char rqnt_ident[80]; /* SC: request identifier */
+ struct mgcp_request *rqnt_queue; /* SC: pending RQNT commands */
+ ast_mutex_t rqnt_queue_lock;
+ struct mgcp_request *cmd_queue; /* SC: pending commands other than RQNT */
+ ast_mutex_t cmd_queue_lock;
+ int delme; /* SC: needed for reload */
+ int needaudit; /* SC: needed for reload */
struct ast_dsp *dsp; /* XXX Should there be a dsp/subchannel? XXX */
/* owner is tracked on the subchannels, and the *sub indicates whos in charge */
/* struct ast_channel *owner; */
static struct mgcp_gateway {
/* A gateway containing one or more endpoints */
char name[80];
+ int isnamedottedip; /* SC: is the name FQDN or dotted ip */
struct sockaddr_in addr;
struct sockaddr_in defaddr;
struct in_addr ourip;
int expire; /* XXX Should we ever expire dynamic registrations? XXX */
struct mgcp_endpoint *endpoints;
struct ast_ha *ha;
+/* SC: obsolete
+ time_t lastouttime;
+ int lastout;
+ int messagepending;
+*/
+ struct mgcp_message *msgs; /* SC: gw msg queue */
+ ast_mutex_t msgs_lock; /* SC: queue lock */
+ int retransid; /* SC: retrans timer id */
+ int delme; /* SC: needed for reload */
struct mgcp_gateway *next;
} *gateways;
+static ast_mutex_t mgcp_reload_lock = AST_MUTEX_INITIALIZER;
+static int mgcp_reloading = 0;
+
static ast_mutex_t gatelock = AST_MUTEX_INITIALIZER;
static int mgcpsock = -1;
static int transmit_connection_del(struct mgcp_subchannel *sub);
static int transmit_audit_endpoint(struct mgcp_endpoint *p);
static void start_rtp(struct mgcp_subchannel *sub);
+static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ int result, int ident, struct mgcp_request *resp);
+static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub);
+static int mgcp_do_reload(void);
+static int mgcp_reload(int fd, int argc, char *argv[]);
static int has_voicemail(struct mgcp_endpoint *p)
{
ast_rtp_destroy(sub->rtp);
sub->rtp = NULL;
}
+ dump_cmd_queues(NULL, sub); /* SC */
return 0;
}
-static int __mgcp_xmit(struct mgcp_subchannel *sub, char *data, int len)
+/* SC: modified for new transport mechanism */
+static int __mgcp_xmit(struct mgcp_gateway *gw, char *data, int len)
{
- struct mgcp_endpoint *p = sub->parent;
int res;
- if (p->parent->addr.sin_addr.s_addr)
- res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->addr, sizeof(struct sockaddr_in));
+ if (gw->addr.sin_addr.s_addr)
+ res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->addr, sizeof(struct sockaddr_in));
else
- res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&p->parent->defaddr, sizeof(struct sockaddr_in));
+ res=sendto(mgcpsock, data, len, 0, (struct sockaddr *)&gw->defaddr, sizeof(struct sockaddr_in));
if (res != len) {
ast_log(LOG_WARNING, "mgcp_xmit returned %d: %s\n", res, strerror(errno));
}
if (mgcpdebug) {
ast_verbose("Transmitting:\n%s\n to %s:%d\n", req->data, inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
}
- res = __mgcp_xmit(sub, req->data, req->len);
+ res = __mgcp_xmit(p->parent, req->data, req->len);
if (res > 0)
res = 0;
return res;
}
-static void dump_queue(struct mgcp_endpoint *p)
+/* SC: modified for new transport framework */
+static void dump_queue(struct mgcp_gateway *gw, struct mgcp_endpoint *p)
{
- struct mgcp_message *cur;
- struct mgcp_subchannel *sub = p->sub;
- do {
- while(sub->msgs) {
- cur = sub->msgs;
- sub->msgs = sub->msgs->next;
- ast_log(LOG_NOTICE, "Removing message from %s@%s-%d tansaction %d\n", p->name, p->parent->name, sub->id, cur->seqno);
- free(cur);
+ struct mgcp_message *cur, *q = NULL, *w, *prev;
+
+ ast_mutex_lock(&gw->msgs_lock);
+ prev = NULL, cur = gw->msgs;
+ while (cur) {
+ if (!p || cur->owner_ep == p) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ gw->msgs = cur->next;
+
+ ast_log(LOG_NOTICE, "Removing message from %s tansaction %d\n",
+ gw->name, cur->seqno);
+
+ w = cur;
+ cur = cur->next;
+ if (q) {
+ w->next = q;
+ }
+ else {
+ w->next = NULL;
+ }
+ q = w;
}
- sub->messagepending = 0;
- sub->msgs = NULL;
- sub = sub->next;
- } while(sub != p->sub);
+ else {
+ prev = cur, cur=cur->next;
+ }
+ }
+ ast_mutex_unlock(&gw->msgs_lock);
+
+ while (q) {
+ cur = q;
+ q = q->next;
+ free(cur);
+ }
}
-static int mgcp_postrequest(struct mgcp_subchannel *sub, unsigned char *data, int len, unsigned int seqno)
+static int retrans_pkt(void *data)
+{
+ struct mgcp_gateway *gw = (struct mgcp_gateway *)data;
+ struct mgcp_message *cur, *exq = NULL, *w, *prev;
+ struct timeval tv;
+ unsigned long t;
+ int res = 0;
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ /* This shouldn't ever happen, but let's be sure */
+ ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
+ return 0;
+ }
+
+ t = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+
+ /* find out expired msgs */
+ ast_mutex_lock(&gw->msgs_lock);
+
+ prev = NULL, cur = gw->msgs;
+ while (cur) {
+ if (cur->retrans < MAX_RETRANS) {
+ cur->retrans++;
+ if (mgcpdebug) {
+ ast_verbose("Retransmitting #%d transaction %d on [%s]\n", cur->retrans, cur->seqno, gw->name);
+ }
+ __mgcp_xmit(gw, cur->buf, cur->len);
+
+ prev = cur;
+ cur = cur->next;
+ }
+ else {
+ if (prev)
+ prev->next = cur->next;
+ else
+ gw->msgs = cur->next;
+
+ ast_log(LOG_WARNING, "Maximum retries exceeded for transaction %d on [%s]\n", cur->seqno, gw->name);
+
+ w = cur;
+ cur = cur->next;
+
+ if (exq) {
+ w->next = exq;
+ }
+ else {
+ w->next = NULL;
+ }
+ exq = w;
+ }
+ }
+
+ if (!gw->msgs) {
+ gw->retransid = -1;
+ res = 0;
+ }
+ else {
+ res = 1;
+ }
+ ast_mutex_unlock(&gw->msgs_lock);
+
+ while (exq) {
+ cur = exq;
+ /* time-out transaction */
+ handle_response(cur->owner_ep, cur->owner_sub, 406, cur->seqno, NULL);
+ exq = exq->next;
+ free(cur);
+ }
+
+ return res;
+}
+
+/* SC: modified for the new transaction mechanism */
+static int mgcp_postrequest(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ unsigned char *data, int len, unsigned int seqno)
{
struct mgcp_message *msg = malloc(sizeof(struct mgcp_message) + len);
struct mgcp_message *cur;
- time_t t;
+ struct mgcp_gateway *gw = ((p && p->parent) ? p->parent : NULL);
+ struct timeval tv;
+
if (!msg) {
return -1;
}
+ if (!gw) {
+ return -1;
+ }
+/* SC
time(&t);
- if (sub->messagepending && (sub->lastouttime + 20 < t)) {
+ if (gw->messagepending && (gw->lastouttime + 20 < t)) {
ast_log(LOG_NOTICE, "Timeout waiting for response to message:%d, lastouttime: %ld, now: %ld. Dumping pending queue\n",
- sub->msgs ? sub->msgs->seqno : -1, (long) sub->lastouttime, (long) t);
+ gw->msgs ? gw->msgs->seqno : -1, (long) gw->lastouttime, (long) t);
dump_queue(sub->parent);
}
+*/
+ msg->owner_sub = sub;
+ msg->owner_ep = p;
msg->seqno = seqno;
msg->next = NULL;
msg->len = len;
+ msg->retrans = 0;
memcpy(msg->buf, data, msg->len);
- cur = sub->msgs;
+
+ ast_mutex_lock(&gw->msgs_lock);
+ cur = gw->msgs;
if (cur) {
while(cur->next)
cur = cur->next;
cur->next = msg;
} else {
- sub->msgs = msg;
+ gw->msgs = msg;
}
- if (!sub->messagepending) {
- sub->messagepending = 1;
- sub->lastout = seqno;
- sub->lastouttime = t;
- __mgcp_xmit(sub, msg->buf, msg->len);
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ /* This shouldn't ever happen, but let's be sure */
+ ast_log(LOG_NOTICE, "gettimeofday() failed!\n");
+ }
+ else {
+ msg->expire = tv.tv_sec * 1000 + tv.tv_usec / 1000 + DEFAULT_RETRANS;
+
+ if (gw->retransid == -1)
+ gw->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, (void *)gw);
+ }
+ ast_mutex_unlock(&gw->msgs_lock);
+/* SC
+ if (!gw->messagepending) {
+ gw->messagepending = 1;
+ gw->lastout = seqno;
+ gw->lastouttime = t;
+*/
+ __mgcp_xmit(gw, msg->buf, msg->len);
/* XXX Should schedule retransmission XXX */
+/* SC
} else
ast_log(LOG_DEBUG, "Deferring transmission of transaction %d\n", seqno);
+*/
return 0;
}
-static int send_request(struct mgcp_subchannel *sub, struct mgcp_request *req, unsigned int seqno)
+/* SC: modified for new transport */
+static int send_request(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ struct mgcp_request *req, unsigned int seqno)
{
- int res;
- struct mgcp_endpoint *p = sub->parent;
- if (mgcpdebug) {
- ast_verbose("Posting Request:\n%s to %s:%d\n", req->data,
- inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ int res = 0;
+ struct mgcp_request **queue, *q, *r, *t;
+ ast_mutex_t *l;
+
+ switch (req->cmd) {
+ case MGCP_CMD_DLCX:
+ queue = &sub->cx_queue;
+ l = &sub->cx_queue_lock;
+ ast_mutex_lock(l);
+ q = sub->cx_queue;
+ /* delete pending cx cmds */
+ while (q) {
+ r = q->next;
+ free(q);
+ q = r;
+ }
+ *queue = NULL;
+ break;
+
+ case MGCP_CMD_CRCX:
+ case MGCP_CMD_MDCX:
+ queue = &sub->cx_queue;
+ l = &sub->cx_queue_lock;
+ ast_mutex_lock(l);
+ break;
+
+ case MGCP_CMD_RQNT:
+ queue = &p->rqnt_queue;
+ l = &p->rqnt_queue_lock;
+ ast_mutex_lock(l);
+ break;
+
+ default:
+ queue = &p->cmd_queue;
+ l = &p->cmd_queue_lock;
+ ast_mutex_lock(l);
+ break;
}
-
- res = mgcp_postrequest(sub, req->data, req->len, seqno);
- return res;
+
+ r = (struct mgcp_request *) malloc (sizeof(struct mgcp_request));
+ if (!r) {
+ ast_log(LOG_WARNING, "Cannot post MGCP request: insufficient memory\n");
+ ast_mutex_unlock(l);
+ return -1;
+ }
+ memcpy(r, req, sizeof(struct mgcp_request));
+
+ if (!(*queue)) {
+ if (mgcpdebug) {
+ ast_verbose("Posting Request:\n%s to %s:%d\n", req->data,
+ inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+
+ res = mgcp_postrequest(p, sub, req->data, req->len, seqno);
+ }
+ else {
+ if (mgcpdebug) {
+ ast_verbose("Queueing Request:\n%s to %s:%d\n", req->data,
+ inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+ }
+
+ /* XXX SC: find tail. We could also keep tail in the data struct for faster access */
+ for (t = *queue; t && t->next; t = t->next);
+
+ r->next = NULL;
+ if (t)
+ t->next = r;
+ else
+ *queue = r;
+
+ ast_mutex_unlock(l);
+
+ return res;
}
static int mgcp_call(struct ast_channel *ast, char *dest, int timeout)
struct mgcp_subchannel *sub = ast->pvt->pvt;
struct mgcp_endpoint *p = sub->parent;
- if (mgcpdebug) {
- ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name);
- }
if (option_debug)
ast_log(LOG_DEBUG, "mgcp_hangup(%s)\n", ast->name);
if (!ast->pvt->pvt) {
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
return 0;
}
+ if (strcmp(sub->magic, MGCP_SUBCHANNEL_MAGIC)) {
+ ast_log(LOG_DEBUG, "Invalid magic. MGCP subchannel freed up already.\n");
+ return 0;
+ }
+ if (mgcpdebug) {
+ ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_hangup(%s) on %s@%s\n", ast->name, p->name, p->parent->name);
+ }
+
if ((p->dtmfinband) && (p->dsp != NULL)){
- ast_dsp_free(p->dsp);
- }
+ /* SC: check whether other channel is active. */
+ if (!sub->next->owner)
+ {
+ if (mgcpdebug) {
+ ast_verbose(VERBOSE_PREFIX_2 "MGCP free dsp on %s@%s\n", p->name, p->parent->name);
+ }
+ ast_dsp_free(p->dsp);
+ p->dsp = NULL;
+ }
+ }
ast_mutex_lock(&sub->lock);
sub->owner = NULL;
sub->rtp = NULL;
}
+ /* SC: Decrement use count */
+ ast_mutex_lock(&usecnt_lock);
+ usecnt--;
+ ast_mutex_unlock(&usecnt_lock);
+ ast_update_use_count();
+ /* SC: Decrement use count */
+
if ((p->hookstate == MGCP_ONHOOK) && (!sub->next->rtp)) {
if (has_voicemail(p)) {
if (mgcpdebug) {
} else {
transmit_modify_request(sub);
}
- ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_answer(%s) on %s@%s-%d\n", ast->name, p->name, p->parent->name, sub->id);
+ /* SC: verbose level check */
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_answer(%s) on %s@%s-%d\n", ast->name, p->name, p->parent->name, sub->id);
+ }
if (ast->_state != AST_STATE_UP) {
ast_setstate(ast, AST_STATE_UP);
if (option_debug)
if (i->dtmfinband) {
i->dsp = ast_dsp_new();
ast_dsp_set_features(i->dsp,DSP_FEATURE_DTMF_DETECT);
+ /* SC: this is to prevent clipping of dtmf tones during dsp processing */
+ ast_dsp_digitmode(i->dsp, DSP_DIGITMODE_NOQUELCH);
} else {
i->dsp = NULL;
}
tmp = NULL;
}
}
- ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_new(%s) created in state: %s\n", tmp->name, ast_state2str(state));
+ /* SC: verbose level check */
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "MGCP mgcp_new(%s) created in state: %s\n", tmp->name, ast_state2str(state));
+ }
} else {
ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
}
ast_verbose(VERBOSE_PREFIX_3 "Registered MGCP gateway '%s' at %s port %d\n", g->name, inet_ntoa(g->addr.sin_addr), ntohs(g->addr.sin_port));
}
}
+ /* SC: not dynamic, check if the name matches */
+ else if (name) {
+ if (strcasecmp(g->name, at)) {
+ g = g->next;
+ continue;
+ }
+ }
+ /* SC: not dynamic, no name, check if the addr matches */
+ else if (!name && sin) {
+ if ((g->addr.sin_addr.s_addr != sin->sin_addr.s_addr) ||
+ (g->addr.sin_port != sin->sin_port)) {
+ g = g->next;
+ continue;
+ }
+ }
+ else {
+ g = g->next;
+ continue;
+ }
+ /* SC */
p = g->endpoints;
while(p) {
if (option_debug)
ast_log(LOG_DEBUG, "Searching on %s@%s for subchannel\n", p->name, g->name);
if (msgid) {
+#if 0 /* SC: new transport mech */
sub = p->sub;
do {
if (option_debug)
if (found) {
break;
}
+#endif
+ /* SC */
+ sub = p->sub;
+ found = 1;
+ /* SC */
+ break;
} else if (name && !strcasecmp(p->name, tmp)) {
ast_log(LOG_DEBUG, "Coundn't determine subchannel, assuming current master %s@%s-%d\n",
p->name, g->name, p->sub->id);
return -1;
}
req->header[req->headers] = req->data + req->len;
- snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+ /* SC: check if we need brackets around the gw name */
+ if (p->parent->isnamedottedip)
+ snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@[%s] MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
+ else
+ snprintf(req->header[req->headers], sizeof(req->data) - req->len, "%s %d %s@%s MGCP 1.0\r\n", verb, oseq, p->name, p->parent->name);
req->len += strlen(req->header[req->headers]);
if (req->headers < MGCP_MAX_HEADERS)
req->headers++;
add_header(&resp, "C", sub->callid);
add_header(&resp, "L", local);
add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
+ /* SC: X header should not be sent. kept for compatibility */
add_header(&resp, "X", sub->txident);
add_header(&resp, "I", sub->cxident);
/*add_header(&resp, "S", "");*/
add_sdp(&resp, sub, rtp);
- return send_request(sub, &resp, oseq);
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_MDCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
}
static int transmit_connect_with_sdp(struct mgcp_subchannel *sub, struct ast_rtp *rtp)
add_header(&resp, "C", sub->callid);
add_header(&resp, "L", local);
add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
+ /* SC: X header should not be sent. kept for compatibility */
add_header(&resp, "X", sub->txident);
/*add_header(&resp, "S", "");*/
add_sdp(&resp, sub, rtp);
- return send_request(sub, &resp, oseq);
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_CRCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
}
static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone)
}
strncpy(p->curtone, tone, sizeof(p->curtone) - 1);
reqprep(&resp, p, "RQNT");
- add_header(&resp, "X", sub->txident);
+ add_header(&resp, "X", p->rqnt_ident); /* SC */
switch (p->hookstate) {
case MGCP_ONHOOK:
add_header(&resp, "R", "hd(N)");
if (strlen(tone)) {
add_header(&resp, "S", tone);
}
- return send_request(sub, &resp, oseq);
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_RQNT;
+ resp.trid = oseq;
+ return send_request(p, NULL, &resp, oseq); /* SC */
}
static int transmit_notify_request_with_callerid(struct mgcp_subchannel *sub, char *tone, char *callerid)
}
}
if (!n)
- n = "O";
+ n = "";
if (!l)
l = "";
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, l, n);
strncpy(p->curtone, tone, sizeof(p->curtone) - 1);
reqprep(&resp, p, "RQNT");
- add_header(&resp, "X", sub->txident);
+ add_header(&resp, "X", p->rqnt_ident); /* SC */
switch (p->hookstate) {
case MGCP_ONHOOK:
add_header(&resp, "R", "L/hd(N)");
ast_verbose(VERBOSE_PREFIX_3 "MGCP Asked to indicate tone: %s on %s@%s-%d in cxmode: %s\n",
tone2, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode]);
}
- return send_request(sub, &resp, oseq);
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_RQNT;
+ resp.trid = oseq;
+ return send_request(p, NULL, &resp, oseq); /* SC */
}
static int transmit_modify_request(struct mgcp_subchannel *sub)
reqprep(&resp, p, "MDCX");
add_header(&resp, "C", sub->callid);
add_header(&resp, "M", mgcp_cxmodes[sub->cxmode]);
+ /* SC: X header should not be sent. kept for compatibility */
add_header(&resp, "X", sub->txident);
add_header(&resp, "I", sub->cxident);
switch (sub->parent->hookstate) {
add_header(&resp, "R", "L/hu(N),L/hf(N),D/[0-9#*](N)");
break;
}
- return send_request(sub, &resp, oseq);
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_MDCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
}
{
struct mgcp_request resp;
reqprep(&resp, p, "AUEP");
- add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,VS,E,MD,M");
- return send_request(p->sub, &resp, oseq);
+ /* SC: removed unknown param VS */
+ add_header(&resp, "F", "A,R,D,S,X,N,I,T,O,ES,E,MD,M");
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_AUEP;
+ resp.trid = oseq;
+ return send_request(p, NULL, &resp, oseq); /* SC */
}
static int transmit_connection_del(struct mgcp_subchannel *sub)
sub->cxident, p->name, p->parent->name, sub->id, mgcp_cxmodes[sub->cxmode], sub->callid);
}
reqprep(&resp, p, "DLCX");
- add_header(&resp, "C", sub->callid);
+ /* SC: check if call id is avail */
+ if (sub->callid[0])
+ add_header(&resp, "C", sub->callid);
+ /* SC: X header should not be sent. kept for compatibility */
add_header(&resp, "X", sub->txident);
- add_header(&resp, "I", sub->cxident);
- return send_request(sub, &resp, oseq);
+ /* SC: check if cxident is avail */
+ if (sub->cxident[0])
+ add_header(&resp, "I", sub->cxident);
+ /* SC: fill in new fields */
+ resp.cmd = MGCP_CMD_DLCX;
+ resp.trid = oseq;
+ return send_request(p, sub, &resp, oseq); /* SC */
}
-static void handle_response(struct mgcp_subchannel *sub, int result, int ident)
+/* SC: cleanup pendng commands */
+static void dump_cmd_queues(struct mgcp_endpoint *p, struct mgcp_subchannel *sub)
{
- struct mgcp_message *cur;
- struct mgcp_endpoint *p = sub->parent;
- time_t t;
- time(&t);
-#if 0
- ast_verbose(VERBOSE_PREFIX_3 "Got response back on sub%d for transaction %d our last send was %d)\n",
- sub->id, ident, sub->msgs ? sub->msgs->seqno : -1);
- ast_verbose(VERBOSE_PREFIX_3 "Here's out outher chan sub%d for transaction %d our last send was %d)\n",
- sub->next->id, ident, sub->next->msgs ? sub->next->msgs->seqno : -1);
-#endif
- if (sub->msgs && (sub->msgs->seqno == ident)) {
- ast_log(LOG_DEBUG, "Got response back on tansaction %d\n", ident);
- cur = sub->msgs;
- sub->msgs = sub->msgs->next;
- ast_log(LOG_DEBUG, "Removing message from %s@%s-%d tansaction %d\n", sub->parent->name, sub->parent->parent->name, sub->id, ident);
- free(cur);
- if (sub->msgs) {
- /* Send next pending message if appropriate */
- sub->messagepending = 1;
- sub->lastout = sub->msgs->seqno;
- sub->lastouttime = t;
- __mgcp_xmit(sub, sub->msgs->buf, sub->msgs->len);
- /* XXX Should schedule retransmission XXX */
- } else {
- sub->messagepending = 0;
+ struct mgcp_request *t, *q;
+
+ if (p) {
+ ast_mutex_lock(&p->rqnt_queue_lock);
+ for (q = p->rqnt_queue; q; t = q->next, free(q), q=t);
+ p->rqnt_queue = NULL;
+ ast_mutex_unlock(&p->rqnt_queue_lock);
+
+ ast_mutex_lock(&p->cmd_queue_lock);
+ for (q = p->cmd_queue; q; t = q->next, free(q), q=t);
+ p->cmd_queue = NULL;
+ ast_mutex_unlock(&p->cmd_queue_lock);
+
+ ast_mutex_lock(&p->sub->cx_queue_lock);
+ for (q = p->sub->cx_queue; q; t = q->next, free(q), q=t);
+ p->sub->cx_queue = NULL;
+ ast_mutex_unlock(&p->sub->cx_queue_lock);
+
+ ast_mutex_lock(&p->sub->next->cx_queue_lock);
+ for (q = p->sub->next->cx_queue; q; t = q->next, free(q), q=t);
+ p->sub->next->cx_queue = NULL;
+ ast_mutex_unlock(&p->sub->next->cx_queue_lock);
+ }
+ else if (sub){
+ ast_mutex_lock(&sub->cx_queue_lock);
+ for (q = sub->cx_queue; q; t = q->next, free(q), q=t);
+ sub->cx_queue = NULL;
+ ast_mutex_unlock(&sub->cx_queue_lock);
+ }
+}
+
+
+/* SC: remove command transaction from queue */
+static struct mgcp_request *find_command(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ struct mgcp_request **queue, ast_mutex_t *l, int ident)
+{
+ struct mgcp_request *prev, *req;
+
+ ast_mutex_lock(l);
+ for (prev = NULL, req = *queue; req; prev = req, req = req->next) {
+ if (req->trid == ident) {
+ /* remove from queue */
+ if (!prev)
+ *queue = req->next;
+ else
+ prev->next = req->next;
+
+ /* send next pending command */
+ if (*queue) {
+ if (mgcpdebug) {
+ ast_verbose("Posting Queued Request:\n%s to %s:%d\n", (*queue)->data,
+ inet_ntoa(p->parent->addr.sin_addr), ntohs(p->parent->addr.sin_port));
+ }
+
+ mgcp_postrequest(p, sub, (*queue)->data, (*queue)->len, (*queue)->trid);
+ }
+ break;
}
- } else {
- ast_log(LOG_NOTICE, "Got response back on %s@%s-%d for transaction %d we aren't sending? (current = %d)\n",
- sub->parent->name, sub->parent->parent->name, sub->id, ident, sub->msgs ? sub->msgs->seqno : -1);
- }
- if ((result >= 400) && (result <= 499)) {
+ }
+ ast_mutex_unlock(l);
+ return req;
+}
+
+/* SC: modified for new transport mechanism */
+static void handle_response(struct mgcp_endpoint *p, struct mgcp_subchannel *sub,
+ int result, int ident, struct mgcp_request *resp)
+{
+ char *c;
+ struct mgcp_request *req;
+ struct mgcp_gateway *gw = p->parent;
+
+ if (result < 200) {
+ /* provisional response */
+ return;
+ }
+
+ if (sub)
+ req = find_command(p, sub, &sub->cx_queue, &sub->cx_queue_lock, ident);
+ else if (!(req = find_command(p, sub, &p->rqnt_queue, &p->rqnt_queue_lock, ident)))
+ req = find_command(p, sub, &p->cmd_queue, &p->cmd_queue_lock, ident);
+
+ if (!req) {
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "No command found on [%s] for transaction %d. Ignoring...\n",
+ gw->name, ident);
+ }
+ return;
+ }
+
+ if (p && (result >= 400) && (result <= 599)) {
switch (result) {
case 401:
p->hookstate = MGCP_OFFHOOK;
case 402:
p->hookstate = MGCP_ONHOOK;
break;
+ case 406:
+ ast_log(LOG_NOTICE, "Transaction %d timed out\n", ident);
+ break;
+ case 407:
+ ast_log(LOG_NOTICE, "Transaction %d aborted\n", ident);
+ break;
}
- ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n", result, p->name, p->parent->name, sub->id);
- if (sub->owner)
- ast_softhangup(sub->owner, AST_SOFTHANGUP_DEV);
- }
+ if (sub)
+ {
+ if (sub->owner) {
+ ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n",
+ result, p->name, p->parent->name, sub ? sub->id:-1);
+ ast_softhangup(sub->owner, AST_SOFTHANGUP_DEV);
+ }
+ }
+ else {
+ if (p->sub->next->owner) {
+ ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n",
+ result, p->name, p->parent->name, sub ? sub->id:-1);
+ ast_softhangup(p->sub->next->owner, AST_SOFTHANGUP_DEV);
+ }
+
+ if (p->sub->owner) {
+ ast_log(LOG_NOTICE, "Terminating on result %d from %s@%s-%d\n",
+ result, p->name, p->parent->name, sub ? sub->id:-1);
+ ast_softhangup(p->sub->owner, AST_SOFTHANGUP_DEV);
+ }
+
+ dump_cmd_queues(p, NULL);
+ }
+ }
+
+ if (resp) {
+ if (req->cmd == MGCP_CMD_CRCX) {
+ if ((c = get_header(resp, "I"))) {
+ if (strlen(c)) {
+ /* SC: if we are hanging up do not process this conn. */
+ if (sub->owner) {
+ if (strlen(sub->cxident)) {
+ if (strcasecmp(c, sub->cxident)) {
+ ast_log(LOG_WARNING, "Subchannel already has a cxident. sub->cxident: %s requested %s\n", sub->cxident, c);
+ }
+ }
+ strncpy(sub->cxident, c, sizeof(sub->cxident) - 1);
+ if (sub->tmpdest.sin_addr.s_addr) {
+ transmit_modify_with_sdp(sub, NULL);
+ }
+ }
+ else {
+ /* XXX SC: delete this one
+ callid and conn id may already be lost.
+ so the following del conn may have a side effect of
+ cleaning up the next subchannel */
+ transmit_connection_del(sub);
+ }
+ }
+ }
+ }
+
+ if (req->cmd == MGCP_CMD_AUEP) {
+ /* Try to determine the hookstate returned from an audit endpoint command */
+ if ((c = get_header(resp, "ES"))) {
+ if (strlen(c)) {
+ if (strstr(c, "hu")) {
+ if (p->hookstate != MGCP_ONHOOK) {
+ /* SC: XXX cleanup if we think we are offhook XXX */
+ if ((p->sub->owner || p->sub->next->owner ) &&
+ p->hookstate == MGCP_OFFHOOK)
+ ast_softhangup(sub->owner, AST_SOFTHANGUP_DEV);
+ p->hookstate = MGCP_ONHOOK;
+
+ /* SC: update the requested events according to the new hookstate */
+ transmit_notify_request(p->sub, "");
+
+ /* SC: verbose level check */
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "Setting hookstate of %s@%s to ONHOOK\n", p->name, gw->name);
+ }
+ }
+ } else if (strstr(c, "hd")) {
+ if (p->hookstate != MGCP_OFFHOOK) {
+ p->hookstate = MGCP_OFFHOOK;
+
+ /* SC: update the requested events according to the new hookstate */
+ transmit_notify_request(p->sub, "");
+
+ /* SC: verbose level check */
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "Setting hookstate of %s@%s to OFFHOOK\n", p->name, gw->name);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (resp && resp->lines) {
+ /* SC: do not process sdp if we are hanging up. this may be a late response */
+ if (sub && sub->owner) {
+ if (!sub->rtp)
+ start_rtp(sub);
+ if (sub->rtp)
+ process_sdp(sub, resp);
+ }
+ }
+ }
+
+ free(req);
}
static void start_rtp(struct mgcp_subchannel *sub)
{
ast_mutex_lock(&sub->lock);
+ /* SC: check again to be on the safe side */
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
/* Allocate the RTP now */
sub->rtp = ast_rtp_new(sched, io, 1, 0);
if (sub->rtp && sub->owner)
ast_indicate(chan, -1);
strncpy(chan->exten, exten, sizeof(chan->exten)-1);
if (strlen(p->callerid)) {
- if (!p->hidecallerid)
+ if (!p->hidecallerid) {
+ /* SC: free existing chan->callerid */
+ if (chan->callerid)
+ free(chan->callerid);
chan->callerid = strdup(p->callerid);
+ }
+ if (chan->ani)
+ free(chan->ani);
chan->ani = strdup(p->callerid);
}
ast_setstate(chan, AST_STATE_RING);
ast_verbose(VERBOSE_PREFIX_3 "Received keepalive request from %s@%s\n", p->name, p->parent->name);
transmit_response(sub, "200", req, "OK");
} else {
- dump_queue(p);
+ dump_queue(p->parent, p);
+ dump_cmd_queues(p, NULL);
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "Resetting interface %s@%s\n", p->name, p->parent->name);
}
}
transmit_response(sub, "200", req, "OK");
transmit_notify_request(sub, "");
+ /* SC: Audit endpoint.
+ Idea is to prevent lost lines due to race conditions
+ */
+ transmit_audit_endpoint(p);
}
} else if (!strcasecmp(req->verb, "NTFY")) {
/* Acknowledge and be sure we keep looking for the same things */
sub->alreadygone = 1;
ast_queue_hangup(sub->owner, 1);
} else {
- ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s-%d) ast_channel already destroyed\n",
- p->name, p->parent->name, sub->id);
+ /* SC: verbose level check */
+ if (option_verbose > 2) {
+ ast_verbose(VERBOSE_PREFIX_3 "MGCP handle_request(%s@%s-%d) ast_channel already destroyed\n",
+ p->name, p->parent->name, sub->id);
+ }
}
}
if ((p->hookstate == MGCP_ONHOOK) && (!sub->rtp) && (!sub->next->rtp)) {
if (strstr(p->curtone, "wt") && (ev[0] == 'A')) {
memset(p->curtone, 0, sizeof(p->curtone));
}
- } else if (!strcasecmp(ev, "T")) {
+ }
+ else if (!strcasecmp(ev, "T")) {
/* Digit timeout -- unimportant */
} else {
ast_log(LOG_NOTICE, "Received unknown event '%s' from %s@%s\n", ev, p->name, p->parent->name);
struct mgcp_request req;
struct sockaddr_in sin;
struct mgcp_subchannel *sub;
- char *c;
int res;
int len;
int result;
/* Try to find who this message is for, if it's important */
sub = find_subchannel(NULL, ident, &sin);
if (sub) {
-#if 0
- if ((c = get_header(&req, "X"))) {
- if (strlen(c)) {
- if ((strcasecmp(sub->txident, c)) && (!strcasecmp(sub->next->txident, c))) {
- ast_log(LOG_WARNING, "Response on sub%d message %d appears to have come from out other subchannel", sub->id, ident);
- }
+ struct mgcp_gateway *gw = sub->parent->parent;
+ struct mgcp_message *cur, *prev;
+
+ ast_mutex_lock(&gw->msgs_lock);
+ for (prev = NULL, cur = gw->msgs; cur; prev = cur, cur = cur->next) {
+ if (cur->seqno == ident) {
+ ast_log(LOG_DEBUG, "Got response back on tansaction %d\n", ident);
+ if (prev)
+ prev->next = cur->next;
+ else
+ gw->msgs = cur->next;
+ break;
}
}
-#endif
- handle_response(sub, result, ident);
- if ((c = get_header(&req, "I"))) {
- if (strlen(c)) {
- if (strlen(sub->cxident)) {
- if (strcasecmp(c, sub->cxident)) {
- ast_log(LOG_WARNING, "Subchannel already has a cxident. sub->cxident: %s requested %s\n", sub->cxident, c);
- }
- }
- strncpy(sub->cxident, c, sizeof(sub->cxident) - 1);
- if (sub->tmpdest.sin_addr.s_addr) {
- transmit_modify_with_sdp(sub, NULL);
- }
- }
- }
- /* Try to determine the hookstate returned from an audit endpoint command */
- if ((c = get_header(&req, "ES"))) {
- if (strlen(c)) {
- if (strstr(c, "hu")) {
- sub->parent->hookstate = MGCP_ONHOOK;
- ast_verbose(VERBOSE_PREFIX_3 "Setting hookstate of %s@%s to ONHOOK\n", sub->parent->name, sub->parent->parent->name);
- } else if (strstr(c, "hd")) {
- sub->parent->hookstate = MGCP_OFFHOOK;
- ast_verbose(VERBOSE_PREFIX_3 "Setting hookstate of %s@%s to OFFHOOK\n", sub->parent->name, sub->parent->parent->name);
- }
- }
- }
+ /* stop retrans timer if the queue is empty */
+ if (!gw->msgs && (gw->retransid != -1)) {
+ ast_sched_del(sched, gw->retransid);
+ gw->retransid = -1;
+ }
- if (req.lines) {
- if (!sub->rtp)
- start_rtp(sub);
- if (sub->rtp)
- process_sdp(sub, &req);
- }
+ ast_mutex_unlock(&gw->msgs_lock);
+
+ if (cur) {
+ handle_response(cur->owner_ep, cur->owner_sub, result, ident, &req);
+ free(cur);
+ return 1;
+ }
+ ast_log(LOG_NOTICE, "Got response back on [%s] for transaction %d we aren't sending?\n",
+ gw->name, ident);
}
} else {
if (!req.endpoint || !strlen(req.endpoint) ||
static void *do_monitor(void *data)
{
int res;
- struct mgcp_pkt *p;
+ int reloading;
/* struct mgcp_gateway *g; */
/* struct mgcp_endpoint *e; */
/*time_t thispass = 0, lastpass = 0;*/
- sched = sched_context_create();
- if (!sched) {
- ast_log(LOG_WARNING, "Unable to create schedule context\n");
- return NULL;
- }
- io = io_context_create();
- if (!io) {
- ast_log(LOG_WARNING, "Unable to create I/O context\n");
- return NULL;
- }
-
/* Add an I/O event to our UDP socket */
if (mgcpsock > -1)
ast_io_add(io, mgcpsock, mgcpsock_read, AST_IO_IN, NULL);
(and thus do not have a separate thread) indefinitely */
/* From here on out, we die whenever asked */
for(;;) {
- /* Check for interfaces needing to be killed */
+ /* Check for a reload request */
+ ast_mutex_lock(&mgcp_reload_lock);
+ reloading = mgcp_reloading;
+ mgcp_reloading = 0;
+ ast_mutex_unlock(&mgcp_reload_lock);
+ if (reloading) {
+ if (option_verbose > 0)
+ ast_verbose(VERBOSE_PREFIX_1 "Reloading MGCP\n");
+ mgcp_do_reload();
+ }
+
+ /* Check for interfaces needing to be killed */
/* Don't let anybody kill us right away. Nobody should lock the interface list
and wait for the monitor list, but the other way around is okay. */
ast_mutex_lock(&monlock);
/* Lock the network interface */
ast_mutex_lock(&netlock);
- p = packets;
- while(p) {
- /* Handle any retransmissions */
- p = p->next;
- }
/* XXX THIS IS COMPLETELY HOSED */
/* The gateway goes into a state of panic */
pthread_testcancel();
/* Wait for sched or io */
res = ast_sched_wait(sched);
+ /* SC: copied from chan_sip.c */
+ if ((res < 0) || (res > 1000))
+ res = 1000;
res = ast_io_wait(io, res);
ast_mutex_lock(&monlock);
if (res >= 0)
return tmpc;
}
+/* SC: modified for reload support */
static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
{
struct mgcp_gateway *gw;
struct mgcp_subchannel *sub;
/*char txident[80];*/
int i=0, y=0;
+ int gw_reload = 0;
+ int ep_reload = 0;
canreinvite = CANREINVITE;
- gw = malloc(sizeof(struct mgcp_gateway));
+
+ /* SC: locate existing gateway */
+ gw = gateways;
+ while (gw) {
+ if (!strcasecmp(cat, gw->name)) {
+ /* gateway already exists */
+ gw->delme = 0;
+ gw_reload = 1;
+ break;
+ }
+ gw = gw->next;
+ }
+
+ if (!gw)
+ gw = malloc(sizeof(struct mgcp_gateway));
+
if (gw) {
- memset(gw, 0, sizeof(struct mgcp_gateway));
- gw->expire = -1;
- strncpy(gw->name, cat, sizeof(gw->name) - 1);
+ if (!gw_reload) {
+ memset(gw, 0, sizeof(struct mgcp_gateway));
+ gw->expire = -1;
+ gw->retransid = -1; /* SC */
+ ast_mutex_init(&gw->msgs_lock);
+ strncpy(gw->name, cat, sizeof(gw->name) - 1);
+ /* SC: check if the name is numeric ip */
+ if (inet_addr(gw->name) != INADDR_NONE)
+ gw->isnamedottedip = 1;
+ }
while(v) {
if (!strcasecmp(v->name, "host")) {
if (!strcasecmp(v->value, "dynamic")) {
gw->expire = -1;
gw->dynamic = 0;
if (ast_get_ip(&gw->addr, v->value)) {
- free(gw);
+ if (!gw_reload)
+ free(gw);
return NULL;
}
}
} else if (!strcasecmp(v->name, "defaultip")) {
if (ast_get_ip(&gw->defaddr, v->value)) {
- free(gw);
+ if (!gw_reload)
+ free(gw);
return NULL;
}
} else if (!strcasecmp(v->name, "permit") ||
} else if (!strcasecmp(v->name, "trunk") ||
!strcasecmp(v->name, "line")) {
- e = malloc(sizeof(struct mgcp_endpoint));
+
+ /* SC: locate existing endpoint */
+ e = gw->endpoints;
+ while (e) {
+ if (!strcasecmp(v->value, e->name)) {
+ /* endpoint already exists */
+ e->delme = 0;
+ ep_reload = 1;
+ break;
+ }
+ e = e->next;
+ }
+
+ if (!e) {
+ e = malloc(sizeof(struct mgcp_endpoint));
+ ep_reload = 0;
+ }
+
if (e) {
- memset(e, 0, sizeof(struct mgcp_endpoint));
- strncpy(e->name, v->value, sizeof(e->name) - 1);
+ if (!ep_reload) {
+ memset(e, 0, sizeof(struct mgcp_endpoint));
+ strncpy(e->name, v->value, sizeof(e->name) - 1);
+ e->needaudit = 1;
+ }
/* XXX Should we really check for uniqueness?? XXX */
strncpy(e->context, context, sizeof(e->context) - 1);
strncpy(e->callerid, callerid, sizeof(e->callerid) - 1);
if (strlen(mailbox)) {
ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, gw->name, e->name);
}
- e->msgstate = -1;
+ if (!ep_reload) {
+ /* XXX SC: potential issue due to reload */
+ e->msgstate = -1;
+ e->parent = gw;
+ }
e->capability = capability;
- e->parent = gw;
e->dtmfinband = inbanddtmf;
e->adsi = adsi;
if (!strcasecmp(v->name, "trunk"))
e->callwaiting = callwaiting;
e->transfer = transfer;
e->threewaycalling = threewaycalling;
- e->onhooktime = time(NULL);
- /* ASSUME we're onhook */
- e->hookstate = MGCP_ONHOOK;
- /*snprintf(txident, sizeof(txident), "%08x", rand());*/
+ if (!ep_reload) {
+ e->onhooktime = time(NULL);
+ /* ASSUME we're onhook */
+ e->hookstate = MGCP_ONHOOK;
+ snprintf(e->rqnt_ident, sizeof(e->rqnt_ident), "%08x", rand());
+ ast_mutex_init(&e->rqnt_queue_lock);
+ ast_mutex_init(&e->cmd_queue_lock);
+ }
+
+ for (i = 0, sub = NULL; i < MAX_SUBS; i++) {
+ if (!ep_reload) {
+ sub = malloc(sizeof(struct mgcp_subchannel));
+ }
+ else {
+ if (!sub)
+ sub = e->sub;
+ else
+ sub = sub->next;
+ }
- for (i = 0; i < MAX_SUBS; i++) {
- sub = malloc(sizeof(struct mgcp_subchannel));
if (sub) {
- ast_verbose(VERBOSE_PREFIX_3 "Allocating subchannel '%d' on %s@%s\n", i, e->name, gw->name);
- memset(sub, 0, sizeof(struct mgcp_subchannel));
- sub->parent = e;
- sub->id = i;
- snprintf(sub->txident, sizeof(sub->txident), "%08x", rand());
- /*strcpy(sub->txident, txident);*/
- sub->cxmode = MGCP_CX_INACTIVE;
+ if (!ep_reload) {
+ ast_verbose(VERBOSE_PREFIX_3 "Allocating subchannel '%d' on %s@%s\n", i, e->name, gw->name);
+ memset(sub, 0, sizeof(struct mgcp_subchannel));
+ strncpy(sub->magic, MGCP_SUBCHANNEL_MAGIC, sizeof(sub->magic) - 1);
+ sub->parent = e;
+ sub->id = i;
+ snprintf(sub->txident, sizeof(sub->txident), "%08x", rand());
+ sub->cxmode = MGCP_CX_INACTIVE;
+ sub->next = e->sub;
+ ast_mutex_init(&sub->cx_queue_lock);
+ e->sub = sub;
+ }
sub->nat = nat;
- sub->next = e->sub;
- e->sub = sub;
} else {
/* XXX Should find a way to clean up our memory */
ast_log(LOG_WARNING, "Out of memory allocating subchannel");
return NULL;
}
}
- /* Make out subs a circular linked list so we can always sping through the whole bunch */
- sub = e->sub;
- /* find the end of the list */
- while(sub->next){
- sub = sub->next;
- }
- /* set the last sub->next to the first sub */
- sub->next = e->sub;
-
+ if (!ep_reload) {
+ /* Make out subs a circular linked list so we can always sping through the whole bunch */
+ sub = e->sub;
+ /* find the end of the list */
+ while(sub->next){
+ sub = sub->next;
+ }
+ /* set the last sub->next to the first sub */
+ sub->next = e->sub;
- e->next = gw->endpoints;
- gw->endpoints = e;
+ e->next = gw->endpoints;
+ gw->endpoints = e;
+ }
}
} else
ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
}
if (!ntohl(gw->addr.sin_addr.s_addr) && !gw->dynamic) {
ast_log(LOG_WARNING, "Gateway '%s' lacks IP address and isn't dynamic\n", gw->name);
- free(gw);
+ if (!gw_reload)
+ free(gw);
return NULL;
}
if (gw->defaddr.sin_addr.s_addr && !ntohs(gw->defaddr.sin_port))
- gw->defaddr.sin_port = htons(DEFAULT_MGCP_PORT);
+ gw->defaddr.sin_port = htons(DEFAULT_MGCP_GW_PORT);
if (gw->addr.sin_addr.s_addr && !ntohs(gw->addr.sin_port))
- gw->addr.sin_port = htons(DEFAULT_MGCP_PORT);
+ gw->addr.sin_port = htons(DEFAULT_MGCP_GW_PORT);
if (gw->addr.sin_addr.s_addr)
if (ast_ouraddrfor(&gw->addr.sin_addr, &gw->ourip))
memcpy(&gw->ourip, &__ourip, sizeof(gw->ourip));
- return gw;
+
+ return (gw_reload ? NULL : gw);
}
static struct ast_rtp *mgcp_get_rtp_peer(struct ast_channel *chan)
"Usage: mgcp no debug\n"
" Disables dumping of MGCP packets for debugging purposes\n";
+static char mgcp_reload_usage[] =
+"Usage: mgcp reload\n"
+" Reloads MGCP configuration from mgcp.conf\n";
+
static struct ast_cli_entry cli_debug =
{ { "mgcp", "debug", NULL }, mgcp_do_debug, "Enable MGCP debugging", debug_usage };
static struct ast_cli_entry cli_no_debug =
{ { "mgcp", "no", "debug", NULL }, mgcp_no_debug, "Disable MGCP debugging", no_debug_usage };
+static struct ast_cli_entry cli_mgcp_reload =
+ { { "mgcp", "reload", NULL }, mgcp_reload, "Reload MGCP configuration", mgcp_reload_usage };
-int load_module()
+static void destroy_endpoint(struct mgcp_endpoint *e)
+{
+ struct mgcp_subchannel *sub = e->sub->next, *s;
+ int i;
+
+ for (i = 0; i < MAX_SUBS; i++) {
+ ast_mutex_lock(&sub->lock);
+ if (strlen(sub->cxident)) {
+ transmit_connection_del(sub);
+ }
+ if (sub->rtp) {
+ ast_rtp_destroy(sub->rtp);
+ sub->rtp = NULL;
+ }
+ memset(sub->magic, 0, sizeof(sub->magic));
+ if (sub->owner) {
+ ast_softhangup(sub->owner, AST_SOFTHANGUP_DEV);
+ }
+ dump_cmd_queues(NULL, sub);
+ ast_mutex_unlock(&sub->lock);
+ sub = sub->next;
+ }
+
+ if (e->dsp) {
+ ast_dsp_free(e->dsp);
+ }
+
+ dump_queue(e->parent, e);
+ dump_cmd_queues(e, NULL);
+
+ sub = e->sub;
+ for (i = 0; (i < MAX_SUBS) && sub; i++) {
+ s = sub;
+ sub = sub->next;
+ free(s);
+ }
+ free(e);
+}
+
+static void destroy_gateway(struct mgcp_gateway *g)
+{
+ if (g->ha)
+ ast_free_ha(g->ha);
+
+ dump_queue(g, NULL);
+
+ free (g);
+}
+
+static void prune_gateways(void)
+{
+ struct mgcp_gateway *g, *z, *r;
+ struct mgcp_endpoint *e, *p, *t;
+
+ ast_mutex_lock(&gatelock);
+
+ /* prune gateways */
+ for (z = NULL, g = gateways; g;) {
+ /* prune endpoints */
+ for (p = NULL, e = g->endpoints; e; ) {
+ if (e->delme || g->delme) {
+ t = e;
+ e = e->next;
+ if (!p)
+ g->endpoints = e;
+ else
+ p->next = e;
+ destroy_endpoint(t);
+ }
+ else {
+ p = e;
+ e = e->next;
+ }
+ }
+
+ if (g->delme) {
+ r = g;
+ g = g->next;
+ if (!z)
+ gateways = g;
+ else
+ z->next = g;
+
+ destroy_gateway(r);
+ }
+ else {
+ z = g;
+ g = g->next;
+ }
+ }
+
+ ast_mutex_unlock(&gatelock);
+}
+
+static int reload_config(void)
{
struct ast_config *cfg;
struct ast_variable *v;
}
v = v->next;
}
+
+ /* SC: mark existing entries for deletion */
+ ast_mutex_lock(&gatelock);
+ g = gateways;
+ while (g) {
+ g->delme = 1;
+ e = g->endpoints;
+ while (e) {
+ e->delme = 1;
+ e = e->next;
+ }
+ g = g->next;
+ }
+ ast_mutex_unlock(&gatelock);
cat = ast_category_browse(cfg, NULL);
while(cat) {
if (strcasecmp(cat, "general")) {
+ ast_mutex_lock(&gatelock);
g = build_gateway(cat, ast_variable_browse(cfg, cat));
if (g) {
if (option_verbose > 2) {
ast_verbose(VERBOSE_PREFIX_3 "Added gateway '%s'\n", g->name);
}
- ast_mutex_lock(&gatelock);
- g->next = gateways;
- gateways = g;
- ast_mutex_unlock(&gatelock);
+ g->next = gateways;
+ gateways = g;
}
+ ast_mutex_unlock(&gatelock);
}
cat = ast_category_browse(cfg, cat);
}
-
+
+ /* SC: prune deleted entries etc. */
+ prune_gateways();
+
if (ntohl(bindaddr.sin_addr.s_addr)) {
memcpy(&__ourip, &bindaddr.sin_addr, sizeof(__ourip));
} else {
hp = gethostbyname(ourhost);
if (!hp) {
ast_log(LOG_WARNING, "Unable to get our IP address, MGCP disabled\n");
+ ast_destroy(cfg);
return 0;
}
memcpy(&__ourip, hp->h_addr, sizeof(__ourip));
}
if (!ntohs(bindaddr.sin_port))
- bindaddr.sin_port = ntohs(DEFAULT_MGCP_PORT);
+ bindaddr.sin_port = ntohs(DEFAULT_MGCP_CA_PORT);
bindaddr.sin_family = AF_INET;
ast_mutex_lock(&netlock);
if (mgcpsock > -1)
ast_mutex_unlock(&netlock);
ast_destroy(cfg);
- /* Make sure we can register our mgcp channel type */
- if (ast_channel_register(type, tdesc, capability, mgcp_request)) {
- ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
- ast_destroy(cfg);
- return -1;
- }
- mgcp_rtp.type = type;
- ast_rtp_proto_register(&mgcp_rtp);
- ast_cli_register(&cli_show_endpoints);
- ast_cli_register(&cli_audit_endpoint);
- ast_cli_register(&cli_debug);
- ast_cli_register(&cli_no_debug);
- /* And start the monitor for the first time */
- restart_monitor();
-
+ /* SC: send audit only to the new endpoints */
g = gateways;
while (g) {
e = g->endpoints;
- while (e) {
+ while (e && e->needaudit) {
+ e->needaudit = 0;
transmit_audit_endpoint(e);
ast_verbose(VERBOSE_PREFIX_3 "MGCP Auditing endpoint %s@%s for hookstate\n", e->name, g->name);
e = e->next;
return 0;
}
+int load_module()
+{
+ int res;
+
+ sched = sched_context_create();
+ if (!sched) {
+ ast_log(LOG_WARNING, "Unable to create schedule context\n");
+ return -1;
+ }
+ io = io_context_create();
+ if (!io) {
+ ast_log(LOG_WARNING, "Unable to create I/O context\n");
+ return -1;
+ }
+
+ if (!(res = reload_config())) {
+ /* Make sure we can register our mgcp channel type */
+ if (ast_channel_register(type, tdesc, capability, mgcp_request)) {
+ ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+ return -1;
+ }
+ mgcp_rtp.type = type;
+ ast_rtp_proto_register(&mgcp_rtp);
+ ast_cli_register(&cli_show_endpoints);
+ ast_cli_register(&cli_audit_endpoint);
+ ast_cli_register(&cli_debug);
+ ast_cli_register(&cli_no_debug);
+ ast_cli_register(&cli_mgcp_reload);
+
+ /* And start the monitor for the first time */
+ restart_monitor();
+ }
+
+ return res;
+}
+
+static int mgcp_do_reload(void)
+{
+ reload_config();
+ return 0;
+}
+
+static int mgcp_reload(int fd, int argc, char *argv[])
+{
+ ast_mutex_lock(&mgcp_reload_lock);
+ if (mgcp_reloading) {
+ ast_verbose("Previous mgcp reload not yet done\n");
+ } else
+ mgcp_reloading = 1;
+ ast_mutex_unlock(&mgcp_reload_lock);
+ restart_monitor();
+ return 0;
+}
+
+int reload(void)
+{
+ mgcp_reload(0, 0, NULL);
+ return 0;
+}
+
int unload_module()
{
#if 0