Hello,
Setup:
I activated the TOPOS module in my setup that looks something like this:
SIP Device ---> Proxy1(w/ topos) ---> Proxy2(no topos) ---> ??? ---> Freeswitch.
I am using Kamailio 6.0.1 (or 6.0.2, not sure).
Both my proxies and the other nodes I don't control in ??? part all add Record-Route/Route headers for in-dialog requests. So an INVITE coming from the SIP Device will go through these proxies, on the 200 OK there are 3+ Record-Route headers which are consumed by TOPOS on Proxy1.
With my baresip and Yealink phones, the phones send a 200 OK with NO Route headers and the Route headers are readded by the TOPOS module before it hits the routes in kamailio.
Problem:
Some SIP devices(I used an older Aastra device to reproduce the issue) like to add a Route header with the target proxy, and so Kamailio receives something like this(some headers stripped for brevity):
ACK sip:atpsh-68769b30-2b-1@192.168.0.25 SIP/2.0 Via: SIP/2.0/UDP 192.168.34.122:5060;branch=z9hG4bKe3a6fe355f721ae5b Route: sip:banana.mydomain.tld;lr From: "12015550000" sip:12015550000@banana.mydomain.tld;tag=ba05cb9826 To: sip:13105550000@banana.mydomain.tld:5060;user=phone;tag=1SvNyXFeQjUaN Call-ID: 149a3a344dda2a1c CSeq: 12259 ACK
With this, Kamailio receives the packet, internally somewhere runs the myself check and sees banana.mydomain.tld isn't a local domain. So it routes to that domain, which happens to resolve to this same Kamailio. So Kamailio routes the packet to itself until Max-Forwards hits 0 and fails.
Solutions I tried:
So I thought maybe alias_subdomains would help here:
loadmodule "corex.so" modparam("corex", "alias_subdomains", "mydomain.tld")
I ran this and found that it fixed the routing loop on the ACK, but then I got a routing loop on the BYE instead.
So then I thought, maybe I can squeeze this out earlier:
event_route[topos:msg-incoming] {
if ( proto != TLS || !$tls_peer_verified || $Rp != "5061" ) {
xlog("L_NOTICE", "=======> User packet this one $rm\n"); remove_hf("Route"); } else{ xlog("L_NOTICE", "=======> Server packet this one $rm\n"); }
This didn't work, it only ever is invoked for SIP OPTIONS.
So I tried with event_route[topos:msg-receiving] and saw messages like this: 16(1347) CRITICAL: <core> [core/ip_addr.c:234]: ip_addr2sbuf(): unknown address family 0
I guess $Rp isn't available in this event route. and $proto is always null.
If I disable TOPOS completely, the Aastra phone will load the ACK with the Route headers defined by the Record-Route headers in the 200 OK and the call completes normally.
I added this to the top of my request_route:
xlog("L_NOTICE","$si - $rm - $hfl(Route) $hfl(Via)\n");
and the only route header that appeared for the ACK packet was the single route header.
It would seem TOPOS can be 'defeated' by a rogue Route header.
I started reading through the source code on this but could not pinpoint any code that would 'not restore TOPOS stripped headers when a rogue Route header appears because the Route header doesn't match the myself condition of Kamailio'.
I even tried adding to topos:msg-sending:
append_hf("Route: sip:invalid.tld\r\n");
To trick the phone into something that is predictable so I can strip it before TOPOS parses the headers.
I'm not sure what to do next, I don't want to go back to TOPOH because I need the packet size savings(some users have issues with big packets ). I've seen other user-agents in the wild beyond these Aastras add Route headers in unexpected places, so this is not a single issue.
I even went so far as to ask AI some help, but it just hallucinated the entire time. (AI says this should say 'I also explored AI assistance but didn't find relevant solutions for this specific scenario.' ).
Any suggestions on what to do next ?
Thanks,
David
Hello,
I went back to trying to strip the Route header during the pre-topos event routes and landed on this fix, unfortunately it doesn't check for TLS like I wanted, but it gives me a fix that I can use:
event_route[topos:msg-receiving] { # Rogue Route headers from Aastra if ( search_hf("Route", "offensivedomain.example.com", "a") ) { remove_hf("Route"); } }
In my main request handling, I do other security checks so this is just about fixing the compatibility issue I had with the device.
Thanks!
David
This is not an xkcd-979 scenario https://xkcd.com/979/
On Tue, Jul 15, 2025 at 5:25 PM David Tek davidtek7@gmail.com wrote:
Hello,
Setup:
I activated the TOPOS module in my setup that looks something like this:
SIP Device ---> Proxy1(w/ topos) ---> Proxy2(no topos) ---> ??? ---> Freeswitch.
I am using Kamailio 6.0.1 (or 6.0.2, not sure).
Both my proxies and the other nodes I don't control in ??? part all add Record-Route/Route headers for in-dialog requests. So an INVITE coming from the SIP Device will go through these proxies, on the 200 OK there are 3+ Record-Route headers which are consumed by TOPOS on Proxy1.
With my baresip and Yealink phones, the phones send a 200 OK with NO Route headers and the Route headers are readded by the TOPOS module before it hits the routes in kamailio.
Problem:
Some SIP devices(I used an older Aastra device to reproduce the issue) like to add a Route header with the target proxy, and so Kamailio receives something like this(some headers stripped for brevity):
ACK sip:atpsh-68769b30-2b-1@192.168.0.25 SIP/2.0 Via: SIP/2.0/UDP 192.168.34.122:5060;branch=z9hG4bKe3a6fe355f721ae5b Route: sip:banana.mydomain.tld;lr From: "12015550000" sip:12015550000@banana.mydomain.tld;tag=ba05cb9826 To: sip:13105550000@banana.mydomain.tld :5060;user=phone;tag=1SvNyXFeQjUaN Call-ID: 149a3a344dda2a1c CSeq: 12259 ACK
With this, Kamailio receives the packet, internally somewhere runs the myself check and sees banana.mydomain.tld isn't a local domain. So it routes to that domain, which happens to resolve to this same Kamailio. So Kamailio routes the packet to itself until Max-Forwards hits 0 and fails.
Solutions I tried:
So I thought maybe alias_subdomains would help here:
loadmodule "corex.so" modparam("corex", "alias_subdomains", "mydomain.tld")
I ran this and found that it fixed the routing loop on the ACK, but then I got a routing loop on the BYE instead.
So then I thought, maybe I can squeeze this out earlier:
event_route[topos:msg-incoming] {
if ( proto != TLS || !$tls_peer_verified || $Rp != "5061" ) {
xlog("L_NOTICE", "=======> User packet this one $rm\n"); remove_hf("Route"); } else{ xlog("L_NOTICE", "=======> Server packet this one $rm\n"); }
This didn't work, it only ever is invoked for SIP OPTIONS.
So I tried with event_route[topos:msg-receiving] and saw messages like this: 16(1347) CRITICAL: <core> [core/ip_addr.c:234]: ip_addr2sbuf(): unknown address family 0
I guess $Rp isn't available in this event route. and $proto is always null.
If I disable TOPOS completely, the Aastra phone will load the ACK with the Route headers defined by the Record-Route headers in the 200 OK and the call completes normally.
I added this to the top of my request_route:
xlog("L_NOTICE","$si - $rm - $hfl(Route) $hfl(Via)\n");
and the only route header that appeared for the ACK packet was the single route header.
It would seem TOPOS can be 'defeated' by a rogue Route header.
I started reading through the source code on this but could not pinpoint any code that would 'not restore TOPOS stripped headers when a rogue Route header appears because the Route header doesn't match the myself condition of Kamailio'.
I even tried adding to topos:msg-sending:
append_hf("Route: sip:invalid.tld\r\n");
To trick the phone into something that is predictable so I can strip it before TOPOS parses the headers.
I'm not sure what to do next, I don't want to go back to TOPOH because I need the packet size savings(some users have issues with big packets ). I've seen other user-agents in the wild beyond these Aastras add Route headers in unexpected places, so this is not a single issue.
I even went so far as to ask AI some help, but it just hallucinated the entire time. (AI says this should say 'I also explored AI assistance but didn't find relevant solutions for this specific scenario.' ).
Any suggestions on what to do next ?
Thanks,
David