分享cgrates的kamailio脚本

该文展示了Kamailio服务器的配置示例,重点在于如何利用evapi模块在SIP对话开始和结束时发送呼叫详细记录(CDR)。配置涵盖了模块加载、参数设置以及事件路由,确保与CGRateS系统的通信,用于计费和资源管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过evapi模块在对话开始和对话结束时发送cdr信息

kamailio.cfg

#!KAMAILIO

# Sample demo config for Kamailio-CGRateS communication
# tested against Kamailio 5.1

####### Defined Values #########

#!define FLT_DIALOG 4
#!define FLT_NATS 5
#!define FLB_NATB 6
#!define FLB_NATSIPPING 7

####### Global Parameters #########

debug=2
log_stderror=no

listen=udp:enp0s3:5060
listen=udp:127.0.0.1:5080
listen=udp:127.0.0.1:5060
listen=udp:enp0s3:5080

memdbg=5
memlog=5
log_facility=LOG_LOCAL0
fork=yes
children=4
tcp_connection_lifetime=3605
use_dns_cache=no
dns_try_ipv6=no
dns_retr_time=1
dns_retr_no=1
dns_servers_no=1
dns_use_search_list=no

####### Modules Section ########

mpath="/usr/lib/x86_64-linux-gnu/kamailio/modules/"

loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "nathelper.so"
loadmodule "htable.so"
loadmodule "auth.so"
loadmodule "evapi.so"
loadmodule "json.so"
loadmodule "dialog.so"
loadmodule "jsonrpcs.so"



# ----------------- setting module-specific parameters ---------------

modparam("evapi", "bind_addr", "127.0.0.1:8448")

# ----- tm params -----
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)

# ----- rr params -----
modparam("rr", "enable_full_lr", 0)
modparam("rr", "append_fromtag", 0)

# ----- registrar params -----
modparam("registrar", "method_filtering", 1)
modparam("registrar", "max_expires", 3600)

# ----- dialog params -----
modparam("dialog", "dlg_flag", FLT_DIALOG)
modparam("dialog", "send_bye", 1)
modparam("dialog", "timeout_noreset", 1)

# ----- nathelper params -----
modparam("nathelper", "natping_interval", 30)
modparam("nathelper", "ping_nated_only", 1)
modparam("nathelper", "sipping_bflag", FLB_NATSIPPING)
modparam("nathelper", "sipping_from", "sip:pinger@kamailio.org")

# params needed for NAT traversal in other modules
modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)")
modparam("usrloc", "nat_bflag", FLB_NATB)

# ----- htable params -----
modparam("htable", "htable", "cgrconn=>size=1;")

####### Routing Logic ########

include_file "kamailio-cgrates.cfg"


# Main SIP request routing logic
request_route {

	# per request initial checks
	route(REQINIT);

	# NAT detection
	route(NATDETECT);

	# CANCEL processing
	if (is_method("CANCEL")) {
		if (t_check_trans()) {
			route(RELAY);
		}
		exit;
	}

	# handle requests within SIP dialogs
	route(WITHINDLG);

	### only initial requests (no To tag)

	# handle retransmissions
	if(t_precheck_trans()) {
		t_check_trans();
		exit;
	}
	t_check_trans();

	# record routing for dialog forming requests (in case they are routed)
	# - remove preloaded route headers
	remove_hf("Route");
	if (is_method("INVITE|SUBSCRIBE"))
		record_route();

	# Not handling requests towards external domains
	if uri != myself {
		sl_send_reply("604", "Only local destinations accepted");
		exit;
	}

	### requests for my local domains

	# SIMPLE AUTH methods
	if is_method("REGISTER|SUBSCRIBE|PUBLISH") {
		route(CGRATES_SIMPLEAUTH_REQUEST);
		exit;
	}

	if ($rU==$null) {
		# request with no Username in RURI
		sl_send_reply("484","Address Incomplete");
		exit;
	}

	if !is_method("INVITE") {
		sl_send_reply("503", "Unsupported method");
                exit;
        }

	route(CGRATES_SESSIONAUTH_REQUEST);
	exit;
}


# Here will land requests after processing them with CGRateS.
route[CGRATES_SIMPLEAUTH_REPLY] {
        if $var(cgrError) != "" {
                xlog("CGR_PROFILE_ERROR: $var(cgrError)");
                sl_send_reply("503","CGR_ERROR");
                exit;
        }

	# parse the CGRateS attributes
	route(PARSE_CGRATES_ATTRIBUTES);

        # password based auth
        route(AUTH);

        if is_method("REGISTER") {
                route(REGISTRAR);
                exit;
        }

        # other methods going over location
        route(LOCATION);
        route(RELAY);
}


# Here will land requests after processing them with CGRateS. Call RELAY or other routes following this route
route[CGRATES_SESSIONAUTH_REPLY] {

	if $var(cgrError) != "" {
		xlog("CGR_AUTH_ERROR: $var(cgrError)");
		sl_send_reply("503","CGR_ERROR");
		exit;
	}

	 # parse the CGRateS attributes
        route(PARSE_CGRATES_ATTRIBUTES);

        # password based auth
        route(AUTH);

	# track the dialog so we can set it's timeout and channel variables to store in CDRs
	# cannot initialize the dialog sooner due to AUTH
	dlg_manage();
	$dlg_var(SetupTime) = $TS;
        $dlg_var(cgrOriginID) = $dlg(callid) + ";" + $dlg(from_tag);
	$dlg_var(cgrTenant) = "cgrates.org";
	$dlg_var(cgrReqType) = $avp(RequestType);
        $dlg_var(cgrAccount) = $fU;
        $dlg_var(cgrDestination) = $rU;
	$dlg_var(paypalAccount) = $avp(PaypalAccount);

	if $var(cgrMaxUsage) == 0 { // Not enough balance, do not allow the call to go through
		sl_send_reply("403","Insufficient credit");
		exit;
	} else if !dlg_set_timeout("$var(cgrMaxUsage)") {
		sl_send_reply("503","CGR_MAX_USAGE_ERROR");
		exit;
	}

	if $var(cgrRoutes) != "" { # Enforce the route variable to the first one received from CGRateS, here more for demo purposes
		$dlg_var(cgrRoutes) = $(var(cgrRoutes){s.select,0,,});
	}

	# user location service
	route(LOCATION);

	# send out
	route(RELAY);

}


# Wrapper for relaying requests
route[RELAY] {
	# enable additional event routes for forwarded requests
	# - serial forking, RTP relaying handling, a.s.o.
	if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {
		if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH");
	}
	if (is_method("INVITE|SUBSCRIBE|UPDATE")) {
		if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
	}
	if (is_method("INVITE")) {
		if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
	}

	if (!t_relay()) {
		sl_reply_error();
	}
	exit;
}

# Per SIP request initial checks
route[REQINIT] {
	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		exit;
	}

	if(is_method("OPTIONS") && uri==myself && $rU==$null) {
		sl_send_reply("200","Keepalive");
		exit;
	}

	if(!sanity_check("1511", "7")) {
		xlog("Malformed SIP message from $si:$sp\n");
		exit;
	}
}

# Handle requests within SIP dialogs
route[WITHINDLG] {
	if (!has_totag()) return;

	# sequential request withing a dialog should
	# take the path determined by record-routing
	if (loose_route()) {
		route(DLGURI);
		if ( is_method("ACK") ) {
			# ACK is forwarded statelessy
			route(NATMANAGE);
		}
		else if ( is_method("NOTIFY") ) {
			# Add Record-Route for in-dialog NOTIFY as per RFC 6665.
			record_route();
		}
		route(RELAY);
		exit;
	}
	if ( is_method("ACK") ) {
		if ( t_check_trans() ) {
			# no loose-route, but stateful ACK;
			# must be an ACK after a 487
			# or e.g. 404 from upstream server
			route(RELAY);
			exit;
		} else {
			# ACK without matching transaction ... ignore and discard
			exit;
		}
	}
	sl_send_reply("404","Not here");
	exit;
}

