Add ability to adapt the IAX trunk packets to the MTU size, to avoid bad audio
authorOlle Johansson <oej@edvina.net>
Wed, 25 Oct 2006 14:06:13 +0000 (14:06 +0000)
committerOlle Johansson <oej@edvina.net>
Wed, 25 Oct 2006 14:06:13 +0000 (14:06 +0000)
when the number of channels fill the MTU on a given link.

In the future, this needs to be configurable per peer with trunking enabled.

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@46195 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CREDITS
channels/chan_iax2.c
configs/iax.conf.sample

diff --git a/CREDITS b/CREDITS
index a516382..248a7ea 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -131,7 +131,7 @@ Slav Klenov & Vanheuverzwijn Joachim - development of the generic jitterbuffer
 Roy Sigurd Karlsbakk - providing funding for generic jitterbuffer development
        roy@karlsbakk.net, Briiz Telecom AS
 
-Voop A/S, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite of SIP transfers
+Voop AS, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite of SIP transfers
 
 Philippe Sultan - RADIUS CDR module
        INRIA, http://www.inria.fr/
@@ -152,6 +152,8 @@ James Rothenberger - Support for IMAP storage integration added by OneBizTone LL
 
 Paul Cadach - Bringing chan_h323 up to date, bug fixes, and more!
 
+Voop AS - Financial support for a lot of work with the SIP driver and the IAX trunk MTU patch
+
 === OTHER CONTRIBUTIONS ===
 John Todd - Monkey sounds and associated teletorture prompt
 Michael Jerris - bug marshaling
index 108846d..79aa934 100644 (file)
@@ -140,6 +140,15 @@ static struct ast_codec_pref prefs;
 
 static const char tdesc[] = "Inter Asterisk eXchange Driver (Ver 2)";
 
+
+/*! \brief Maximum transimission unit for the UDP packet in the trunk not to be
+    fragmented. This is based on 1516 - ethernet - ip - udp - iax minus one g711 frame = 1240 */
+#define MAX_TRUNK_MTU 1240 
+
+static int global_max_trunk_mtu;       /*!< Maximum MTU, 0 if not used */
+static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ;   /*!< Trunk MTU statistics */
+
+
 static char context[80] = "default";
 
 static char language[MAX_LANGUAGE] = "";
@@ -183,8 +192,7 @@ int (*iax2_regfunk)(const char *username, int onoff) = NULL;
 #define IAX_CAPABILITY_MEDBANDWIDTH    (IAX_CAPABILITY_FULLBANDWIDTH &         \
                                         ~AST_FORMAT_SLINEAR &                  \
                                         ~AST_FORMAT_ULAW &                     \
-                                        ~AST_FORMAT_ALAW &                     \
-                                        ~AST_FORMAT_G722) 
+                                        ~AST_FORMAT_ALAW) 
 /* A modem */
 #define IAX_CAPABILITY_LOWBANDWIDTH    (IAX_CAPABILITY_MEDBANDWIDTH &          \
                                         ~AST_FORMAT_G726 &                     \
@@ -374,6 +382,7 @@ struct iax2_trunk_peer {
        unsigned char *trunkdata;
        unsigned int trunkdatalen;
        unsigned int trunkdataalloc;
+       int trunkmaxmtu;
        int trunkerror;
        int calls;
        AST_LIST_ENTRY(iax2_trunk_peer) list;
@@ -787,6 +796,7 @@ static int iax2_sendtext(struct ast_channel *c, const char *text);
 static int iax2_setoption(struct ast_channel *c, int option, void *data, int datalen);
 static int iax2_transfer(struct ast_channel *c, const char *dest);
 static int iax2_write(struct ast_channel *c, struct ast_frame *f);
+static int send_trunk(struct iax2_trunk_peer *tpeer, struct timeval *now);
 static int send_command(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
 static int send_command_final(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
 static int send_command_immediate(struct chan_iax2_pvt *, char, int, unsigned int, const unsigned char *, int, int);
@@ -1752,7 +1762,7 @@ retry:
                                iax2_frame_free(frame.data);
                        jb_destroy(pvt->jb);
                        /* gotta free up the stringfields */
-                       ast_string_field_free_pools(pvt);
+                       ast_string_field_free_all(pvt);
                        free(pvt);
                }
        }
@@ -2057,11 +2067,44 @@ static int iax2_show_stats(int fd, int argc, char *argv[])
        ast_cli(fd, "    IAX Statistics\n");
        ast_cli(fd, "---------------------\n");
        ast_cli(fd, "Outstanding frames: %d (%d ingress, %d egress)\n", iax_get_frames(), iax_get_iframes(), iax_get_oframes());
+       ast_cli(fd, "%d timed and %d untimed transmits; MTU %d/%d/%d\n", trunk_timed, trunk_untimed,
+               trunk_maxmtu, trunk_nmaxmtu, global_max_trunk_mtu);
+
        ast_cli(fd, "Packets in transmit queue: %d dead, %d final, %d total\n\n", dead, final, cnt);
+
+       trunk_timed = trunk_untimed = 0;
+       if (trunk_maxmtu > trunk_nmaxmtu)
+               trunk_nmaxmtu = trunk_maxmtu;
        
        return RESULT_SUCCESS;
 }
 
