PJSIP fails when extension AND trunk(s) are both at same local IP address (as from Obihai device)

I have been trying to figure this out for three nights now and it’s driving me crazy. Under Chan_SIP, I can have an extension AND two trunks associated with the SAME Obihai device at the SAME local IP address. Each of the three has a unique username, the extension is an extension number and is treated like a regular extension, whereas the trunks are also identified by unique usernames which are (in Chan_SIP) matched in the Trunk Name settings. Both trunks are used for incoming calls to the PBX but are handled very differently (for example one goes to the from-internal context while the other goes to a custom context that eventually winds up at the from-pstn-e164-us context). I mention this only to point out that the trunks cannot be combined, but I don’t think that’s the real problem anyway.

If I convert the extension to PJSIP and disable the trunks it works exactly as it should for calls to and from the extension. But the minute I add in PJSIP trunks to replace the Chan_SIP ones, the extension either stops working altogether or won’t receive incoming calls. If you attempt to place a call from the extension, it tries to go the the trunk that winds up at the from-pstn-e164-us context and that fails because that’s the wrong context. I’m still not sure why calls to the extension fail, but they do. I have tried changing various settings but as far as I can tell the big problem is that PJSIP cannot properly determine whether an incoming call or outgoing call from/to that IP address should be considered to be a call from/to the extension or one of the two trunks. My suspicion is that once one of the trunks is enabled it just totally fails to reccognize the extension anymore.

In trying to figure this out I stumbled across an interesting configuration issue. There is a file in the /etc/asterisk directory called pjsip_identify.conf that is generated by FreePBX, and for extensions there are no entries whatsoever. But every PJSIP trunk has an entry such as this:

[trunk_name]
type=identify
endpoint=trunk_name
match=sip_server_from_pjsip_settings

If I am understanding this correctly, it is telling Asterisk that if it sees a call from the address specified in the match field, it should associate it with the trunk named in the context name and endpoint setting. It appears this context is auto-generated when you create a FreePBX trunk and there is no way to change it.:confounded: And if it is doing what I think it is, then it is ignoring and overriding both the default Endpoint Identifier Order specified in the Chan PJSIP Settings, as well as any expliit “Match Inbound Authentication” setting in the trunk itself (in both cases I have set those to match on “Auth Username” first, also tried using “Username” first).

The problem is that I can’t really test this theory for two reasons: First, it appears that FreePBX neither lets you remove or modify those generated entries in pjsip_identify.conf. But even if it did, I can’t find any reference that shows how you’d match on “Auth Username” or “Username” (not sure which is technically correct) in the pjsip_identify.conf file. FreePBX does have pjsip_identify_custom.conf and pjsip_identify_custom_override.conf files, both of which are currently empty, but again without knowing what a proper entry should look like to match on the trunk name rather than the IP address I don’t know how you’d use those.

I think that when creating the pjsip_identify.conf file, either FreePBX should honor the default Endpoint Identifier Order specified in the Chan PJSIP Settings or any expliit “Match Inbound Authentication” setting in the trunk itself, or perhaps maybe there should be a new setting to explicitly set the match type in the pjsip_identify.conf file. Unless I am totally barking up the wrong tree here, in which case I have difficulty understanding why PJSIP has so much trouble sorting out traffic from the same local IP address based on extension name/trunk name, when Chan_SIP handles that same situation like it’s a walk in the park. I do note that if you have two extensions coming from the same device (a 2-line VoIP adapter) the PBX has no issue keeping those straight, which again is what makes me think those entries in the pjsip_identify.conf file that appear for trunks only may be the problem.

Does anyone know how I can resolve this so that Asterisk can keep the traffic straight? Is there a way to keep FreePBX from generating that entry in pjsip_identify.conf for a particular trunk, or to modify it so it matches on the trunk name? Or, is there a better reference for PJSIP that actually explains how to use the pjsip_identify*.conf files (particularly pjsip_identify_custom.conf and pjsip_identify_custom_override.conf) in a way that might give me a workaround for this situation?

Edit: I created an issue here: https://issues.freepbx.org/browse/FREEPBX-20801

I suspect that this issue is related to static configuration of the trunk. Normally, a trunk to an FXO device (GV would be similar) would be configured Registration Receive, with the device registering to the PBX. In that case, the pjsip.identify.conf file is not populated with the SIP Server address. With Registration Receive, the SIP Server address would be grayed out in the GUI. I am guessing that you chose static because you are short of SPx slots in the OBi.

