Environment
- Asterisk 22.8.2 (chan_pjsip / res_pjsip)
- Avaya Communication Manager (SIP trunk over TCP, port 5062)
- Asterisk acts as SIP proxy between Avaya CM and local SIP endpoints
- TCP transport mandatory (customer requirement, UDP not acceptable)
Problem
Intermittent call failures on inbound calls from Avaya CM. Roughly 1 in 8–10 calls would fail silently: Asterisk stopped responding to Avaya SIP packets mid-ringing, the channel would be marked as CONGESTION, and Dial() would return non-zero. Avaya would eventually send CANCEL and receive a 481 Call/Transaction Does Not Exist.
Asterisk logs showed:
Spawn extension (users, s, N) exited non-zero on 'PJSIP/trunk-120-XXXXXXXX'
No obvious errors — the failure was silent from the dialplan perspective.
Root Cause — TCP Idle Timer on Avaya CM
A tcpdump capture during a failing call revealed the issue. After the SIP 180 Ringing was sent to Avaya and while waiting for the called party to answer, approximately 6 seconds of SIP silence elapsed. Avaya CM then sent a TCP FIN+ACK, closing the connection:
# tcpdump on the Asterisk server, filtered to Avaya IP
11:19:17.924 Asterisk → Avaya [P.] length 438 ← SIP OPTIONS (qualify)
11:19:17.926 Avaya → Asterisk [P.] length 348 ← SIP 200 OK
...
[~6 seconds of silence — call is ringing, no SIP traffic]
...
11:19:24.272 Avaya → Asterisk [F.] length 0 ← TCP FIN ← ROOT CAUSE
11:19:24.273 Asterisk → Avaya [.] ack ← Asterisk acknowledges
11:19:24.274 Asterisk → Avaya [F.] length 0 ← Asterisk closes its side
Avaya CM has a TCP idle timer of approximately 6 seconds. After this period with no TCP data on the connection, it sends FIN+ACK regardless of active SIP dialogs. Asterisk’s pjsip detects the TCP close, marks the channel as unavailable, and Dial() exits non-zero — the post-Dial DIALSTATUS handlers become unreachable.
First Attempt — qualify_frequency=5 (partial fix)
Raising qualify_frequency to 5 seconds on the trunk AOR generates SIP OPTIONS every 5 seconds, resetting Avaya’s idle timer. This reduced the failure rate significantly (~2 months of reduced incidents), but did not eliminate the problem.
The margin between the 5s OPTIONS interval and the ~6s timer is only 1 second — too thin to survive scheduling jitter, especially during calls where the OPTIONS timing can slip relative to last SIP dialog traffic.
[trunk-120](default_trunk_aor)
type=aor
contact=sip:172.26.7.35:5062
qualify_frequency=5 ; helps but not sufficient alone
Second Attempt — keep_alive_interval in [global] — DO NOT USE WITH AVAYA
Based on Asterisk documentation, keep_alive_interval in the [global] section sends lightweight CRLF packets (\r\n\r\n, 4 bytes, per RFC 5626) on idle TCP connections to keep them alive.
Avaya CM does NOT support RFC 5626 CRLF keepalives.
When Avaya receives the 4-byte CRLF packet, it immediately responds with TCP FIN+ACK — the keepalive attempt actively causes the disconnect. The tcpdump evidence:
# Every CRLF keepalive packet immediately triggers FIN from Avaya
15:04:53.835 Asterisk → Avaya [P.] length 4 ← CRLF keepalive (\r\n\r\n)
15:04:53.840 Avaya → Asterisk [.] ack
15:04:53.840 Avaya → Asterisk [F.] length 0 ← IMMEDIATE FIN
15:04:53.841 Asterisk → Avaya [F.] length 0 ← connection closed
15:04:53.841 Avaya → Asterisk [.] ack
15:04:53.999 Asterisk → Avaya [S] ← Asterisk immediately reconnects
...
15:04:56.835 Asterisk → Avaya [P.] length 4 ← next CRLF keepalive
15:04:56.836 Avaya → Asterisk [F.] length 0 ← IMMEDIATE FIN again
With keep_alive_interval=3, Asterisk was reconnecting every 3 seconds. The connection was never stable. Do not use this setting with Avaya CM.
Solution — Per-socket TCP keepalive on the pjsip transport
The correct fix uses OS-level TCP keepalive probes configured per-socket directly on the pjsip transport. These are pure TCP protocol-level ACK frames with no SIP payload — they pass through the Avaya SIP stack invisibly and reset the TCP idle timer at the OS/network level.
Unlike keep_alive_interval (which sends SIP application data that Avaya rejects), TCP keepalive probes ([.], length 0) are accepted by Avaya without issue:
# With tcp_keepalive_enable=yes on the transport:
15:14:43.072 Asterisk → Avaya [.] length 0 ← TCP keepalive probe
15:14:43.072 Avaya → Asterisk [.] ack ← acknowledged, NO FIN ✓
15:14:46.080 Asterisk → Avaya [.] length 0 ← probe (+3s)
15:14:46.080 Avaya → Asterisk [.] ack ← acknowledged, NO FIN ✓
15:14:49.088 Asterisk → Avaya [.] length 0 ← probe (+3s)
15:14:49.088 Avaya → Asterisk [.] ack ← acknowledged, NO FIN ✓
The TCP connection remains stable indefinitely. No FINs, no reconnects.
Final working configuration
pjsip_transports.conf (or wherever your transport is defined):
[transport-tcp]
type=transport
protocol=tcp
bind=<your_asterisk_ip>:5062
tcp_keepalive_enable=yes
tcp_keepalive_idle_time=3 ; send first probe after 3s idle (< Avaya's ~6s timer)
tcp_keepalive_interval_time=3 ; subsequent probes every 3s
tcp_keepalive_probe_count=3 ; declare dead after 3 failed probes
pjsip_trunks.conf (Avaya trunk AOR):
[trunk-avaya](default_trunk_aor)
type=aor
contact=sip:<avaya_ip_address>:5062
qualify_frequency=5 ; keep as secondary safety net for monitoring + SIP-level keepalive
pjsip.conf [global] section — ensure keep_alive_interval is NOT set (or set to 0):
[global]
keep_alive_interval=0 ; DO NOT enable — Avaya rejects CRLF keepalives with TCP FIN
Apply with:
asterisk -rx "pjsip reload"
Key findings summary
| Mechanism | Result with Avaya CM |
|---|---|
keep_alive_interval (CRLF, RFC 5626) |
Breaks connection — Avaya responds with immediate TCP FIN |
qualify_frequency=5 (SIP OPTIONS) |
Reduces failures but insufficient — 1s margin on ~6s timer |
tcp_keepalive_enable=yes (TCP probe) |
Works — Avaya accepts TCP ACK probes, no FIN triggered |
tcp_keepalive_enable + qualify_frequency=5 |
Optimal — TCP keepalive keeps connection alive, OPTIONS provides SIP-level monitoring |
The tcp_keepalive_* parameters operate at the socket level via SO_KEEPALIVE + TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT and apply only to connections using that specific transport — no system-wide sysctl changes required.
Hope this helps others hitting the same wall with Avaya CM over TCP.