+/*! \brief Set trunk MTU from CLI */
+static int iax2_set_mtu(int fd, int argc, char *argv[])
+{
+       int mtuv;
+
+       if (argc != 4)
+               return RESULT_SHOWUSAGE; 
+       if (strncasecmp(argv[3], "default", strlen(argv[3])) == 0) 
+               mtuv = MAX_TRUNK_MTU; 
+       else                                         
+               mtuv = atoi(argv[3]); 
+
+       if (mtuv == 0) {
+               ast_cli(fd, "Trunk MTU control disabled (mtu was %d)\n", global_max_trunk_mtu); 
+               global_max_trunk_mtu = 0; 
+               return RESULT_SUCCESS; 
+       }
+       if (mtuv < 172 || mtuv > 4000) {
+               ast_cli(fd, "Trunk MTU must be between 172 and 4000\n"); 
+               return RESULT_SHOWUSAGE; 
+       }
+       ast_cli(fd, "Trunk MTU changed from %d to %d\n", global_max_trunk_mtu, mtuv); 
+       global_max_trunk_mtu = mtuv; 
+       return RESULT_SUCCESS;
+}
+
 static int iax2_show_cache(int fd, int argc, char *argv[])
 {
        struct iax2_dpcache *dp = NULL;
@@ -2882,9 +2925,6 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
                iaxs[callno]->initid = ast_sched_add(sched, autokill * 2, auto_congest, CALLNO_TO_PTR(callno));
        }
 
-       /* send the command using the appropriate socket for this peer */
-       iaxs[callno]->sockfd = cai.sockfd;
-
        /* Transmit the string in a "NEW" request */
        send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
 
@@ -3526,6 +3566,8 @@ static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr)
        struct ast_frame *f;
        struct iax2_trunk_peer *tpeer;
        void *tmp, *ptr;
+       struct timeval now;
+       int res; 
        struct ast_iax2_meta_trunk_entry *met;
        struct ast_iax2_meta_trunk_mini *mtm;
 
@@ -3574,6 +3616,18 @@ static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr)
                tpeer->trunkdatalen += f->datalen;
 
                tpeer->calls++;
+
+               /* track the largest mtu we actually have sent */
+               if (tpeer->trunkdatalen + f->datalen + 4 > trunk_maxmtu) 
+                       trunk_maxmtu = tpeer->trunkdatalen + f->datalen + 4 ; 
+
+               /* if we have enough for a full MTU, ship it now without waiting */
+               if (global_max_trunk_mtu > 0 && tpeer->trunkdatalen + f->datalen + 4 >= global_max_trunk_mtu) {
+                       gettimeofday(&now, NULL);
+                       res = send_trunk(tpeer, &now); 
+                       trunk_untimed ++; 
+               }
+
                ast_mutex_unlock(&tpeer->lock);
        }
        return 0;
@@ -5936,6 +5990,7 @@ static int timing_read(int *id, int fd, short events, void *cbdata)
                        drop = tpeer;
                } else {
                        res = send_trunk(tpeer, &now);
+                       trunk_timed++; 
                        if (iaxtrunkdebug)
                                ast_verbose(" - Trunk peer (%s:%d) has %d call chunk%s in transit, %d bytes backloged and has hit a high water mark of %d bytes\n", ast_inet_ntoa(tpeer->addr.sin_addr), ntohs(tpeer->addr.sin_port), res, (res != 1) ? "s" : "", tpeer->trunkdatalen, tpeer->trunkdataalloc);
                }               
@@ -8310,7 +8365,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
                                        peer->expire = -1;
                                        ast_clear_flag(peer, IAX_DYNAMIC);
                                        if (ast_dnsmgr_lookup(v->value, &peer->addr.sin_addr, &peer->dnsmgr)) {
-                                               ast_string_field_free_pools(peer);
+                                               ast_string_field_free_all(peer);
                                                free(peer);
                                                return NULL;
                                        }
