Skip to content

Nethvoice: Support inbound DID extraction from SIP header P-Preferred-Identity #8179

Description

@margit86

Description

Some SIP providers use the P-Preferred-Identity header to carry the called number (DID) on incoming calls when the trunk is configured as a multi-number trunk.

In this scenario, the number normally used by NethVoice through the Asterisk configuration files to identify the inbound route does not allow the different DIDs associated with the same trunk to be distinguished.

For example, the provider sends an incoming INVITE containing:

P-Preferred-Identity: <sip:0123456789@provider.example>

where 0123456789 represents the DID that was called.

As a consequence, different inbound routes cannot be configured according to the number dialed by the caller.

Current behavior

At the moment this behavior can be implemented by manually adding a custom dialplan context to Asterisk's extensions_custom.conf.

The purpose of this feature is to include the required dialplan logic directly in the Asterisk configuration files provided by NethVoice, making the following context available by default:

from-pstn-preferredin

The administrator can then simply configure the trunk to use from-pstn-preferredin as its inbound context.

This avoids having to manually add and maintain the same custom dialplan code in extensions_custom.conf on every installation where a provider uses P-Preferred-Identity to transport the called DID.

The expected workflow would therefore be:

SIP trunk
   |
   | context = from-pstn-preferredin
   v
[from-pstn-preferredin]
   |
   | extract DID from P-Preferred-Identity
   v
[from-pstn]
   |
   v
standard inbound routing

Adding this context to the Asterisk configuration files provided by NethVoice makes the configuration reproducible and managed by the product, instead of relying on installation-specific changes to extensions_custom.conf.

Proposed solution

Add the from-pstn-preferredin dialplan context to the Asterisk configuration files provided by NethVoice.

When a trunk is configured with:

context=from-pstn-preferredin

the context should:

  1. Read the P-Preferred-Identity header from the incoming SIP INVITE.
  2. Extract the user/number part from the SIP URI.
  3. Use the extracted number as the DID.
  4. Forward the call to the standard from-pstn context.
  5. Fall back to the original called extension if the channel type cannot be handled.

The feature remains explicitly enabled on a per-trunk basis simply by selecting from-pstn-preferredin as the trunk context.

Current workaround

The following custom dialplan is currently added manually to extensions_custom.conf:

[from-pstn-preferredin]
exten => _.,1,NoOp(Attempting to extract DID from SIP P-Preferred-Identity header)
exten => _.,n,GotoIf($["${CHANNEL(channeltype)}"="SIP"]?SIP)
exten => _.,n,GotoIf($["${CHANNEL(channeltype)}"="PJSIP"]?PJSIP)
exten => _.,n,NoOp(Unable to determine SIP channel type)
exten => _.,n,Goto(from-pstn,${EXTEN},1)

exten => _.,n(SIP),Goto(from-pstn,${CUT(CUT(SIP_HEADER(P-Preferred-Identity),@,1),:,2)},1)
exten => _.,n(PJSIP),Goto(from-pstn,${CUT(CUT(PJSIP_HEADER(read,P-Preferred-Identity),@,1),:,2)},1)

Once this context is included in the Asterisk configuration files provided by NethVoice, the administrator would only need to configure the incoming trunk with:

context=from-pstn-preferredin

without adding any custom dialplan to extensions_custom.conf.

Expected behavior

Given an incoming INVITE containing:

P-Preferred-Identity: <sip:0123456789@provider.example>

and a trunk configured with:

context=from-pstn-preferredin

the dialplan included in the Asterisk configuration files provided by NethVoice should extract:

0123456789

and continue the call through:

from-pstn,0123456789,1

allowing the standard inbound routing mechanism to differentiate the individual numbers belonging to the same multi-number trunk.

Activity

  1. added theissue type on Sep 23, 2026
  2. added
    nethvoiceBug or features releted to the NethVoice project
    on Sep 23, 2026
  3. self-assigned this
    on Oct 27, 2026
  4. moved this from Todo to In Progress in NethVoiceon Oct 27, 2026
  5. added
    testingPackages are available from testing repositories
    on Oct 27, 2026
  6. moved this from In Progress to Testing in NethVoiceon Oct 27, 2026
  7. edospadoni commented on Oct 27, 2026

    @edospadoni
    Member

    Test

    PR: nethesis/ns8-nethvoice#1115

    Setup: a NethVoice instance with a PJSIP trunk whose provider delivers the called DID in the P-Preferred-Identity header (multi-number trunk), i.e. an INVITE carrying P-Preferred-Identity: <sip:0123456789@provider.example> with the same Request-URI number for every DID. If no such provider is available, an INVITE with that header can be injected with sipp towards the trunk (through nethvoice-proxy when the instance is behind it). If the [from-pstn-preferredin] workaround was added by hand to extensions_custom.conf, remove it after the update, otherwise Asterisk logs duplicate priority warnings on that context at every reload.

    Asterisk CLI on NS8: runagent -m nethvoiceX podman exec freepbx asterisk -rx "<command>".

    Test Case 1 - Context generated

    • After the update asterisk -rx "dialplan show from-pstn-preferredin" shows the context with five priorities: NoOp, GotoIf on the channel type, Set(PPI_DID=...), GotoIf on empty PPI_DID, Goto(from-pstn,${PPI_DID},1)
    • The FreePBX trunk page accepts from-pstn-preferredin as context (Connectivity > Trunks > trunk > PJSIP Settings > Advanced > Context) and the configuration applies without errors

    Test Case 2 - DID taken from P-Preferred-Identity

    • Set the trunk context to from-pstn-preferredin and create two inbound routes for two DIDs of the trunk with different destinations
    • Call the first DID: the call reaches its destination and the Asterisk full log shows Goto(from-pstn,<DID1>,1) with the number carried by the header
    • Call the second DID: the call reaches the other destination
    • CDR and CTI call history show the DID carried by the header as called number

    Test Case 3 - Fallback and regression

    • An INVITE without P-Preferred-Identity on the same trunk (e.g. with sipp) is routed by from-pstn with the number of the Request-URI, the call is not dropped
    • Trunks left on their default context (from-pstn-toheader / from-pstn-identity) keep working as before, inbound and outbound

    Test Case 4 - Behind nethvoice-proxy (when available)

    • Same checks of test case 2 with a registered trunk through the proxy
    • Trunk without registration through the proxy: the provider keeps the root prefix in the Request-URI and the inbound routes match the bare DID taken from the header

    Testing image (built from nethesis/ns8-nethvoice#1115, not merged yet):

    ghcr.io/nethesis/nethvoice:pr-1115.1459-1.1ddd9c5
    

    Update command (replace nethvoiceX with the NethVoice instance):

    api-cli run update-module --data '{"module_url":"ghcr.io/nethesis/nethvoice:pr-1115.1459-1.1ddd9c5","instances":["nethvoiceX"],"force":true}'
    

    cc @margit86

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

nethvoiceBug or features releted to the NethVoice projecttestingPackages are available from testing repositories

Projects

  • Status
    Testing

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions