MT#60408 Introduce an optional behavior on pending transaction

We need to take care of cases, when one leg has pending
transaction(s), and an opposite leg triggers media re-negotiations,
which assumes we have to update the first leg as well (the one which
has a pending transaction).

Perviously we used to only support sending a fake 200OK to the one,
who triggered a media attributes re-negotiation, and scheduling
an update for the opposite leg for a later time (as soon as its done
with its own transaction(s) ).

For now we can optionally decide whether:
- to send 200OK to the one who triggers re-INVITE
- to send 491 Pending to the one who triggers re-INVITE

Using the approach with 491, gives a solution to the problem,
when a fake 200OK is sent to the remote side, and ACK after a while
is not matched to any of the existing local transactions.

This can happen in case, we have sent a fake 200OK, but SBC triggers
one more transaction towards the same side (over already existing one).
This behavior leads to a failure when trying to match coming ACK to the fake 200OK.

By default, enabled behavior - generate fake 200OK.
Optionally it's now possible to enable 491 Pending response,
by setting sems.conf option:
- send_491_on_pending_session_leg = 'yes'

Change-Id: I17f41833651eb006666315c1f9a7cfd4c0441f8a
mr12.5.1
Donat Zenichev 2 years ago
parent 731b041b71
commit cb48c879b7

@ -447,18 +447,49 @@ void CallLeg::onB2BEvent(B2BEvent* ev)
if (dynamic_cast<ApplyPendingUpdatesEvent*>(ev)) applyPendingUpdate();
break;
case B2BSipRequest:
case B2BSipRequest: {
B2BSipRequestEvent *req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
if (!sip_relay_only) {
// disable forwarding of relayed request if we are not connected [yet]
// (only we known that, the B leg has just delayed information about being
// connected to us and thus it can't set)
// Need not to be done if we have only one possible B leg so instead of
// checking call_status we can check if sip_relay_only is set or not
B2BSipRequestEvent *req_ev = dynamic_cast<B2BSipRequestEvent*>(ev);
/** disable forwarding of relayed request if we are not connected [yet]
* (only we known that, the B leg has just delayed information about being
* connected to us and thus it can't set)
* Need not to be done if we have only one possible B leg so instead of
* checking call_status we can check if sip_relay_only is set or not
*/
if (req_ev) req_ev->forward = false;
} else {
if (req_ev && req_ev->forward) {
/* in case of already ongoing negotiation in the other leg */
if (req_ev->req.method == SIP_METH_INVITE && dlg->getUACInvTransPending()) {
if (AmConfig::send_491_on_pending_session_leg) {
/** do not send right away one more re-INVITE towards it,
* just delay the current leg, which sent us re-INVITE, with with a 491 response.
*/
DBG("Cannot forward INVITE into another leg, already present pending session with it!\n");
AmB2BSession::relayError(req_ev->req.method, req_ev->req.cseq, true, 491, SIP_REPLY_PENDING);
return;
} else {
/** send a fake 200OK to the one who initiated media re-negotiation,
* in order to let it be sure the re-negotiation is completed, and then
* after a while, when an opposite leg is done with its pending transaction(s),
* propose it new media attributes. This behavior, however, can trigger issues with non
* matched ACK for the faked 200OK, in case B2B establishes other transactions
* within the period till ACK is received (for the fake 200OK).
*/
DBG("Pending UAC INVITE transaction, planning session update (Reinvite) for later.\n");
pending_updates.push_back(new Reinvite(req_ev->req.hdrs,
req_ev->req.body, /* establishing = */ false,
/* relayed */ false, /* r_cseq */ 0));
DBG("For now replying with fake 200 OK.\n");
acceptPendingInviteB2B(req_ev->req);
return;
}
}
}
}
// continue handling in AmB2bSession
AmB2BSession::onB2BEvent(ev);
} break;
default:
AmB2BSession::onB2BEvent(ev);

@ -128,7 +128,9 @@ unsigned int AmConfig::ShutdownModeErrCode = 503;
string AmConfig::ShutdownModeErrReason = "Server shutting down";
bool AmConfig::skip_cpslimit_emergency = true;
bool AmConfig::send_491_on_pending_session_leg = false;
string AmConfig::OptionsTranscoderOutStatsHdr; // empty by default
string AmConfig::OptionsTranscoderInStatsHdr; // empty by default
string AmConfig::TranscoderOutStatsHdr; // empty by default
@ -466,6 +468,11 @@ int AmConfig::readConfiguration()
if (cfg.hasParameter("skip_cpslimit_emergency"))
skip_cpslimit_emergency = cfg.getParameter("skip_cpslimit_emergency")=="yes";
// send_491_on_pending_session_leg = true - generate 491 Pending, on pending transaction
// send_491_on_pending_session_leg = false - generate a fake 200OK, on pending transaction
if (cfg.hasParameter("send_491_on_pending_session_leg"))
send_491_on_pending_session_leg = cfg.getParameter("send_491_on_pending_session_leg")=="yes";
if(cfg.hasParameter("log_sessions"))
LogSessions = cfg.getParameter("log_sessions")=="yes";

@ -285,6 +285,19 @@ struct AmConfig
/* Global policy for treating emergency calls on CPSLimit triggering */
static bool skip_cpslimit_emergency;
/** Defines whether we send 491 upon receiving re-INVITE in one leg,
* meanwhile in an opposite leg there is still pending transaction.
* Example: ACK hasn't been received in A leg, meanwhile in the B leg
* remote SIP point already sends us re-INVITE to re-negotiate the media.
* - if true, 491 is sent on the re-INVITE, and it's meant, remote point
* re-sends re-INVITE slightly later again.
* - if false, a fake 200OK is generated towards remote side as an answer
* to re-INVITE, and for another leg media updates are scheduled
* for later time (after its handshake is finished for e.g.)
* By default: generate fake 200OK.
*/
static bool send_491_on_pending_session_leg;
/** Read global configuration file and insert values. Maybe overwritten by
* command line arguments */
static int readConfiguration();

Loading…
Cancel
Save