@@ -8321,7 +8376,7 @@ static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, st
                                        inet_aton("255.255.255.255", &peer->mask);
                        } else if (!strcasecmp(v->name, "defaultip")) {
                                if (ast_get_ip(&peer->defaddr, v->value)) {
-                                       ast_string_field_free_pools(peer);
+                                       ast_string_field_free_all(peer);
                                        free(peer);
                                        return NULL;
                                }
@@ -8457,7 +8512,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
        
        if (user) {
                if (firstpass) {
-                       ast_string_field_free_pools(user);
+                       ast_string_field_free_all(user);
                        memset(user, 0, sizeof(struct iax2_user));
                        if (ast_string_field_init(user, 32)) {
                                free(user);
@@ -8655,7 +8710,7 @@ static void destroy_user(struct iax2_user *user)
                ast_variables_destroy(user->vars);
                user->vars = NULL;
        }
-       ast_string_field_free_pools(user);
+       ast_string_field_free_all(user);
        free(user);
 }
 
@@ -8696,7 +8751,7 @@ static void destroy_peer(struct iax2_peer *peer)
        register_peer_exten(peer, 0);
        if (peer->dnsmgr)
                ast_dnsmgr_release(peer->dnsmgr);
-       ast_string_field_free_pools(peer);
+       ast_string_field_free_all(peer);
        free(peer);
 }
 
@@ -8743,6 +8798,7 @@ static int set_config(char *config_file, int reload)
        int format;
        int portno = IAX_DEFAULT_PORTNO;
        int  x;
+       int mtuv; 
        struct iax2_user *user;
        struct iax2_peer *peer;
        struct ast_netsock *ns;
@@ -8904,6 +8960,15 @@ static int set_config(char *config_file, int reload)
                        trunkfreq = atoi(v->value);
                        if (trunkfreq < 10)
                                trunkfreq = 10;
+               } else if (!strcasecmp(v->name, "trunkmtu")) {
+                       mtuv = atoi(v->value);
+                       if (mtuv  == 0 )  
+                               global_max_trunk_mtu = 0; 
+                       else if (mtuv >= 172 && mtuv < 4000) 
+                               global_max_trunk_mtu = mtuv; 
+                       else 
+                               ast_log(LOG_NOTICE, "trunkmtu value out of bounds (%d) at line %d\n",
+                                       mtuv, v->lineno); 
                } else if (!strcasecmp(v->name, "autokill")) {
                        if (sscanf(v->value, "%d", &x) == 1) {
                                if (x >= 0)
@@ -9086,6 +9151,7 @@ static int reload_config(void)
        strcpy(language, "");
        strcpy(mohinterpret, "default");
        strcpy(mohsuggest, "");
+       global_max_trunk_mtu = MAX_TRUNK_MTU;
        amaflags = 0;
        delayreject = 0;
        ast_clear_flag((&globalflags), IAX_NOTRANSFER); 
@@ -9096,6 +9162,9 @@ static int reload_config(void)
        set_config(config,1);
        prune_peers();
        prune_users();
+        trunk_timed = trunk_untimed = 0; 
+       trunk_nmaxmtu = trunk_maxmtu = 0; 
+
        for (reg = registrations; reg; reg = reg->next)
                iax2_do_register(reg);
        /* Qualify hosts, too */
@@ -9577,6 +9646,13 @@ static char show_stats_usage[] =
 "Usage: iax2 list stats\n"
 "       Display statistics on IAX channel driver.\n";
 
+static char set_mtu_usage[] =
+"Usage: iax2 set mtu <value>\n"
+"       Set the system-wide IAX IP mtu to <value> bytes net or zero to disable.\n"
+"       Disabling means that the operating system must handle fragmentation of UDP packets\n"
+"       when the IAX2 trunk packet exceeds the UDP payload size.\n"
+"       This is substantially below the IP mtu. Try 1240 on ethernets.\n"
+"       Must be 172 or greater for G.711 samples.\n"; 
 static char show_cache_usage[] =
 "Usage: iax2 list cache\n"
 "       Display currently cached IAX Dialplan results.\n";
@@ -9706,6 +9782,10 @@ static struct ast_cli_entry cli_iax2[] = {
        iax2_show_threads, "Display IAX helper thread info",
        show_threads_usage },
 
+       { { "iax2", "set", "mtu", NULL },
+       iax2_set_mtu, "Set the IAX systemwide trunking MTU",
+       set_mtu_usage, NULL, NULL },
+
        { { "iax2", "list", "users", NULL },
        iax2_show_users, "List defined IAX users",
        show_users_usage },
@@ -9884,8 +9964,6 @@ static int load_module(void)
        for (x=0;x<IAX_MAX_CALLS;x++)
                ast_mutex_init(&iaxsl[x]);
 
-       ast_cond_init(&sched_cond, NULL);
-
        if (!(sched = sched_context_create())) {
                ast_log(LOG_ERROR, "Failed to create scheduler context\n");
                return AST_MODULE_LOAD_FAILURE;
index 234d18e..adb9f02 100644 (file)
@@ -156,7 +156,16 @@ forcejitterbuffer=no
 ; minregexpire = 60
 ; maxregexpire = 60
 ;
+; With a large amount of traffic on IAX2 trunks, there is a risk of bad voice quality due to
+; the fact that the IAX2 trunking scheme depends on the Linux system to handle fragmentation of
+; UDP packets. This may not be very efficient. 
+; This setting sets the maximum transmission unit for IAX2 UDP trunking.
+; default is 1240 bytes. Zero disables this functionality and let's the O/S handle fragmentation.
+;
+; trunkmtu = 0
+;
 ; IAX helper threads
+
 ; Establishes the number of iax helper threads to handle I/O.
 ; iaxthreadcount = 10
 ; Establishes the number of extra dynamic threads that may be spawned to handle I/O