diff options
author | russell <russell@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-05-12 15:07:30 +0000 |
---|---|---|
committer | russell <russell@f38db490-d61c-443f-a65b-d21fe96a405b> | 2008-05-12 15:07:30 +0000 |
commit | 7e66e729a9ff57a89574e0410c0bcc649d83a783 (patch) | |
tree | b69feb34f854797df2ad0d5d96dcd6659a5353a5 | |
parent | c5dc1aae5f510d53e0403e68fed157239593d6f2 (diff) |
Merge rev 114891 from 1.4
git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.4.19.2@115643 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | channels/chan_iax2.c | 368 |
1 files changed, 245 insertions, 123 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index b0cb32097..3aeae2a87 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -809,6 +809,17 @@ static struct chan_iax2_pvt *iaxs[IAX_MAX_CALLS]; static ast_mutex_t iaxsl[ARRAY_LEN(iaxs)]; static struct timeval lastused[ARRAY_LEN(iaxs)]; +/*! + * \brief Another container of iax2_pvt structures + * + * Active IAX2 pvt structs are also stored in this container, if they are a part + * of an active call where we know the remote side's call number. The reason + * for this is that incoming media frames do not contain our call number. So, + * instead of having to iterate the entire iaxs array, we use this container to + * look up calls where the remote side is using a given call number. + */ +static struct ao2_container *iax_peercallno_pvts; + /* Flag to use with trunk calls, keeping these calls high up. It halves our effective use but keeps the division between trunked and non-trunked better. */ #define TRUNK_CALL_START ARRAY_LEN(iaxs) / 2 @@ -1183,16 +1194,179 @@ static int iax2_getpeername(struct sockaddr_in sin, char *host, int len) return res; } +static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) +{ + /* Decrement AUTHREQ count if needed */ + if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { + struct iax2_user *user; + struct iax2_user tmp_user = { + .name = pvt->username, + }; + + user = ao2_find(users, &tmp_user, OBJ_POINTER); + if (user) { + ast_atomic_fetchadd_int(&user->curauthreq, -1); + user = user_unref(user); + } + + ast_clear_flag(pvt, IAX_MAXAUTHREQ); + } + + /* No more pings or lagrq's */ + AST_SCHED_DEL(sched, pvt->pingid); + AST_SCHED_DEL(sched, pvt->lagid); + AST_SCHED_DEL(sched, pvt->autoid); + AST_SCHED_DEL(sched, pvt->authid); + AST_SCHED_DEL(sched, pvt->initid); + AST_SCHED_DEL(sched, pvt->jbid); +} + +static void store_by_peercallno(struct chan_iax2_pvt *pvt) +{ + if (!pvt->peercallno) { + ast_log(LOG_ERROR, "This should not be called without a peer call number.\n"); + return; + } + + ao2_link(iax_peercallno_pvts, pvt); +} + +static void remove_by_peercallno(struct chan_iax2_pvt *pvt) +{ + if (!pvt->peercallno) { + ast_log(LOG_ERROR, "This should not be called without a peer call number.\n"); + return; + } + + ao2_unlink(iax_peercallno_pvts, pvt); +} + +static void update_max_trunk(void) +{ + int max = TRUNK_CALL_START; + int x; + + /* XXX Prolly don't need locks here XXX */ + for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) { + if (iaxs[x]) { + max = x + 1; + } + } + + maxtrunkcall = max; + if (option_debug && iaxdebug) + ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max); +} + +static void iax2_frame_free(struct iax_frame *fr) +{ + AST_SCHED_DEL(sched, fr->retrans); + iax_frame_free(fr); +} + +static void iax2_destroy(int callno) +{ + struct chan_iax2_pvt *pvt; + struct ast_channel *owner; + +retry: + pvt = iaxs[callno]; + gettimeofday(&lastused[callno], NULL); + + owner = pvt ? pvt->owner : NULL; + + if (owner) { + if (ast_mutex_trylock(&owner->lock)) { + ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n"); + ast_mutex_unlock(&iaxsl[callno]); + usleep(1); + ast_mutex_lock(&iaxsl[callno]); + goto retry; + } + } + if (!owner) { + iaxs[callno] = NULL; + } + + if (pvt) { + if (!owner) { + pvt->owner = NULL; + } else { + /* If there's an owner, prod it to give up */ + /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup() + * because we already hold the owner channel lock. */ + ast_queue_hangup(owner); + } + + if (pvt->peercallno) { + remove_by_peercallno(pvt); + } + + if (!owner) { + ao2_ref(pvt, -1); + pvt = NULL; + } + } + + if (owner) { + ast_mutex_unlock(&owner->lock); + } + + if (callno & 0x4000) { + update_max_trunk(); + } +} + +static void pvt_destructor(void *obj) +{ + struct chan_iax2_pvt *pvt = obj; + struct iax_frame *cur = NULL; + + iax2_destroy_helper(pvt); + + /* Already gone */ + ast_set_flag(pvt, IAX_ALREADYGONE); + + AST_LIST_LOCK(&iaxq.queue); + AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { + /* Cancel any pending transmissions */ + if (cur->callno == pvt->callno) { + cur->retries = -1; + } + } + AST_LIST_UNLOCK(&iaxq.queue); + + if (pvt->reg) { + pvt->reg->callno = 0; + } + + if (!pvt->owner) { + jb_frame frame; + if (pvt->vars) { + ast_variables_destroy(pvt->vars); + pvt->vars = NULL; + } + + while (jb_getall(pvt->jb, &frame) == JB_OK) { + iax2_frame_free(frame.data); + } + + jb_destroy(pvt->jb); + ast_string_field_free_memory(pvt); + } +} + static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host) { struct chan_iax2_pvt *tmp; jb_conf jbconf; - if (!(tmp = ast_calloc(1, sizeof(*tmp)))) + if (!(tmp = ao2_alloc(sizeof(*tmp), pvt_destructor))) { return NULL; + } if (ast_string_field_init(tmp, 32)) { - free(tmp); + ao2_ref(tmp, -1); tmp = NULL; return NULL; } @@ -1261,23 +1435,6 @@ static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short return 0; } -static void update_max_trunk(void) -{ - int max = TRUNK_CALL_START; - int x; - - /* XXX Prolly don't need locks here XXX */ - for (x = TRUNK_CALL_START; x < ARRAY_LEN(iaxs) - 1; x++) { - if (iaxs[x]) { - max = x + 1; - } - } - - maxtrunkcall = max; - if (option_debug && iaxdebug) - ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max); -} - static void update_max_nontrunk(void) { int max = 1; @@ -1349,6 +1506,28 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc char host[80]; if (new <= NEW_ALLOW) { + if (callno) { + struct chan_iax2_pvt *pvt; + struct chan_iax2_pvt tmp_pvt = { + .callno = dcallno, + .peercallno = callno, + /* hack!! */ + .frames_received = full_frame, + }; + + memcpy(&tmp_pvt.addr, sin, sizeof(tmp_pvt.addr)); + + if ((pvt = ao2_find(iax_peercallno_pvts, &tmp_pvt, OBJ_POINTER))) { + if (return_locked) { + ast_mutex_lock(&iaxsl[pvt->callno]); + } + res = pvt->callno; + ao2_ref(pvt, -1); + pvt = NULL; + return res; + } + } + /* Look for an existing connection first */ for (x=1;(res < 1) && (x<maxnontrunkcall);x++) { ast_mutex_lock(&iaxsl[x]); @@ -1429,6 +1608,10 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc ast_string_field_set(iaxs[x], accountcode, accountcode); ast_string_field_set(iaxs[x], mohinterpret, mohinterpret); ast_string_field_set(iaxs[x], mohsuggest, mohsuggest); + + if (iaxs[x]->peercallno) { + store_by_peercallno(iaxs[x]); + } } else { ast_log(LOG_WARNING, "Out of resources\n"); ast_mutex_unlock(&iaxsl[x]); @@ -1440,12 +1623,6 @@ static int find_callno(unsigned short callno, unsigned short dcallno, struct soc return res; } -static void iax2_frame_free(struct iax_frame *fr) -{ - AST_SCHED_DEL(sched, fr->retrans); - iax_frame_free(fr); -} - /*! * \brief Queue a frame to a call's owning asterisk channel * @@ -1910,32 +2087,6 @@ static int send_packet(struct iax_frame *f) return res; } -static void iax2_destroy_helper(struct chan_iax2_pvt *pvt) -{ - /* Decrement AUTHREQ count if needed */ - if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) { - struct iax2_user *user; - struct iax2_user tmp_user = { - .name = pvt->username, - }; - - user = ao2_find(users, &tmp_user, OBJ_POINTER); - if (user) { - ast_atomic_fetchadd_int(&user->curauthreq, -1); - user_unref(user); - } - - ast_clear_flag(pvt, IAX_MAXAUTHREQ); - } - /* No more pings or lagrq's */ - AST_SCHED_DEL(sched, pvt->pingid); - AST_SCHED_DEL(sched, pvt->lagid); - AST_SCHED_DEL(sched, pvt->autoid); - AST_SCHED_DEL(sched, pvt->authid); - AST_SCHED_DEL(sched, pvt->initid); - AST_SCHED_DEL(sched, pvt->jbid); -} - /*! * \note Since this function calls iax2_queue_hangup(), the pvt struct * for the given call number may disappear during its execution. @@ -1961,76 +2112,6 @@ static int iax2_predestroy(int callno) return 0; } -static void iax2_destroy(int callno) -{ - struct chan_iax2_pvt *pvt; - struct iax_frame *cur; - struct ast_channel *owner; - -retry: - pvt = iaxs[callno]; - gettimeofday(&lastused[callno], NULL); - - owner = pvt ? pvt->owner : NULL; - - if (owner) { - if (ast_mutex_trylock(&owner->lock)) { - ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n"); - ast_mutex_unlock(&iaxsl[callno]); - usleep(1); - ast_mutex_lock(&iaxsl[callno]); - goto retry; - } - } - if (!owner) - iaxs[callno] = NULL; - if (pvt) { - if (!owner) - pvt->owner = NULL; - iax2_destroy_helper(pvt); - - /* Already gone */ - ast_set_flag(pvt, IAX_ALREADYGONE); - - if (owner) { - /* If there's an owner, prod it to give up */ - /* It is ok to use ast_queue_hangup() here instead of iax2_queue_hangup() - * because we already hold the owner channel lock. */ - ast_queue_hangup(owner); - } - - AST_LIST_LOCK(&iaxq.queue); - AST_LIST_TRAVERSE(&iaxq.queue, cur, list) { - /* Cancel any pending transmissions */ - if (cur->callno == pvt->callno) - cur->retries = -1; - } - AST_LIST_UNLOCK(&iaxq.queue); - - if (pvt->reg) - pvt->reg->callno = 0; - if (!owner) { - jb_frame frame; - if (pvt->vars) { - ast_variables_destroy(pvt->vars); - pvt->vars = NULL; - } - - while (jb_getall(pvt->jb, &frame) == JB_OK) - iax2_frame_free(frame.data); - jb_destroy(pvt->jb); - /* gotta free up the stringfields */ - ast_string_field_free_memory(pvt); - free(pvt); - } - } - if (owner) { - ast_mutex_unlock(&owner->lock); - } - if (callno & 0x4000) - update_max_trunk(); -} - static int update_packet(struct iax_frame *f) { /* Called with iaxsl lock held, and iaxs[callno] non-NULL */ @@ -5716,7 +5797,13 @@ static int complete_transfer(int callno, struct iax_ies *ies) pvt->rseqno = 0; pvt->iseqno = 0; pvt->aseqno = 0; + + if (pvt->peercallno) { + remove_by_peercallno(pvt); + } pvt->peercallno = peercallno; + store_by_peercallno(pvt); + pvt->transferring = TRANSFER_NONE; pvt->svoiceformat = -1; pvt->voiceformat = 0; @@ -7053,8 +7140,18 @@ static int socket_process(struct iax2_thread *thread) if (!inaddrcmp(&sin, &iaxs[fr->callno]->addr) && !minivid && f.subclass != IAX_COMMAND_TXCNT && /* for attended transfer */ - f.subclass != IAX_COMMAND_TXACC) /* for attended transfer */ - iaxs[fr->callno]->peercallno = (unsigned short)(ntohs(mh->callno) & ~IAX_FLAG_FULL); + f.subclass != IAX_COMMAND_TXACC) { /* for attended transfer */ + unsigned short new_peercallno; + + new_peercallno = (unsigned short) (ntohs(mh->callno) & ~IAX_FLAG_FULL); + if (new_peercallno && new_peercallno != iaxs[fr->callno]->peercallno) { + if (iaxs[fr->callno]->peercallno) { + remove_by_peercallno(iaxs[fr->callno]); + } + iaxs[fr->callno]->peercallno = new_peercallno; + store_by_peercallno(iaxs[fr->callno]); + } + } if (ntohs(mh->callno) & IAX_FLAG_FULL) { if (option_debug && iaxdebug) ast_log(LOG_DEBUG, "Received packet %d, (%d, %d)\n", fh->oseqno, f.frametype, f.subclass); @@ -10845,6 +10942,7 @@ static int __unload_module(void) ao2_ref(peers, -1); ao2_ref(users, -1); + ao2_ref(iax_peercallno_pvts, -1); return 0; } @@ -10865,6 +10963,24 @@ static int peer_set_sock_cb(void *obj, void *arg, int flags) return 0; } +static int pvt_hash_cb(const void *obj, const int flags) +{ + const struct chan_iax2_pvt *pvt = obj; + + return pvt->peercallno; +} + +static int pvt_cmp_cb(void *obj, void *arg, int flags) +{ + struct chan_iax2_pvt *pvt = obj, *pvt2 = arg; + + /* The frames_received field is used to hold whether we're matching + * against a full frame or not ... */ + + return match(&pvt2->addr, pvt2->peercallno, pvt2->callno, pvt, + pvt2->frames_received) ? CMP_MATCH : 0; +} + /*! \brief Load IAX2 module, load configuraiton ---*/ static int load_module(void) { @@ -10881,6 +10997,12 @@ static int load_module(void) ao2_ref(peers, -1); return AST_MODULE_LOAD_FAILURE; } + iax_peercallno_pvts = ao2_container_alloc(IAX_MAX_CALLS, pvt_hash_cb, pvt_cmp_cb); + if (!iax_peercallno_pvts) { + ao2_ref(peers, -1); + ao2_ref(users, -1); + return AST_MODULE_LOAD_FAILURE; + } ast_custom_function_register(&iaxpeer_function); |