Hi Alexandre, it took quite a lot of searching, but now it's done. It was a general RTPProxy/PThread issue. The problem seems to be, that the PThread is created before the fork of the main process. Then the Thread waits forever...
Attached an changed main.c + rtpp_defines.h which modifies this behaviour and starts the Thread after the fork. I will create an according patch and send it to the RTPProxy-Dev-Mailinglist as well. Have fun, Carsten 2011/3/22 Carsten Bock <[email protected]>: > Hi Alexandre, > > it seem to be a general RTP-Proxy/PThread issue. It seems not to be > related to the XML-RPC notification; the thread, which processes the > timeouts, seems to waits forever... I will check... > > Carsten > > 2011/3/21 Alexandre Abreu <[email protected]>: >> Hi Carsten, >> >> After more testing, I just realized that the notification works only if I >> use "-f" parameter in RTPPROXY. If I omit this parameter (to run in >> background mode), the session timeout but there's no notification sent >> (BYE). Why's that? >> >> Thanks, >> Alexandre. >> >> -----Mensagem original----- >> De: Alexandre Abreu [mailto:[email protected]] >> Enviada em: quarta-feira, 16 de março de 2011 14:41 >> Para: 'Carsten Bock' >> Cc: '[email protected]' >> Assunto: RES: RTPPROXY timeout patch. >> >> Carsten, >> >> Here it goes: >> >> [root@devel rtpproxy-carsten]# ./rtpproxy -T 10 -f -F -i -l 192.168.200.90 >> -s udp:*:7722 -d DBUG ERR INFO -n tcp:127.0.0.1:7723 >> INFO:main: rtpproxy started, pid 22495 >> rtpproxy: >>> Running Timeout-Process >> >> DBUG:handle_command: received command "22428_8 Uc0,8,101 080b5d23d1603667 >> 192.168.200.114 6380 0073852a;1" >> INFO:handle_command: new session 080b5d23d1603667, tag 0073852a;1 requested, >> type strong >> INFO:handle_command: new session on a port 48662 created, tag 0073852a;1 >> INFO:handle_command: pre-filling caller's address with 192.168.200.114:6380 >> DBUG:doreply: sending reply "22428_8 48662 192.168.200.90" >> DBUG:handle_command: received command "22436_8 Lc0,8,101 080b5d23d1603667 >> 192.168.200.149 7614 0073852a;1 c758967a;1 >> xmlrpc:http://127.0.0.1:8000/RPC2" >> INFO:handle_command: lookup on ports 48662/58604, session timer restarted >> INFO:handle_command: setting custom timeout handler >> (xmlrpc:http://127.0.0.1:8000/RPC2) >> INFO:handle_command: pre-filling callee's address with 192.168.200.149:7614 >> DBUG:doreply: sending reply "22436_8 58604 192.168.200.90" >> INFO:process_rtp: session timeout >> INFO:remove_session: RTP stats: 982 in from callee, 4 in from caller, 986 >> relayed, 0 dropped >> INFO:remove_session: RTCP stats: 5 in from callee, 1 in from caller, 6 >> relayed, 0 dropped >> INFO:remove_session: session on ports 48662/58604 is cleaned up >> ERR:do_timeout_notification: Timeout socket is: èîÈÄÐÊ@ÍÈÄÈÄ`ê >> DBUG:reconnect_timeout_handler: reconnecting timeout socket >> ERR:reconnect_timeout_handler: can't create timeout socket: Address family >> not supported by protocol >> ERR:do_timeout_notification: unable to send timeout notification >> >> Very weird chars in the debug of what Timeout socket is... >> >> But the custom timeout handler are being printed correctly from the config >> file. >> INFO:handle_command: setting custom timeout handler >> (xmlrpc:http://127.0.0.1:8000/RPC2) >> Here I am running CentOS 5.2 32-bit. >> >> Thanks, >> Alexandre >> >> -----Mensagem original----- >> De: [email protected] [mailto:[email protected]] Em nome >> de Carsten Bock Enviada em: quarta-feira, 16 de março de 2011 14:18 >> Para: Alexandre Abreu >> Cc: [email protected] >> Assunto: Re: RTPPROXY timeout patch. >> >> Hi Alexandre, >> >> My version of RTP-Proxy is following: >> >> bock@bock-tde:~/ims/rtpproxy$ ./rtpproxy -v Basic version: 20040107 >> Extension 20050322: Support for multiple RTP streams and MOH Extension >> 20060704: Support for extra parameter in the V command Extension 20071116: >> Support for RTP re-packetization Extension 20071218: Support for forking >> (copying) RTP stream Extension 20080403: Support for RTP statistics querying >> Extension 20081102: Support for setting codecs in the update/lookup command >> Extension 20081224: Support for session timeout notifications Extension >> 20090810: Support for automatic bridging Extension 20100819: Support for >> timeout notifications using XML-RPC towards Kamailio/sip-router.org >> >> Please find attached a modified rtpp_notify.c file. I have just added some >> tiny debug output in order to see some points. >> Now you should see the following Debug-Outputs: >> >> rtpproxy: >>> Running Timeout-Process >> >> Then the notifier process is running. That would be good. If not, that's the >> reason why it is not working. When the request comes in, you should see the >> following: >> >> INFO:handle_command: setting custom timeout handler >> (xmlrpc:http://localhost:8000/RPC2) >> >> Then the Timeout-Socket was properly set, that would be good as well. >> Now the timeout: >> >> INFO:process_rtp: session timeout >> [...] >> ERR:do_timeout_notification: Timeout socket is: >> xmlrpc:http://localhost:8000/RPC2 >> >> That would be great, because then the Timeout towards the Kamailio should be >> triggerd. >> If these parts are ok, then there must be some issue either in the XML-RPC >> client library or in the communication between the RTP-Proxy and Kamailio. I >> hope you did a trace on the XML-RPC Port both on the RTPproxy and on the >> Kamailio? What distro are you using? My tests were only on Ubuntu and >> Debian, which are quite similar. >> >> Hope we find this issue, >> >> Kind regards, >> Carsten >> >> P.S.: I think i removed that check for the port for testing, that's why my >> version accepted the socket without port... (now i'm using "-n >> tcp:127.0.0.1:9999") >> >> 2011/3/16 Alexandre Abreu <[email protected]>: >>> Carsten, >>> >>> Indeed. Very strange. >>> >>> Are we running the same RTPPROXY version? How can you start using '-n >>> tcp:127.0.0.1' without specifying a port? >>> >>> [root@devel rtpproxy-carsten]# ./rtpproxy -T 10 -f -F -i -l >>> 192.168.200.90 -s udp:*:7722 -d DBUG ERR INFO -n tcp:127.0.0.1 >>> rtpproxy: can't parse host:port in TCP address >>> rtpproxy: can't start notification thread >>> >>> [root@devel rtpproxy-carsten]# ./rtpproxy -T 10 -f -F -i -l >>> 192.168.200.90 -s udp:*:7722 -d DBUG ERR INFO -n tcp:127.0.0.1:7723 >>> INFO:main: rtpproxy started, pid 21169 >>> >>> DBUG:handle_command: received command "17828_9 Uc0,8,101 >>> 4b10ce04de4f8818 >>> 192.168.200.114 6380 9c4b6265;1" >>> INFO:handle_command: new session 4b10ce04de4f8818, tag 9c4b6265;1 >>> requested, type strong >>> INFO:handle_command: new session on a port 43750 created, tag >>> 9c4b6265;1 >>> INFO:handle_command: pre-filling caller's address with >>> 192.168.200.114:6380 >>> DBUG:doreply: sending reply "17828_9 43750 192.168.200.90 " >>> DBUG:handle_command: received command "17847_9 Lc0,8,101 >>> 4b10ce04de4f8818 >>> 192.168.200.149 7386 9c4b6265;1 dd69ab1d;1 >>> xmlrpc:http://localhost:8000/RPC2" >>> INFO:handle_command: lookup on ports 43750/55796, session timer >>> restarted >>> INFO:handle_command: setting custom timeout handler >>> (xmlrpc:http://localhost:8000/RPC2) >>> INFO:handle_command: pre-filling callee's address with >>> 192.168.200.149:7386 >>> DBUG:doreply: sending reply "17847_9 55796 192.168.200.90 " >>> INFO:process_rtp: session timeout >>> INFO:remove_session: RTP stats: 548 in from callee, 5 in from caller, >>> 553 relayed, 0 dropped >>> INFO:remove_session: RTCP stats: 3 in from callee, 1 in from caller, 4 >>> relayed, 0 dropped >>> INFO:remove_session: session on ports 43750/55796 is cleaned up >>> DBUG:reconnect_timeout_handler: reconnecting timeout socket >>> ERR:reconnect_timeout_handler: can't create timeout socket: Address >>> family not supported by protocol >>> ERR:do_timeout_notification: unable to send timeout notification >>> >>> Above the error message. >>> >>> [root@devel ~]# md5sum rtpproxy-carsten.tar.gz >>> c02b1e2ac57d39562e86bcfc4ee592b8 rtpproxy-carsten.tar.gz >>> >>> Thanks, >>> Alexandre. >>> >>> -----Mensagem original----- >>> De: [email protected] [mailto:[email protected]] Em >>> nome de Carsten Bock Enviada em: quarta-feira, 16 de março de 2011 >>> 13:03 >>> Para: Alexandre Abreu >>> Cc: [email protected] >>> Assunto: Re: RTPPROXY timeout patch. >>> >>> Hi Alexandre, >>> >>> That is strange: >>> >>> I run the RTP-Proxy like this (directly from the TAR-File, i sent you) >>> and Kamailio with attached config-file. >>> >>> bock@bock-tde:~/ims/rtpproxy$ sudo ./rtpproxy -T 10 -f -F -i -l >>> 127.0.0.1 -s udp:*:22222 -d DBUG -n tcp:127.0.0.1 >>> rtpproxy: Timer started. >>> INFO:main: rtpproxy started, pid 4203 >>> [... Kamailio connects to RTP-Proxy...] >>> DBUG:handle_command: received command "4259_8 >>> UAc98,97,99,104,3,0,8,9,96 56f0f83a-5373-46a1-b6f6-9ce2f93e68d5 >>> 195.71.4.203 4008 5371f039-40d0-4944-aae7-6f75071a2f8c;1" >>> INFO:handle_command: new session 56f0f83a-5373-46a1-b6f6-9ce2f93e68d5, >>> tag 5371f039-40d0-4944-aae7-6f75071a2f8c;1 requested, type strong >>> INFO:handle_command: new session on a port 45508 created, tag >>> 5371f039-40d0-4944-aae7-6f75071a2f8c;1 >>> INFO:handle_command: pre-filling caller's address with >>> 195.71.4.203:4008 >>> DBUG:doreply: sending reply "4259_8 45508 127.0.0.1 " >>> DBUG:handle_command: received command "4259_9 LAc98,96 >>> 56f0f83a-5373-46a1-b6f6-9ce2f93e68d5 195.71.4.203 4000 >>> 5371f039-40d0-4944-aae7-6f75071a2f8c;1 >>> 9915df0c-30fc-49c5-aa8a-c86b4242c094;1 >>> xmlrpc:http://localhost:8000/RPC2" >>> INFO:handle_command: lookup on ports 45508/45238, session timer >>> restarted >>> INFO:handle_command: setting custom timeout handler >>> (xmlrpc:http://localhost:8000/RPC2) >>> INFO:handle_command: pre-filling callee's address with >>> 195.71.4.203:4000 >>> DBUG:doreply: sending reply "4259_9 45238 127.0.0.1 " >>> INFO:process_rtp: session timeout >>> ERR:rtpp_notify_schedule: XMLRPC xmlrpc:http://localhost:8000/RPC2 >>> INFO:remove_session: RTP stats: 0 in from callee, 0 in from caller, 0 >>> relayed, 0 dropped >>> INFO:remove_session: RTCP stats: 0 in from callee, 0 in from caller, 0 >>> relayed, 0 dropped >>> INFO:remove_session: session on ports 45508/45238 is cleaned up >>> ERR:do_timeout_notification: Timeout socket: >>> xmlrpc:http://localhost:8000/RPC2 >>> >>> And it works for me: >>> >>> U 2011/03/16 16:50:14.350721 127.0.0.1:5060 -> 127.0.0.1:15061 BYE >>> sip:[email protected]:15061;ob SIP/2.0. >>> Via: SIP/2.0/UDP 127.0.0.1;branch=z9hG4bK06e9.245e2dd7.0. >>> To: sip:2@localhost;tag=5371f039-40d0-4944-aae7-6f75071a2f8c. >>> From: sip:1@localhost;tag=9915df0c-30fc-49c5-aa8a-c86b4242c094. >>> CSeq: 7905 BYE. >>> Call-ID: 56f0f83a-5373-46a1-b6f6-9ce2f93e68d5. >>> Content-Length: 0. >>> User-Agent: kamailio (3.2.0-dev2 (x86_64/linux)). >>> Max-Forwards: 70. >>> . >>> >>> U 2011/03/16 16:50:14.350801 127.0.0.1:5060 -> 127.0.0.1:15060 BYE >>> sip:[email protected]:15060;transport=UDP;ob SIP/2.0. >>> Via: SIP/2.0/UDP 127.0.0.1;branch=z9hG4bK06e9.345e2dd7.0. >>> To: sip:1@localhost;tag=9915df0c-30fc-49c5-aa8a-c86b4242c094. >>> From: sip:2@localhost;tag=5371f039-40d0-4944-aae7-6f75071a2f8c. >>> CSeq: 7905 BYE. >>> Call-ID: 56f0f83a-5373-46a1-b6f6-9ce2f93e68d5. >>> Content-Length: 0. >>> User-Agent: kamailio (3.2.0-dev2 (x86_64/linux)). >>> Max-Forwards: 70. >>> . >>> [...] >>> >>> Maybe, you can add some more debug-info from RTP-Proxy... >>> And can you verify, that the RTP-Proxy is not trying to send the request? >>> >>> Kind regards, >>> Carsten >>> >>> 2011/3/16 Alexandre Abreu <[email protected]>: >>>> Hi Carsten, >>>> >>>> Even with your RTPPROXY tarball I was unable to get this working. >>>> Session remains after RTPPROXY timeout. >>>> I am using KAMAILIO 3.1 branch from GIT and as I told you, I moved >>>> the rtpproxy/ from GIT-MASTER to the Kamailio branch (waiting your >> backport). >>> Is >>>> there anything else regarding this feature that should also should be >>> moved >>>> (beyond rtpproxy/)? >>>> >>>> Thanks, >>>> Alexandre >> >> > > > > -- > Carsten Bock > http://www.ng-voice.com > mailto:[email protected] > -- Carsten Bock http://www.ng-voice.com mailto:[email protected]
/* * Copyright (c) 2004-2006 Maxim Sobolev <[email protected]> * Copyright (c) 2006-2007 Sippy Software, Inc., http://www.sippysoft.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/resource.h> #include <sys/un.h> #include <sys/uio.h> #include <ctype.h> #include <sys/select.h> #include <sys/stat.h> #include <assert.h> #include <errno.h> #include <fcntl.h> #include <grp.h> #include <limits.h> #include <netdb.h> #include <poll.h> #include <pthread.h> #include <pwd.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <string.h> #include <strings.h> #include <unistd.h> #include "rtp.h" #include "rtp_resizer.h" #include "rtp_server.h" #include "rtpp_defines.h" #include "rtpp_command.h" #include "rtpp_command_async.h" #include "rtpp_log.h" #include "rtpp_record.h" #include "rtpp_session.h" #include "rtpp_network.h" #include "rtpp_notify.h" #include "rtpp_util.h" static const char *cmd_sock = CMD_SOCK; static const char *pid_file = PID_FILE; static rtpp_log_t glog; static void usage(void); static void send_packet(struct cfg *, struct rtpp_session *, int, struct rtp_packet *); static void usage(void) { fprintf(stderr, "usage: rtpproxy [-2fvFiPa] [-l addr1[/addr2]] " "[-6 addr1[/addr2]] [-s path]\n\t[-t tos] [-r rdir [-S sdir]] [-T ttl] " "[-L nfiles] [-m port_min]\n\t[-M port_max] [-u uname[:gname]] " "[-n timeout_socket] [-d log_level[:log_facility]]\n"); exit(1); } static void fatsignal(int sig) { rtpp_log_write(RTPP_LOG_INFO, glog, "got signal %d", sig); exit(0); } static void ehandler(void) { unlink(cmd_sock); unlink(pid_file); rtpp_log_write(RTPP_LOG_INFO, glog, "rtpproxy ended"); rtpp_log_close(glog); } static void init_config(struct cfg *cf, int argc, char **argv) { int ch, i; char *bh[2], *bh6[2], *cp; const char *errmsg; struct passwd *pp; struct group *gp; bh[0] = bh[1] = bh6[0] = bh6[1] = NULL; cf->stable.port_min = PORT_MIN; cf->stable.port_max = PORT_MAX; cf->stable.max_ttl = SESSION_TIMEOUT; cf->stable.tos = TOS; cf->stable.rrtcp = 1; cf->stable.ttl_mode = TTL_UNIFIED; cf->stable.log_level = -1; cf->stable.log_facility = -1; cf->stable.timeout_socket = NULL; pthread_mutex_init(&cf->glock, NULL); if (getrlimit(RLIMIT_NOFILE, &(cf->stable.nofile_limit)) != 0) err(1, "getrlimit"); while ((ch = getopt(argc, argv, "vf2Rl:6:s:S:t:r:p:T:L:m:M:u:Fin:Pad:")) != -1) switch (ch) { case 'f': cf->stable.nodaemon = 1; break; case 'l': bh[0] = optarg; bh[1] = strchr(bh[0], '/'); if (bh[1] != NULL) { *bh[1] = '\0'; bh[1]++; cf->stable.bmode = 1; } break; case '6': bh6[0] = optarg; bh6[1] = strchr(bh6[0], '/'); if (bh6[1] != NULL) { *bh6[1] = '\0'; bh6[1]++; cf->stable.bmode = 1; } break; case 's': if (strncmp("udp:", optarg, 4) == 0) { cf->stable.umode = 1; optarg += 4; } else if (strncmp("udp6:", optarg, 5) == 0) { cf->stable.umode = 6; optarg += 5; } else if (strncmp("unix:", optarg, 5) == 0) { cf->stable.umode = 0; optarg += 5; } cmd_sock = optarg; break; case 't': cf->stable.tos = atoi(optarg); if (cf->stable.tos > 255) errx(1, "%d: TOS is too large", cf->stable.tos); break; case '2': cf->stable.dmode = 1; break; case 'v': printf("Basic version: %d\n", CPROTOVER); for (i = 1; proto_caps[i].pc_id != NULL; ++i) { printf("Extension %s: %s\n", proto_caps[i].pc_id, proto_caps[i].pc_description); } exit(0); break; case 'r': cf->stable.rdir = optarg; break; case 'S': cf->stable.sdir = optarg; break; case 'R': cf->stable.rrtcp = 0; break; case 'p': pid_file = optarg; break; case 'T': cf->stable.max_ttl = atoi(optarg); break; case 'L': cf->stable.nofile_limit.rlim_cur = cf->stable.nofile_limit.rlim_max = atoi(optarg); if (setrlimit(RLIMIT_NOFILE, &(cf->stable.nofile_limit)) != 0) err(1, "setrlimit"); if (getrlimit(RLIMIT_NOFILE, &(cf->stable.nofile_limit)) != 0) err(1, "getrlimit"); if (cf->stable.nofile_limit.rlim_max < atoi(optarg)) warnx("limit allocated by setrlimit (%d) is less than " "requested (%d)", (int) cf->stable.nofile_limit.rlim_max, atoi(optarg)); break; case 'm': cf->stable.port_min = atoi(optarg); break; case 'M': cf->stable.port_max = atoi(optarg); break; case 'u': cf->stable.run_uname = optarg; cp = strchr(optarg, ':'); if (cp != NULL) { if (cp == optarg) cf->stable.run_uname = NULL; cp[0] = '\0'; cp++; } cf->stable.run_gname = cp; cf->stable.run_uid = -1; cf->stable.run_gid = -1; if (cf->stable.run_uname != NULL) { pp = getpwnam(cf->stable.run_uname); if (pp == NULL) err(1, "can't find ID for the user: %s", cf->stable.run_uname); cf->stable.run_uid = pp->pw_uid; if (cf->stable.run_gname == NULL) cf->stable.run_gid = pp->pw_gid; } if (cf->stable.run_gname != NULL) { gp = getgrnam(cf->stable.run_gname); if (gp == NULL) err(1, "can't find ID for the group: %s", cf->stable.run_gname); cf->stable.run_gid = gp->gr_gid; } break; case 'F': cf->stable.no_check = 1; break; case 'i': cf->stable.ttl_mode = TTL_INDEPENDENT; break; case 'n': if(strncmp("unix:", optarg, 5) == 0) optarg += 5; if(strlen(optarg) == 0) errx(1, "timeout notification socket name too short"); cf->stable.timeout_socket = optarg; break; case 'P': cf->stable.record_pcap = 1; break; case 'a': cf->stable.record_all = 1; break; case 'd': cp = strchr(optarg, ':'); if (cp != NULL) { cf->stable.log_facility = rtpp_log_str2fac(cp + 1); if (cf->stable.log_facility == -1) errx(1, "%s: invalid log facility", cp + 1); *cp = '\0'; } cf->stable.log_level = rtpp_log_str2lvl(optarg); if (cf->stable.log_level == -1) errx(1, "%s: invalid log level", optarg); break; case '?': default: usage(); } if (cf->stable.rdir == NULL && cf->stable.sdir != NULL) errx(1, "-S switch requires -r switch"); if (cf->stable.no_check == 0 && getuid() == 0 && cf->stable.run_uname == NULL) { if (cf->stable.umode != 0) { errx(1, "running this program as superuser in a remote control " "mode is strongly not recommended, as it poses serious security " "threat to your system. Use -u option to run as an unprivileged " "user or -F is you want to run as a superuser anyway."); } else { warnx("WARNING!!! Running this program as superuser is strongly " "not recommended, as it may pose serious security threat to " "your system. Use -u option to run as an unprivileged user " "or -F to surpress this warning."); } } /* make sure that port_min and port_max are even */ if ((cf->stable.port_min % 2) != 0) cf->stable.port_min++; if ((cf->stable.port_max % 2) != 0) { cf->stable.port_max--; } else { /* * If port_max is already even then there is no * "room" for the RTCP port, go back by two ports. */ cf->stable.port_max -= 2; } if (!IS_VALID_PORT(cf->stable.port_min)) errx(1, "invalid value of the port_min argument, " "not in the range 1-65535"); if (!IS_VALID_PORT(cf->stable.port_max)) errx(1, "invalid value of the port_max argument, " "not in the range 1-65535"); if (cf->stable.port_min > cf->stable.port_max) errx(1, "port_min should be less than port_max"); cf->sessions = malloc((sizeof cf->sessions[0]) * (((cf->stable.port_max - cf->stable.port_min + 1) * 2) + 1)); cf->rtp_servers = malloc((sizeof cf->rtp_servers[0]) * (((cf->stable.port_max - cf->stable.port_min + 1) * 2) + 1)); cf->pfds = malloc((sizeof cf->pfds[0]) * (((cf->stable.port_max - cf->stable.port_min + 1) * 2) + 1)); if (bh[0] == NULL && bh[1] == NULL && bh6[0] == NULL && bh6[1] == NULL) { bh[0] = "*"; } for (i = 0; i < 2; i++) { if (bh[i] != NULL && *bh[i] == '\0') bh[i] = NULL; if (bh6[i] != NULL && *bh6[i] == '\0') bh6[i] = NULL; } i = ((bh[0] == NULL) ? 0 : 1) + ((bh[1] == NULL) ? 0 : 1) + ((bh6[0] == NULL) ? 0 : 1) + ((bh6[1] == NULL) ? 0 : 1); if (cf->stable.bmode != 0) { if (bh[0] != NULL && bh6[0] != NULL) errx(1, "either IPv4 or IPv6 should be configured for external " "interface in bridging mode, not both"); if (bh[1] != NULL && bh6[1] != NULL) errx(1, "either IPv4 or IPv6 should be configured for internal " "interface in bridging mode, not both"); if (i != 2) errx(1, "incomplete configuration of the bridging mode - exactly " "2 listen addresses required, %d provided", i); } else if (i != 1) { errx(1, "exactly 1 listen addresses required, %d provided", i); } for (i = 0; i < 2; i++) { cf->stable.bindaddr[i] = NULL; if (bh[i] != NULL) { cf->stable.bindaddr[i] = host2bindaddr(cf, bh[i], AF_INET, &errmsg); if (cf->stable.bindaddr[i] == NULL) errx(1, "host2bindaddr: %s", errmsg); continue; } if (bh6[i] != NULL) { cf->stable.bindaddr[i] = host2bindaddr(cf, bh6[i], AF_INET6, &errmsg); if (cf->stable.bindaddr[i] == NULL) errx(1, "host2bindaddr: %s", errmsg); continue; } } if (cf->stable.bindaddr[0] == NULL) { cf->stable.bindaddr[0] = cf->stable.bindaddr[1]; cf->stable.bindaddr[1] = NULL; } } static int init_controlfd(struct cfg *cf) { struct sockaddr_un ifsun; struct sockaddr_storage ifsin; char *cp; int i, controlfd, flags; if (cf->stable.umode == 0) { unlink(cmd_sock); memset(&ifsun, '\0', sizeof ifsun); #if defined(HAVE_SOCKADDR_SUN_LEN) ifsun.sun_len = strlen(cmd_sock); #endif ifsun.sun_family = AF_LOCAL; strcpy(ifsun.sun_path, cmd_sock); controlfd = socket(PF_LOCAL, SOCK_STREAM, 0); if (controlfd == -1) err(1, "can't create socket"); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &controlfd, sizeof controlfd); if (bind(controlfd, sstosa(&ifsun), sizeof ifsun) < 0) err(1, "can't bind to a socket"); if ((cf->stable.run_uname != NULL || cf->stable.run_gname != NULL) && chown(cmd_sock, cf->stable.run_uid, cf->stable.run_gid) == -1) err(1, "can't set owner of the socket"); if (listen(controlfd, 32) != 0) err(1, "can't listen on a socket"); } else { cp = strrchr(cmd_sock, ':'); if (cp != NULL) { *cp = '\0'; cp++; } if (cp == NULL || *cp == '\0') cp = CPORT; i = (cf->stable.umode == 6) ? AF_INET6 : AF_INET; if (setbindhost(sstosa(&ifsin), i, cmd_sock, cp) != 0) exit(1); controlfd = socket(i, SOCK_DGRAM, 0); if (controlfd == -1) err(1, "can't create socket"); if (bind(controlfd, sstosa(&ifsin), SS_LEN(&ifsin)) < 0) err(1, "can't bind to a socket"); } flags = fcntl(controlfd, F_GETFL); fcntl(controlfd, F_SETFL, flags | O_NONBLOCK); return controlfd; } static void process_rtp_servers(struct cfg *cf, double dtime) { int j, k, sidx, len, skipfd; struct rtpp_session *sp; skipfd = 0; for (j = 0; j < cf->rtp_nsessions; j++) { sp = cf->rtp_servers[j]; if (sp == NULL) { skipfd++; continue; } if (skipfd > 0) { cf->rtp_servers[j - skipfd] = cf->rtp_servers[j]; sp->sridx = j - skipfd; } for (sidx = 0; sidx < 2; sidx++) { if (sp->rtps[sidx] == NULL || sp->addr[sidx] == NULL) continue; while ((len = rtp_server_get(sp->rtps[sidx], dtime)) != RTPS_LATER) { if (len == RTPS_EOF) { rtp_server_free(sp->rtps[sidx]); sp->rtps[sidx] = NULL; if (sp->rtps[0] == NULL && sp->rtps[1] == NULL) { assert(cf->rtp_servers[sp->sridx] == sp); cf->rtp_servers[sp->sridx] = NULL; sp->sridx = -1; } break; } for (k = (cf->stable.dmode && len < LBR_THRS) ? 2 : 1; k > 0; k--) { sendto(sp->fds[sidx], sp->rtps[sidx]->buf, len, 0, sp->addr[sidx], SA_LEN(sp->addr[sidx])); } } } } cf->rtp_nsessions -= skipfd; } static void rxmit_packets(struct cfg *cf, struct rtpp_session *sp, int ridx, double dtime) { int ndrain, i, port; struct rtp_packet *packet = NULL; /* Repeat since we may have several packets queued on the same socket */ for (ndrain = 0; ndrain < 5; ndrain++) { if (packet != NULL) rtp_packet_free(packet); packet = rtp_recv(sp->fds[ridx]); if (packet == NULL) break; packet->laddr = sp->laddr[ridx]; packet->rport = sp->ports[ridx]; packet->rtime = dtime; i = 0; if (sp->addr[ridx] != NULL) { /* Check that the packet is authentic, drop if it isn't */ if (sp->asymmetric[ridx] == 0) { if (memcmp(sp->addr[ridx], &packet->raddr, packet->rlen) != 0) { if (sp->canupdate[ridx] == 0) { /* * Continue, since there could be good packets in * queue. */ continue; } /* Signal that an address has to be updated */ i = 1; } else if (sp->canupdate[ridx] != 0 && sp->last_update[ridx] != 0 && dtime - sp->last_update[ridx] > UPDATE_WINDOW) { sp->canupdate[ridx] = 0; } } else { /* * For asymmetric clients don't check * source port since it may be different. */ if (!ishostseq(sp->addr[ridx], sstosa(&packet->raddr))) /* * Continue, since there could be good packets in * queue. */ continue; } sp->pcount[ridx]++; } else { sp->pcount[ridx]++; sp->addr[ridx] = malloc(packet->rlen); if (sp->addr[ridx] == NULL) { sp->pcount[3]++; rtpp_log_write(RTPP_LOG_ERR, sp->log, "can't allocate memory for remote address - " "removing session"); remove_session(cf, GET_RTP(sp)); /* Break, sp is invalid now */ break; } /* Signal that an address have to be updated. */ i = 1; } /* * Update recorded address if it's necessary. Set "untrusted address" * flag in the session state, so that possible future address updates * from that client won't get address changed immediately to some * bogus one. */ if (i != 0) { sp->untrusted_addr[ridx] = 1; memcpy(sp->addr[ridx], &packet->raddr, packet->rlen); if (sp->prev_addr[ridx] == NULL || memcmp(sp->prev_addr[ridx], &packet->raddr, packet->rlen) != 0) { sp->canupdate[ridx] = 0; } port = ntohs(satosin(&packet->raddr)->sin_port); rtpp_log_write(RTPP_LOG_INFO, sp->log, "%s's address filled in: %s:%d (%s)", (ridx == 0) ? "callee" : "caller", addr2char(sstosa(&packet->raddr)), port, (sp->rtp == NULL) ? "RTP" : "RTCP"); /* * Check if we have updated RTP while RTCP is still * empty or contains address that differs from one we * used when updating RTP. Try to guess RTCP if so, * should be handy for non-NAT'ed clients, and some * NATed as well. */ if (sp->rtcp != NULL && (sp->rtcp->addr[ridx] == NULL || !ishostseq(sp->rtcp->addr[ridx], sstosa(&packet->raddr)))) { if (sp->rtcp->addr[ridx] == NULL) { sp->rtcp->addr[ridx] = malloc(packet->rlen); if (sp->rtcp->addr[ridx] == NULL) { sp->pcount[3]++; rtpp_log_write(RTPP_LOG_ERR, sp->log, "can't allocate memory for remote address - " "removing session"); remove_session(cf, sp); /* Break, sp is invalid now */ break; } } memcpy(sp->rtcp->addr[ridx], &packet->raddr, packet->rlen); satosin(sp->rtcp->addr[ridx])->sin_port = htons(port + 1); /* Use guessed value as the only true one for asymmetric clients */ sp->rtcp->canupdate[ridx] = NOT(sp->rtcp->asymmetric[ridx]); rtpp_log_write(RTPP_LOG_INFO, sp->log, "guessing RTCP port " "for %s to be %d", (ridx == 0) ? "callee" : "caller", port + 1); } } if (sp->resizers[ridx].output_nsamples > 0) rtp_resizer_enqueue(&sp->resizers[ridx], &packet); if (packet != NULL) send_packet(cf, sp, ridx, packet); } if (packet != NULL) rtp_packet_free(packet); } static void send_packet(struct cfg *cf, struct rtpp_session *sp, int ridx, struct rtp_packet *packet) { int i, sidx; GET_RTP(sp)->ttl[ridx] = cf->stable.max_ttl; /* Select socket for sending packet out. */ sidx = (ridx == 0) ? 1 : 0; /* * Check that we have some address to which packet is to be * sent out, drop otherwise. */ if (sp->addr[sidx] == NULL || GET_RTP(sp)->rtps[sidx] != NULL) { sp->pcount[3]++; } else { sp->pcount[2]++; for (i = (cf->stable.dmode && packet->size < LBR_THRS) ? 2 : 1; i > 0; i--) { sendto(sp->fds[sidx], packet->data.buf, packet->size, 0, sp->addr[sidx], SA_LEN(sp->addr[sidx])); } } if (sp->rrcs[ridx] != NULL && GET_RTP(sp)->rtps[ridx] == NULL) rwrite(sp, sp->rrcs[ridx], packet); } static void process_rtp(struct cfg *cf, double dtime, int alarm_tick) { int readyfd, skipfd, ridx; struct rtpp_session *sp; struct rtp_packet *packet; /* Relay RTP/RTCP */ skipfd = 0; for (readyfd = 0; readyfd < cf->nsessions; readyfd++) { sp = cf->sessions[readyfd]; if (alarm_tick != 0 && sp != NULL && sp->rtcp != NULL && sp->sidx[0] == readyfd) { if (get_ttl(sp) == 0) { rtpp_log_write(RTPP_LOG_INFO, sp->log, "session timeout"); rtpp_notify_schedule(cf, sp); remove_session(cf, sp); } else { if (sp->ttl[0] != 0) sp->ttl[0]--; if (sp->ttl[1] != 0) sp->ttl[1]--; } } if (cf->pfds[readyfd].fd == -1) { /* Deleted session, count and move one */ skipfd++; continue; } /* Find index of the call leg within a session */ for (ridx = 0; ridx < 2; ridx++) if (cf->pfds[readyfd].fd == sp->fds[ridx]) break; /* * Can't happen. */ assert(ridx != 2); /* Compact pfds[] and sessions[] by eliminating removed sessions */ if (skipfd > 0) { cf->pfds[readyfd - skipfd] = cf->pfds[readyfd]; cf->sessions[readyfd - skipfd] = cf->sessions[readyfd]; sp->sidx[ridx] = readyfd - skipfd; } if (sp->complete != 0) { if ((cf->pfds[readyfd].revents & POLLIN) != 0) rxmit_packets(cf, sp, ridx, dtime); if (sp->resizers[ridx].output_nsamples > 0) { while ((packet = rtp_resizer_get(&sp->resizers[ridx], dtime)) != NULL) { send_packet(cf, sp, ridx, packet); rtp_packet_free(packet); } } } } /* Trim any deleted sessions at the end */ cf->nsessions -= skipfd; } int main(int argc, char **argv) { int i, len, timeout, controlfd, alarm_tick; double sptime, eptime, last_tick_time; unsigned long delay; struct cfg cf; char buf[256]; memset(&cf, 0, sizeof(cf)); init_config(&cf, argc, argv); seedrandom(); init_hash_table(&cf.stable); init_port_table(&cf); controlfd = init_controlfd(&cf); if (cf.stable.nodaemon == 0) { if (rtpp_daemon(0, 0) == -1) err(1, "can't switch into daemon mode"); /* NOTREACHED */ } if (cf.stable.timeout_socket != NULL) { cf.timeout_handler = rtpp_notify_init(cf.stable.timeout_socket); if (cf.timeout_handler == NULL) errx(1, "can't start notification thread"); } glog = cf.stable.glog = rtpp_log_open(&cf.stable, "rtpproxy", NULL, LF_REOPEN); atexit(ehandler); rtpp_log_write(RTPP_LOG_INFO, cf.stable.glog, "rtpproxy started, pid %d", getpid()); i = open(pid_file, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); if (i >= 0) { len = sprintf(buf, "%u\n", (unsigned int)getpid()); write(i, buf, len); close(i); } else { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable.glog, "can't open pidfile for writing"); } signal(SIGHUP, fatsignal); signal(SIGINT, fatsignal); signal(SIGKILL, fatsignal); signal(SIGPIPE, SIG_IGN); signal(SIGTERM, fatsignal); signal(SIGXCPU, fatsignal); signal(SIGXFSZ, fatsignal); signal(SIGVTALRM, fatsignal); signal(SIGPROF, fatsignal); signal(SIGUSR1, fatsignal); signal(SIGUSR2, fatsignal); if (cf.stable.run_uname != NULL || cf.stable.run_gname != NULL) { if (drop_privileges(&cf) != 0) { rtpp_log_ewrite(RTPP_LOG_ERR, cf.stable.glog, "can't switch to requested user/group"); exit(1); } } cf.stable.controlfd = controlfd; cf.sessions[0] = NULL; cf.nsessions = 0; cf.rtp_nsessions = 0; rtpp_command_async_init(&cf); sptime = 0; last_tick_time = 0; for (;;) { pthread_mutex_lock(&cf.glock); if (cf.rtp_nsessions > 0 || cf.nsessions > 0) { timeout = RTPS_TICKS_MIN; } else { timeout = TIMETICK * 1000; } pthread_mutex_unlock(&cf.glock); eptime = getdtime(); delay = (eptime - sptime) * 1000000.0; if (delay < (1000000 / POLL_LIMIT)) { usleep((1000000 / POLL_LIMIT) - delay); sptime = getdtime(); } else { sptime = eptime; } pthread_mutex_lock(&cf.glock); if (cf.nsessions > 0) { i = poll(cf.pfds, cf.nsessions, timeout); pthread_mutex_unlock(&cf.glock); if (i < 0 && errno == EINTR) continue; } else { pthread_mutex_unlock(&cf.glock); usleep(timeout * 1000); } eptime = getdtime(); pthread_mutex_lock(&cf.glock); if (cf.rtp_nsessions > 0) { process_rtp_servers(&cf, eptime); } pthread_mutex_unlock(&cf.glock); if (eptime > last_tick_time + TIMETICK) { alarm_tick = 1; last_tick_time = eptime; } else { alarm_tick = 0; } pthread_mutex_lock(&cf.glock); process_rtp(&cf, eptime, alarm_tick); pthread_mutex_unlock(&cf.glock); } exit(0); }
/* * Copyright (c) 2004-2006 Maxim Sobolev <[email protected]> * Copyright (c) 2006-2007 Sippy Software, Inc., http://www.sippysoft.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef _RTPP_DEFINES_H_ #define _RTPP_DEFINES_H_ #include "config.h" #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/resource.h> #include <netinet/in.h> #include <poll.h> #ifdef HAVE_STDINT_H #include <stdint.h> #endif #include "rtpp_log.h" /* * Version of the command protocol, bump only when backward-incompatible * change is introduced */ #define CPROTOVER 20040107 #define PORT_MIN 35000 #define PORT_MAX 65000 #define TIMETICK 1.0 /* in seconds */ #define SESSION_TIMEOUT 60 /* in ticks */ #define TOS 0xb8 #define LBR_THRS 128 /* low-bitrate threshold */ #define CPORT "22222" #define POLL_LIMIT 200 /* maximum number of poll(2) calls per second */ #define UPDATE_WINDOW 10.0 /* in seconds */ /* Dummy service, getaddrinfo needs it */ #define SERVICE "34999" #define CMD_SOCK "/var/run/rtpproxy.sock" #define PID_FILE "/var/run/rtpproxy.pid" /* * TTL counters are used to detect the absence of audio packets * in either direction. When the counter reaches 0, the call timeout * occurs. */ typedef enum { TTL_UNIFIED = 0, /* all TTL counters must reach 0 */ TTL_INDEPENDENT = 1 /* any TTL counter reaches 0 */ } rtpp_ttl_mode; struct bindaddr_list { struct sockaddr_storage bindaddr; struct bindaddr_list *next; }; struct cfg { struct cfg_stable { int nodaemon; char * timeout_socket; int dmode; int bmode; /* Bridge mode */ int umode; /* UDP control mode */ int port_min; /* Lowest UDP port for RTP */ int port_max; /* Highest UDP port number for RTP */ int max_ttl; /* * The first address is for external interface, the second one - for * internal one. Second can be NULL, in this case there is no bridge * mode enabled. */ struct sockaddr *bindaddr[2]; /* RTP socket(s) addresses */ int tos; const char *rdir; const char *sdir; int record_pcap; /* Record in the PCAP format? */ int record_all; /* Record everything */ int rrtcp; /* Whether or not to relay RTCP? */ rtpp_log_t glog; struct rlimit nofile_limit; char *run_uname; char *run_gname; int no_check; rtpp_ttl_mode ttl_mode; uid_t run_uid; gid_t run_gid; int log_level; int log_facility; uint16_t port_table[65536]; int port_table_len; uint8_t rand_table[256]; int controlfd; } stable; struct rtpp_session **sessions; struct rtpp_session **rtp_servers; struct pollfd *pfds; int nsessions; int rtp_nsessions; int sessions_active; unsigned long long sessions_created; int nofile_limit_warned; struct bindaddr_list *bindaddr_list; struct rtpp_session *hash_table[256]; struct rtpp_timeout_handler *timeout_handler; int port_table_idx; pthread_mutex_t glock; }; #endif
_______________________________________________ Devel mailing list [email protected] http://lists.rtpproxy.org/mailman/listinfo/devel
