OpenSIPS version
3.6.7 (x86_64/linux) — the relevant code path is also present on master.
Environment
A proxy with two listening sockets — an external one (client-facing) and an internal one (towards the main registrar) — running mid_registrar (reproduced in mode=2, AOR throttling, but the relevant code path is
mode-independent).
Description
When mid_registrar generates its own REGISTER / de-REGISTER toward the main registrar (e.g. on contact trim, expiry, or refresh), the request leaves via the external socket instead of the internal one used by the normally
proxied REGISTER. The client-initiated REGISTER is relayed correctly (the script controls the socket), but the module-generated transaction is not subject to that logic and ends up with the wrong source / Via. On an anycast node
this is harmful: the source / Via is the shared anycast address, so the registrar's response can be delivered to a different cluster member and the transaction never completes.
Root cause
The self-generated request is built in ulcb.c with a NULL send socket:
/* ulcb.c: send_unregister() */
if (tmb.new_auto_dlg_uac(from, to, ruri, callid, NULL, &dlg)) { // socket arg is NULL
https://github.com/OpenSIPS/opensips/blob/master/modules/mid_registrar/ulcb.c#L82
unregister_contact() reads back every ucontact key it needs (from / to / main_reg_uri / next-hop / callid / cseq) but never a socket, so nothing is passed down:
https://github.com/OpenSIPS/opensips/blob/master/modules/mid_registrar/ulcb.c#L115-L167
With dst.send_sock unset, the core's get_send_socket() falls back to the inbound bind_address (= the external / anycast socket), or — for timer-driven expire/refresh, where there is no inbound message — to the protocol default
socket:
https://github.com/OpenSIPS/opensips/blob/master/forward.c#L177-L189
For contrast, the save path already selects a meaningful socket (c->sock ? c->sock : bind_address); only the regeneration path drops it:
https://github.com/OpenSIPS/opensips/blob/master/modules/mid_registrar/save.c#L787
Expected
The module-generated (de)REGISTER should egress the socket appropriate for reaching the main registrar — ideally the same socket used when the corresponding upstream registration was sent — independent of which socket the client
REGISTER arrived on, and without relying on bind_address.
Proposed fix (any of)
1. Persist the send socket used for the upstream registration alongside the other stored ucontact keys and pass it to new_auto_dlg_uac() instead of NULL.
2. Add a mid_registrar modparam to bind the outgoing socket (analogous to uac_registrant's forced_socket_column).
3. As a minimum, honor force_send_socket() set from local_route for these locally-generated requests.
Workaround
Detect the locally-generated REGISTER in local_route and call force_send_socket().
OpenSIPS version
3.6.7 (x86_64/linux) — the relevant code path is also present on
master.Environment
A proxy with two listening sockets — an external one (client-facing) and an internal one (towards the main registrar) — running
mid_registrar(reproduced inmode=2, AOR throttling, but the relevant code path ismode-independent).
Description
When
mid_registrargenerates its own REGISTER / de-REGISTER toward the main registrar (e.g. on contact trim, expiry, or refresh), the request leaves via the external socket instead of the internal one used by the normallyproxied REGISTER. The client-initiated REGISTER is relayed correctly (the script controls the socket), but the module-generated transaction is not subject to that logic and ends up with the wrong source /
Via. On an anycast nodethis is harmful: the source /
Viais the shared anycast address, so the registrar's response can be delivered to a different cluster member and the transaction never completes.Root cause
The self-generated request is built in
ulcb.cwith a NULL send socket: