### Note WITH_NAT is unset so this route does nothing... route[NATMANAGE] { #!ifdef WITH_NAT if(is_request()) { if(has_totag()) { if(check_route_param("nat=yes")) { setbflag(FLB_NATB); } } } if(!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return; # We are sending everything to route(RTPENGINE_MANAGE), not based on NAT # route(RTPENGINE_MANAGE); if(is_request()) { if(!has_totag()) { if(t_is_branch_route()) { add_rr_param(";nat=yes"); } } } if(is_reply()) { if(isbflagset(FLB_NATB)) { if(is_first_hop()) { set_contact_alias(); } } } if(isbflagset(FLB_NATB)) { # no connect message in a dialog involving NAT traversal if(is_request()) { if(has_totag()) { set_forward_no_connect(); } } } #!endif return; } # Manage outgoing branches branch_route[MANAGE_BRANCH] { # Drop selected branch duplicates route(MULTIBRANCH_INVITE); # Route to NATMANAGE and RTPENGINE_MANAGE route(NATMANAGE); route(RTPENGINE_MANAGE); # Modify headers based on destination and privacy if(!isflagset(FLT_LCR) && $xavu(id=>pai)!=$null) { append_hf("P-Asserted-Identity: $xavu(id=>pai)\r\n"); } } # Manage incoming replies reply_route { if(!sanity_check("17604", "6")) { sd_journal_print("LOG_INFO", "[$cfg(route)] Malformed SIP response from $si:$sp"); drop; } } # Manage incoming replies in transaction context onreply_route[MANAGE_REPLY] { if(status=~"[12][0-9][0-9]") { route(NATMANAGE); route(RTPENGINE_MANAGE); } # Set multi-leg accounting variables route(ACC_MULTI_LEG); route(ACC_CDR_EXTRA_CHAIN); } # Drop selected outgoing branches route[MULTIBRANCH_INVITE] { #!ifdef WITH_APPEND_BRANCHES # Only process on branch_route if(!t_is_branch_route()) return; # Only process INVITEs with multiple branches (flag set in route[LOCATION]) if(!isflagset(FLT_MULTIBRANCH_INVITE)) return; # Drop selected branches where lookup_branches found no match or # where the Request-URI matches the caller's Contact-URI if(uri==myself || (@contact.uri.user==@ruri.user && @contact.uri.hostport==@ruri.hostport) || (route(ASTERISK_IS_SRC) && @contact.uri.hostport==@ruri.hostport)) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] Loop detected for branched call to $ou from $ct on branch $ru"); drop; } # Drop branches where $ru has an 'rinstance' parameter and $fU corresponds # to $rU, indicating a Bria or Zoiper client is registered on the device if($(ru{param.value,rinstance})!=$null) { if(($fU=~"2001" && $rU=="user1") || ($fU=~"2001" && $rU=="user2")) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] Loop detected for branched call to $ou from $ct on branch $ru with rinstance $(ru{param.value,rinstance})"); drop; } } #!endif return; } # Test if request is from Asterisk route[ASTERISK_IS_SRC] { #!ifdef WITH_ASTERISK if(src_port==ASTERISK_PORT && (src_ip==ASTERISK_IP4_LAN || src_ip==ASTERISK_IP4_WAN || src_ip==[ASTERISK_IP6_LAN] || src_ip==[ASTERISK_IP6_WAN])) { if(is_present_hf("X-Asterisk-LinkedID")) { $avp(linkedid)=$hdr(X-Asterisk-LinkedID); remove_hf("X-Asterisk-LinkedID"); } return 1; } #!endif return -1; } # RTPengine management route route[RTPENGINE_MANAGE] { #!ifdef WITH_RTPENGINE # Define $avp(dst) for interface direction, and $avp(rp) for protocol -- $nh(d) and $rP don't exist in reply route if(is_request()) { $avp(dst)=$(nh(d){s.unbracket}); $avp(dst_port)=$nh(p); $avp(rp)=$(rP{s.toupper}); } else { $avp(dst)=$T_req($(si{s.unbracket})); $avp(dst_port)=$T_req($sp); $avp(rp)=$T_req($(proto{s.toupper})); } # Handle INVITE in failure_route if(t_is_failure_route()) { if(is_method("INVITE")) { if(rtpengine_manage()) { # Reset branch flags for failover (VoiceMail or LCR auth) # resetbflag(FLB_RTPENGINE); resetbflag(FLB_RTPENGINE_AVP); resetbflag(FLB_RTPENGINE_AVPF); resetbflag(FLB_RTPENGINE_SAVP); resetbflag(FLB_RTPENGINE_SAVPF); sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine (in failure route) delete $rdir(name) $rm ($mt), source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), status $T_reply_code"); } } return; } # Handle BYE if(is_method("BYE")) { if(is_request()) { # RTPengine mos_A/B_label must be set for BYE transaction $avp(mos_A_label)="caller"; $avp(mos_B_label)="callee"; if(rtpengine_manage()) { route(ACC_CDR_EXTRA_RTPENGINE); sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine delete $rdir(name) $rm ($mt), source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), status $T_reply_code"); } } return; } # Handle CANCEL if(is_method("CANCEL")) { if(is_request()) { if(rtpengine_manage()) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine delete $rdir(name) $rm ($mt), source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), status $T_reply_code"); } } return; } # Don't process below this point without SDP if(!has_body("application/sdp")) { return; } if(is_method("ACK")) { # RTPengine answer on ACK with SDP (NO-SDP-INVITE-reply-ACK case) if($dlg_var(rtpengine_answer)!=$null) { if(rtpengine_manage($dlg_var(rtpengine_answer))) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine answer $rdir(name) $rm ($mt), source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), dialog flags: $dlg_var(rtpengine_answer)"); $dlg_var(rtpengine_answer)=$null; } # Remember the caller rtp_transport_protocol in the dialog (for re-INVITE) if($dlg_var(caller_rtp_transport_protocol)==$null) { sdp_transport("$avp(caller_rtp_transport_protocol)"); $dlg_var(caller_rtp_transport_protocol)=$avp(caller_rtp_transport_protocol); } } return; } if(is_method("INVITE|UPDATE")) { # RTPengine answer on reply to INVITE (standard INVITE-reply case) if(is_reply()) { if($xavp(ra=>$T_branch_idx)!=$null) { if(rtpengine_manage($xavp(ra=>$T_branch_idx))) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine answer $rdir(name) $rm ($mt), status $rs, source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), flags: $xavp(ra=>$T_branch_idx)"); } # Remember the callee rtp_transport_protocol in the dialog (for re-INVITE) if(status=~"2[0-9][0-9]") { if($dlg_var(callee_rtp_transport_protocol)==$null) { sdp_transport("$avp(callee_rtp_transport_protocol)"); $dlg_var(callee_rtp_transport_protocol)=$avp(callee_rtp_transport_protocol); } } return; } } # RTPengine prepare default offer/answer flags $xavp(ro=>$T_branch_idx)="replace-origin replace-session-connection strict-source"; $xavp(ra=>$T_branch_idx)="replace-origin replace-session-connection strict-source"; # Use $dlg_var for RTP profile (on reinvites) # RTP/AVP, RTP/SAVP, RTP/AVPF, RTP/SAVPF, or DTLS transport, using OSRTP for unknowns destinations if($avp(rp)=~"^WS") { # SDES-off needs to happen even on re-INVITE (in case of adding or removing video) $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " DTLS DTLS=passive SDES-off"; } else if(is_request() && $rdir(name)=="downstream") { if(isbflagset(FLB_RTPENGINE_SAVP) || $dlg_var(callee_rtp_transport_protocol)=="RTP/SAVP") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/SAVP DTLS=off SDES-pad"; } else if(isbflagset(FLB_RTPENGINE_SAVPF) || $dlg_var(callee_rtp_transport_protocol)=="RTP/SAVPF") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/SAVPF DTLS=off SDES-pad"; } else if(isbflagset(FLB_RTPENGINE_AVP) || $dlg_var(callee_rtp_transport_protocol)=="RTP/AVP") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/AVP"; } else if(isbflagset(FLB_RTPENGINE_AVPF) || $dlg_var(callee_rtp_transport_protocol)=="RTP/AVPF") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/AVPF"; } else { # If the destination's profile is unknown try OSRTP #$xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/AVP OSRTP-offer OSRTP-accept DTLS=off SDES-pad"; #$xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " DTLS=off SDES-pad"; # Default to SRTP on first offer (Bria/Zoiper don't really accept OSRTP, encryption only allows "never" or "always") if(!has_totag()) { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/SAVP DTLS=off SDES-pad"; } } } else { if($dlg_var(caller_rtp_transport_protocol)=="RTP/SAVP") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/SAVP DTLS=off SDES-pad"; } else if($dlg_var(caller_rtp_transport_protocol)=="RTP/SAVPF") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/SAVPF DTLS=off SDES-pad"; } else if($dlg_var(caller_rtp_transport_protocol)=="RTP/AVP") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/AVP"; } else if ($dlg_var(caller_rtp_transport_protocol)=="RTP/AVPF") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/AVPF"; } else { # If the destination's profile is unknown try OSRTP #$xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/AVP OSRTP-offer OSRTP-accept DTLS=off SDES-pad"; #$xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " DTLS=off SDES-pad"; # Default to SRTP on first offer (Bria/Zoiper don't really accept OSRTP, encryption only allows "never" or "always") $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " RTP/SAVP DTLS=off SDES-pad"; } } # Remember the rtp_transport_protocol in the dialog (for re-INVITE) sdp_transport("$avp(rtp_transport_protocol)"); if(is_request()) { if($dlg_var(caller_rtp_transport_protocol)==$null) { $dlg_var(caller_rtp_transport_protocol)=$avp(rtp_transport_protocol); } } else { if($dlg_var(callee_rtp_transport_protocol)==$null) { $dlg_var(callee_rtp_transport_protocol)=$avp(rtp_transport_protocol); } } # DTLS/SDES handling for Websocket, Bria, Zoiper, etc. if($avp(rtp_transport_protocol)=~"^UDP/TLS") { $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " DTLS DTLS=passive SDES-off"; } else if($avp(rtp_transport_protocol)=~"^RTP/SAVP") { $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " DTLS=off SDES-pad " + $avp(rtp_transport_protocol); } else { $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " " + $avp(rtp_transport_protocol); } # Only on first offer if(!has_totag()) { # Add via-branch=next functionality $xavp(ro=>$T_branch_idx)="via-branch=next " + $xavp(ro=>$T_branch_idx); $xavp(ra=>$T_branch_idx)="via-branch=1 " + $xavp(ra=>$T_branch_idx); # Call leg labels $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " label=caller"; $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " label=callee"; # Call recording (don't record voicemail) if($rU=="voicemail") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " record-call=off"; } else { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " record-call"; } # Interface direction if(src_ip==10.0.0.0/24 || src_ip==2001:dB8:1234::/48) { if(is_in_subnet("$avp(dst)", "10.0.0.0/24,2001:dB8:1234::/48")) { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " direction=priv direction=priv"; } else { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " direction=priv direction=pub"; } } else if(is_in_subnet("$avp(dst)", "10.0.0.0/24,2001:dB8:1234::/48")) { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " direction=pub direction=priv"; } else { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " direction=pub direction=pub"; } # Interface address-family if(af==INET && is_ipv6($avp(dst))) { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " address-family=IP6"; } else if(af==INET6 && (is_ipv4($avp(dst)) || !is_ip($avp(dst)))) { # Default to IPv4 when $avp(dst) is not an IP address $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " address-family=IP4"; } } if(is_request()) { # ICE & RTCP if(proto==WSS || proto==WS) { # WebSocket clients often use fake IPv4 addresses in the c= line, # but are speaking IPv6, in which case, the audio will never really # connect if RTPengine thinks it's looking for IPv4 input. It also # seems to be SIP-source-address disables "learning mode" and removes endless # logging of "Successful STUN binding request... Kernelizing media stream" if($avp(rp)=~"^WS") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " SIP-source-address ICE=force-relay"; $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " SIP-source-address ICE=force-relay"; } else { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " SIP-source-address ICE=remove rtcp-mux-demux"; } } else if(sdp_with_ice()) { if($avp(rp)=~"^WS") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " rtcp-mux-offer"; $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " SIP-source-address"; } else { # Still need to remove here so we don't offer ICE to SIP trunks (if we have ICE on the caller side -- we don't care about the answer) $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " ICE=remove"; } } else { if($avp(rp)=~"^WS") { $xavp(ro=>$T_branch_idx)=$xavp(ro=>$T_branch_idx) + " rtcp-mux-offer"; $xavp(ra=>$T_branch_idx)=$xavp(ra=>$T_branch_idx) + " SIP-source-address"; } } } # RTPengine offer if(rtpengine_manage($xavp(ro=>$T_branch_idx))) { if(is_request()) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine offer $rdir(name) $rm ($mt), source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), flags: $xavp(ro=>$T_branch_idx)"); } else { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine offer $rdir(name) $rm ($mt), status $rs, source $proto:$si:$sp, destination $avp(rp):$avp(dst):$avp(dst_port), flags: $xavp(ro=>$T_branch_idx)"); # Store $xavp(ra=>$T_branch_idx) for answer on ACK $dlg_var(rtpengine_answer)=$xavp(ra=>$T_branch_idx); } } # RTPengine music on hold if(is_method("INVITE") && has_totag()) { if(is_audio_on_hold()) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine play $rdir(name) audio on hold"); play_media("from-tag=$tt file=/etc/rtpengine/wallace_chapman-check_it_out.mp3 repeat-times=10"); dlg_setflag(FLD_RTPENGINE_PLAY_MEDIA); } else if(dlg_isflagset(FLD_RTPENGINE_PLAY_MEDIA)) { sd_journal_print("LOG_DEBUG", "[$cfg(route)][$T_branch_idx] RTPengine stop $rdir(name) audio on hold"); stop_media("from-tag=$tt"); dlg_resetflag(FLD_RTPENGINE_PLAY_MEDIA); } } } #!endif return; } # Set acc multi-leg accounting variables route[ACC_MULTI_LEG] { if(status=~"2[0-9][0-9]") { $avp(leg_dst_user)=$(T_branch(uri){uri.user}); $avp(leg_dst_domain)=$(T_branch(uri){uri.host}); $avp(leg_sip_code)=$T_reply_code; $avp(leg_time)=$timef(%F %T); $avp(leg_to_tag)=$tt; } return; } # Route to set acc_cdr_extra "chain" in "$dlg_var(chain)" # https://www.kamailio.org/docs/modules/stable/modules/acc#multi-cdr-call-legs route[ACC_CDR_EXTRA_CHAIN] { #!ifdef WITH_ACCDB_CDR_EXTRA if(dlg_isflagset(FLD_ACC_CDR_EXTRA_CHAIN)) { dlg_resetflag(FLD_ACC_CDR_EXTRA_CHAIN); jansson_set("integer", "id", "$dlg(ref)", "$var(chain)"); if($xavu(caller)!=$null) jansson_set("string", "from_uri", "$xavu(caller)", "$var(chain)"); if($xavu(callee)!=$null) jansson_set("string", "to_uri", "$xavu(callee)", "$var(chain)"); if($xavu(time)!=$null) jansson_set("string", "time", "$xavu(time)", "$var(chain)"); if($xavu(reason)!=$null) jansson_set("string", "reason", "$xavu(reason)", "$var(chain)"); if($xavu(replaces=>ci)!=$null) { jansson_set("string", "callid", "$xavu(replaces=>ci)", "$var(chain_replaces)"); if($xavu(replaces=>ft)!=$null) jansson_set("string", "from_tag", "$xavu(replaces=>ft)", "$var(chain_replaces)"); if($xavu(replaces=>tt)!=$null) jansson_set("string", "to_tag", "$xavu(replaces=>tt)", "$var(chain_replaces)"); jansson_set("obj", "replaces", "$var(chain_replaces)", "$var(chain)"); } # Save to dialog/acc_cdrs if($dlg_var(chain)==$null) { $dlg_var(chain)="[]"; } jansson_append("obj", "", "$var(chain)", "$dlg_var(chain)"); } #!endif return; } # Route to set acc_cdr_extra "rtpengine" with RTPengine statistics in "$dlg_var(rtpengine)" route[ACC_CDR_EXTRA_RTPENGINE] { #!ifdef WITH_ACCDB_CDR_EXTRA #!ifdef WITH_RTPENGINE # MOS if($avp(mos_min)!=$null) { jansson_set("string", "dialog.min", "$avp(mos_min)", "$vn(mos)"); if($avp(mos_min_at)!=$null) jansson_set("string", "dialog.min_at", "$avp(mos_min_at)", "$vn(mos)"); if($avp(mos_min_packetloss)!=$null) jansson_set("integer", "dialog.min_packetloss", "$avp(mos_min_packetloss)", "$vn(mos)"); if($avp(mos_min_jitter)!=$null) jansson_set("integer", "dialog.min_jitter", "$avp(mos_min_jitter)", "$vn(mos)"); if($avp(mos_min_roundtrip)!=$null) jansson_set("integer", "dialog.min_roundtrip", "$avp(mos_min_roundtrip)", "$vn(mos)"); if($avp(mos_min_roundtrip_leg)!=$null) jansson_set("integer", "dialog.min_roundtrip_leg", "$avp(mos_min_roundtrip_leg)", "$vn(mos)"); } if($avp(mos_max)!=$null) { jansson_set("string", "dialog.max", "$avp(mos_max)", "$vn(mos)"); if($avp(mos_max_at)!=$null) jansson_set("string", "dialog.max_at", "$avp(mos_max_at)", "$vn(mos)"); if($avp(mos_max_packetloss)!=$null) jansson_set("integer", "dialog.max_packetloss", "$avp(mos_max_packetloss)", "$vn(mos)"); if($avp(mos_max_jitter)!=$null) jansson_set("integer", "dialog.max_jitter", "$avp(mos_max_jitter)", "$vn(mos)"); if($avp(mos_max_roundtrip)!=$null) jansson_set("integer", "dialog.max_roundtrip", "$avp(mos_max_roundtrip)", "$vn(mos)"); if($avp(mos_max_roundtrip_leg)!=$null) jansson_set("integer", "dialog.max_roundtrip_leg", "$avp(mos_max_roundtrip_leg)", "$vn(mos)"); } if($avp(mos_average)!=$null) { jansson_set("string", "dialog.average", "$avp(mos_average)", "$vn(mos)"); if($avp(mos_average_packetloss)!=$null) jansson_set("integer", "dialog.average_packetloss", "$avp(mos_average_packetloss)", "$vn(mos)"); if($avp(mos_average_jitter)!=$null) jansson_set("integer", "dialog.average_jitter", "$avp(mos_average_jitter)", "$vn(mos)"); if($avp(mos_average_roundtrip)!=$null) jansson_set("integer", "dialog.average_roundtrip", "$avp(mos_average_roundtrip)", "$vn(mos)"); if($avp(mos_average_roundtrip_leg)!=$null) jansson_set("integer", "dialog.average_roundtrip_leg", "$avp(mos_average_roundtrip_leg)", "$vn(mos)"); if($avp(mos_average_samples)!=$null) jansson_set("integer", "dialog.average_samples", "$avp(mos_average_samples)", "$vn(mos)"); } # MOS _A if($avp(mos_min_A)!=$null) { jansson_set("string", "A.min", "$avp(mos_min_A)", "$vn(mos)"); if($avp(mos_min_at_A)!=$null) jansson_set("string", "A.min_at", "$avp(mos_min_at_A)", "$vn(mos)"); if($avp(mos_min_packetloss_A)!=$null) jansson_set("integer", "A.min_packetloss", "$avp(mos_min_packetloss_A)", "$vn(mos)"); if($avp(mos_min_jitter_A)!=$null) jansson_set("integer", "A.min_jitter", "$avp(mos_min_jitter_A)", "$vn(mos)"); if($avp(mos_min_roundtrip_A)!=$null) jansson_set("integer", "A.min_roundtrip", "$avp(mos_min_roundtrip_A)", "$vn(mos)"); if($avp(mos_min_roundtrip_leg_A)!=$null) jansson_set("integer", "A.min_roundtrip_leg", "$avp(mos_min_roundtrip_leg_A)", "$vn(mos)"); } if($avp(mos_max_A)!=$null) { jansson_set("string", "A.max", "$avp(mos_max_A)", "$vn(mos)"); if($avp(mos_max_at_A)!=$null) jansson_set("string", "A.max_at", "$avp(mos_max_at_A)", "$vn(mos)"); if($avp(mos_max_packetloss_A)!=$null) jansson_set("integer", "A.max_packetloss", "$avp(mos_max_packetloss_A)", "$vn(mos)"); if($avp(mos_max_jitter_A)!=$null) jansson_set("integer", "A.max_jitter", "$avp(mos_max_jitter_A)", "$vn(mos)"); if($avp(mos_max_roundtrip_A)!=$null) jansson_set("integer", "A.max_roundtrip", "$avp(mos_max_roundtrip_A)", "$vn(mos)"); if($avp(mos_max_roundtrip_leg_A)!=$null) jansson_set("integer", "A.max_roundtrip_leg", "$avp(mos_max_roundtrip_leg_A)", "$vn(mos)"); } if($avp(mos_average_A)!=$null) { jansson_set("string", "A.average", "$avp(mos_average_A)", "$vn(mos)"); if($avp(mos_average_packetloss_A)!=$null) jansson_set("integer", "A.average_packetloss", "$avp(mos_average_packetloss_A)", "$vn(mos)"); if($avp(mos_average_jitter_A)!=$null) jansson_set("integer", "A.average_jitter", "$avp(mos_average_jitter_A)", "$vn(mos)"); if($avp(mos_average_roundtrip_A)!=$null) jansson_set("integer", "A.average_roundtrip", "$avp(mos_average_roundtrip_A)", "$vn(mos)"); if($avp(mos_average_roundtrip_leg_A)!=$null) jansson_set("integer", "A.average_roundtrip_leg", "$avp(mos_average_roundtrip_leg_A)", "$vn(mos)"); if($avp(mos_average_samples_A)!=$null) jansson_set("integer", "A.average_samples", "$avp(mos_average_samples_A)", "$vn(mos)"); } # MOS _B if($avp(mos_min_B)!=$null) { jansson_set("string", "B.min", "$avp(mos_min_B)", "$vn(mos)"); if($avp(mos_min_at_B)!=$null) jansson_set("string", "B.min_at", "$avp(mos_min_at_B)", "$vn(mos)"); if($avp(mos_min_packetloss_B)!=$null) jansson_set("integer", "B.min_packetloss", "$avp(mos_min_packetloss_B)", "$vn(mos)"); if($avp(mos_min_jitter_B)!=$null) jansson_set("integer", "B.min_jitter", "$avp(mos_min_jitter_B)", "$vn(mos)"); if($avp(mos_min_roundtrip_B)!=$null) jansson_set("integer", "B.min_roundtrip", "$avp(mos_min_roundtrip_B)", "$vn(mos)"); if($avp(mos_min_roundtrip_leg_B)!=$null) jansson_set("integer", "B.min_roundtrip_leg", "$avp(mos_min_roundtrip_leg_B)", "$vn(mos)"); } if($avp(mos_max_B)!=$null) { jansson_set("string", "B.max", "$avp(mos_max_B)", "$vn(mos)"); if($avp(mos_max_at_B)!=$null) jansson_set("string", "B.max_at", "$avp(mos_max_at_B)", "$vn(mos)"); if($avp(mos_max_packetloss_B)!=$null) jansson_set("integer", "B.max_packetloss", "$avp(mos_max_packetloss_B)", "$vn(mos)"); if($avp(mos_max_jitter_B)!=$null) jansson_set("integer", "B.max_jitter", "$avp(mos_max_jitter_B)", "$vn(mos)"); if($avp(mos_max_roundtrip_B)!=$null) jansson_set("integer", "B.max_roundtrip", "$avp(mos_max_roundtrip_B)", "$vn(mos)"); if($avp(mos_max_roundtrip_leg_B)!=$null) jansson_set("integer", "B.max_roundtrip_leg", "$avp(mos_max_roundtrip_leg_B)", "$vn(mos)"); } if($avp(mos_average_B)!=$null) { jansson_set("string", "B.average", "$avp(mos_average_B)", "$vn(mos)"); if($avp(mos_average_packetloss_B)!=$null) jansson_set("integer", "B.average_packetloss", "$avp(mos_average_packetloss_B)", "$vn(mos)"); if($avp(mos_average_jitter_B)!=$null) jansson_set("integer", "B.average_jitter", "$avp(mos_average_jitter_B)", "$vn(mos)"); if($avp(mos_average_roundtrip_B)!=$null) jansson_set("integer", "B.average_roundtrip", "$avp(mos_average_roundtrip_B)", "$vn(mos)"); if($avp(mos_average_roundtrip_leg_B)!=$null) jansson_set("integer", "B.average_roundtrip_leg", "$avp(mos_average_roundtrip_leg_B)", "$vn(mos)"); if($avp(mos_average_samples_B)!=$null) jansson_set("integer", "B.average_samples", "$avp(mos_average_samples_B)", "$vn(mos)"); } # Save to dialog/acc_cdrs if($vn(mos)!=$null) { jansson_set("obj", "mos", "$vn(mos)", "$dlg_var(rtpengine)"); } #!endif #!endif return; }