I believe a workaround is to eliminate the pjsip extension for the OBi. Set up a Custom Extension with a Dial string that sends the call to some number on the OBi trunk. Set up X_InboundCallRoute to route calls to that number to the desired Phone port. For outgoing calls, have the phone port’s OutboundCallRoute send the call to the PBX trunk.

Is your from-internal trunk related to OBiTALK? Please explain the usage. You may be able to have only one trunk, because the other calls (from FXO port or from GV) would have a single destination number that you could specially as desired.

You should match first on Auth Username, because the OBi will send the caller ID of the external caller in the From header, so Username wouldn’t match anything on those calls. Of course, if you are down to one trunk (and no extension), then just matching on IP would be fine.

Stewart1, you’re sort of correct. These are OBi202’s and each services two extensions and three Google Voice accounts (well, only two GV accounts on the second one, but I want to leave a SP slot open on that one for possible other usage). Because one of the accounts has to be SIP for the Voice Gateways to work (because of the silly way Obihai does Voice Gateways), one of the two extensions is configured as a regular extension using SIP. The second extension is a Custom extension in FreePBX and piggybacks on the first extension’s connection. So if you assume that SP4 is used for the first extension, calls from the second extension are routed out through a Voice Gateway, where the Name and AuthUserID are both set to the extension number and the Access Number is set to SP4(ip_address_of_PBX). Those calls go to a SIP trunk in FreePBX that are named the same as the extension number and of course that’s the one that goes to the from-internal context. All incoming GV calls are also routed to SP4(ip_address_of_PBX) but using a second Voice Gateway with a different AuthUserID and FreePBX trunk name, and that is the trunk that winds up at the from-pstn-e164-us context. By using two different Voice Gateways and trunks it makes it easy to keep extension traffic and trunk traffic separated.

(Calls from FreePBX TO the second extension are routed via the custom extension, while calls to the GV accounts are handled by custom trunks, and in both cases the Dial string looks like sip/extension_or_prefixed_called_number@ip_address_of_Obihai:X_UserAgentPort_number. GV calls have a prefix appended depending on which account is to be used and the Obihai then strips the prefix and sends the call out via the correct account. Point is that calls TO the extension or to GV are not handled by trunks at all).

Basically I am using a technique I found at https://twosortoftechguys.wordpress.com/2018/12/05/how-to-use-an-obihai-200-series-voip-device-as-a-gateway-between-google-voice-and-freepbx/ - I don’t fully understand it but it seems to work great when using Chan_SIP. But when I tried to convert the extensions and trunks to PJSIP everything went to hell, particularly with regard to the first extension. And I believe the main reason that PJSIP isn’t working like SIP is because FreePBX is automatically generating those damn entries in pjsip_identify.conf that cannot be suppressed or modified even when they are clearly inappropriate. I even tried experimenting with creating similar entries in both pjsip_identify_custom.conf and pjsip_identify_custom_override.conf but leaving the match field empty and various other permutations, but in each case it either did nothing or alternately suppressed ALL the identifies for ALL trunks (as revealed by running pjsip list identifies from the Asterisk CLI). Nothing I did could get rid of the identify sections for the trunks that were being messed up by them. I’m not sure why the FreePBX developers thought that you’d only ever want to match a FreePBX PJSIP trunk using an IP address, but that clearly wasn’t a good design decision.

I do get what you are saying, that I could make a PJSIP trunk the primary connection to SP4, and make both extensions custom extensions. But that all seems a bit counter-intuitive to me. For one thing it means I’d have three trunks from that IP address rather than two as I have now, and since it would still be matching by IP address rather than Auth Username I doubt that incoming traffic would be properly directed to the correct trunk, unless I am missing something here. I struggled to understand how this works in Chan_SIP and to basically have to go and reconfigure everything in that way would probably cause me more hours of hair-pulling and screaming into the night. It makes sense to me to piggyback all the other connections on an extension, but not so much to start with a trunk connection as the main connection (maybe because I have already lost too much sleep over this). If FreePBX would just give you a “no identify context” option for PJSIP trunks, or maybe make replacement contexts in pjsip_identify_custom_override.conf actually negate similarly named contexts in pjsip_identify.conf, it would make things a whole lot easier!

