Hi!
In openser-CVS there is a new module called domainpolicy.
It implements draft-lendl-domain-policy-ddds-02. This version supports
draft-lendl-speermint-federations-02 and
draft-lendl-speermint-technical-policy-00.
This is about federation based and static peering (for SIP providers
which do not offer public SIP peering).
For background of this please refer to the SPEERMINT working group:
http://www.ietf.org/html.charters/speermint-charter.html
Attached are 3 documents:
one about building openser+domainpolicy
one about configuration and
one is a small howto about federations and domainpolicies.
You can also find a quick summary of domainpolicy in this mail:
http://www1.ietf.org/mail-archive/web/speermint/current/msg01598.html
Currently, openser CVS does not support interims solutions for
infrastructure ENUM lookups - the patch for this in in the queue.
Meanwhile you can download it from enum.at (see above link) too.
regards
Klaus
PS: If you do not want to use openser-CVS you can find the module or
complete tar balls for openser 1.1 at
http://www.enum.at/index.php?id=dokumente
--
Klaus Darilion
nic.at
This files describes how to install the openser for the Austrian IENUM Trial.
openser-ienum changelog:
Version 0.4:
- based on openser-1.1.0 from 2006-10-02
- domainpolicy module 0.4 + sql patch
- aligns with draft-lendl-domain-policy-ddds-02
- a few updates and bug-fixes
- supports draft-lendl-speermint-federations-02 and
draft-lendl-speermint-technical-policy-00
- handling of non-terminal NAPTR records, as well as the DB schema has changed.
Version 0.3:
- based on openser-1.1.0 from 2006-07-14
Version 0.2:
- based on openser-CVS head from 2006-05-22
- tls patch not needed anymore as applied to CVS head
- domainpolicy module 0.2 + sql patch
- supports integer name based AVPs in "attr" column of domainpolicy table
- supports forcing a send_socket via dp_apply_policy
- bug fixed (NAPTR lookup need \0 terminated string)
Version 0.1: initial release
- based on openser-CVS head from 2006-05-17
- ienum patch
- tls patch
- domainpolicy module 0.1 + sql patch
Compared to openser CVS, this openser has the following differences:
- ienum patch to allow ienum queries
- domainpolicy modul to allow ad-hoc peering
This file decribes how to build your IENUM-openser.
1. You have to get the sources. There a 2 choices. You can either download the patched
openser-tarball from
www.enum.at or you patch it yourself.
1.1. Download the patched tarball: Go to enum.at, download the patched tarball (currently
version 0.4).
Untar it using the command "tar -xvjf openser-ienum-0.4.tgz"
cd sip-server
1.2 Checkout openser from sourceforge's CVS and apply the patches manually. (Patches
were tested to apply to openser-1.1.0 from 2006-10-02).
checkout openser-cvs
cvs -d:pserver:anonymous@openser.cvs.sourceforge.net:/cvsroot/openser login
cvs -z3 -d:pserver:anonymous@openser.cvs.sourceforge.net:/cvsroot/openser co -r
rel_1_1_0 -P sip-server
download ienum patch
wget -O ienum-patch.txt
"http://sourceforge.net/tracker/download.php?group_id=139143&atid=743022&file_id=177989&aid=1344272"
download domainpolicy modul und SQL patch from enum.at homepage
openser-domainpolicy_0.4.tgz
openser-domainpolicy-sql-patch_0.3.txt
apply patches
cd sip-server
patch -p0 <../openser-domainpolicy-sql-patch_0.3.txt
cd modules
tar -xvzf ../../domainpolicy_0.4.tgz
cd ..
patch -p0 <../ienum-patch.txt
2. Building the ienum-openser
When building openser, you have to install some development libraries, e.g. libssl-dev for
TLS and mysql or postgresql development libraries. Further you have to install the
libradius-ng-dev package. This must be built from the sources of radiusclient-ng available
from berlios.
Depending on your Linux Distribution there are different choices to build:
2.1. Debian
The easiest way is build packages:
make deb
This will build TLS enabled openser packages. As this will build all available modules,
you need several development libraries installed on the system. E.g. the libmysql-dev,
libpg-dev and the radiusclient-ng devel libraries. As the radiusclient-ng is not part of
debian, you have to install it manually as described on the openser wiki:
http://www.openser.org/dokuwiki/doku.php?id=howto_make_debian_packages
Depending on your setup, and the used DB (mysql or postgresql) install the built debian
packages. E.g.:
NOTE: this step replaces existing installed openser packages. Take care as openser-1.1
syntax is not 100% compatible with openser-1.0 syntax.
cd ..
su
dpkg -i openser_1.1.0-0_i386.deb openser-mysql-module_1.1.0-0_i386.deb
Set up the database tables: NOTE: edit the script /usr/sbin/openser_mysql.sh (or set
environment variables, e.g. "export DBNAME=ienum_borderproxy") to reflect your
DB environment (e.g. to do not overwrite existing openser databases).
openser_mysql.sh create
Now you can edit the config in /etc/openser/openser.cfg and use the start/stop script
/etc/init.d/openser.
For debugging watch syslog logging in /var/log/syslog
2.2. Others
You can install openser manually. First you have to modify the Makefile to remove the
needed modules from "exclude_modules", e.g. mysql or postgres. Then build the
usual way:
NOTE: this step replaces existing manually installed openser. Take care as openser-1.1
syntax is not 100% compatible with openser-1.0 syntax.
export TLS=1
make all
su
make install
This will install openser in /usr/local/sbin/openser and /usr/local/lib/openser/modules/.
The configuration is stored in /usr/local/etc/openser/openser.cfg. There is no startup
script installed, thus you have to copy it manually from etc/openser.init to
/etc/init.d/openser and adopt it to your paths.
3. Config
If you are used to ser or openser-1.0.x, please the migration info on
http://openser.org/dokuwiki/doku.php?id=migrating_openser_v1.0.x_to_v1.1.x
Configuration for the ienum openser
1. Introduction
Using Infrastructure ENUM and SIP VoIP peering requires 2 steps:
1. perform IENUM lookups
2. interconnect with the destination published in the IENUM NAPTRs
Typically, a VoIP setup has a central node which performs the routing logic. I will call
this the "main proxy". We can add the previously described steps in the main
proxy or use dedicated nodes for doing the IENUM lookup and session peering. This has many
advantages regarding security, flexibility and reliability. We suggest a setup of a
dedicated "border proxy" which performs the IENUM lookup and takes care of
peering (e.g. authentication of peering partners). This border proxy is a logical node,
that means it can be installed on the same PC as the main proxy.
Nevertheless doing the IENUM lookup and the peering in the main proxy. If you want to do
this, please use the ideas and config snippets from our suggested scenario (a dedicated
border proxy) to adopt your main proxy's routing logic.
2. The Border Proxy
The border proxy takes care of the IENUM lookup and the SIP peering. The border proxy will
perform the following steps:
For outgoing calls:
- bring number into E.164 format
- perform IENUM lookup
- perform ENUM lookup (optional)
- if no destination is found in IENUM/ENUM, send an apropriate SIP response back to
the main proxy. The main proxy will then perform the default routing (usually sends the
call to the PSTN gateway)
- perform a domain policy lookup. This step detects if the destination found in IENUM
will accept SIP calls from us
- apply destination domain's peering policy (e.g. set proper TLS parameters, send to
certain port, ...)
- add P-Asserted-Idendity: Header and Privacy Header the send proper CLI and CLIP/CLIR
- activate accounting
- send the call to the destination
For incoming calls:
- receive call
- authenticate sender (e.g. TLS or IP address)
- activate accounting
- adjust CLI to fit local dialplan (e.g reformat P-Asserted-Idendity header)
- forward the call to the main proxy
3. Configuration
3.1. Main Proxy
The main proxy should forward the call to the border proxy for IENUM lookup and peering.
If the main proxy receives a certain response code, it will failover to the standard route
to the PSTN.
E.g. if the main proxy is (open)ser based, the respective config snippets will be:
route{
# all the authentication and NAT traversal stuff
...
# user is authenticated. check if destination is an phone number
if (uri =~ "^sip:(\+|\*)?[0-9]+@") { # PSTN number detected
if (uri=~"^sip:[1-9]") {
xlog("L_WARN", "number normalization: illegal number, 404...");
sl_send_reply("404", "Not found - illegal number");
exit;
}
if (uri=~"^sip:0[1-9]") {
xlog("L_INFO","Austria national number detected, remove 0, prefix
+43\n");
strip(1);
prefix("+43");
} else if (uri=~"^sip:00[1-9]") {
xlog("L_INFO","international number detected, remove 00, prefix
+\n");
strip(2);
prefix("+");
} else if (uri=~"^sip:000") {
xlog("L_INFO", "000 detected, illegal number, 404...");
sl_send_reply("404", "Not found - illegal number");
exit;
}
}
# usually calls sent to the gateway have an Remote-Party-ID header
# to signal the CLI to the gateway. We send this RPID header also to the
# border proxy
# create new AVP with the Remote-Party-ID header
avp_printf("$avp(s:temp)","$avp(s:rpid);party=calling;id-type=subscriber;screen=yes");
append_hf("Remote-Party-ID: $avp(s:temp)\r\n");
# now, usually we would send the call to the PSTN gateway. But instead
# we activate a failure route and send it to the border proxy
t_on_failure("1");
# debpending on your domain setup you may need to rewrite the domain
# to address the border proxy
# rewritehostport("bp1.istp1.com");
# send the request to the border proxy
if (!t_relay()) {
sl_reply_error();
};
exit;
}
failure_route[1] {
# handles error on border proxy
xlog("L_INFO","failure_route(1): reply status = $rs\n");
# if caller cancels the call we will also enter the failure route
if(t_was_cancelled()) {
xlog("L_INFO","caller cancelled call ...exit\n");
exit;
}
# border proxy will return "499" in case of unsuccessful IENUM/domainpolicy
lookup
if(t_check_status("499")) {
xlog("L_INFO","callee neither in I-ENUM nor in U-ENUM...sending to PSTN
Gateway...");
rewritehostport("gateway.istp1.com");
t_relay();
exit;
}
xlog("L_INFO","send down error code from border proxy to
user...exit\n");
exit;
}
If the main proxy is Asterisk based, the respective snippet may look like this
(untested):
[frompbx]
; border proxy will send "503" if no IENUM result found
; in this example the Asterisk is also the gateway. If not you
; have to adopt the second Dial parameter to send the call to your
; gateway
exten => _[1-9].,1,Dial(SIP/+431${EXTEN}(a)bp1.itsp1.com,90)
exten => _[1-9].,2,GotoIf($["${DIALSTATUS}" =
"CONGESTION"]?103:3)
exten => _[1-9].,3,Hangup
exten => _[1-9].,103,Dial(ZAP/g1/${EXTEN},90)
exten => _0[1-9].,1,Dial(SIP/+43${EXTEN:1}@bp1.itsp1.com,90)
exten => _0[1-9].,2,GotoIf($["${DIALSTATUS}" =
"CONGESTION"]?103:3)
exten => _0[1-9].,3,Hangup
exten => _0[1-9].,103,Dial(ZAP/g1/${EXTEN},90)
exten => _00[1-9].,1,Dial(SIP/+${EXTEN:2}@bp1.itsp1.com,90)
exten => _00[1-9].,2,GotoIf($["${DIALSTATUS}" =
"CONGESTION"]?103:3)
exten => _00[1-9].,3,Hangup
exten => _00[1-9].,103,Dial(ZAP/g1/${EXTEN},90)
3.2 Border Proxy
The border proxy performs the IENUM/ENUM lookups and the domainpolicy lookup. Further it
may perform authentication for peering purposes.
Again some config snippets for openser (you need the patched ienum-openser from
www.enum.at)
...config section
...module parameter section
loadmodule "/usr/lib/openser/modules/enum.so"
loadmodule "/usr/lib/openser/modules/domainpolicy.so"
loadmodule "/usr/lib/openser/modules/tlsops.so"
# carrier enum
modparam("enum", "i_enum_suffix", "e164.arpa.")
modparam("enum", "bl_algorithm", "c")
# domainpolicy module (this must be named like in the domainpolicy table)
modparam("domainpolicy", "port_override_avp",
"portoverride")
modparam("domainpolicy", "transport_override_avp",
"transportoverride")
modparam("domainpolicy", "domain_prefix_avp",
"domainprefix")
modparam("domainpolicy", "domain_suffix_avp",
"domainsuffix")
modparam("domainpolicy", "send_socket_avp",
"sendsocket")
route{
.....
# now, we split routing depending on the direction
# the protocol can be used to identify the direction
# if we require TCP or TLS for peering, but use UDP inside
# we can use the protocol to detect the call direction
# tls: request from peering partner
# udp,tcp: local request
if( (proto==tls) or (proto==tcp) ){
route(2);
} else {
route(3);
};
}
route[2] {
# TLS requests - thus requests received from peering partners.
# This requests are already authenticated via TLS certificate
# validation, thus no further SIP based authentication needed.
# Just forward the requests to the main proxy.
# Beforehand, we map from the anonymous IENUM SIP URIs to the
# user's AoR using the dbaliases module. Note: this mapping can
# also be done in the main proxy.
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(2):
TLS requests\n");
if (alias_db_lookup("dbaliases")) {
xlog("L_INFO","dbaliases lookup successful, new URI = $ru\n");
avp_write("s:outbound.itsp1.ienum.labs.nic.at","$avp(i:679)");
avp_pushto("$du","$avp(i:679)");
# route(5); # optional process received P-Asserted_Identiy header
exit;
}
xlog("L_INFO","dbaliases lookup failed ... 404\n");
sl_send_reply("404","Unknown user");
exit;
}
route[3] {
# UDP/TCP - thus requests received from the local main proxy.
# This requests are already authenticated by the main proxy,
# thus no further SIP based authentication needed.
#
# Perform the following steps:
# 1. Try I-ENUM lookup
# 2. Try U-ENUM lookup
# 3. reply with 499
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(3):
UDP/TCP requests\n");
if ( i_enum_query() ) {
xlog("L_INFO","I-ENUM lookup successful, new URI = $ru, check if
useful...\n");
if (uri == myself) {
xlog("L_INFO","I-ENUM SIP URI belongs to myself, relay back to main
proxy...\n");
sl_send_reply("100","I-ENUM succeeded, number belongs to us, sending
back to main proxy...");
route(2);
exit;
}
if (dp_can_connect()) { # check the domainpolicy of the destination
xlog("L_INFO","dp_can_connect succeeded:\n");
xlog("L_INFO"," port_override_avp =
'$avp(s:portoverride)'\n");
xlog("L_INFO"," transport_override_avp =
'$avp(s:transportoverride)'\n");
xlog("L_INFO"," domain_prefix_avp =
'$avp(s:domainprefix)'\n");
xlog("L_INFO"," domain_suffix_avp =
'$avp(s:domainsuffix)'\n");
xlog("L_INFO"," send_socket_avp =
'$avp(s:sendsocket)'\n");
xlog("L_INFO"," tls_client_domain_avp =
'$avp(i:400)'\n");
# apply domain policy
if (dp_apply_policy()) {
xlog("L_INFO","I-ENUM SIP URI is a known peering partner, use this uri
and relay request...\n");
xlog("L_INFO"," R-URI = $ru\n");
xlog("L_INFO"," d-URI = $du\n");
sl_send_reply("100","I-ENUM und domainpolicy succeeded, sending to
other domain...");
route(4);
exit;
}
xlog("L_INFO","dp_apply_policy failed\n");
} else {
xlog("L_INFO","dp_apply_policy failed\n");
}
} else {
xlog("L_INFO","I-ENUM lookup failed, fallback to U-ENUM ...\n");
}
revert_uri();
if ( enum_query() ) {
xlog("L_INFO","U-ENUM lookup successful, new URI = $ru, relay
request...\n");
sl_send_reply("100","U-ENUM succeeded, sending to other
domain...");
route(4);
exit;
}
xlog("L_INFO","I-ENUM lookup failed, send 499 back to main proxy
...\n");
sl_send_reply("499","Number not in ENUM or domainpolicy failed");
exit;
}
route[4]{
# This route block reformats the rpid from the internal number format
# to the number format used in the I-ENUM trial.
# Thus, the proxy will take the existing rpid information (Remote-Party-ID header
# or P-Asserted_Identity header) and build an P-Asserted-Identity header with
# a tel URI.
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(4):
add P-Asserted-Identity header\n");
# write the complete content of the Remote-Party-ID header into an AVP
append_hf("Remote-Party-ID: $avp(s:rpidheader)\r\n");
# replace the sip uri with a tel uri
# \1 \2 \3 \4
avp_subst("$avp(s:rpidheader)",
"/^(.*)<sip:([^@]*)@[a-zA-Z0-9.]+(.*)>(.*)/tel:\2/gi");
# remove the Remote-Party-ID header and create the P-Asserted-Identity header
remove_hf("Remote-Party-ID");
append_hf("P-Asserted-Identity: $avp(s:rpidheader)\r\n");
route(1);
exit;
}
4. Appendix
4.1. Full Config of a Main Proxy
#
# $Id: openser.cfg,v 1.5 2005/10/28 19:45:33 bogdan_iancu Exp $
#
# simple quick-start config script
#
# Austrian I-ENUM Trial
# configuration of main proxy
# ----------- global configuration parameters ------------------------
debug=3 # debug level (cmd line: -dddddddddd)
fork=yes
#log_stderror=yes # (cmd line: -E)
/* Uncomment these lines to enter debugging mode
fork=no
log_stderror=yes
*/
check_via=no # (cmd. line: -v)
dns=no # (cmd. line: -r)
rev_dns=no # (cmd. line: -R)
children=4
fifo="/tmp/openser_fifo"
# syslog logging, configure in /etc/syslog
log_facility=LOG_LOCAL0 # /var/log/openser.log
server_header ="server_header: itsp1, main proxy"
user_agent_header="user_agent_header: itsp1, main proxy"
listen=udp:10.10.0.41:5060
listen=tcp:10.10.0.41:5060
listen=tls:10.10.0.41:5061
# our domain
alias=itsp1.ienum.labs.nic.at
# for addressing via outbound proxy and pre loaded route set
# (otherwise will is_myself in loose_route not match)
alias=outbound.itsp1.ienum.labs.nic.at
#
# uncomment the following lines for TLS support
#disable_tls = 0
#listen = tls:your_IP:5061
#tls_verify = 1
#tls_require_certificate = 1
#tls_method = TLSv1
#tls_certificate = "/etc/openser/tls/user/user-cert.pem"
#tls_private_key = "/etc/openser/tls/user/user-privkey.pem"
#tls_ca_list = "/etc/openser/tls/user/user-calist.pem"
# ------------------ module loading ----------------------------------
# Uncomment this if you want to use SQL database
loadmodule "/usr/lib/openser/modules/mysql.so"
loadmodule "/usr/lib/openser/modules/sl.so"
loadmodule "/usr/lib/openser/modules/tm.so"
loadmodule "/usr/lib/openser/modules/rr.so"
loadmodule "/usr/lib/openser/modules/maxfwd.so"
loadmodule "/usr/lib/openser/modules/usrloc.so"
loadmodule "/usr/lib/openser/modules/registrar.so"
loadmodule "/usr/lib/openser/modules/textops.so"
loadmodule "/usr/lib/openser/modules/xlog.so"
loadmodule "/usr/lib/openser/modules/avpops.so"
loadmodule "/usr/lib/openser/modules/acc.so"
loadmodule "/usr/lib/openser/modules/uri.so"
# Uncomment this if you want digest authentication
# mysql.so must be loaded !
loadmodule "/usr/lib/openser/modules/auth.so"
loadmodule "/usr/lib/openser/modules/auth_db.so"
loadmodule "/usr/lib/openser/modules/enum.so"
loadmodule "/usr/lib/openser/modules/domain.so"
# ----------------- setting module-specific parameters ---------------
# read and write DB access
modparam("acc" , "db_url",
"mysql://openser:openserrw@127.0.0.1/openser")
# accounting parameters
modparam("acc", "early_media", 1)
modparam("acc", "report_ack", 1)
modparam("acc", "report_cancels", 0)
modparam("acc", "multi_leg_enabled", 0)
modparam("acc", "failed_transaction_flag", 4) # radius, syslog and DB
modparam("acc", "log_flag", 1) # syslog
modparam("acc", "db_flag", 2) # DB
modparam("acc", "db_missed_flag", 3) # DB missed calles table
# the following modules also supports multidomain, but are not used
# by us: group, group_radius, speeddial, uri_db
modparam("alias_db|usrloc|registrar|avpops", "use_domain", 1)
# -- usrloc params --
# modparam("usrloc", "db_mode", 0)
# Uncomment this if you want to use SQL database
# for persistent storage and comment the previous line
modparam("usrloc", "db_mode", 2)
# -- auth params --
# Uncomment if you are using auth module
#
modparam("auth_db", "calculate_ha1", yes)
#
# If you set "calculate_ha1" parameter to yes (which true in this config),
# uncomment also the following parameter)
#
modparam("auth_db", "password_column", "password")
modparam("auth_db|usrloc|registrar", "use_domain", 1)
# -- rr params --
# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)
# ------------------------- request routing logic -------------------
# main routing logic
route{
xlog("L_ERR","[$Tf] [$si:$sp] $rm $ru (From: $fu -> To: $tu) request
received....\n");
# initial sanity checks -- messages with
# max_forwards==0, or excessively long requests
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
};
if (msg:len >= 2048 ) {
sl_send_reply("513", "Message too big");
exit;
};
# mark transaction for accounting
setflag(1);
setflag(2);
setflag(4);
if ( is_method("CANCEL") && !t_check_trans() ) {
# CANCEL without matching INVITE transaction, ignore
# may happen if the INVITE is slower than the CANCEL
# ignore the CANCEL, as the client will retransmit it, and maybe next
time
# the INVITE transaction is already created
xlog("L_WARN","CANCEL without matching transaction ...
ignore and discard.\n");
exit;
}
if ( is_method("CANCEL") ) {
# CANCEL with matching INVITE transaction, just
# t_relay
xlog("L_INFO","CANCEL with matching transaction ...
t_relay.\n");
t_relay();
exit;
}
# subsequent messages withing a dialog should take the
# path determined by record-routing
if (loose_route()) {
if (!has_totag()) {
xlog("L_WARN","loose_route request without to-tag,
403...\n");
sl_send_reply("403", "out-of-dialog loose_route not
allowed");
exit;
}
xlog("L_WARN","request loose_route processed...\n");
route(1);
};
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
# non loose-route, but stateful ACK; must be an ACK after a 487
xlog("L_INFO","local end-to-end ACK for an existent
INVITE transaction detected ...t_relay()\n");
t_relay();
} else {
xlog("L_WARN","ACK without matching transaction ...
ignore and discard.\n");
}
exit;
}
if ( has_totag() ) {
# in-dialog requests should be handled by loose_route
# this should be fixed in the client or in openser
xlog("L_ERR","in-dialog request was not catched by
loose_route block, 403... \n");
xlog("L_ERR","this is the complete message:\n");
xlog("L_ERR","$mb\n");
sl_send_reply("403","in-dialog request without loose_route
is not allowed, this is a bug in the client or in this proxy\n");
exit;
}
# we record-route all messages -- to make sure that
# subsequent messages will go through our proxy; that's
# particularly good if upstream and downstream entities
# use different transport protocol
#
# record route will be done after loose_route to do not change the
# ftag parameter in the Route header (eyebeam)
if (!method=="REGISTER")
record_route();
if (!uri==myself) {
xlog("L_WARN","outoing call ...\n");
route(1);
};
# if the request is for our domain use UsrLoc
# (in case, it does not work, use the following command
# with proper names and addresses in it)
if (uri==myself) {
if (method=="REGISTER") {
if (t_newtran()) {
# Uncomment this if you want to use digest authentication
if (!www_authorize("", "subscriber")) {
www_challenge("", "0");
exit;
};
save("location");
exit;
} else sl_reply_error();
t_release();
};
if (is_method("INVITE")) {
# Uncomment this if you want to use digest authentication
if (from_uri == myself) {
xlog("L_INFO","INVITE from local user, check auth\n");
if (!proxy_authorize("", "subscriber")) {
xlog("L_INFO","auth failed...challenge\n");
proxy_challenge("", "0");
exit;
};
consume_credentials();
# if avp rpid available add rpid header
remove_hf("Remote-Party-ID");
remove_hf("P-Asserted-Identity");
if (is_avp_set("$avp(s:rpid)")) {
xlog("L_INFO","RPID defined: rpid=$avp(s:rpid)\n");
#add Remote-Party-ID header
# create new AVP with the Remote-Party-ID header
avp_printf("$avp(s:temp)","$avp(s:rpid);party=calling;id-type=subscriber;screen=yes");
xlog("L_INFO", "Remote-Party-ID header: $avp(s:temp)");
append_hf("Remote-Party-ID: $avp(s:temp)\r\n");
# #add P-Asserted-Identity header
# # create new AVP with the P-Asserted-Identity header
# avp_printf("$avp(s:temp)","$avp(s:rpid)");
# xlog("L_INFO", "P-Asserted-Identity header: $avp(s:temp)");
# append_hf("$avp(s:temp)","P-Asserted-Identity");
} else {
xlog("L_INFO","RPID not defined...\n");
}
if (uri =~ "^sip:(\+|\*)?[0-9]+@") { # PSTN number detected
if (uri=~"^sip:[1-9]") {
xlog("L_WARN", "number normalization: illegal number, 404...");
sl_send_reply("404", "Not found - illegal number");
exit;
}
if (uri=~"^sip:0[1-9]") {
xlog("L_INFO","Austria national number detected, remove 0, prefix
+43\n");
strip(1);
prefix("+43");
} else if (uri=~"^sip:00[1-9]") {
xlog("L_INFO","international number detected, remove 00,
prefix +\n");
strip(2);
prefix("+");
} else if (uri=~"^sip:000") {
xlog("L_INFO", "000 detected, illegal number, 404...");
sl_send_reply("404", "Not found - illegal number");
exit;
}
t_on_failure("1");
# rewritehostport("bp1.itsp1.ienum.labs.nic.at");
route(1);
}
if (!uri==myself) {
append_hf("P-hint: Proxy1 outbound alias\r\n");
route(1);
};
} else {
xlog("L_INFO","incoming call...\n");
}
}
# native SIP destinations are handled using our USRLOC DB
if (!lookup("location")) {
sl_send_reply("404", "Not Found in location table");
exit;
};
xlog("L_INFO","user found, route(1)...\n");
append_hf("P-hint: usrloc applied\r\n");
};
route(1);
}
route[1] {
# send it out now; use stateful forwarding as it works reliably
# even for UDP2TCP
xlog("L_INFO","t_relay....\n");
if (!t_relay()) {
sl_reply_error();
};
exit;
}
failure_route[1] {
# handles error on border proxy
xlog("L_INFO","failure_route(1): reply status = $rs\n");
if(t_was_cancelled()) {
xlog("L_INFO","caller cancelled call ...exit\n");
exit;
}
if(t_check_status("499")) {
xlog("L_INFO","callee neither in I-ENUM nor in
U-ENUM...sending to PSTN Gateway...404");
t_reply("404","no PSTN gateway configured");
exit;
}
xlog("L_INFO","send down error code from upstream
proxy...exit\n");
exit;
}
route[5] {
# dummy route for aliases lookup
lookup("aliases");
}
4.2. Full Config of a Border Proxy
#
# $Id: openser.cfg,v 1.5 2005/10/28 19:45:33 bogdan_iancu Exp $
#
# simple quick-start config script
#
# Austrian I-ENUM Trial
# configuration of main proxy
# ----------- global configuration parameters ------------------------
debug=4 # debug level (cmd line: -dddddddddd)
fork=yes
#log_stderror=yes # (cmd line: -E)
/* Uncomment these lines to enter debugging mode
fork=no
log_stderror=yes
*/
check_via=no # (cmd. line: -v)
dns=no # (cmd. line: -r)
rev_dns=no # (cmd. line: -R)
children=4
fifo="/tmp/openserbp_fifo"
# syslog logging, configure in /etc/syslog
log_facility=LOG_LOCAL1 # /var/log/openserbp.log
server_header ="server_header: itsp1, border proxy"
user_agent_header="user_agent_header: itsp1, border proxy"
listen=udp:10.10.0.41:5062
listen=udp:10.10.0.46:5062
listen=tcp:10.10.0.41:5062
listen=tcp:10.10.0.41:5080
listen=tls:10.10.0.41:5063
listen=tls:10.10.0.41:5065
alias=itsp1.ienum.labs.nic.at
#
# uncomment the following lines for TLS support
#
# default fed = fedA
disable_tls = 0
tls_verify_server = 1
tls_verify_client = 1
tls_require_client_certificate = 1
tls_method = TLSv1
tls_certificate = "/etc/certs/fedA/itsp1/cert.pem"
tls_private_key = "/etc/certs/fedA/itsp1/privkey.pem"
tls_ca_list = "/etc/certs/fedA/demoCA/cacert.pem"
#tls_ciphers_list= "NULL"
#tls core supports only integer client domain AVPs
tls_client_domain_avp = 400
tls_client_domain["v2"] {
#specify parameters for a domain in particular, otherwise,
#it will use the default. These are the possible parameters to
#change for each domain
tls_certificate = "/etc/certs/fedA/itsp1/cert.pem"
tls_private_key = "/etc/certs/fedA/itsp1/privkey.pem"
tls_ca_list = "/etc/certs/fedA/demoCA/cacert.pem"
tls_method=TLSv1
# tls_ciphers_list= "NULL"
tls_verify_server = 1
}
tls_client_domain["fedc"] {
#specify parameters for a domain in particular, otherwise,
#it will use the default. These are the possible parameters to
#change for each domain
tls_certificate = "/etc/certs/fedC/itsp1/cert.pem"
tls_private_key = "/etc/certs/fedC/itsp1/privkey.pem"
tls_ca_list = "/etc/certs/fedC/demoCA/cacert.pem"
tls_method=TLSv1
tls_verify_server = 1
}
tls_server_domain[10.10.0.41:5065] {
#specify parameters for a domain in particular, otherwise,
#it will use the default. These are the possible parameters to
#change for each domain
tls_certificate = "/etc/certs/fedA/itsp1/cert.pem"
tls_private_key = "/etc/certs/fedA/itsp1/privkey.pem"
tls_ca_list = "/etc/certs/fedA/demoCA/cacert.pem"
tls_method=TLSv1
tls_verify_client = 1
tls_require_client_certificate = 1
}
tls_server_domain[10.10.0.41:5067] {
#specify parameters for a domain in particular, otherwise,
#it will use the default. These are the possible parameters to
#change for each domain
tls_certificate = "/etc/certs/fedC/itsp1/cert.pem"
tls_private_key = "/etc/certs/fedC/itsp1/privkey.pem"
tls_ca_list = "/etc/certs/fedC/demoCA/cacert.pem"
tls_method=TLSv1
tls_verify_client = 0
tls_require_client_certificate = 1
}
# ------------------ module loading ----------------------------------
# Uncomment this if you want to use SQL database
loadmodule "/usr/lib/openser/modules/mysql.so"
loadmodule "/usr/lib/openser/modules/sl.so"
loadmodule "/usr/lib/openser/modules/tm.so"
loadmodule "/usr/lib/openser/modules/rr.so"
loadmodule "/usr/lib/openser/modules/maxfwd.so"
loadmodule "/usr/lib/openser/modules/textops.so"
loadmodule "/usr/lib/openser/modules/xlog.so"
loadmodule "/usr/lib/openser/modules/avpops.so"
loadmodule "/usr/lib/openser/modules/options.so"
loadmodule "/usr/lib/openser/modules/acc.so"
loadmodule "/usr/lib/openser/modules/uri.so"
loadmodule "/usr/lib/openser/modules/alias_db.so"
loadmodule "/usr/lib/openser/modules/enum.so"
loadmodule "/usr/lib/openser/modules/domain.so"
loadmodule "/usr/lib/openser/modules/domainpolicy.so"
loadmodule "/usr/lib/openser/modules/tlsops.so"
# ----------------- setting module-specific parameters ---------------
# -- database configuration --
# read only DB access
# modparam("domain", "db_url",
"postgres://openserro:openserro@localhost/openser") # XXX user at43 db for
production use
modparam("domain|uri_db|alias_db|lcr", "db_url",
"mysql://openserro:openserro@127.0.0.1/openserbp")
#modparam("avpops", "avp_url",
"postgres://serro:rororo@83.136.32.160/at43")
# read and write DB access
modparam("acc" , "db_url",
"mysql://openser:openserrw@127.0.0.1/openserbp")
# the following modules also supports multidomain, but are not used
# by us: group, group_radius, speeddial, uri_db
modparam("alias_db|usrloc|registrar|avpops", "use_domain", 1)
modparam("domain", "db_mode", 1) # Use caching in domain module
# accounting parameters
modparam("acc", "early_media", 1)
modparam("acc", "report_ack", 1)
modparam("acc", "report_cancels", 0)
modparam("acc", "multi_leg_enabled", 0)
modparam("acc", "failed_transaction_flag", 4) # radius, syslog and DB
modparam("acc", "log_flag", 1) # syslog
modparam("acc", "db_flag", 2) # DB
modparam("acc", "db_missed_flag", 3) # DB missed calles table
#modparam("acc", "swap_direction", 0)
#modparam("acc", "detect_direction", 1)
# extra accounting of TLS paramters and sockets
modparam("acc", "db_extra",
"source_ip=$si;source_port=$sp;received_ip=$Ri;received_port=$Rp;tls_peer_subject=$tls_peer_subject;tls_peer_issuer=$tls_peer_issuer;tls_my_subject=$tls_my_subject;tls_my_issuer=$tls_my_issuer")
# carrier enum
modparam("enum", "branchlabel", "i")
modparam("enum", "bl_algorithm", "cc")
# we do never read from the default AVP table, thus we define a dummy
# table which will never be used
#modparam("avpops", "avp_table", "dummy")
# -- rr params --
# add value to ;lr param to make some broken UAs happy
modparam("rr", "enable_full_lr", 1)
modparam("domainpolicy", "port_override_avp",
"portoverride")
modparam("domainpolicy", "transport_override_avp",
"transportoverride")
modparam("domainpolicy", "domain_prefix_avp",
"domainprefix")
modparam("domainpolicy", "domain_suffix_avp",
"domainsuffix")
modparam("domainpolicy", "send_socket_avp",
"sendsocket")
# ------------------------- request routing logic -------------------
# main routing logic
route{
remove_hf("Supported");
route(21);
# initial sanity checks -- messages with
# max_forwards==0, or excessively long requests
if (!mf_process_maxfwd_header("10")) {
sl_send_reply("483","Too Many Hops");
exit;
};
if (msg:len >= 2048 ) {
sl_send_reply("513", "Message too big");
exit;
};
setflag(1);
setflag(2);
setflag(3);
setflag(4);
# we do not allow REGISTER messages, they are
# not needed for VoIP peering
if (is_method("REGISTER")) {
xlog("L_INFO","[$Tf] [$si:$sp] $rm $ru (From: $fu -> To: $tu) reject
with 405\n");
sl_send_reply("405","REGISTER is not allowed");
exit;
}
# subsequent messages withing a dialog should take the
# path determined by record-routing
if (loose_route()) {
if (!has_totag()) {
xlog("L_WARN","loose_route request without to-tag, 403...\n");
sl_send_reply("403", "out-of-dialog loose_route not allowed");
exit;
}
xlog("L_WARN","request loose_route processed...\n");
route(1);
};
# we record-route all messages -- to make sure that
# subsequent messages will go through our proxy; that's
# particularly good if upstream and downstream entities
# use different transport protocol
record_route();
if ( is_method("ACK") ) {
if ( t_check_trans() ) {
# non loose-route, but stateful ACK; must be an ACK after a 487
xlog("L_INFO","local end-to-end ACK for an existent
INVITE transaction detected ...t_relay()\n");
t_relay();
} else {
xlog("L_WARN","ACK without matching transaction ...
ignore and discard.\n");
}
exit;
}
if ( has_totag() ) {
# in-dialog requests should be handled by loose_route
# this should be fixed in the client or in openser
xlog("L_ERR","in-dialog request was not catched by
loose_route block, 403... \n");
xlog("L_ERR","this is the complete message:\n");
xlog("L_ERR","$mb\n");
sl_send_reply("403","in-dialog request without loose_route
is not allowed, this is a bug in the client or in this proxy\n");
exit;
}
#
# We only allow requests which are directly addressed to us.
# This is a security feature to avoid bypassing of the
# main proxy routing logic
if (!uri==myself) {
xlog("L_INFO","[$Tf] [$si:$sp] $rm $ru (From: $fu -> To: $tu) non
local domain, reject with 403\n");
sl_send_reply("403","please address requests directly to me");
exit;
};
# allow OPTIONS requests for keep alive monitoring
if ( is_method("OPTIONS") && !(uri =~ "sip:.*@.*") ) {
# OPTIONS request for us
options_reply();
exit;
}
#
# at this time, we only have out-of-dialog requests
# now, we split routing depending on the direction
# the protocol can be used to identify the direction
# tls: request from peering partner
# udp,tcp: local request
if(proto==tls) {
route(2);
} else {
route(3);
};
exit;
}
route[1] {
# send it out now; use stateful forwarding as it works reliably
# even for UDP2TCP
if (!t_relay()) {
sl_reply_error();
};
exit;
}
route[2] {
# TLS requests - thus requests received from peering partners.
# This requests are already authenticated via TLS certificate
# validation, thus no further SIP based authentication needed.
# Just forward the requests to the main proxy.
# Beforehand, we map from the anonymous IENUM SIP URIs to the
# user's AoR using the dbaliases module. Note: this mapping can
# also be done in the main proxy.
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(2):
TLS requests\n");
if (alias_db_lookup("dbaliases")) {
xlog("L_INFO","dbaliases lookup successful, new URI = $ru\n");
avp_write("s:outbound.itsp1.ienum.labs.nic.at","$avp(i:679)");
avp_pushto("$du","$avp(i:679)");
route(5);
exit;
}
xlog("L_INFO","dbaliases lookup failed ... 404\n");
sl_send_reply("404","Unknown user");
exit;
}
route[3] {
# UDP/TCP - thus requests received from the local main proxy.
# This requests are already authenticated by the main proxy,
# thus no further SIP based authentication needed.
#
# Perform the following steps:
# 1. Try I-ENUM lookup
# 2. Try U-ENUM lookup
# 3. reply with 499
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(3):
UDP/TCP requests\n");
if ( i_enum_query("ienum.labs.nic.at") ) {
xlog("L_INFO","I-ENUM lookup successful, new URI = $ru, check if
useful...\n");
if (uri == myself) {
xlog("L_INFO","I-ENUM SIP URI belongs to myself, relay back to main
proxy...\n");
sl_send_reply("100","I-ENUM succeeded, number belongs to us, sending
back to main proxy...");
route(2);
exit;
}
if (dp_can_connect()) { # check the domainpolicy of the destination
xlog("L_INFO","dp_can_connect succeeded:\n");
xlog("L_INFO"," port_override_avp =
'$avp(s:portoverride)'\n");
xlog("L_INFO"," transport_override_avp =
'$avp(s:transportoverride)'\n");
xlog("L_INFO"," domain_prefix_avp =
'$avp(s:domainprefix)'\n");
xlog("L_INFO"," domain_suffix_avp =
'$avp(s:domainsuffix)'\n");
xlog("L_INFO"," send_socket_avp =
'$avp(s:sendsocket)'\n");
xlog("L_INFO"," tls_client_domain_avp =
'$avp(i:400)'\n");
# apply domain policy
if (dp_apply_policy()) {
xlog("L_INFO","I-ENUM SIP URI is a known peering partner, use this uri
and relay request...\n");
xlog("L_INFO"," R-URI = $ru\n");
xlog("L_INFO"," d-URI = $du\n");
sl_send_reply("100","I-ENUM und domainpolicy succeeded, sending to
other domain...");
route(4);
exit;
}
xlog("L_INFO","dp_apply_policy failed\n");
} else {
xlog("L_INFO","dp_apply_policy failed\n");
}
} else {
xlog("L_INFO","I-ENUM lookup failed, fallback to U-ENUM ...\n");
}
revert_uri();
if ( enum_query("ienum.labs.nic.at") ) {
xlog("L_INFO","U-ENUM lookup successful, new URI = $ru, relay
request...\n");
sl_send_reply("100","U-ENUM succeeded, sending to other
domain...");
route(4);
exit;
}
xlog("L_INFO","I-ENUM lookup failed, send 499 back to main proxy
...\n");
sl_send_reply("499","Number not in ENUM or domainpolicy failed");
exit;
}
route[4]{
# This route block reformats the rpid from the internal number format
# to the number format used in the I-ENUM trial.
# Thus, the proxy will take the existing rpid information (Remote-Party-ID header
# or P-Asserted_Identity header) and build an P-Asserted-Identity header with
# a tel URI.
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(4):
add P-Asserted-Identity header\n");
# uncomment the next few lines if the local rpid format is
# Remote-Party-ID: <sip:+43101@proxy1.ienum.labs.nic.at>;...
# first the dirty hack version: just use a regexp over the whole message
# this will fail with unusual formated SIP messages
#if (subst('/^Remote-Party-ID:(.*)sip:([^@]*)@[a-zA-Z0-9.]+(.*)$/P-Asserted-Identity:
tel:\2\r/ig')) {
# xlog("L_INFO","successfuly replaced Remote-Party-ID with
/P-Asserted-Identity...\n");
#} else {
# xlog("L_INFO","failure replacing Remote-Party-ID with
P-Asserted-Identity...500\n");
# sl_send_reply("500","failure replacing Remote-Party-ID with
P-Asserted-Identity");
# exit;
#}
#
# now the more complicated version using AVPs, should be more robust:
# write the complete content of the Remote-Party-ID header into an AVP
append_hf("Remote-Party-ID: $avp(s:rpidheader)\r\n");
# replace the sip uri with a tel uri
# \1 \2 \3 \4
avp_subst("$avp(s:rpidheader)",
"/^(.*)<sip:([^@]*)@[a-zA-Z0-9.]+(.*)>(.*)/tel:\2/gi");
# remove the Remote-Party-ID header and create the P-Asserted-Identity header
remove_hf("Remote-Party-ID");
append_hf("P-Asserted-Identity: $avp(s:rpidheader)\r\n");
route(1);
exit;
}
route[5]{
# This route block reformats the rpid received in the P-Asserted-Identity header
# (the number format used in the I-ENUM trial) into the local used format.
# The local used format may be for example a Remote-Party-ID header, a
# P-Asserted_Identity header or a rewritten From header.
xlog("L_INFO","[$Tf] $rm $ru (From: $fu -> To: $tu) entering route(5):
process P-Asserted-Identity header\n");
route(1);
exit;
}
onreply_route {
# default onreply_route
route(22);
}
route[21] {
xlog("L_INFO","$ci [$Tf]: -------------------- new SIP request received
--------------------\n");
xlog("L_INFO","$ci [$si:$sp] $rm $ru (From: $fu -> To: $tu)\n");
xlog("L_INFO","[$Tf] $$tls_peer_subject =
'$tls_peer_subject'\n");
xlog("L_INFO","[$Tf] $$tls_peer_subject_cn =
'$tls_peer_subject_cn'\n");
xlog("L_DBG", "message buffer:\n");
xlog("L_DBG", "$mb\n");
}
route[22] {
xlog("L_INFO","$ci [$Tf]: -------------------- new SIP response received
--------------------\n");
xlog("L_INFO","$ci [$si:$sp] $rs ($cs) $rr (From: $fu <- To:
$tu)\n");
xlog("L_INFO","[$Tf] $$tls_peer_subject =
'$tls_peer_subject'\n");
xlog("L_INFO","[$Tf] $$tls_peer_subject_cn =
'$tls_peer_subject_cn'\n");
xlog("L_DBG", "message buffer:\n");
xlog("L_DBG", "$mb\n");
}
1. Why do we need SIP peering?
When SIP was developed, there was no need for SIP peering. SIP was considered to be as
open as email. Like everybody can send emails to everyone, every SIP user should be able
to contact every other SIP user via SIP. Therefore, two requirements are necessary:
- The SIP provider of the caller must allow calling SIP URIs which are outside of the
local SIP domain.
- The SIP provider of the callee has to allow incoming SIP calls from callers outside of
the local domain.
For further discussion, a SIP services which fulfill these two requirements will be
referred as "open SIP service".
Currently there are lots of open SIP services, e.g.
iptel.org or
fwd.pulver.com.
But there are also other SIP services, which are "closed" or
"restricted". Closed means, that the SIP service provider allows only internal
SIP calls (when calling other users of this SIP service provider). All calls to other
users will be routed via the PSTN. Restricted means, that the SIP service provider allows
SIP calls from/to certain domains, but not all domains. This of course requires
authentication of incoming calls. (examples of closed service providers are yahoo
broadband, inode, vonage, IMS/NGN)
There are mainly 3 reasons why a service provider does not offer an open service, but only
closed or restricted:
- business model: The service provider need the PSTN fees for its business model.
- security: The service provider is afraid of possible security impacts
- spit: The service provider is afraid of SPIT (VoIP SPAM) which will probably occur
sooner or later (like email spam)
Having "open" SIP providers while other SIP providers are "closed"
leads to several interconnection problems. Following are a few examples:
- A user of an open SIP service calls a user of a closed SIP service using the callees
SIP URI. The callees SIP provider ignores the incoming SIP messages, causing failed call
setups (without any information why the call failed).
- A user of a closed SIP service calls a user of an open SIP service via its E.164 phone
number. Although the callee can be called directly via ENUM+SIP, the call will be routed
via the PSTN causing higher call setup times, call costs and probably worse quality due to
multiple transcoding and packetization.
- Two users want to have a SIP video conversation amongst them. Both SIP providers
support video sessions, but as the interconnect between both providers will be done via
the PSTN, there is no possibility to have multimedia session (video, IM, presence).
To resolve these issues, a dedicated peering mode is necessary. Probably lots of people
will argue that peering is not necessary and SIP providers should just open their SIP
services. But this will lead to the same problems we have with email. Further, if there
are service providers which do not want open there service for everybody, their position
should be accepted.
Even if a service provider opens its SIP service, it might want to apply some restrictions
on how the interconnect will be done.
Thus, to encourage SIP providers to open their SIP service, we need to offer them
mechanisms to authenticate other service providers. There are several ways to authenticate
SIP users or other SIP service providers, e.g: SIP over TLS (certificate based
authentication), IP address based (TCP or TLS as UDP can be spoofed easily), SIP
identity-draft, SIP+S/MIME, SIP+domainkeys, Layer2 VPNs, IPsec, ...
Regardless of the technology the service provider requires for authenticating calls, the
technology must be somehow announced to the other service providers. This is the reason
why we need
- draft-lendl-domain-policy-ddds-02.txt and
- draft-lendl-speermint-federations-02.txt
These drafts define federations and how a service prodiver can publish its peering
policies (= federation memberships).
2. What is a federation
A federation is like a club. All the members share a common interest. In case of VoIP
peering, the members share the common wish to interconnect directly via SIP instead of the
old style TDM/SS7 interconnect. A federation needs at least 2 members (this is like a
bilateral peering agreement), but usualy may have several members which treat each other
as identical.
There will be lots of different federations with different goals an dinterconnect
policies. E.g. one federation may have to policy to have settlement free peering (sender
keeps all) and everbody may participate as long they authenticate via TLS. Other
federations may still stay with current settlement policies (termination fees ...) and
members most fulfill certain requirements (e.g. must be a mobile operator ...).
Thus, it may often happen that a VoIP service provider is a member of several federations.
The next sections shows how service providers may participate in several federations and
how the domainpolicy technology can be used to achieve "plug-and-play"
interworking in several federations.
3. Use Cases - The Federations
In the following example we show the use case of a VoIP service provider. This virtual
VoIP service provicer is located in Austria and offers VoIP services via cable technology.
It is an early adopter of the "triple play" offering TV, Internet and Voice to
its customers. Let's call this VoIP service provider "CableItsp1".
There are also other cable technology based VoIP service providers in Austria. As they
mostly have their local regions without overlapping, these cable guys are not competitors.
Their competitors are traditional voice service providers (VSPs). These cable guides set
up a federation called "cable-guys-austria" to fight jointly against their
competitors. Further there business model is not based on selling voice mintues, but an a
monthly subscription fee with flat rate. Thus, having settlement free voice interconnect
between each others will be a benefit as they can simplify their billing systems.
Further, there is a worldwide federation called "voip-providers-international"
which allow every VoIP service provider to join and allow settlement free worldwide
interconnect - as long as the members pay they monthly federation subscription. Thus this
federation may be interesting for VoIP service providers with a lot of international
connections.
To have one more example, there is an Austrian federation called
"voip-peering-austria" which still insists of interconnect fees. Thus, the
simple but very reasonable goal is to keep VoIP calls on IP and avoid transcoding and
gatewaying for TDM based interconnect. The settlement must be still agreed bilatereal
between the VSPs.
All of these federations have different goals and different interconnect policies:
cable-guys-austria
==================
subscription fee: no
settlement: no fees
technology: SIP via UDP via IPsec (commom shared secret) via well known IP addresses
URI (identifier):
http://www.cableguysaustria.at/peering-v1
domain-suffix: cableguysaustria.at
voip-providers-international
============================
subscription fee: yes, monthly
settlement: no fees
technology: SIP via TCP with IP access lists
SIP-port: 5080
URI (identifier):
http://www.voip-providers-international.com/peeringv1
voip-peering-austria
====================
subscription fee: no
settlement: fees must be agreed bilateral
technology: SIP via TLS, certificate signed by voip-peering-austria
TLS-port: 6666
URI (identifier):
http://www.voip-peering-austria.at/TlsPeeringV1
Note: Why some use a dedicated port instead of the SIP default port will be described
later.
4. Finding and Peering
4.1. Is the callee reachable via SIP?
Before any interconnect policies can be applied, the originating service provider has to
find out to which VSP a phone number belongs. These can be done via lookup tables.
Probably the most open and commen lookup mechanism is ENUM, especially infrastructure
ENUM. The VSP registers the ENUM domains for its numbers and announces the SIP URI for
inbound routing in the NAPTR. Thus, the originating service provider performs an IENUM
lookup for the called phone number. If there is no ENUM entry for a called number, the
originating VSP will send the call to its PSTN gateway as usual. If there is a SIP URI in
ENUM, the next step is:
4.2. Does the callee's VSP accept VoIP interconnect from the originator?
The SIP URI returned from the IENUM lookup points to the terminating service provider.
Thus, the domain in the SIP URI is the "key" to find out the peering policies of
the terminating service provider.
For example, the IENUM lookup for the called number returns sip:+4312345@sip.itsp66.com.
Then, the domain "sip.itsp66.com" will be used as the input data for the
domainpolicy algorithm described in draft-lendl-domain-policy-ddds. The originating
service provider will perform NAPTR DNS lookups for the domain "sip.itsp66.com"
with service types "D2P+...". In this example, there will be 3 NAPTRs returned:
$ORIGIN
sip.itsp66.com.
IN NAPTR 10 50 "U" "D2P+SIP:fed"
"!^.*$!http://www.cableguysaustria.at/peering-v1!" .
IN NAPTR 20 50 "U" "D2P+SIP:fed"
"!^.*$!http://www.voip-peering-austria.at/TlsPeeringV1!" .
IN NAPTR 30 50 "U" "D2P+SIP:fed"
"!^.*$!http://sipxyconnect.example.org/!" .
Now the originator verifies the published federation identifiers agains a local table, in
which the originating VSP has configured its federation membership. If there is a matching
federation, the next step will applies.
4.3. Apply the federation policies
Different federations will use different techniques to interconnect and authenticate each
other. Thus, before sending the call to the terminating VSP, the federatations policy must
be applied. E.g. if a federation authenticate each other using TLS with certificates
signed by the federation's CA, the border proxy must switch to TLS and present the
proper certificate to the ingress point of the terminating VSP. Some of these
"attributes" must be applied by the border proxy (e.g. tranport protocol,
dedicated SIP signaling port, TLS certificate ...) whereas others might be application
agnostic (e.g. IPsec encryption, VPNs ...).
5. ENUM routing and federation policies
When a VSP publishes its ingress proxy for VoIP peering in ENUM, it publishes only a
single NAPTR. Although then ENUM standard allows publishing of several "SIP
NAPTRs" for a certain number there is no semantics defined how to deal with multiple
received SIP NAPTRs (serial forking? parallel forking? which one to choose?). Thus it is
suggested to publish only a single SIP NAPTR in ENUM.
Using plain ENUM and plain SIP, all incoming requests for a certain phone number will be
received on the same ingress proxy. This is fine in a "open SIP service"
environment, but not in a dedicated peering scenario where traffic from certain peering
partners should be received on certain ingress points. A common use case is the use of
TLS. As TLS does not allow "virtual hosting", authentication with different
certificates for different peering partners requires a dedicated socket for each "TLS
domain". Another use case is peering in a dedicated VLAN with public IP addresses. If
a VSP has an ingress proxy in the public Internet and an ingress proxy in a private LAN
(for dedicated peering partners), there must be an algorithm which allows to originating
VSP to resolve from the common SIP URI to the respective IP address (public or private).
This can be done by applying federation policies to the SIP URI. We will call these
"override attributes" as they override the default SIP routing behavior
described in RFC 3263. Each federation defines its override attributes which allow to
"manipulate" the default RFC3263 call routing according to the federation's
policy.
Currently the openser domainpolicy module supports the following override attributes:
- port_override: If this attribute is set, the port in the destination URI is set to this
port. Setting an override port disables NAPTR and SRV lookups according to RFC 3263.
- transport_override: If this attribute is set, the transport parameter in the
destination URI is set to the specified transport ("udp", "tcp",
"tls"). Setting an override
transport also disables NAPTR lookups, but retains an SRV lookup according to RFC
3263.
- domain_prefix: If this attribute is set, the domain in the destination URI will be
prefixed with this "subdomain". E.g. if the domain in the request URI is
"example.com" and the domain_prefix contains "inbound", the domain in
the destinaton URI is set to "inbound.example.com".
- domain_suffix: If this attribute is set, the domain in the destination URI will have
the content of the attribute appended to it. E.g. if the domain in the request URI is
"example.com" and the domain_suffix contains "myroot.com", the domain
in the destination URI is set to "example.com.myroot.com".
Thus, using override attributes manipulates the destination URI - the socket to which the
message will be sent. Nevertheless, the request URI (the SIP URI in the first line of the
SIP request) is untouched as will always be the value of the ENUM lookup.
6. Configuring inbound routing
This section shows how the previously presented VSP "CableItsp1" configures it
inbound routing. As desrcribed earlier, CableItsp1 is member of 3 different federations,
each having its own interconnect policies.
6.1. ENUM
CableItsp1 chooses a simple format for its NAPTRs: the user part is the E.164 phone number
and the domain part is its domain cableitsp1.at, for example:
$ORIGIN 5.4.3.2.1.3.4.e164.arpa.
IN NAPTR 10 50 "U" "E2U+sip"
"!^.*$!sip:+4312345@cableitsp1.at!" .
6.2. Announcing federation memberships
CableItsp1 announces its federation memberships (= its interconnect policies) in the DNS:
$ORIGIN cableitsp1.at.
IN NAPTR 10 50 "U" "D2P+SIP:fed"
"!^.*$!http://www.cableguysaustria.at/peering-v1!" .
IN NAPTR 20 50 "U" "D2P+SIP:fed"
"!^.*$!http://www.voip-peering-austria.at/TlsPeeringV1!" .
IN NAPTR 30 50 "U" "D2P+SIP:fed"
"!^.*$!http://www.voip-providers-international.com/peeringv1!" .
6.3. Federation ingress configuration
6.3.1 Federation cable-guys-austria
As this federation uses IPsec for authentication, CableItsp1 decided to use a dedicated IP
address 11.22.33.44 on its border proxy for dealing with the IPsec stuff. Thus, the domain
cableitsp1.at.cableguysaustria.at (SIP URI domain + domain-suffix according to the
federation policies) will resolve to 11.22.33.44. CableItsp1 configues the IPsec to use
the shared secret of the federation and forbidds any SIP signalling from/to 11.22.33.44
without IPsec. The list of IP addresses of interconnect partners (members of this
federation) will be downloaded once a day from the federations web server and the IPsec
configuration will be updated accordingly.
In the openser call routing, inbound calls from this federation can be detected by:
if(dst_ip==11.22.33.44) {
log("message received via IPsec\n");
...
};
6.3.2 Federation voip-providers-international
This federation uses SIP over TCP over port 5080. Thus, CableItsp1 configures its border
proxy to also listen on port 5080 for TCP, and adds the IP addresses of the peers to the
IP access list on its firewall.
In the openser call routing, inbound calls from this federation can be detected by:
if( (dst_port==5080) && (proto==TCP) ) {
log("message received via TCP port 5080\n");
...
};
6.3.3 Federation voip-peering-austria
This federation uses TLS for authentication. CableItsp1 generated a certificate signing
request (CSR) using the openssl tools. This CSR was signed by the voip-peering-austria CA.
Then the border proxy was configured to listen on port 6666 for TLS and use the respective
certificate and CA list:
listen=tls:10.10.0.41:6666
tls_server_domain[10.10.0.41:6666] {
tls_certificate = "/etc/certs/voip-peering-austria/cert.pem"
tls_private_key = "/etc/certs/voip-peering-austria/privkey.pem"
tls_ca_list = "/etc/certs/voip-peering-austria/cacert.pem"
tls_method=TLSv1
tls_verify_client = 1
tls_require_client_certificate = 1
}
In the openser call routing, inbound calls from this federation can be detected by:
if( (dst_port==6666) && (proto==TLS) ) {
log("message received via TLS on port 6666\n");
...
};
7. Configuring outbound routing
The call routing configuration for domainpolicy based routing is rather simple:
if (dp_can_connect()) { # check the domainpolicy of the destination
# apply domain policy
if (dp_apply_policy()) {
t_relay();
exit;
}
...
}
The configuration for the domainpolicy module is mainly entering the federation policy in
the domainpolicy table.
The domainpolicy table consists of the following columns:
id: unique id
rule: Name of column containing the domain policy rule name which is equal to the URI as
published in the domain policy NAPTRs (the federation identifier).
att: The override attribute. Name of column containing the AVP's name. If the rule
stored in this row triggers, than dp_can_connect() will add an AVP with that name.
val: Value of the override attribute. Name of column containing the value for AVPs
created by dp_can_connect().
comment: a description of these table entry
The id and comment table are not used by openser.
The following table shows the required domainpolicy table for the previously defined
federations:
id rule type att val comment
1
http://www.voip-providers-international.com/peeringv1 fed portoverride 5080
2
http://www.voip-providers-international.com/peeringv1 fed transportoverride tcp
3
http://www.voip-peering-austria.at/TlsPeeringV1 fed tls_client_domain
voip-peering-austria
4
http://www.voip-peering-austria.at/TlsPeeringV1 fed portoverride 6666
5
http://www.voip-peering-austria.at/TlsPeeringV1 fed transportoverride tls
6
http://www.cableguysaustria.at/peering-v1 fed transportoverride udp
7
http://www.cableguysaustria.at/peering-v1 fed sendsocket 11.22.33.44
Further, it requires the configuration of a TLS client domain and the AVPs for the
domainpolicy module:
tls_client_domain["voip-peering-austria"] {
tls_certificate = "/etc/certs/voip-peering-austria/cert.pem"
tls_private_key = "/etc/certs/voip-peering-austria/privkey.pem"
tls_ca_list = "/etc/certs/voip-peering-austria/cacert.pem"
tls_method=TLSv1
tls_require_server_certificate = 1
}
modparam("domainpolicy", "port_override_avp",
"portoverride")
modparam("domainpolicy", "transport_override_avp",
"transportoverride")
modparam("domainpolicy", "domain_prefix_avp",
"domainprefix")
modparam("domainpolicy", "domain_suffix_avp",
"domainsuffix")
modparam("domainpolicy", "sendsocket_avp",
"sendsocket")