# Handle SIP registrations
route[REGISTRAR] {
	if(isflagset(FLT_NATS)) {
		setbflag(FLB_NATB);
	}
	if (!save("location"))
		sl_reply_error();
	exit;
}

# User location service
route[LOCATION] {
	$avp(oexten) = $rU;
	if (!lookup("location")) {
		$var(rc) = $rc;
		t_newtran();
		switch ($var(rc)) {
			case -1:
			case -3:
				send_reply("404", "Not Found");
				exit;
			case -2:
				send_reply("405", "Method Not Allowed");
				exit;
		}
	}
}


# user uthentication
route[AUTH] {
	if (is_method("REGISTER")) {
		if ( strempty($au) || !pv_www_authenticate("$td", "$avp(Password)", "0") ) {
			www_challenge("$td", "0");
			exit;
		}
	} else { # All other methods here
		if ( strempty($au) || !pv_proxy_authenticate("$td", "$avp(Password)", "0") ) {
			proxy_challenge("$td", "0");
			exit;
		}
	}
	consume_credentials();
	return;
}


# Caller NAT detection
route[NATDETECT] {
	force_rport();
	if (nat_uac_test("19")) {
		if (is_method("REGISTER")) {
			fix_nated_register();
		} else {
			if(is_first_hop())
				set_contact_alias();
		}
		setflag(FLT_NATS);
	}
	return;
}

# RTPProxy control and singaling updates for NAT traversal
route[NATMANAGE] {
	if (is_request()) {
		if(has_totag()) {
			if(check_route_param("nat=yes")) {
				setbflag(FLB_NATB);
			}
		}
	}
	if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB)))
		return;

	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();
		}
	}
	return;
}

# URI update for dialog requests
route[DLGURI] {
	if(!isdsturiset()) {
		handle_ruri_alias();
	}
	return;
}


# Manage outgoing branches
branch_route[MANAGE_BRANCH] {
	route(NATMANAGE);
}

# Manage incoming replies
onreply_route[MANAGE_REPLY] {
	if(status=~"[12][0-9][0-9]")
		route(NATMANAGE);
}

# Manage failure routing cases
failure_route[MANAGE_FAILURE] {
	route(NATMANAGE);

	if (t_is_canceled()) {
		exit;
	}

	if (t_check_status("3[0-9][0-9]")) {
		t_reply("404","Not found");
		exit;
	}
}

kamailio-cgrates.cfg

# Kamailio-CGRateS related route blocks


# Called on new connection over evapi, should normally be the case of CGRateS engine
event_route[evapi:connection-new] {
    $sht(cgrconn=>cgr) = $evapi(srcaddr) + ":" + $evapi(srcport); # Detect presence of at least one connection
}


# Called when the connection with CGRateS closes
event_route[evapi:connection-closed] {
	$var(connClosed) = $evapi(srcaddr) + ":" + $evapi(srcport);
	if $sht(cgrconn=>cgr) == $var(connClosed) {
		$sht(cgrconn=>cgr) = $null;
	}
}