The other thing I have thought about doing, but would prefer not to, is to create the PJSIP trunks, copy all the contexts except the identify context to the respective _custom files, and then delete the PJSIP trunks and replace them with Custom trunks pointing to the copied contexts. That might work but it kind of defeats the whole purpose of using FreePBX. Easier for now to just keep using Chan_SIP for everything, until this issue gets fixed.

This isn’t a bug. It’s an authing issue. Both Chan_SIP and Chan_PJSIP auth by Username then IP. With Chan_SIP you can’t change this order, with PJSIP you can. Since you don’t generally challenge your incoming calls on a trunk with a username/password (since providers don’t send them) you end up always allowing incoming over the IP.

It also doesn’t matter if its PJSIP or Chan_SIP, when authing by IP the first peer/endpoint to match that IP (host or match sections) that’s the one that’s going to win.

You would need to setup both trunks to auth against an username/pass and have the Obi send user auth for both trunks (have it register to the PBX) so when it sends calls to the PBX it uses username/pass to auth with vs IP.

1 Like

If doing this system with pjsip, I’d setup SP4 to register to a single trunk on the PBX with from-internal context. No VGs, no pjsip extension. Since there is just one trunk, static configuration would also work.

Calls from OBi phone ports would supply the proper caller ID and be sent to the trunk. Incoming calls to the custom extensions would be sent on the trunk; the X_InboundCallRoute would direct it to ph1 or ph2.

Calls to GV would use a prefix as they do now. This could be added by the Outbound Route (if you don’t need failover) or with custom trunks as you have now.

Calls from GV would be sent on the trunk with the GV number as the destination. This would be caught by a Misc Application and routed as your Inbound Routes do now.

However, this all seems pretty weird. chan_sip works fine for this application. A future Asterisk release won’t include it by default, but you’ll be able to add it manually. By the time it’s really unavailable, pjsip will have matured considerably. Sure, some conservative organizations are migrating everything to pjsip now, so they won’t risk an unwelcome surprise. But I’d expect someone trying to shave a few bucks by using analog phones and GV to be at the opposite end of that scale.

It is MOST DEFINITELY a bug in FreePBX!!! Either that or a severe failure on the part of the developers who designed the PJSIP trunk creation process to think through the implications of their default configuration.

Not in a FreePBX PJSIP trunk, you can’t. Did you even read my initial or followup posts? That’s the entire issue here, that in FreePBX it authenticates by IP and will not let you change that!

Just because something is “generally” done one way doesn’t mean there are never edge cases where something needs to be done the other way. Not all trunks connect to/from a commercial VoIP provider.

CORRECT! And that’s the whole problem, if you can’t change the trunk to NOT auth by IP.

WHICH IS EXACTLY WHAT I WANT TO DO!!! But FreePBX WON’T LET ME because of the context it drops into pjsip_identify.conf for EVERY trunk that tells it to identify by IP only. There is currently NO way that I can find to tell FreePBX NOT to write this context into pjsip_identify.conf, or to modify it to use username/pass as you suggest! That is what I consider to be, if not a bug, then at least a serious lack of aforethought when designing the PJSIP trunk configuration section Sure, it works fine for the probably 99%+ of cases where auth by IP gives the desired results, but there are cases where auth by IP screws everything up, such as the one I’m dealing with. I would imagine that others have experienced this but have, as Stewart1 suggested, gone back to Chan_SIP for now, which is what I ultimately wound up doing.

I guarantee you that the adoption of PJSIP will be slowed if everyone who tries it and then takes the time to report a bug is told it’s not a bug and then more or less told it’s their fault because they didn’t do it right, when it’s FreePBX that is making it impossible to do it right. From where I sit, you owe me a retraction and an apology. This is not an Asterisk issue, it is a FreePBX issue, because whoever wrote the PJSIP trunk configuration section apparently ASSUMED that auth by IP would be the desired auth mode for 100% of PJSIP trunks, and therefore by not providing ANY option to do auth using any other method.

