From cb48c879b777cf5ec1e259ab3f37e9e175cebed6 Mon Sep 17 00:00:00 2001 From: Donat Zenichev Date: Thu, 27 Jun 2024 11:57:13 +0200 Subject: [PATCH] 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 --- apps/sbc/CallLeg.cpp | 47 ++++++++++++++++++++++++++++++++++++-------- core/AmConfig.cpp | 9 ++++++++- core/AmConfig.h | 13 ++++++++++++ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/apps/sbc/CallLeg.cpp b/apps/sbc/CallLeg.cpp index 4caf1f75..6d49f2bc 100644 --- a/apps/sbc/CallLeg.cpp +++ b/apps/sbc/CallLeg.cpp @@ -447,18 +447,49 @@ void CallLeg::onB2BEvent(B2BEvent* ev) if (dynamic_cast(ev)) applyPendingUpdate(); break; - - case B2BSipRequest: + case B2BSipRequest: { + B2BSipRequestEvent *req_ev = dynamic_cast(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(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); diff --git a/core/AmConfig.cpp b/core/AmConfig.cpp index e32b3a18..5c19186f 100644 --- a/core/AmConfig.cpp +++ b/core/AmConfig.cpp @@ -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"; diff --git a/core/AmConfig.h b/core/AmConfig.h index 93db08b1..ca9ad1a0 100644 --- a/core/AmConfig.h +++ b/core/AmConfig.h @@ -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();