Summary:ASTERISK-20010: Bridge CDR fails to be created when, in a Local channel bridging two outbound channels that is optimized away, the CDR userfield setting is set on both outbound channels
Reporter:Matt Jordan (mjordan)Labels:
Date Opened:2012-06-15 15:06:32Date Closed:2014-03-05 09:46:32.000-0600
Versions:11 10.5.1 Frequency of
Description:This is a weird one, so bear with me.

Consider the following dialplan:



exten => 1,1,NoOp()
same => n,Dial(SIP/phone_a,,U(phone_a^s^1))
same => n,Hangup()

exten => 2,1,NoOp()
same => n,Dial(SIP/phone_b,,U(phone_b^s^1))
same => n,Hangup()

exten => s,1,NoOp()
same => n,Set(CDR(userfield)=foo)
same => n,Return()

exten => s,1,NoOp()
same => n,Set(CDR(userfield)=yackity)
same => n,Return()


Say we cause phone_a and phone_b to be bridged by creating a local channel that bridges the two:


CLI> channel originate Local/1@default extension 2@default


This would create a bridge between Local;2 and SIP/phone_a.  When SIP/phone_a answers, it will run Local;1 on the pbx_thread and dial SIP/phone_b.  SIP/phone_b will then get masqueraded into the Local;2 channel, and SIP/phone_a and SIP/phone_b will be bridged.

What should happen in that scenario is the creation of two CDR records: one for Local;2 and SIP/phone_a being bridged, and one for SIP/phone_a and SIP/phone_b.

Instead, only the Local;2 and SIP/phone_a bridge CDR record is created.  The one for SIP/phone_a and SIP/phone_b is never created.

The offending code is in features.c (see below).  A special section of code (added when CELs were first introduced, way back in r203638) will, if the peer userfield is set, copy over the peer userfield to the chan CDR.  It then disables the peer CDR record.  Unfortunately, in this case, the peer is SIP/phone_a, who just got their CDR record disabled.  The disabled flag is preserved on the channel across all subsequent actions - when the bridge with the Local channel is broken the CDR record is written, but - because SIP/phone_a still exists and is about to be bridged with SIP/phone_b - it hangs around with a disabled CDR record.

/* copy the userfield from the B-leg to A-leg if applicable */
if (ast_channel_cdr(chan) && ast_channel_cdr(peer) && !ast_strlen_zero(ast_channel_cdr(peer)->userfield)) {
char tmp[256];

if (!ast_strlen_zero(ast_channel_cdr(chan)->userfield)) {
snprintf(tmp, sizeof(tmp), "%s;%s", ast_channel_cdr(chan)->userfield, ast_channel_cdr(peer)->userfield);
ast_cdr_appenduserfield(chan, tmp);
} else {
ast_cdr_setuserfield(chan, ast_channel_cdr(peer)->userfield);
/* Don't delete the CDR; just disable it. */
ast_set_flag(ast_channel_cdr(peer), AST_CDR_FLAG_POST_DISABLED);
we_disabled_peer_cdr = 1;

When the bridge between SIP/phone_a and SIP/phone_b gets set up, the CDRs are reset on both channels - but because the disabled flag was set on SIP/phone_a's CDR, the disabled flag is preserved.  This prevents the bridge_cdr from being created for this bridge, and hence nothing gets written.

Note that this will only occur if the userfield itself is set - no other CDR record would have this happen, as this piece of code in features.c is specific to that CDR field.

The workaround is simple: don't set the userfield on the channel that is bridged with Local;2.  This isn't imperative, as you can still set CDR information on the resulting bridge between SIP/phone_a and SIP/phone_b in the outbound handlers for the SIP/phone_b channel.
Comments:By: Matt Jordan (mjordan) 2014-03-05 09:46:33.052-0600

To fix this in 1.8 and 11 would be nigh impossible without breaking CDRs during transfers.

The major refactoring that occurred in Asterisk 12 solves this issue by generating CDRs based on events from Stasis. Those events clearly convey the participants in bridges, such that the caching/swapping that occurs in 1.8/11 no longer happens. This means that the userfield always gets assigned properly in bridges.