Summary
The main theme I explore in these articles is when and how a SIP Proxy should alter (or “fix”) embedded sender address information – IP and port – in a SIP request that it has received. The headers that are most relevant here are Via, Contact and Record-Route. To be complete, any discussion of this topic has to also consider selection of sockets and transport protocol, particularly as the context for this investigation was a project I had undertaken to create a WebRTC-SIP gateway. While the articles focus on the facilities available in OpenSIPS, there will be much common ground for Kamailio users and the underlying principles should be true for just about any SIP Proxy or SBC.
In this, part 1, I provide an introduction explaining the underlying principles and looking at the relevant components in a typical SIP request. The remaining articles are as follows:
- Part 2 looks at Via headers and the rport parameter
- Part 3 examines how Contact header fixing is sometimes required for loose routing
- Part 4 focuses on socket selection and transport protocol transcoding
Together, I hope these articles will provide a useful tutorial and reference that will help others to get to grips with this subject matter.
Context
My original goal was to construct a WebRTC-SIP gateway using rtpengine. Some of this, particularly how to use rtpengine, is already documented in an earlier article here. Over time, the WebRTC-SIP gateway project gradually evolved into a general purpose SBC capable of transcoding between a range of different transport protocols including WebRTC, TCP, TLS and UDP. My ultimate aim was to create an SBC capable of working with the widest possible range of far-end networking scenarios, including customer equipment located behind NAT or behind a Proxy/SBC, SIP clients using TLS and browser-based clients using WebRTC. I have also been able to draw on my experience over the summer configuring and commissioning an OpenSIPS-based multi-tenant SBC for interconnection with Microsoft Teams using Direct Routing.
Working through a large number of test case scenarios, I found that address disparity was not exclusively a NAT related issue. WebRTC presents its own unique problems, but I also found that the use of ephemeral ports for TCP and TLS communication can look very similar to the port address translation that occurs in a NAT router. Potentially, there can be numerous combinations for the different customer network scenarios, communication protocols, headers, header parameters and socket selection in each direction (upstream and downstream). OpenSIPS and Kamailio both offer functions, within the nathelper module, intended to detect and fix headers where the address has been mangled by NAT. These are invaluable tools, but in some situations they can give a false-positive response that means your script may be “fixing” an address that should have been left unchanged.
Headers that define routing of requests and responses
Via headers
Via headers define the path or route that SIP responses should follow. Each node along the route, starting with the device that generated the request, adds one Via header to a SIP request. New headers are always added above any existing ones (the order is important). The Via header contains an IP address and port. It also defines the transport protocol. They are discussed in detail in part 2.
Contact headers
The Contact header defines the address and unique contact details of the sender’s device. Unlike a Via header, it usually contains a URI – i.e. a Username, IP address and port. On multi-channel equipment, the username – along with optional parameter values – may help the sending device to identify the associated channel if it receives a request later in the dialog.
In normal usage, we would not expect to see a new Contact header added by a proxy or other downstream device. The original Contact header is simply retained and passed on all the way through the Proxy chain. However, there may be times when it is appropriate for the first downstream Proxy to “fix” the address and/or port in the Contact header it received. This is discussed in detail in part 3.
The OpenSIPS toolkit
OpenSIPS provides several parameters and functions to help script writers with NAT detection and address re-writing. Some of these are core parameters and functions while others are within loadable modules. Here is a quick summary of the most relevant and useful ones:
Core parameters and functions
advertised_address
set_advertised_address()
advertised_port
set_advertised_port()
force_rport()
force_tcp_alias()
Sets a default value for the “home” address across the whole script
Resets the “home” address within a script segment at run time
Sets a default value for the “home” port across the whole script
Resets the “home” port within a script segment at run time
Tells OpenSIPS to behave as if the topmost Via header had an “rport” parameter, even if it didn’t. See RFC 3581 for details of how the rport parameter works. This is discussed in detail in part 2.
Tells OpenSIPS to behave as if the topmost Via contained an “alias” parameter, even if it didn’t. See RFC 5923 for details of how the alias parameter works. This is discussed in detail in part 2.
The RR Module
record_route()
record_route_preset()
add_rr_param()
loose_route()
Automatically inserts Record_Route header(s) into a SIP Request
As record_route, but you can specify the header URI’s
Adds a user-defined parameter to the Record_Route header(s)
Checks if the current SIP request is loose-routed. If it is, this function will strip out the topmost Route header and then set the destination based on the next Route header if one exists.
The NATHELPER module
This module provides some additional functions that are often used alongside those in the RR module, but they are focussed on detection of far-end NAT and on “fixing” the address embedded in certain headers:
nat_uac_test()
fix_nated_contact()
fix_nated_register()
Offers the script writer a number of ways to detect if a request came from a source device that is behind a NAT router. Its use is discussed in greater detail in part 2 and part 3 of this article.
Automatically changes the address and port in the Contact header so they match the source address and port. It can be used with SIP Requests or SIP Responses. See part 3 of this article for more details.
Its role is a little like fix_nated_contact(), but for REGISTER requests. However, the way it works is significantly different because it does not alter the Contact header. Instead it stores the source address in a way that allows it to be written to the ‘received’ field of the location table, when registration details are saved.
The usual sequence for using the above functions would be roughly as follows:
The above flow chart diagram is highly simplified and does not attempt to show how a REGISTER request might be handled. It also does not show how the decision to call fix_nated_contact() would be made. I will look at this decision in more detail in part 3. There may also be good reasons to move the nat detection and contact fixing below the “Request Type?” decision because there may be times when you would want to perform slightly different tests for initial requests and loose-routed requests.
Why might a Proxy need to fix a Contact address?
The network environment and equipment being used at the customer’s premises is largely outside the control of a service provider. This makes it necessary for the service provider to have the flexibility to work with a variety of interconnection scenarios. Far-end NAT routers are by far the most common reason why an Internet Telephony Service Provider (ITSP) would need to perform Contact address fixing in their Proxy server. However, there are several other scenarios that can result in a similar address mismatch – some of these should trigger address fixing and some should not. Here is a list of common scenarios:
NAT router
Proxy server
SIP Client on mobile device
Browser/WebRTC Client
TCP/TLS ephemeral port
Nearly always requires address fixing
Must not trigger address fixing (e.g. Microsoft Teams Proxy)
Mobile network is usually equivalent to double NAT
Almost always requires address fixing
Depends if connection re-use trumps fundamental SIP standards
I will discuss all of the above use cases in more detail in later parts of this article. In most cases it is possible to create code that automatically makes the right choice, but there are cases where it is just not possible to know. The last case listed above, ephemeral ports for TCP/TLS, is an example where the designer of a Proxy solution has to make a judgement call or possibly allow unique selection for each customer.
Here are links to the other articles in this series and to articles covering related topics:
About responses & Via’s: https://kb.smartvox.co.uk/opensips/nat-contact-and-via-fixing-in-sip-part-2/
About Contact fixing: https://kb.smartvox.co.uk/opensips/nat-contact-and-via-fixing-in-sip-part-3/
About transport protocol: https://kb.smartvox.co.uk/opensips/nat-contact-and-via-fixing-in-sip-part-4/
About Record-Route: Contact and Record-Route headers explained – The Smartvox Knowledgebase
First of all, I would like to express my appreciation and gratitude for the work done to structure all of this information in such a fundamental way. I’ve never seen such a good written description of the matter.
Working in the VoIP/SIP field for quite a long time already, I’ve never thought that I might need to structure my knowledge. And this article is the way to do that 🙂
Other than that, from a literary point of view, the article is written pretty much well!
Thanks Donat.