This shows you the differences between two versions of the page.
— | kamailio:kamailio-mixed-ipv4-ipv6 [2011/06/28 21:06] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Run your own SIP VoIP service on both IPv4 and IPv6 ====== | ||
+ | |||
+ | < | ||
+ | Main author: | ||
+ | Daniel-Constantin Mierla <miconda [at] gmail.com> | ||
+ | - founder Kamailio SIP Server project | ||
+ | </ | ||
+ | |||
+ | At the date of publishing the article, the world celebrated [[http:// | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | The target with this tutorial is to have a service that is able to handle VoIP traffic: | ||
+ | * in IPv4 networks | ||
+ | * in IPv6 networks | ||
+ | * bridge between IPv4 and IPv6 networks when needed | ||
+ | |||
+ | [[http:// | ||
+ | |||
+ | Did I say NAT? Fortunately it will disappear soon, when (if) IPv6 will take the control in Internet communications. | ||
+ | |||
+ | ===== About the environment ===== | ||
+ | |||
+ | To make the guidelines for this tutorial, I built a local lab environment where I used: | ||
+ | * Kamailio devel version running on Ubuntu 11.04 with next IP addresses: | ||
+ | * IPv4: 192.168.178.26 | ||
+ | * IPv6: fec0: | ||
+ | * Jitsi SIP softphone running on Mac OS X with next IP address: | ||
+ | * IPv4: 192.168.178.21 | ||
+ | * Jitsi SIP softphone running on Ubuntu 10.10 with next IP address: | ||
+ | * IPv6: fec0: | ||
+ | |||
+ | To install Kamailio devel version (to become 3.2.0 release in Autumn 2011), use following guidelines: | ||
+ | * [[http:// | ||
+ | |||
+ | < | ||
+ | |||
+ | However, the same concept can be applied to the latest stable v3.1.x, just use the appropriate functions from rtpproxy module - the development version introduced a new function rtpproxy_manage() which is a wrapper around existing functions in v3.1.x: rtpproxy_offer(), | ||
+ | |||
+ | Debian/ | ||
+ | * [[http:// | ||
+ | |||
+ | Jitsi is a cross platform (Linux, Mac OS X and Windows) open source SIP softphone available at: | ||
+ | * http:// | ||
+ | |||
+ | FYI: I tested also with Linphone on IPv6 and seemed to be fine -- a side not: Linphone for Mac OS X didn't have support for video and I wanted to test also multi-streams IPv4-IPv6 bridging (audio and video in this case). | ||
+ | |||
+ | RTPProxy is available at: | ||
+ | * http:// | ||
+ | * for this article I installed RTPProxy from Ubuntu default APT repository with: | ||
+ | |||
+ | < | ||
+ | apt-get install rtpproxy | ||
+ | </ | ||
+ | |||
+ | RTPProxy creates a special user (named rtpproxy) to run under unprivileged permissions. You have to allow Kamailio to communicate with it via control socket. One easy way is to edit / | ||
+ | |||
+ | < | ||
+ | USER=kamailio | ||
+ | </ | ||
+ | |||
+ | Note that RTPProxy has to be started with special parameters in order o bridge between IPv4 and IPv6 addresses: | ||
+ | |||
+ | < | ||
+ | -l ADDR_IPV4 -6 /ADDR_IPV6 | ||
+ | </ | ||
+ | |||
+ | You have to change the **/ | ||
+ | ===== Sample Scenario ===== | ||
+ | |||
+ | I called using one Jitsi phone on IPv6 and one on IPv4, turning on video conversation -- this is the most interesting case in my opinion: bridging real-time communication between IPv6 and IPv4 networks. | ||
+ | |||
+ | {{ http:// | ||
+ | |||
+ | Here is the result - **I is talking to me**. | ||
+ | |||
+ | {{ http:// | ||
+ | |||
+ | ==== SIP Traffic ==== | ||
+ | |||
+ | For the technical guys, I pasted below the relevant SIP signaling messages for this call (I stripped some of the SDP content since it was too big and irrelevant for this case -- just codecs attributes). | ||
+ | |||
+ | Some remarks about the SIP traffic: | ||
+ | * call comes in from IPv6 (frame [1] INVITE) with headers and SDP body filled with IPv6 addresses | ||
+ | * when the call is forwarded by Kamailio to callee (frame [2]), you can observe: | ||
+ | * presence of two Record-Route heders, one with Kamailio' | ||
+ | * although incoming INVITE had SDP body with caller' | ||
+ | * when call is answered, the 200ok SIP replies comes in from callee (frame [3]), with IPv4 addresses in SDP body | ||
+ | * then the 200ok reply is relayed to caller (frame [4]), but now the SDP body has the IPv6 address of RTPProxy | ||
+ | * the communication is ready to start, each side sending packets only in one type of network. SIP routing is driven by Record-Route headers and RTP by IP address in SDP body. | ||
+ | |||
+ | < | ||
+ | [1] fec0: | ||
+ | |||
+ | INVITE sip: | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | CSeq: 2 INVITE | ||
+ | From: " | ||
+ | To: < | ||
+ | Via: SIP/2.0/UDP [fec0: | ||
+ | Max-Forwards: | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3522Linux | ||
+ | Content-Type: | ||
+ | Content-Length: | ||
+ | |||
+ | v=0 | ||
+ | o=103 0 0 IN IP6 fec0: | ||
+ | s=- | ||
+ | c=IN IP6 fec0: | ||
+ | t=0 0 | ||
+ | m=audio 5004 RTP/AVP 9 96 97 0 8 98 3 99 5 6 4 15 101 | ||
+ | a=rtpmap:9 G722/8000 | ||
+ | ... | ||
+ | |||
+ | |||
+ | [2] 192.168.178.26.5060 > 192.168.178.21.5060 | ||
+ | |||
+ | INVITE sip: | ||
+ | Record-Route: | ||
+ | Record-Route: | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | CSeq: 2 INVITE | ||
+ | From: " | ||
+ | To: < | ||
+ | Max-Forwards: | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3522Linux | ||
+ | Content-Type: | ||
+ | Via: SIP/2.0/UDP 192.168.178.26; | ||
+ | Via: SIP/2.0/UDP [fec0: | ||
+ | Content-Length: | ||
+ | |||
+ | v=0 | ||
+ | o=103 0 0 IN IP4 192.168.178.26 | ||
+ | s=- | ||
+ | c=IN IP4 192.168.178.26 | ||
+ | t=0 0 | ||
+ | m=audio 61536 RTP/AVP 9 96 97 0 8 98 3 99 5 6 4 15 101 | ||
+ | a=rtpmap:9 G722/8000 | ||
+ | ... | ||
+ | a=nortpproxy: | ||
+ | |||
+ | |||
+ | [3] 192.168.178.21.5060 > 192.168.178.26.5060 | ||
+ | |||
+ | SIP/2.0 200 OK | ||
+ | To: < | ||
+ | Via: SIP/2.0/UDP 192.168.178.26; | ||
+ | Record-Route: | ||
+ | CSeq: 2 INVITE | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | From: " | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3521Mac OS X | ||
+ | Content-Type: | ||
+ | Content-Length: | ||
+ | |||
+ | v=0 | ||
+ | o=102 0 0 IN IP4 192.168.178.21 | ||
+ | s=- | ||
+ | c=IN IP4 192.168.178.21 | ||
+ | t=0 0 | ||
+ | m=audio 5008 RTP/AVP 9 96 97 0 8 98 99 5 6 15 101 | ||
+ | a=rtpmap:9 G722/8000 | ||
+ | ... | ||
+ | |||
+ | |||
+ | [4] fec0: | ||
+ | |||
+ | SIP/2.0 200 OK | ||
+ | To: < | ||
+ | Via: SIP/2.0/UDP [fec0: | ||
+ | Record-Route: | ||
+ | CSeq: 2 INVITE | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | From: " | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3521Mac OS X | ||
+ | Content-Type: | ||
+ | Content-Length: | ||
+ | |||
+ | v=0 | ||
+ | o=102 0 0 IN IP6 fec0: | ||
+ | s=- | ||
+ | c=IN IP6 fec0: | ||
+ | t=0 0 | ||
+ | m=audio 38516 RTP/AVP 9 96 97 0 8 98 99 5 6 15 101 | ||
+ | a=rtpmap:9 G722/8000 | ||
+ | ... | ||
+ | a=nortpproxy: | ||
+ | |||
+ | |||
+ | [5] fec0: | ||
+ | |||
+ | ACK sip: | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | CSeq: 2 ACK | ||
+ | Via: SIP/2.0/UDP [fec0: | ||
+ | From: " | ||
+ | To: " | ||
+ | Max-Forwards: | ||
+ | Route: < | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3522Linux | ||
+ | Content-Length: | ||
+ | |||
+ | |||
+ | [6] 192.168.178.26.5060 > 192.168.178.21.5060 | ||
+ | |||
+ | ACK sip: | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | CSeq: 2 ACK | ||
+ | Via: SIP/2.0/UDP 192.168.178.26; | ||
+ | Via: SIP/2.0/UDP [fec0: | ||
+ | From: " | ||
+ | To: " | ||
+ | Max-Forwards: | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3522Linux | ||
+ | Content-Length: | ||
+ | |||
+ | |||
+ | |||
+ | [7] 192.168.178.21.5060 > 192.168.178.26.5060 | ||
+ | |||
+ | BYE sip: | ||
+ | CSeq: 1 BYE | ||
+ | From: < | ||
+ | To: " | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | Max-Forwards: | ||
+ | Route: < | ||
+ | Via: SIP/2.0/UDP 192.168.178.21: | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3521Mac OS X | ||
+ | Content-Length: | ||
+ | |||
+ | |||
+ | |||
+ | [8] fec0: | ||
+ | |||
+ | BYE sip: | ||
+ | CSeq: 1 BYE | ||
+ | From: < | ||
+ | To: " | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | Max-Forwards: | ||
+ | Via: SIP/2.0/UDP [FEC0: | ||
+ | Via: SIP/2.0/UDP 192.168.178.21: | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3521Mac OS X | ||
+ | Content-Length: | ||
+ | |||
+ | |||
+ | [9] fec0: | ||
+ | |||
+ | SIP/2.0 200 OK | ||
+ | To: " | ||
+ | Via: SIP/2.0/UDP [FEC0: | ||
+ | CSeq: 1 BYE | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | From: < | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3522Linux | ||
+ | Content-Length: | ||
+ | |||
+ | |||
+ | [10] 192.168.178.26.5060 > 192.168.178.21.5060 | ||
+ | |||
+ | SIP/2.0 200 OK | ||
+ | To: " | ||
+ | Via: SIP/2.0/UDP 192.168.178.21: | ||
+ | CSeq: 1 BYE | ||
+ | Call-ID: cc8f5a3f528f880e3caabbf11605b0c2@0: | ||
+ | From: < | ||
+ | Contact: " | ||
+ | User-Agent: Jitsi1.0-beta1-nightly.build.3522Linux | ||
+ | Content-Length: | ||
+ | |||
+ | </ | ||
+ | |||
+ | ==== Location Records ==== | ||
+ | |||
+ | The location records in Kamailio taken with kamctl looked like: | ||
+ | |||
+ | < | ||
+ | # kamctl ul show | ||
+ | |||
+ | Domain:: location table=512 records=2 max_slot=1 | ||
+ | AOR:: 102 | ||
+ | Contact:: sip: | ||
+ | Expires:: | ||
+ | Callid:: 54cf25d15fe668a1d542616093f99c70@0: | ||
+ | Cseq:: 2 | ||
+ | User-agent:: | ||
+ | Received:: | ||
+ | State:: CS_DIRTY | ||
+ | Flags:: 0 | ||
+ | Cflag:: 2 | ||
+ | Socket:: udp: | ||
+ | Methods:: | ||
+ | AOR:: 103 | ||
+ | Contact:: sip: | ||
+ | Expires:: | ||
+ | Callid:: b5291cb60c79d65c65291364c7103138@0: | ||
+ | Cseq:: 2 | ||
+ | User-agent:: | ||
+ | State:: CS_DIRTY | ||
+ | Flags:: 0 | ||
+ | Cflag:: 64 | ||
+ | Socket:: udp: | ||
+ | Methods:: | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Kamailio Configuration File ===== | ||
+ | |||
+ | The configuration file for IPv4-IPv6 support is available for download further down. You have to copy it over / | ||
+ | |||
+ | Then you have to edit it and update the values for IPv4 and IPv6 addresses acording to your environment, | ||
+ | |||
+ | < | ||
+ | #!define ADDR_IPV4 192.168.178.26 | ||
+ | #!define ADDR_IPV6 [fec0: | ||
+ | </ | ||
+ | |||
+ | Then you are ready to go - start kamailio with this configuration file. | ||
+ | |||
+ | To check if it is listening on both IPv4 and IPv6, use kamctl tool: | ||
+ | |||
+ | < | ||
+ | # kamctl ps | ||
+ | |||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | Process:: | ||
+ | </ | ||
+ | |||
+ | You can spot easily the UDP receivers on IPv6 address. | ||
+ | |||
+ | ==== kamailio.cfg ==== | ||
+ | |||
+ | Here is the content you have to place in **/ | ||
+ | |||
+ | <code c> | ||
+ | #!KAMAILIO | ||
+ | # | ||
+ | # Kamailio (OpenSER) SIP Server v3.2 - default configuration script | ||
+ | # - web: http:// | ||
+ | # - git: http:// | ||
+ | # | ||
+ | # Direct your questions about this file to: < | ||
+ | # | ||
+ | # Refer to the Core CookBook at http:// | ||
+ | # for an explanation of possible statements, functions and parameters. | ||
+ | # | ||
+ | # Several features can be enabled using '# | ||
+ | # | ||
+ | # *** To run in debug mode: | ||
+ | # - define WITH_DEBUG | ||
+ | # | ||
+ | # *** To enable mysql: | ||
+ | # - define WITH_MYSQL | ||
+ | # | ||
+ | # *** To enable authentication execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_AUTH | ||
+ | # - add users using ' | ||
+ | # | ||
+ | # *** To enable IP authentication execute: | ||
+ | # - enable mysql | ||
+ | # - enable authentication | ||
+ | # - define WITH_IPAUTH | ||
+ | # - add IP addresses with group id ' | ||
+ | # | ||
+ | # *** To enable persistent user location execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_USRLOCDB | ||
+ | # | ||
+ | # *** To enable presence server execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_PRESENCE | ||
+ | # | ||
+ | # *** To enable nat traversal execute: | ||
+ | # - define WITH_NAT | ||
+ | # - install RTPProxy: http:// | ||
+ | # - start RTPProxy: | ||
+ | # rtpproxy -l _your_public_ip_ -s udp: | ||
+ | # | ||
+ | # *** To enable PSTN gateway routing execute: | ||
+ | # - define WITH_PSTN | ||
+ | # - set the value of pstn.gw_ip | ||
+ | # - check route[PSTN] for regexp routing condition | ||
+ | # | ||
+ | # *** To enable database aliases lookup execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_ALIASDB | ||
+ | # | ||
+ | # *** To enable speed dial lookup execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_SPEEDDIAL | ||
+ | # | ||
+ | # *** To enable multi-domain support execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_MULTIDOMAIN | ||
+ | # | ||
+ | # *** To enable TLS support execute: | ||
+ | # - adjust CFGDIR/ | ||
+ | # - define WITH_TLS | ||
+ | # | ||
+ | # *** To enable XMLRPC support execute: | ||
+ | # - define WITH_XMLRPC | ||
+ | # - adjust route[XMLRPC] for access policy | ||
+ | # | ||
+ | # *** To enable anti-flood detection execute: | ||
+ | # - adjust pike and htable=> | ||
+ | # block if more than 16 requests in 2 seconds and ban for 300 seconds) | ||
+ | # - define WITH_ANTIFLOOD | ||
+ | # | ||
+ | # *** To block 3XX redirect replies execute: | ||
+ | # - define WITH_BLOCK3XX | ||
+ | # | ||
+ | # *** To enable VoiceMail routing execute: | ||
+ | # - define WITH_VOICEMAIL | ||
+ | # - set the value of voicemail.srv_ip | ||
+ | # - adjust the value of voicemail.srv_port | ||
+ | # | ||
+ | # | ||
+ | # *** To enable IPv6 routing execute: | ||
+ | # - define WITH_IPV6 | ||
+ | # - define ADDR_IPV4 to server' | ||
+ | # - define ADDR_IPV6 to server' | ||
+ | # - enable nat traversal | ||
+ | # * run RTPProxy in bridge mode between ADDR_IPV4 and ADDR_IPV6 | ||
+ | # * - RTPProxy options: -l ADDR_IPV4 -6 /ADDR_IPV6 | ||
+ | # | ||
+ | # *** To enhance accounting execute: | ||
+ | # - enable mysql | ||
+ | # - define WITH_ACCDB | ||
+ | # - add following columns to database | ||
+ | #!ifdef ACCDB_COMMENT | ||
+ | ALTER TABLE acc ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE acc ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE acc ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE acc ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE acc ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE missed_calls ADD COLUMN src_user VARCHAR(64) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE missed_calls ADD COLUMN src_domain VARCHAR(128) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE missed_calls ADD COLUMN dst_ouser VARCHAR(64) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE missed_calls ADD COLUMN dst_user VARCHAR(64) NOT NULL DEFAULT ''; | ||
+ | ALTER TABLE missed_calls ADD COLUMN dst_domain VARCHAR(128) NOT NULL DEFAULT ''; | ||
+ | #!endif | ||
+ | |||
+ | ####### Defined Values ######### | ||
+ | |||
+ | #!define WITH_MYSQL | ||
+ | #!define WITH_AUTH | ||
+ | #!define WITH_USRLOCDB | ||
+ | #!define WITH_NAT | ||
+ | #!define WITH_IPV6 | ||
+ | #!define ADDR_IPV4 192.168.178.26 | ||
+ | #!define ADDR_IPV6 [fec0: | ||
+ | |||
+ | # *** Value defines - IDs used later in config | ||
+ | #!ifdef WITH_MYSQL | ||
+ | # - database URL - used to connect to database server by modules such | ||
+ | # as: auth_db, acc, usrloc, a.s.o. | ||
+ | #!define DBURL " | ||
+ | #!endif | ||
+ | #!ifdef WITH_MULTIDOMAIN | ||
+ | # - the value for ' | ||
+ | #!define MULTIDOMAIN 1 | ||
+ | #!else | ||
+ | #!define MULTIDOMAIN 0 | ||
+ | #!endif | ||
+ | |||
+ | # - flags | ||
+ | # FLT_ - per transaction (message) flags | ||
+ | # FLB_ - per branch flags | ||
+ | #!define FLT_ACC 1 | ||
+ | #!define FLT_ACCMISSED 2 | ||
+ | #!define FLT_ACCFAILED 3 | ||
+ | #!define FLT_NATS 5 | ||
+ | |||
+ | #!define FLB_NATB 1 | ||
+ | #!define FLB_NATSIPPING 2 | ||
+ | #!define FLB_IPV6 6 | ||
+ | #!define FLB_V4V6 7 | ||
+ | |||
+ | ####### Global Parameters ######### | ||
+ | |||
+ | #!ifdef WITH_DEBUG | ||
+ | debug=4 | ||
+ | log_stderror=yes | ||
+ | #!else | ||
+ | debug=2 | ||
+ | log_stderror=no | ||
+ | #!endif | ||
+ | |||
+ | memdbg=5 | ||
+ | memlog=5 | ||
+ | |||
+ | log_facility=LOG_LOCAL0 | ||
+ | |||
+ | fork=yes | ||
+ | children=4 | ||
+ | |||
+ | /* uncomment the next line to disable TCP (default on) */ | ||
+ | # | ||
+ | |||
+ | /* uncomment the next line to disable the auto discovery of local aliases | ||
+ | based on reverse DNS on IPs (default on) */ | ||
+ | # | ||
+ | |||
+ | /* add local domain aliases */ | ||
+ | # | ||
+ | |||
+ | #!ifdef WITH_IPV6 | ||
+ | listen=ADDR_IPV4 | ||
+ | listen=ADDR_IPV6 | ||
+ | #!else | ||
+ | /* uncomment and configure the following line if you want Kamailio to | ||
+ | bind on a specific interface/ | ||
+ | # | ||
+ | #!endif | ||
+ | |||
+ | /* port to listen to | ||
+ | * - can be specified more than once if needed to listen on many ports */ | ||
+ | port=5060 | ||
+ | |||
+ | #!ifdef WITH_TLS | ||
+ | enable_tls=yes | ||
+ | #!endif | ||
+ | |||
+ | ####### Custom Parameters ######### | ||
+ | |||
+ | # These parameters can be modified runtime via RPC interface | ||
+ | # - see the documentation of ' | ||
+ | # | ||
+ | # Format: group.id = value ' | ||
+ | # Access: $sel(cfg_get.group.id) or @cfg_get.group.id | ||
+ | # | ||
+ | |||
+ | #!ifdef WITH_PSTN | ||
+ | # PSTN GW Routing | ||
+ | # | ||
+ | # - pstn.gw_ip: valid IP or hostname as string value, example: | ||
+ | # pstn.gw_ip = " | ||
+ | # | ||
+ | # - by default is empty to avoid misrouting | ||
+ | pstn.gw_ip = "" | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_VOICEMAIL | ||
+ | # VoiceMail Routing on offline, busy or no answer | ||
+ | # | ||
+ | # - by default Voicemail server IP is empty to avoid misrouting | ||
+ | voicemail.srv_ip = "" | ||
+ | voicemail.srv_port = " | ||
+ | #!endif | ||
+ | |||
+ | ####### Modules Section ######## | ||
+ | |||
+ | # set paths to location of modules | ||
+ | #!ifdef LOCAL_TEST_RUN | ||
+ | mpath=" | ||
+ | #!else | ||
+ | mpath="/ | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_MYSQL | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | |||
+ | #!ifdef WITH_AUTH | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | #!ifdef WITH_IPAUTH | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_ALIASDB | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_SPEEDDIAL | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_MULTIDOMAIN | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_PRESENCE | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_NAT | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_TLS | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_ANTIFLOOD | ||
+ | loadmodule " | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_XMLRPC | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_DEBUG | ||
+ | loadmodule " | ||
+ | #!endif | ||
+ | |||
+ | # ----------------- setting module-specific parameters --------------- | ||
+ | |||
+ | |||
+ | # ----- mi_fifo params ----- | ||
+ | modparam(" | ||
+ | |||
+ | |||
+ | # ----- tm params ----- | ||
+ | # auto-discard branches from previous serial forking leg | ||
+ | modparam(" | ||
+ | # default retransmission timeout: 30sec | ||
+ | modparam(" | ||
+ | # default invite retransmission timeout after 1xx: 120sec | ||
+ | modparam(" | ||
+ | |||
+ | |||
+ | # ----- rr params ----- | ||
+ | # add value to ;lr param to cope with most of the UAs | ||
+ | modparam(" | ||
+ | # do not append from tag to the RR (no need for this script) | ||
+ | modparam(" | ||
+ | |||
+ | |||
+ | # ----- registrar params ----- | ||
+ | modparam(" | ||
+ | /* uncomment the next line to disable parallel forking via location */ | ||
+ | # modparam(" | ||
+ | /* uncomment the next line not to allow more than 10 contacts per AOR */ | ||
+ | # | ||
+ | |||
+ | |||
+ | # ----- acc params ----- | ||
+ | /* what special events should be accounted ? */ | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | /* by default ww do not adjust the direct of the sequential requests. | ||
+ | if you enable this parameter, be sure the enable " | ||
+ | in " | ||
+ | modparam(" | ||
+ | /* account triggers (flags) */ | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | " | ||
+ | modparam(" | ||
+ | /* enhanced DB accounting */ | ||
+ | #!ifdef WITH_ACCDB | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | " | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | # ----- usrloc params ----- | ||
+ | /* enable DB persistency for location entries */ | ||
+ | #!ifdef WITH_USRLOCDB | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | # ----- auth_db params ----- | ||
+ | #!ifdef WITH_AUTH | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | |||
+ | # ----- permissions params ----- | ||
+ | #!ifdef WITH_IPAUTH | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | #!endif | ||
+ | |||
+ | |||
+ | # ----- alias_db params ----- | ||
+ | #!ifdef WITH_ALIASDB | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | # ----- speedial params ----- | ||
+ | #!ifdef WITH_SPEEDDIAL | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | # ----- domain params ----- | ||
+ | #!ifdef WITH_MULTIDOMAIN | ||
+ | modparam(" | ||
+ | # use caching | ||
+ | modparam(" | ||
+ | # register callback to match myself condition with domains list | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | #!ifdef WITH_PRESENCE | ||
+ | # ----- presence params ----- | ||
+ | modparam(" | ||
+ | |||
+ | # ----- presence_xml params ----- | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | #!ifdef WITH_NAT | ||
+ | # ----- rtpproxy params ----- | ||
+ | # modparam(" | ||
+ | modparam(" | ||
+ | |||
+ | # ----- nathelper params ----- | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | |||
+ | # params needed for NAT traversal in other modules | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | |||
+ | #!ifdef WITH_TLS | ||
+ | # ----- tls params ----- | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_ANTIFLOOD | ||
+ | # ----- pike params ----- | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | |||
+ | # ----- htable params ----- | ||
+ | # ip ban htable with autoexpire after 5 minutes | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_XMLRPC | ||
+ | # ----- xmlrpc params ----- | ||
+ | modparam(" | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_DEBUG | ||
+ | # ----- debugger params ----- | ||
+ | modparam(" | ||
+ | #!endif | ||
+ | |||
+ | ####### Routing Logic ######## | ||
+ | |||
+ | |||
+ | # Main SIP request routing logic | ||
+ | # - processing of any incoming SIP request starts with this route | ||
+ | route { | ||
+ | |||
+ | # per request initial checks | ||
+ | route(REQINIT); | ||
+ | |||
+ | # NAT detection | ||
+ | route(NATDETECT); | ||
+ | |||
+ | # handle requests within SIP dialogs | ||
+ | route(WITHINDLG); | ||
+ | |||
+ | ### only initial requests (no To tag) | ||
+ | |||
+ | # CANCEL processing | ||
+ | if (is_method(" | ||
+ | { | ||
+ | if (t_check_trans()) | ||
+ | t_relay(); | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | t_check_trans(); | ||
+ | |||
+ | # authentication | ||
+ | route(AUTH); | ||
+ | |||
+ | # record routing for dialog forming requests (in case they are routed) | ||
+ | # - remove preloaded route headers | ||
+ | remove_hf(" | ||
+ | if (is_method(" | ||
+ | record_route(); | ||
+ | |||
+ | # account only INVITEs | ||
+ | if (is_method(" | ||
+ | { | ||
+ | setflag(FLT_ACC); | ||
+ | } | ||
+ | |||
+ | # dispatch requests to foreign domains | ||
+ | route(SIPOUT); | ||
+ | |||
+ | ### requests for my local domains | ||
+ | |||
+ | # handle presence related requests | ||
+ | route(PRESENCE); | ||
+ | |||
+ | # handle registrations | ||
+ | route(REGISTRAR); | ||
+ | |||
+ | if ($rU==$null) | ||
+ | { | ||
+ | # request with no Username in RURI | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | # dispatch destinations to PSTN | ||
+ | route(PSTN); | ||
+ | |||
+ | # user location service | ||
+ | route(LOCATION); | ||
+ | |||
+ | route(RELAY); | ||
+ | } | ||
+ | |||
+ | |||
+ | route[RELAY] { | ||
+ | |||
+ | # enable additional event routes for forwarded requests | ||
+ | # - serial forking, RTP relaying handling, a.s.o. | ||
+ | if (is_method(" | ||
+ | t_on_branch(" | ||
+ | t_on_reply(" | ||
+ | } | ||
+ | if (is_method(" | ||
+ | t_on_failure(" | ||
+ | } | ||
+ | |||
+ | if (!t_relay()) { | ||
+ | sl_reply_error(); | ||
+ | } | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | # Per SIP request initial checks | ||
+ | route[REQINIT] { | ||
+ | #!ifdef WITH_ANTIFLOOD | ||
+ | # flood dection from same IP and traffic ban for a while | ||
+ | # be sure you exclude checking trusted peers, such as pstn gateways | ||
+ | # - local host excluded (e.g., loop to self) | ||
+ | if(src_ip!=myself) | ||
+ | { | ||
+ | if($sht(ipban=> | ||
+ | { | ||
+ | # ip is already blocked | ||
+ | xdbg(" | ||
+ | exit; | ||
+ | } | ||
+ | if (!pike_check_req()) | ||
+ | { | ||
+ | xlog(" | ||
+ | $sht(ipban=> | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | #!endif | ||
+ | |||
+ | if (!mf_process_maxfwd_header(" | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | if(!sanity_check(" | ||
+ | { | ||
+ | xlog(" | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # Handle requests within SIP dialogs | ||
+ | route[WITHINDLG] { | ||
+ | if (has_totag()) { | ||
+ | # sequential request withing a dialog should | ||
+ | # take the path determined by record-routing | ||
+ | if (loose_route()) { | ||
+ | if (is_method(" | ||
+ | setflag(FLT_ACC); | ||
+ | setflag(FLT_ACCFAILED); | ||
+ | } | ||
+ | if ( is_method(" | ||
+ | # ACK is forwarded statelessy | ||
+ | route(NATMANAGE); | ||
+ | } | ||
+ | route(RELAY); | ||
+ | } else { | ||
+ | if (is_method(" | ||
+ | # in-dialog subscribe requests | ||
+ | route(PRESENCE); | ||
+ | exit; | ||
+ | } | ||
+ | if ( is_method(" | ||
+ | if ( t_check_trans() ) { | ||
+ | # no loose-route, | ||
+ | # must be an ACK after a 487 | ||
+ | # or e.g. 404 from upstream server | ||
+ | t_relay(); | ||
+ | exit; | ||
+ | } else { | ||
+ | # ACK without matching transaction ... ignore and discard | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | sl_send_reply(" | ||
+ | } | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # Handle SIP registrations | ||
+ | route[REGISTRAR] { | ||
+ | if (is_method(" | ||
+ | { | ||
+ | if(isflagset(FLT_NATS)) | ||
+ | { | ||
+ | setbflag(FLB_NATB); | ||
+ | # uncomment next line to do SIP NAT pinging | ||
+ | ## setbflag(FLB_NATSIPPING); | ||
+ | } | ||
+ | if(af==INET6) | ||
+ | setbflag(FLB_IPV6); | ||
+ | if (!save(" | ||
+ | sl_reply_error(); | ||
+ | |||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # USER location service | ||
+ | route[LOCATION] { | ||
+ | |||
+ | #!ifdef WITH_SPEEDIAL | ||
+ | # search for short dialing - 2-digit extension | ||
+ | if($rU=~" | ||
+ | sd_lookup(" | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_ALIASDB | ||
+ | # search in DB-based aliases | ||
+ | alias_db_lookup(" | ||
+ | #!endif | ||
+ | |||
+ | $avp(oexten) = $rU; | ||
+ | if (!lookup(" | ||
+ | $var(rc) = $rc; | ||
+ | route(TOVOICEMAIL); | ||
+ | t_newtran(); | ||
+ | switch ($var(rc)) { | ||
+ | case -1: | ||
+ | case -3: | ||
+ | send_reply(" | ||
+ | exit; | ||
+ | case -2: | ||
+ | send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # when routing via usrloc, log the missed calls also | ||
+ | if (is_method(" | ||
+ | { | ||
+ | setflag(FLT_ACCMISSED); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # Presence server route | ||
+ | route[PRESENCE] { | ||
+ | if(!is_method(" | ||
+ | return; | ||
+ | |||
+ | #!ifdef WITH_PRESENCE | ||
+ | if (!t_newtran()) | ||
+ | { | ||
+ | sl_reply_error(); | ||
+ | exit; | ||
+ | }; | ||
+ | |||
+ | if(is_method(" | ||
+ | { | ||
+ | handle_publish(); | ||
+ | t_release(); | ||
+ | } | ||
+ | else | ||
+ | if( is_method(" | ||
+ | { | ||
+ | handle_subscribe(); | ||
+ | t_release(); | ||
+ | } | ||
+ | exit; | ||
+ | #!endif | ||
+ | |||
+ | # if presence enabled, this part will not be executed | ||
+ | if (is_method(" | ||
+ | { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | # Authentication route | ||
+ | route[AUTH] { | ||
+ | #!ifdef WITH_AUTH | ||
+ | if (is_method(" | ||
+ | { | ||
+ | # authenticate the REGISTER requests (uncomment to enable auth) | ||
+ | if (!www_authorize(" | ||
+ | { | ||
+ | www_challenge(" | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | if ($au!=$tU) | ||
+ | { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | } else { | ||
+ | |||
+ | #!ifdef WITH_IPAUTH | ||
+ | if(allow_source_address()) | ||
+ | { | ||
+ | # source IP allowed | ||
+ | return; | ||
+ | } | ||
+ | #!endif | ||
+ | |||
+ | # authenticate if from local subscriber | ||
+ | if (from_uri==myself) | ||
+ | { | ||
+ | if (!proxy_authorize(" | ||
+ | proxy_challenge(" | ||
+ | exit; | ||
+ | } | ||
+ | if (is_method(" | ||
+ | { | ||
+ | if ($au!=$fU || $au!=$tU) { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | if ($au!=$rU) { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | #!ifdef WITH_MULTIDOMAIN | ||
+ | if ($fd!=$rd) { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | #!endif | ||
+ | } else { | ||
+ | if ($au!=$fU) { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | consume_credentials(); | ||
+ | # caller authenticated | ||
+ | } else { | ||
+ | # caller is not local subscriber, then check if it calls | ||
+ | # a local destination, | ||
+ | if (!uri==myself) | ||
+ | { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | #!endif | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | # Caller NAT detection route | ||
+ | route[NATDETECT] { | ||
+ | if(af==INET6) | ||
+ | return; | ||
+ | |||
+ | #!ifdef WITH_NAT | ||
+ | force_rport(); | ||
+ | if (nat_uac_test(" | ||
+ | if (is_method(" | ||
+ | fix_nated_register(); | ||
+ | } else { | ||
+ | fix_nated_contact(); | ||
+ | } | ||
+ | setflag(FLT_NATS); | ||
+ | } | ||
+ | #!endif | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | # RTPProxy control | ||
+ | route[NATMANAGE] { | ||
+ | #!ifdef WITH_NAT | ||
+ | if (is_request()) { | ||
+ | if(has_totag()) { | ||
+ | if(check_route_param(" | ||
+ | setbflag(FLB_NATB); | ||
+ | } else { | ||
+ | if(check_route_param(" | ||
+ | setbflag(FLB_V4V6); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB) | ||
+ | || isbflagset(FLB_V4V6))) | ||
+ | return; | ||
+ | |||
+ | if(isbflagset(FLB_V4V6)) { | ||
+ | if(af==INET6) { | ||
+ | rtpproxy_manage(" | ||
+ | } else { | ||
+ | rtpproxy_manage(" | ||
+ | } | ||
+ | } else { | ||
+ | if(af==INET6) { | ||
+ | rtpproxy_manage(" | ||
+ | } else { | ||
+ | rtpproxy_manage(" | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if (is_request()) { | ||
+ | if (!has_totag()) { | ||
+ | if(isbflagset(FLB_V4V6)) { | ||
+ | add_rr_param("; | ||
+ | } else { | ||
+ | add_rr_param("; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | if (is_reply()) { | ||
+ | if(isbflagset(FLB_NATB)) { | ||
+ | if(af==INET) { | ||
+ | fix_nated_contact(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | #!endif | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | # Routing to foreign domains | ||
+ | route[SIPOUT] { | ||
+ | if (!uri==myself) | ||
+ | { | ||
+ | append_hf(" | ||
+ | route(RELAY); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # PSTN GW routing | ||
+ | route[PSTN] { | ||
+ | #!ifdef WITH_PSTN | ||
+ | # check if PSTN GW IP is defined | ||
+ | if (strempty($sel(cfg_get.pstn.gw_ip))) { | ||
+ | xlog(" | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | # route to PSTN dialed numbers starting with ' | ||
+ | # | ||
+ | # - update the condition to match your dialing rules for PSTN routing | ||
+ | if(!($rU=~" | ||
+ | return; | ||
+ | |||
+ | # only local users allowed to call | ||
+ | if(from_uri!=myself) { | ||
+ | sl_send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | $ru = " | ||
+ | |||
+ | route(RELAY); | ||
+ | exit; | ||
+ | #!endif | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | # XMLRPC routing | ||
+ | #!ifdef WITH_XMLRPC | ||
+ | route[XMLRPC] { | ||
+ | # allow XMLRPC from localhost | ||
+ | if ((method==" | ||
+ | && | ||
+ | # close connection only for xmlrpclib user agents (there is a bug in | ||
+ | # xmlrpclib: it waits for EOF before interpreting the response). | ||
+ | if ($hdr(User-Agent) =~ " | ||
+ | set_reply_close(); | ||
+ | set_reply_no_connect(); | ||
+ | dispatch_rpc(); | ||
+ | exit; | ||
+ | } | ||
+ | send_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | #!endif | ||
+ | |||
+ | # route to voicemail server | ||
+ | route[TOVOICEMAIL] { | ||
+ | #!ifdef WITH_VOICEMAIL | ||
+ | if(!is_method(" | ||
+ | return; | ||
+ | |||
+ | # check if VoiceMail server IP is defined | ||
+ | if (strempty($sel(cfg_get.voicemail.srv_ip))) { | ||
+ | xlog(" | ||
+ | return; | ||
+ | } | ||
+ | if($avp(oexten)==$null) | ||
+ | return; | ||
+ | |||
+ | $ru = " | ||
+ | + $sel(cfg_get.voicemail.srv_port); | ||
+ | route(RELAY); | ||
+ | exit; | ||
+ | #!endif | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | # detect if IPv4 - IPv6 bridging is needed | ||
+ | route[IPV4V6] { | ||
+ | if(!has_totag()) { | ||
+ | if(af==INET6 && !isbflagset(FLB_IPV6)) { | ||
+ | setbflag(FLB_V4V6); | ||
+ | } else { | ||
+ | if(af==INET && isbflagset(FLB_IPV6)) { | ||
+ | setbflag(FLB_V4V6); | ||
+ | } | ||
+ | } | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # manage outgoing branches | ||
+ | branch_route[MANAGE_BRANCH] { | ||
+ | xdbg(" | ||
+ | route(IPV4V6); | ||
+ | route(NATMANAGE); | ||
+ | } | ||
+ | |||
+ | # manage incoming replies | ||
+ | onreply_route[MANAGE_REPLY] { | ||
+ | xdbg(" | ||
+ | if(status=~" | ||
+ | route(NATMANAGE); | ||
+ | } | ||
+ | |||
+ | # manage failure routing cases | ||
+ | failure_route[MANAGE_FAILURE] { | ||
+ | route(NATMANAGE); | ||
+ | |||
+ | if (t_is_canceled()) { | ||
+ | exit; | ||
+ | } | ||
+ | |||
+ | #!ifdef WITH_BLOCK3XX | ||
+ | # block call redirect based on 3xx replies. | ||
+ | if (t_check_status(" | ||
+ | t_reply(" | ||
+ | exit; | ||
+ | } | ||
+ | #!endif | ||
+ | |||
+ | #!ifdef WITH_VOICEMAIL | ||
+ | # serial forking | ||
+ | # - route to voicemail on busy or no answer (timeout) | ||
+ | if (t_check_status(" | ||
+ | route(TOVOICEMAIL); | ||
+ | exit; | ||
+ | } | ||
+ | #!endif | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | |||
+ | ===== Remarks ===== | ||
+ | |||
+ | With this kind of deployment, you can have SIP/VoIP conversations like: | ||
+ | * IPv4 to IPv4 | ||
+ | * IPv6 to IPv6 | ||
+ | * IPv4 to IPv6 | ||
+ | * IPv6 to IPv4 | ||
+ | |||
+ | The communication is not limited to voice and video, you can do Instant Messaging, Presence notifications of Desktop sharing as well. | ||
+ | |||
+ | The NAT concept is gone in IPv6, however the platform is intended to run in mixed environment, | ||