I agree that to do it that way does seem pretty weird, especially when (once again) I am having to try to work around a bug (or design flaw, if you prefer) in FreePBX. The only reason I was trying to get this converted now is because I’m getting older and am not going to live forever, and at some point a relative may have to take this over, and I’d like to make it as painless as possible for them by doing the hard stuff now. I don’t think anyone in my family understands this the way I do, and they certainly won’t understand it if I have to do all kinds of weird stuff to make it work just because FreePBX is doing something by default that it shouldn’t do without giving the user the option to do it differently. But yeah, for now just sticking with Chan_SIP is a viable solution, but I would HOPE that the FreePBX team would want to improve the compatibility of FreePBX and PJSIP, and would want to actually address design flaws or bugs such as this. Anyway, thanks for your suggestions, but for now the easiest approach is to wait and see if they will fix this bug and stick with Chan_SIP in the meantime.

So you’re saying that you can’t setup the PJSIP trunk to use Inbound Authentication for the device? I can’t seem to reproduce what you are speaking about.

Here is a PJSIP trunk setup to accept incoming registrations. You will note that the identify_by (which tells it how to auth) is username,ip so it will auth against a username/pass first then by IP if needed.

 Endpoint:  TestInbound                                          Unavailable   0 of inf
     InAuth:  TestInbound/TestInbound
        Aor:  TestInbound                                        1
  Transport:  0.0.0.0-udp               udp      3     96  0.0.0.0:5160


 ParameterName                      : ParameterValue
 =========================================================
 100rel                             : yes
 accept_multiple_sdp_answers        : false
 accountcode                        : 
 acl                                : 
 aggregate_mwi                      : true
 allow                              : (ulaw|g722|opus)
 allow_overlap                      : true
 allow_subscribe                    : true
 allow_transfer                     : true
 aors                               : TestInbound
 asymmetric_rtp_codec               : false
 auth                               : TestInbound
 bind_rtp_to_media_address          : false
 bundle                             : false
 call_group                         : 
 callerid                           : <unknown>
 callerid_privacy                   : allowed_not_screened
 callerid_tag                       : 
 connected_line_method              : invite
 contact_acl                        : 
 context                            : from-pstn
 cos_audio                          : 0
 cos_video                          : 0
 device_state_busy_at               : 0
 direct_media                       : false
 direct_media_glare_mitigation      : none
 direct_media_method                : invite
 disable_direct_media_on_nat        : false
 dtls_auto_generate_cert            : No
 dtls_ca_file                       : 
 dtls_ca_path                       : 
 dtls_cert_file                     : 
 dtls_cipher                        : 
 dtls_fingerprint                   : SHA-256
 dtls_private_key                   : 
 dtls_rekey                         : 0
 dtls_setup                         : active
 dtls_verify                        : No
 dtmf_mode                          : auto
 fax_detect                         : false
 fax_detect_timeout                 : 0
 follow_early_media_fork            : true
 force_avp                          : false
 force_rport                        : true
 from_domain                        : 
 from_user                          : 
 g726_non_standard                  : false
 ice_support                        : false
 identify_by                        : username,ip
 ignore_183_without_sdp             : false
 inband_progress                    : false
 incoming_mwi_mailbox               : 
 language                           : en
 mailboxes                          : 
 max_audio_streams                  : 1
 max_video_streams                  : 1
 media_address                      : 
 media_encryption                   : no
 media_encryption_optimistic        : false
 media_use_received_transport       : false
 message_context                    : 
 moh_passthrough                    : false
 moh_suggest                        : default
 mwi_from_user                      : 
 mwi_subscribe_replaces_unsolicited : no
 named_call_group                   : 
 named_pickup_group                 : 
 notify_early_inuse_ringing         : false
 one_touch_recording                : false
 outbound_auth                      : 
 outbound_proxy                     : 
 pickup_group                       : 
 preferred_codec_only               : false
 record_off_feature                 : automixmon
 record_on_feature                  : automixmon
 refer_blind_progress               : true
 rewrite_contact                    : true
 rpid_immediate                     : false
 rtcp_mux                           : false
 rtp_engine                         : asterisk
 rtp_ipv6                           : false
 rtp_keepalive                      : 0
 rtp_symmetric                      : true
 rtp_timeout                        : 0
 rtp_timeout_hold                   : 0
 sdp_owner                          : -
 sdp_session                        : Asterisk
 send_connected_line                : no
 send_diversion                     : true
 send_pai                           : false
 send_rpid                          : false
 set_var                            : 
 srtp_tag_32                        : false
 sub_min_expiry                     : 0
 subscribe_context                  : 
 suppress_q850_reason_headers       : false
 t38_udptl                          : false
 t38_udptl_ec                       : none
 t38_udptl_ipv6                     : false
 t38_udptl_maxdatagram              : 0
 t38_udptl_nat                      : false
 timers                             : yes
 timers_min_se                      : 90
 timers_sess_expires                : 1800
 tone_zone                          : 
 tos_audio                          : 0
 tos_video                          : 0
 transport                          : 0.0.0.0-udp
 trust_connected_line               : yes
 trust_id_inbound                   : false
 trust_id_outbound                  : false
 use_avpf                           : false
 use_ptime                          : false
 user_eq_phone                      : false
 voicemail_extension                : 
 webrtc                             : no