# Message received from CGRateS, dispatch it to own route
event_route[evapi:message-received] {
	json_get_field("$evapi(msg)", "Event", "$var(Event)");
	route($(var(Event){s.rm,"})); # String characters are kept by json_get_field, remove them here
}


# Called by Kamailio on new dialog
event_route[dialog:start] {
	route(CGR_CALL_START);
}


# Called by Kamailio on dialog end
event_route[dialog:end] {
	route(CGR_CALL_END);
}

# Parse the CGRateS attributes from encoded variable into pseudovariables
route[PARSE_CGRATES_ATTRIBUTES] {
        # convert encoded attributes into individual Kamailio pseudovariables
        $var(idx) = 0;
        while !strempty($(var(cgrAttributes){s.select,$var(idx),,})) {
                $avp($(var(cgrAttributes){s.select,$var(idx),,}{s.select,0,:}))
                        = $(var(cgrAttributes){s.select,$var(idx),,}{s.select,1,:});
                $var(idx) = $var(idx) + 1;
        }
}


# CGRateS request for session disconnect
route[CGR_SESSION_DISCONNECT] {
        json_get_field("$evapi(msg)", "HashEntry", "$var(HashEntry)");
        json_get_field("$evapi(msg)", "HashId", "$var(HashId)");
        json_get_field("$evapi(msg)", "Reason", "$var(Reason)");
        jsonrpc_exec('{"jsonrpc":"2.0","id":1, "method":"dlg.end_dlg","params":[$(var(HashEntry){s.rm,"}),$(var(HashId){s.rm,"})]}');
}

route[CGR_DLG_LIST] {
 if $sht(cgrconn=>cgr) == $null {
                sl_send_reply("503","Charging controller unreachable");
                exit;
        }
        jsonrpc_exec('{"jsonrpc":"2.0","id":1, "method":"dlg.list","params":[]}');
		evapi_relay("{\"event\":\"CGR_DLG_LIST_REPLY\",
			\"jsonrpl_body\":$jsonrpl(body)}");
}


# Route to mainly query account password from CGRateS
route[CGRATES_SIMPLEAUTH_REQUEST] {
	 if $sht(cgrconn=>cgr) == $null {
                sl_send_reply("503","Charging controller unreachable");
                exit;
        }
	evapi_async_relay("{\"event\":\"CGR_AUTH_REQUEST\",
		\"tr_index\":\"$T(id_index)\",
		\"tr_label\":\"$T(id_label)\",
		\"cgr_flags\":\"*attributes\",
		\"cgr_context\":\"simpleauth\",
		\"reply_route\":\"CGR_SIMPLEAUTH_REPLY\",
		\"Account\":\"$fU\"}");
}


# Route receiving simpleauth replies, sanitizes them and dispatch back into Kamailio inside CGRATES_SIMPLEAUTH_REPLY
route[CGR_SIMPLEAUTH_REPLY] {

	json_get_field("$evapi(msg)", "TransactionIndex", "$var(TransactionIndex)");
        $var(TransactionIndex) = $(var(TransactionIndex){s.rm,"});
        $var(id_index) = $(var(TransactionIndex){s.int});

        json_get_field("$evapi(msg)", "TransactionLabel", "$var(TransactionLabel)");
        $var(TransactionLabel) = $(var(TransactionLabel){s.rm,"});
        $var(id_label) = $(var(TransactionLabel){s.int});

        json_get_field("$evapi(msg)", "Attributes", "$var(cgrAttributes)");
	$var(cgrAttributes) = $(var(cgrAttributes){s.rm,"});

	json_get_field("$evapi(msg)", "Error", "$var(cgrError)");
        $var(cgrError) = $(var(cgrError){s.rm,"});

        t_continue("$var(id_index)", "$var(id_label)", "CGRATES_SIMPLEAUTH_REPLY"); # Unpark the transaction

}


# Request session auth information from CGRateS
route[CGRATES_SESSIONAUTH_REQUEST] {
	# Auth INVITEs with CGRateS
	if $sht(cgrconn=>cgr) == $null {
		sl_send_reply("503","Charging controller unreachable");
		exit;
	}
	evapi_async_relay("{\"event\":\"CGR_AUTH_REQUEST\",
		\"tr_index\":\"$T(id_index)\",
		\"tr_label\":\"$T(id_label)\",
		\"cgr_flags\":\"*attributes;*accounts;*routes;*resources;*thresholds\",
		\"reply_route\":\"CGR_SESSIONAUTH_REPLY\",
		\"Account\":\"$fU\",
		\"Destination\":\"$rU\",
		\"SetupTime\":\"$TS\"}");
}


# Process SESSIONAUTH_reply from CGRateS
route[CGR_SESSIONAUTH_REPLY] {
	json_get_field("$evapi(msg)", "TransactionIndex", "$var(TransactionIndex)");
	$var(TransactionIndex) = $(var(TransactionIndex){s.rm,"});
	$var(id_index) = $(var(TransactionIndex){s.int});

	json_get_field("$evapi(msg)", "TransactionLabel", "$var(TransactionLabel)");
	$var(TransactionLabel) = $(var(TransactionLabel){s.rm,"});
	$var(id_label) = $(var(TransactionLabel){s.int});

	json_get_field("$evapi(msg)", "Attributes", "$var(cgrAttributes)");
	$var(cgrAttributes) = $(var(cgrAttributes){s.rm,"});
	
	json_get_field("$evapi(msg)", "ResourceAllocation", "$var(cgrResourceAllocation)");
	$var($var(cgrResourceAllocation)) = $(var(cgrResourceAllocation){s.rm,"});

	json_get_field("$evapi(msg)", "MaxUsage", "$var(MaxUsage)");
	$var(cgrMaxUsage) = $(var(MaxUsage){s.int});

	json_get_field("$evapi(msg)", "Routes", "$var(cgrRoutes)");
	$var($var(cgrRoutes)) = $(var(cgrRoutes){s.rm,"});

	json_get_field("$evapi(msg)", "Error", "$var(cgrError)");
	$var(cgrError) = $(var(cgrError){s.rm,"});

	t_continue("$var(id_index)", "$var(id_label)", "CGRATES_SESSIONAUTH_REPLY"); # Unpark the transaction 
}


# Inform CGRateS about CALL_START (start prepaid sessions loops)
route[CGR_CALL_START] {
	if $sht(cgrconn=>cgr) == $null {
		xlog("Charging controller unreachable");
		exit;
	}
	 evapi_relay("{\"event\":\"CGR_CALL_START\",
		\"h_entry\":\"$dlg(h_entry)\",
		\"h_id\":\"$dlg(h_id)\",
		\"cgr_flags\":\"*attributes;*accounts;*resources;*thresholds\",
		\"OriginID\":\"$dlg_var(cgrOriginID)\",
		\"RequestType\":\"$dlg_var(cgrReqType)\",
		\"Tenant\":\"$dlg_var(cgrTenant)\",
		\"Account\":\"$dlg_var(cgrAccount)\",
		\"Destination\":\"$dlg_var(cgrDestination)\",
		\"SetupTime\":\"$dlg_var(SetupTime)\",
		\"AnswerTime\":\"$TS\"}");
}


# Inform CGRateS about CALL_END (stop debit loops, perform accounting if desired in this way)
route[CGR_CALL_END] {
	if $sht(cgrconn=>cgr) == $null {
		xlog("Charging controller unreachable");
		exit;
	}
	$var(callDur) = $TS - $dlg(start_ts);
	 evapi_relay("{\"event\":\"CGR_CALL_END\",
		\"cgr_flags\":\"*accounts;*resources\",
		\"OriginID\":\"$dlg_var(cgrOriginID)\",
		\"RequestType\":\"$dlg_var(cgrReqType)\",
		\"Tenant\":\"$dlg_var(cgrTenant)\",
		\"Account\":\"$dlg_var(cgrAccount)\", 
		\"Destination\":\"$dlg_var(cgrDestination)\",
		\"AnswerTime\":\"$dlg(start_ts)\",
		\"PaypalAccount\":\"$dlg_var(paypalAccount)\",
		\"SetupTime\":\"$dlg_var(SetupTime)\",
		\"Usage\":\"$var(callDur)\"}");
}

cgrates/data/tutorials/kamevapi at master · cgrates/cgrates · GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值