Asterisk 22, pjsip and a trunk

Asterisk, pjsip and a trunk in 2025

About

I've come back to asterisk a few times in my lifespan and every time I have to rediscover the software in its entirety as every unhelpful dead-end hyperlinked forum post has accepted answers to dead documentation links made defunct a decade earlier and examples for older modules (sip instead of pjsip) which nobody should be using anymore.

Asterisk

I built the asterisk AUR package for this purpose but your distro may include it already. Otherwise building it from source is fine.

Enabling PJSIP

Edit and uncomment require = chan_pjsip.so in /etc/asterisk/modules.conf and restart the service. By default, PJSIP will ignore random calls from internet port-knockers and bots.

PJSIP

PJSIP is an open source library for implementing the SIP protocol. Most of the world transitioned to this years ago and Asterisk uses this over the now legacy SIP driver ,too.

Listening

A basic configuration example would involve listening on 5060 for new clients. My asterisk VM's IP is 10.9.23.2 in this example so I will have it listen on that:

[transport-udp]
type=transport
protocol=udp
bind=10.9.23.2

This (And a firewall exemption) will allow my desktop sip client to log into it, but will also allow incoming connections from my trunk provider.

Defining some endpoints

Before we do anything we need to define some endpoints to register with us. For my purposes I made one for my desktop and one for my phone using Zoiper as the client.

pjsip.conf consists of "configuration sections" which look like [this] and they can be any type=thing on the inside. There are a few unwritten rules about the naming conventions when referencing other sections in a given section which these examples will adhere to.

Here's a few example sections for definint two endpoints which can be registered by a desktop and smartphone. These endpoints reference their similarly named auth and aor blocks but this can be done more efficiently with common blocks for repeat settings.

# /etc/asterisk/pjsip.conf
[transport-udp]
type=transport
protocol=udp
bind=10.9.23.2

[user1]
type=auth
auth_type=userpass
username=user1
password=7d784791-6234-43e8-b00b-7319479fcef4

[user1]
type=aor
max_contacts=1

[user1]
type=endpoint
context=from-internal
disallow=all
allow=ulaw
auth=user1
aors=user1

[user2]
type=auth
auth_type=userpass
username=user2
password=8140cd56-907d-45f7-8f18-c1353ef5c1ff

[user2]
type=aor
max_contacts=1

[user2]
type=endpoint
context=from-internal
disallow=all
allow=ulaw
auth=user2
aors=user2

Notice the context from-internal which is important to note in the next file, extensions.conf. For when these clients try to make a phone call after registering with us.

Defining a trunk

If you're going to make calls to the outside world a provider likely gave you a login to use their services. While you can open something like Zoiper and register straight to the account and public number they provided you it would make more sense to use local extensions and only evoke use of the trunk when needed.

Here is a basic example of what a trunk might look like:

[trunk_myprovider]
type=registration
outbound_auth=trunk_myprovider
server_uri=sip:sip.myprovider.com
client_uri=sip:54321@sip.myprovider.com
retry_interval=60
expiration=3600

[trunk_myprovider]
type=auth
auth_type=userpass
username=54321
password=myP@5sw0rd!

[trunk_myprovider]
type=aor
contact=sip:sip.myprovider.com:5060

[trunk_myprovider]
type=endpoint
context=from-external
disallow=all
allow=ulaw
outbound_auth=trunk_myprovider
aors=trunk_myprovider

[trunk_myprovider]
type=identify
endpoint=trunk_myprovider
match=sip.myprovider.com

This defines a registration, authentication, aor, endpoint and identify block for the sip trunk company providing you your trunk using ulaw. We will reference this configuration in our dial plan.

Notice the endpoint block for the trunk has context=from-external which we will create a configuration section for incoming calls.

You can substitute in the details for your provider.

extensions (Dial plans)

extensions.conf

This is where most of the magic happens. Here we define what happens with phone calls with a few dial plans. Remember our from-internal section from above? That block is where we'll handle calls from our internal registrations, allowing them to call each other but also dial out if they meet the conditions. We'll make a block for that here and also an outbound and default block for all other cases:

; By default/fallback, have the PBX answer and play the weasels message before hanging up.
[default]
exten =  _X.,1,Answer()
same  =  n,Wait(1)
same  =  n,Playback(tt-weasels)
same  =  n,HangUp()

[outbound]
; If we reach this plan assume we're calling a real world number.

exten => _X.,1,NoOp() ; catch-all, do nothing
same  => n,Set(CALLERID(NUM)=61400000551) ; Set the caller-id to the number given to you by your provider.

same  => n,Dial(PJSIP/${EXTEN}@trunk_myprovider) ; Dial the number on this catch as the extension of the trunk provider.

; What to do with inbound calls
[from-external]
exten => 61400000551,1,Answer() ; Answer our public number
same => n,Dial(PJSIP/user1,30) ; Ring user1 for 30

[from-internal]
; How to handle calls from our own internal extensions.

; If the extension 100 is dialed, play hello-world then hang up.
exten => 100,1,Answer()
same  =  n,Wait(1)
same  =  n,Playback(hello-world)
same  =  n,Hangup()

; If the extensions 200 or 201 are dialed call each PJSIP extension respectively.
exten => 200,1,Dial(PJSIP/user1)
exten => 201,1,Dial(PJSIP/user2)

; If a 04 or +61 number is being dialed, go to our outbound plan to prepare and send the call to the outside.
; This can be modified to carefully only allow calls local to your region too. Or a _X. extension can be defined to catch all remaining calls.

;include => outbound ; You can do this instead of using Goto. Doesn't scale as well.
exten => _04XXXXXXXX,1,NoOp()        ; For all real numbers starting with 04XXXXXXXX
same  =  n,Goto(outbound,${EXTEN},1) ; Go-to outbound
exten => _614XXXXXXXX,1,NoOp()       ; For all real numbers starting with 04XXXXXXXX
same  =  n,Goto(outbound,${EXTEN},1) ; Go-to outbound, too.

; Go to default (weasels) if we don't know where else to send the call.
;include => default ; You can do this instead of using Goto. Doesn't scale as well.
exten => _X.,1,NoOp()               ; Catch-all for all other numbers
same  =  n,Goto(default,${EXTEN},1) ; Go-to default (Play weasels)

Finishing up

Thanks for reading. This setup is good enough for my single internal extension on my desktop and serves as a good example to expand upon for larger configurations.