My pjsip.identify.conf doesn’t have an identify section for this endpoint but my pjsip.auth.conf has an auth section for this endpoint. I can make a second trunk do the same thing.

Here’s the thing man, Trunks and Extensions are just terms. To Asterisk they are all just PJSIP endpoints. So if you can setup 5 PJSIP extensions to auth from the same remote location (i.e. all 5 have the same source IP) then clearly they are authing against the username not the IP.

When you do this with the trunk interface then the Authentication needs to be set to Inbound and Registration needs to be set to Receive. The trunk name is the username and you need to set the password. As long as you don’t add anything to the Match (Permit) field, an identify section will not be created.

Further testing also shows that when the PJSIP trunk has it’s authentication set the way I did, it ignores the Match (Permit) field and never makes an identify section. So it would never attempt to match against the IP.

Inbound based registration overrides the use of the IP to auth with for this type of thing.

Sure, but in the OP’s original design, he had to configure it statically because he burned the last remaining SPx on an extension. In addition, he can’t auth by user ID because the device can use only the From header to transmit caller ID.

And, I don’t know whether pjsip is actually failing to auth on the static trunk or there is a configuration issue with it.

In any case, I believe that the pjsip setup I outlined, though not tested, is likely to work with little effort.

Probably because you are not doing what I am doing, but that’s understandable.

I don’t think that’s the part you should be looking at. If you had not set up your trunk the way you did, at the top there would be a section that looks like this:

 ParameterName : ParameterValue
 ==============================================
 endpoint      : EndpointName
 match         : dotted.ip.address/255.255.255.255
 match_header  :
 srv_lookups   : true

This is generated on all PJSIP trunks (except ones configured like you did yours) and seems to override the identify_by selection. Believe me, I did try setting the “Match Inbound Authentication” setting in the trunk to match by Auth Username but that made no difference because of the context that is getting written to the pjsip_identify.conf file.

Well, the problem with that is that assuming your approach would work, I’d wind up having to muck around in the Obihai configurations to get them to send passwords, which is not currently a requirement, and also I suspect that then it would not refuse connections from other IP addresses that might have the username and password. So again, this is basically a “work around the bug in FreePBX approach”, rather than admitting there is a bug that should be fixed. I believe I did try setting the Authentication to inbound and it did not help (I tried a LOT of different things), but admittedly I did not try also adding a password on the two Voice Gateways or in the Trunk configurations.

I should probably mention that one reason I am resistant to doing anything that involves changing the configuration on the Obihais is because the way I have been doing this is to keep the original installation (the one that is being replaced) online during the day, then late at night power it down and bring up the new one to work on it. We probably won’t actually replace the old one with the new one for another week or two. So if I make configuration changes on the Obihais at night, I have to remember to change them back before bringing the old system back up. Obviously a minor change like adding or removing passwords in voice gateways isn’t a big deal, but reconfiguring trunks and extensions is.

So because of that I can’t actually try your workaround until late tonight at the earliest, but it always grinds my gears a little to have to work around problems that are solely caused by FreePBX making assumptions that aren’t always true. I’ve been fighting with this sort of thing in FreePBX for years and sometimes the objectionable assumptions finally do get fixed eventually (I suspect it happens when a developer get bitten in the butt by one of them) but sometimes it takes YEARS. I’m hoping it won’t take years for this one to be addressed!

You could, ya know, use the _custom.conf files and make your own PJSIP configurations for this. That way you get around the restrictions of the GUI and modules writing things out how you don’t want.

You mean like I said at the end of my second post in this thread?

Yup, exactly that.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.