Thanks to serendipitous timing on tensai's part, I learned today that OpenSER version 1.2.0 was released just yesterday. It looks exciting; a few of the most fundamental problems with SER have finally been fixed. Number one on this list is script variables. Now you can do:
$ruri = "sip:" + $from.user + ":" + $to.domain;
Second on the list is that you can now "extend OpenSER functionality by writing Perl applications to be executed embedded in configuration files."
Other wonderful things:
- Pseudo variables can be used directly in the script
- Pseudo variable transformations
- DNS NAPTR/SRV failover
- An XMLRPC management interface
- XMPP (Jabber) Gateway
- LCR database caching
All in all, it looks like an exciting release. If (Open)SER disappointed you in the past, now might be the time to look again at OpenSER.
The problem is that
NAT don't get
along. The reason they don't get along (besides that NAT is evil) is that the
RTP packets (the
media stream) may follow a completely different, and more direct, path from
endpoint to endpoint than the SIP packets do. This is a good thing, except in
the face of NAT. If there's a NAT involved, the RTP packets often can't get
through. SER+MediaProxy solves this by tricking the endpoints into thinking
mediaproxy is the other endpoint for RTP data. MediaProxy, which is not behind
a NAT, is able to receive data from both parties and forward it on to each
party (via the NAT holes already punched by them talking to the media proxy).
It's an elegant setup. It's a wonderful solution to an evil problem. But it
turns out, quite surprisingly, that MediaProxy doesn't scale as well as other
solutions. That is, there's a too-low ceiling on the number of simultaneous
calls that MediaProxy can handle before the CPU maxes out.
Here's a setup that scales much better. Instead of doing the MediaProxy dance
when NAT is detected, instead have SER proxy the packets through Asterisk. Have
Asterisk set up to accept calls from SER (who has already authenticated the
user), or to do the user authentication (in addition to SER?), and it will
happily insert itself in the middle of the call as a back-to-back user agent.
If you do this you will want to take care that you avoid gratuitous transcoding
because that will certainly not scale as well as MediaProxy. But if you can
keep from transcoding, this scales more than 2x better than MediaProxy.
This is a surprising result, because Asterisk is more complicated than
MediaProxy, and the whole back-to-back user agent scheme is a lot more
complicated than simply forwarding packets which is in essence all MediaProxy
has to do, and that Asterisk has generally done a poor job of supporting SIP.
MediaProxy is written in Python, and Asterisk in C. That might be part of it.
Jared Smith tells me that
there are various new optimizations for bridging calls in Asterisk, such as
header caching. That might be another part of it.
Since setting up Asterisk this way is no harder and I think quite a bit easier
than setting up MediaProxy (esp. in light of the "don't use_mediaproxy() (and
friends) more than once in a route" bug), and Asterisk scales better in the
real world, I have to recommend Asterisk as your media "proxy".
The story doesn't end here, though. It continues to evolve. It remains to be
seen whether another implementation of a media proxy than MediaProxy or
RTPProxy (which is still not as scalable as Asterisk in our tests) can do the
job more efficiently still. I think that it should be possible. I might do it
as a learning project in Erlang, but no promises. At this point I think it is
doubtful whether a naïve implementation in any language, even C, would outrun
Asterisk with its bridging optimizations. On the other hand, forwarding RTP and
RTCP packets unchanged isn't rocket science and it just might be easy to outrun
Thanks to Elliot at Arrivaltel and Jared for helping
me with testing, pontificating, and discussion, and to Brent Thomson for
casting a shadow of doubt over MediaProxy (for other reasons) in my mind in the
SER (and OpenSER) is a SIP router. That is, it receives SIP messages and decides where to send them or how to reply to them, in order to connect the two endpoints of a SIP call. It doesn't do media, it doesn't do voicemail or IVR or PBX or any of these other things. It's focus is routing, and it does it well and fast, and can handle a whole boatload of traffic.
But SER is not perfect. In fact, I would classify SER as barely adequate. In my
opinion, a SIP router should be four things:
- Easy to Administer
By robust I mean it doesn't crash, it's fault-tolerant, and it doesn't require 'reboots' (of the software, not the OS). I like to call this lack of reboot the "living" property, following Steve Yegge's post The Pinocchio Problem. SER is fairly robust, in that if you have a good dialplan and no flaky modules it doesn't crash often, and it's tolerant of rogue packets and other external problems. It is most definitely not living software though, and the constant restarting of the server during development and debugging is a serious problem.
By powerful I mean you can do whatever you need or want to do. You want to hook it up to your database for user authentication. You want to send yourself a Jabber message when Such and Such calls So and So. You want to do least-cost routing or some kind of convoluted load balancing that nobody has ever tried before. This is power. SER has this power, ultimately, through writing modules and using the exec module, it can be difficult (writing a C module), expensive (spawning a new process for every message!), or both. SER is severly limited by its scripting language which has no concept of variables (except storing stuff in an RDBMS with AVPs) and almost no string processing abilities.
By scalable I don't mean speed (though speed can't hurt), but throughput. One
SER server can handle on the order of 6000 SIP messages per second. (Your
mileage may vary, depending on what you're doing and how you do it.) This is
where SER really shines. You might even say it's SER's raison d'être. It's also
important to be able to distribute load across multiple servers. You can do
with SER also.
By easy to administer I mean that a normal human being should be able to configure the daemon, write the routing scripts, and debug the routing scripts. Given, the person doing the routing scripts will need to have a solid understanding of SIP. But with SER, you have to practically be a SIP guru in order to set up even a basic route script. The scripting syntax is simple enough, but as mentioned above the limitations make doing anything nontrivial a real acrobatic programming trick. Don't even get me started on SER route script debugging. I'll just say it involves
ngrep and either very terse and unhelpful logs, or very verbose and unhelpful logs.
Ok, so we see how SER measures up to my ideal. What can be done? Can the SER people step up to the plate and bridge the gap? Anything is possible, but I'm not holding my breath. Why not? Because of technical and architectural reasons, let alone community reasons. Technical because SER is written in C, and C makes things hard, and when things are hard niceties like living software often don't happen. Architectural because SER is locked into their scripting language and the design decisions that go along with that. They'd have to break compatibility (or walk on eggshells) to fix that. Even if they surprise us in these areas, there's still the difficulty of writing modules and extensions in C.
No, we can't expect the SER people to make this leap for us. They might, and
that would be nice, but let's not count on it. So where does that leave us? A
SER heir could be written with these four aspects in mind. I think it could be
done and done well if the right tools are chosen. Although I'm not 100% sure
yet, I think the most important tool will be Erlang.
Erlang almost solves the robust and scalable aspects from the very start.
Erlang makes living software almost by default. Erlang was designed by and for
the telephony industry. Erlang has good performance. The only part I'm not sure
about yet (because I'm not familiar enough with Erlang), is whether it will be
easy or hard to do routing scripts. Obviously, I think, you wouldn't ask
sysadmins to write them in Erlang. It's a language with a different paradigm
and that would be a serious barrier to entry. However, it would be nice to
allow scripting to be done in Erlang for those with that skill. I don't know
whether it will be hard to make a sensible DSL in Erlang or not. Based on my experience with
ejabberd the daemon running/interaction might
need some TLC, but I'm confident that can be accomplished with a few good shell
Will I write it? Not while you're looking. But I do want to learn Erlang and I
do like to do real programs when learning a language. So don't be surprised if
an infant heir to the throne of SER comes out of left field some day.
In the meantime, please enjoy Erlang: The Movie.
Last night's presentation on OpenSER and MediaProxy went well, I think. We
talked a lot about SIP and NAT, and looked at some sample configurations. The
question came up about why or when one should want to deal with SER, and my
answer is either when you need the scalability of SER, or when you want to use
MediaProxy. There are other reasons why you might want to user SER, but IMHO
MediaProxy is the first and most important reason to use SER. Unless you like
troubleshooting NAT problems...
The slides and sample files are at http://hans.fugal.net/utaug. There are
hyperlinks in the slides, but I couldn't figure out how to get them to stand
out, so watch that mouse cursor. The sample
openser.cfg has not been tested,
but was pruned from a working config. If you see a problem with it let me know
and I'll fix it.