A comment that I see frequently when helping people with PJSIP is the lack of a general section (with global options) and how this causes their configuration to be larger than it needs to be. I thought I would take this blog post to explain some of the design choices that went into PJSIP configuration support and some functionality that can be used to slim down configuration.
When modules or new functionality is written they tend to either use other core functionality that existed at the time, spur new functionality to be written, or do things their own way. Many historical modules (such as chan_sip) are a good example of this. Wheh chan_sip was written the only core functionality that existed for configuration was the .conf file parser. It was rather simple in that it simply read in a .conf file, turned it into data structures, and presented it to the module. Through development of chan_sip some lack of functionality in the configuration parser was compensated for by doing it within the module. This is the inheritance that exists today – whereby you can configure something in the general section and it is then inherited by user or peers. This is fragile, however, as logic has to be explicitly added to do this inheritance – it is not automatically done.
When PJSIP was being written it was decided that a new data (not specifically configuration) layer would be written. This would serve the same purpose that a lot of the logic in chan_sip serves for parsing options, storing state, that kind of stuff. It was done in a generic fashion though so other modules could use it and additional functionality (such as push configuration) could be done. This API is called sorcery and is used by PJSIP. The .conf file support continues to use the same configuration parser as chan_sip however.
An important thing to note is that sorcery takes a different approach to configuration than historical modules do – it validates configuration more closely. It tries to not allow you to get into a state where the configuration you have provided is invalid and could cause undefined behavior. This means when something isn’t working it is critical to look at the log messages as it will clearly tell you what is wrong if the problem is configuration related.
The sorcery functionality was also written to not duplicate existing functionality. This means that functionality which can already be done by something like the configuration parser was not done in sorcery through other means.
As I mentioned when chan_sip was written the configuration parser was lacking functionality. As time went on this functionality was added yet many people don’t seem to know it is there. The one I’d like to focus on in this blog post is templates. Templates are a method which provide the inheritance mechanism that many used the general section for in chan_sip. They take the contents of another context and add it to the current one. For example, given the following:
[trunk_defaults](!) type = wizard transport = ipv4 endpoint/allow_subscribe = no endpoint/allow = !all,ulaw aor/qualify_frequency = 30 registration/expiration = 1800 [myitsp](trunk_defaults) sends_auth = yes sends_registrations = yes endpoint/context = DID_myitsp remote_hosts = sip1.myitsp.net,sip2.myitsp.net accepts_registrations = no endpoint/send_rpid = yes endpoint/send_pai = yes outbound_auth/username = my_username outbound_auth/password = my_password
A “myitsp” wizard entry is created which inherits some default settings from the “trunk_defaults” context. You can configure multiple ITSPs all inheriting from this “trunk_defaults” context.
You can also create another template for users:
[user_defaults](!) type = wizard accepts_registrations = yes sends_registrations = no accepts_auth = yes sends_auth = no endpoint/allow = !all,ulaw,gsm,g722 endpoint/direct_media = no endpoint/rtp_symmetric = yes endpoint/rewrite_contact = yes aor/qualify_frequency = 30 aor/max_contacts = 1 aor/remove_existing = yes aor/minimum_expiration = 30 [bob](user_defaults) inbound_auth/username = bob inbound_auth/password = bobspassword [alice](user_defaults) endpoint/callerid = Alice <1001> endpoint/allow = !all,ulaw inbound_auth/username = alice inbound_auth/password = alicespassword
This would create two wizard entries – one for “bob” and one for “alice” which both inherit from “user_defaults”.
A fun fact is that because this is done in the configuration file parser it can be used anywhere a .conf file is used – including in sip.conf!
While this is only one aspect I hope in the future to shine light on other things which I feel people have run into often. Stay tuned for those!