Index: rtp.c =================================================================== RCS file: /usr/cvsroot/asterisk/rtp.c,v retrieving revision 1.66 diff -u -r1.66 rtp.c --- rtp.c 15 May 2004 16:26:52 -0000 1.66 +++ rtp.c 18 May 2004 22:16:07 -0000 @@ -34,6 +34,7 @@ #include #include #include +#include #define RTP_MTU 1200 @@ -45,8 +46,12 @@ static int dtmftimeout = 3000; /* 3000 samples */ -static int rtpstart = 0; -static int rtpend = 0; +#define DEFAULT_PORTSTART 8000 +#define DEFAULT_PORTEND 31000 +static int conf_portstart = DEFAULT_PORTSTART; +static int conf_portend = DEFAULT_PORTEND; +static int portstart = DEFAULT_PORTSTART; +static int portend = DEFAULT_PORTEND; // The value of each payload format mapping: struct rtpPayloadType { @@ -740,88 +745,183 @@ return ""; } -static struct ast_rtcp *ast_rtcp_new(void) +/* + * ast_rtp_set_portrange: set the range of ports RTP may use + */ +void ast_rtp_set_portrange(int start, int end) +{ + if (start>0) + portstart = start; + else + portstart = conf_portstart; + if (end>0) + portend = end; + else + portend = conf_portend; + + if (portstart >= portend) { + ast_log(LOG_WARNING, "Unreasonable values for RTP port range, using defaults\n"); + portstart = 8000; + portend = 31000; + } +} + +/* + * ast_rtp_get_portrange: report the range of ports RTP is using + */ +void ast_rtp_get_portrange(int *start, int *end) +{ + *start = portstart; + *end = portend; +} + +/* + * Create a socket for RTP + */ +static int rtp_socket(void) { - struct ast_rtcp *rtcp; long flags; - rtcp = malloc(sizeof(struct ast_rtcp)); - if (!rtcp) - return NULL; - memset(rtcp, 0, sizeof(struct ast_rtcp)); - rtcp->s = socket(AF_INET, SOCK_DGRAM, 0); - rtcp->us.sin_family = AF_INET; - if (rtcp->s < 0) { - free(rtcp); + int s; + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno)); - return NULL; + return -1; } - flags = fcntl(rtcp->s, F_GETFL); - fcntl(rtcp->s, F_SETFL, flags | O_NONBLOCK); - return rtcp; + flags = fcntl(s, F_GETFL); + fcntl(s, F_SETFL, flags | O_NONBLOCK); + return s; } -struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode) +/* + * ast_rtp_create: create an RTP structure + * and open the streams on given interface + */ +struct ast_rtp *ast_rtp_create(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct sockaddr_in *interface) { struct ast_rtp *rtp; - int x; - int flags; - int startplace; + int p, s; + + /* create rtp channel */ rtp = malloc(sizeof(struct ast_rtp)); - if (!rtp) + if (!rtp) { + ast_log(LOG_WARNING, "Failed to allocate RTP structure\n"); return NULL; + } memset(rtp, 0, sizeof(struct ast_rtp)); - rtp->them.sin_family = AF_INET; - rtp->us.sin_family = AF_INET; - rtp->s = socket(AF_INET, SOCK_DGRAM, 0); - rtp->ssrc = rand(); - rtp->seqno = rand() & 0xffff; - if (rtp->s < 0) { + + if (interface) { /* did the caller want a specific interface? */ + memcpy(&rtp->us, interface, sizeof(struct sockaddr_in)); + p = ntohs(interface->sin_port); + } else { + rtp->us.sin_family = AF_INET; + p = 0; + } + + rtp->s = rtp_socket(); + if (rtp->s < 0){ /* can't get a socket */ free(rtp); - ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno)); return NULL; } + if (p > 0){ /* we got a requested port number from caller */ + rtp->us.sin_port = htons(p); + if (bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) + p = 0; /* bind failed, we'll try again later with default range */ + close(rtp->s); + } + + /* if needed, create rtcp channel */ if (sched && rtcpenable) { rtp->sched = sched; - rtp->rtcp = ast_rtcp_new(); - } - flags = fcntl(rtp->s, F_GETFL); - fcntl(rtp->s, F_SETFL, flags | O_NONBLOCK); - /* Find us a place */ - x = (rand() % (rtpend-rtpstart)) + rtpstart; - x = x & ~1; - startplace = x; - for (;;) { - /* Must be an even port number by RTP spec */ - rtp->us.sin_port = htons(x); - if (rtp->rtcp) - rtp->rtcp->us.sin_port = htons(x + 1); - if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us)) && - (!rtp->rtcp || !bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us)))) - break; - if (errno != EADDRINUSE) { - ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno)); + rtp->rtcp = malloc(sizeof(struct ast_rtcp)); + if (!rtp->rtcp) { /* failed to allocate */ + ast_log(LOG_WARNING, "Failed to allocate RTCP structure\n"); close(rtp->s); - if (rtp->rtcp) { - close(rtp->rtcp->s); - free(rtp->rtcp); - } free(rtp); return NULL; } - x += 2; - if (x > rtpend) - x = (rtpstart + 1) & ~1; - if (x == startplace) { - ast_log(LOG_WARNING, "No RTP ports remaining\n"); + memset(rtp->rtcp, 0, sizeof(struct ast_rtcp)); + rtp->rtcp->s = rtp_socket(); + if (rtp->rtcp->s < 0) { + free(rtp->rtcp); close(rtp->s); - if (rtp->rtcp) { - close(rtp->rtcp->s); - free(rtp->rtcp); - } free(rtp); return NULL; } + memcpy(&rtp->rtcp->us, &rtp->us, sizeof(struct sockaddr_in)); + if (p > 0) { + rtp->rtcp->us.sin_port = htons(p+1); /* let's try the next port */ + if (bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))) { + close(rtp->s); /* ok, the second port has failed to bind */ + close(rtp->rtcp->s); /* close all and try the default port ranges */ + p = 0; + } + } + } + + if (p==0) { /* still don't have a port: find us a pair of adjacent ports in the range */ + p = (rand() % (portend-portstart)) + portstart; + p = p & ~1; /* must be an even port number by RTP spec */ + s = p; /* save where we started */ + for (;;) { + int err; + rtp->us.sin_port = htons(p); + rtp->s = rtp_socket(); /* get the first socket */ + if (rtp->s < 0){ + if (rtp->rtcp) + free(rtp->rtcp); + free(rtp); + return NULL; + } + if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))){ + if (!rtp->rtcp){ + break; /* we're done */ + } else { /* get the second socket */ + rtp->rtcp->us.sin_port = htons(p+1); + rtp->rtcp->s = rtp_socket(); + if (rtp->rtcp->s < 0){ + free(rtp->rtcp); + close(rtp->s); + free(rtp); + return NULL; + } + if (!bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))){ + break; /* ok, we're done */ + } + } + } + err = errno; + /* if we're still here, didn't get all ports yet */ + close(rtp->s); + if (rtp->rtcp) + close(rtp->rtcp->s); + if (err != EADDRINUSE) { /* hard error */ + ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(err)); + if (rtp->rtcp) + free(rtp->rtcp); + free(rtp); + return NULL; + } + p += 2; /* keep searching */ + if (p > portend) + p = (portstart + 1) & ~1; + if (p == s) { + ast_log(LOG_WARNING, "No RTP ports remaining\n"); + if (rtp->rtcp) + free(rtp->rtcp); + free(rtp); + return NULL; + } + } + } + if (option_verbose > 4) { + ast_verbose(VERBOSE_PREFIX_4 "RTP opened port %s:%d\n", inet_ntoa(rtp->us.sin_addr), p); + if (rtp->rtcp) + ast_verbose(VERBOSE_PREFIX_4 "RTP opened port %s:%d (rtcp)\n", inet_ntoa(rtp->rtcp->us.sin_addr), p+1); } + + rtp->them.sin_family = rtp->us.sin_family; + rtp->ssrc = rand(); + rtp->seqno = rand() & 0xffff; if (io && sched && callbackmode) { /* Operate this one in a callback mode */ rtp->sched = sched; @@ -832,6 +932,13 @@ return rtp; } +/* deprecated: for compatibility with older code, remove after a limited time period */ +struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode) +{ + ast_log(LOG_NOTICE, "Use of ast_rtp_new is deprecated. Please use ast_rtp_create.\n"); + return ast_rtp_create(sched, io, rtcpenable, callbackmode, NULL); +} + int ast_rtp_settos(struct ast_rtp *rtp, int tos) { int res; @@ -1428,36 +1535,56 @@ { struct ast_config *cfg; char *s; - rtpstart = 5000; - rtpend = 31000; cfg = ast_load("rtp.conf"); if (cfg) { if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) { - rtpstart = atoi(s); - if (rtpstart < 1024) - rtpstart = 1024; - if (rtpstart > 65535) - rtpstart = 65535; + conf_portstart = atoi(s); + if (conf_portstart < 1024) + conf_portstart = 1024; + if (conf_portstart > 65535) + conf_portstart = 65535; } if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) { - rtpend = atoi(s); - if (rtpend < 1024) - rtpend = 1024; - if (rtpend > 65535) - rtpend = 65535; + portend = atoi(s); + if (portend < 1024) + portend = 1024; + if (portend > 65535) + portend = 65535; } ast_destroy(cfg); } - if (rtpstart >= rtpend) { - ast_log(LOG_WARNING, "Unreasonable values for RTP start/end\n"); - rtpstart = 5000; - rtpend = 31000; + if (conf_portstart >= conf_portend) { + ast_log(LOG_WARNING, "Unreasonable values for RTP port range, using defaults\n"); + conf_portstart = DEFAULT_PORTSTART; + conf_portend = DEFAULT_PORTEND; } - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend); + if (option_verbose) + ast_verbose("RTP allocating by default from port range %d - %d\n", conf_portstart, conf_portend); } void ast_rtp_init(void) { ast_rtp_reload(); }