/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ /* rate-counter implementation */ /* * (C) 2009-2012 by Holger Hans Peter Freyther * (C) 2009-2012 by On-Waves * (C) 2017-2020 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include static const struct rate_ctr_desc mgcp_general_ctr_desc[] = { /* rx_msgs = rx_msgs_retransmitted + rx_msgs_handled + rx_msgs_unhandled + err_rx_msg_parse + err_rx_no_endpoint */ [MGCP_GENERAL_RX_MSGS_TOTAL] = { "mgcp:rx_msgs", "total number of MGCP messages received." }, [MGCP_GENERAL_RX_MSGS_RETRANSMITTED] = { "mgcp:rx_msgs_retransmitted", "number of received retransmissions." }, [MGCP_GENERAL_RX_MSGS_HANDLED] = { "mgcp:rx_msgs_handled", "number of handled MGCP messages." }, [MGCP_GENERAL_RX_MSGS_UNHANDLED] = { "mgcp:rx_msgs_unhandled", "number of unhandled MGCP messages." }, [MGCP_GENERAL_RX_FAIL_MSG_PARSE] = { "mgcp:err_rx_msg_parse", "error parsing MGCP message." }, [MGCP_GENERAL_RX_FAIL_NO_ENDPOINT] = { "mgcp:err_rx_no_endpoint", "can't find MGCP endpoint, probably we've used all allocated endpoints." }, }; static const struct rate_ctr_group_desc mgcp_general_ctr_group_desc = { .group_name_prefix = "mgcp", .group_description = "mgcp general statistics", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_ctr = ARRAY_SIZE(mgcp_general_ctr_desc), .ctr_desc = mgcp_general_ctr_desc }; static const struct rate_ctr_desc mgcp_crcx_ctr_desc[] = { [MGCP_CRCX_SUCCESS] = { "crcx:success", "CRCX command processed successfully." }, [MGCP_CRCX_FAIL_BAD_ACTION] = { "crcx:bad_action", "bad action in CRCX command." }, [MGCP_CRCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in CRCX command." }, [MGCP_CRCX_FAIL_MISSING_CALLID] = { "crcx:missing_callid", "missing CallId in CRCX command." }, [MGCP_CRCX_FAIL_INVALID_MODE] = { "crcx:invalid_mode", "invalid connection mode in CRCX command." }, [MGCP_CRCX_FAIL_LIMIT_EXCEEDED] = { "crcx:limit_exceeded", "limit of concurrent connections was reached." }, [MGCP_CRCX_FAIL_UNKNOWN_CALLID] = { "crcx:unkown_callid", "unknown CallId in CRCX command." }, [MGCP_CRCX_FAIL_ALLOC_CONN] = { "crcx:alloc_conn_fail", "connection allocation failure." }, [MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC] = { "crcx:no_remote_conn_desc", "no opposite end specified for connection." }, [MGCP_CRCX_FAIL_START_RTP] = { "crcx:start_rtp_failure", "failure to start RTP processing." }, [MGCP_CRCX_FAIL_NO_OSMUX] = { "crcx:no_osmux", "no osmux offered by peer." }, [MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS] = { "crcx:conn_opt", "connection options invalid." }, [MGCP_CRCX_FAIL_CODEC_NEGOTIATION] = { "crcx:codec_nego", "codec negotiation failure." }, [MGCP_CRCX_FAIL_BIND_PORT] = { "crcx:bind_port", "port bind failure." }, [MGCP_CRCX_FAIL_AVAIL] = { "crcx:unavailable", "endpoint unavailable." }, [MGCP_CRCX_FAIL_CLAIM] = { "crcx:claim", "endpoint can not be claimed." }, }; static const struct rate_ctr_group_desc mgcp_crcx_ctr_group_desc = { .group_name_prefix = "crcx", .group_description = "crxc statistics", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_ctr = ARRAY_SIZE(mgcp_crcx_ctr_desc), .ctr_desc = mgcp_crcx_ctr_desc }; static const struct rate_ctr_desc mgcp_mdcx_ctr_desc[] = { [MGCP_MDCX_SUCCESS] = { "mdcx:success", "MDCX command processed successfully." }, [MGCP_MDCX_FAIL_WILDCARD] = { "mdcx:wildcard", "wildcard endpoint names in MDCX commands are unsupported." }, [MGCP_MDCX_FAIL_NO_CONN] = { "mdcx:no_conn", "endpoint specified in MDCX command has no active connections." }, [MGCP_MDCX_FAIL_INVALID_CALLID] = { "mdcx:callid", "invalid CallId specified in MDCX command." }, [MGCP_MDCX_FAIL_INVALID_CONNID] = { "mdcx:connid", "invalid connection ID specified in MDCX command." }, [MGCP_MDCX_FAIL_UNHANDLED_PARAM] = { "crcx:unhandled_param", "unhandled parameter in MDCX command." }, [MGCP_MDCX_FAIL_NO_CONNID] = { "mdcx:no_connid", "no connection ID specified in MDCX command." }, [MGCP_MDCX_FAIL_CONN_NOT_FOUND] = { "mdcx:conn_not_found", "connection specified in MDCX command does not exist." }, [MGCP_MDCX_FAIL_INVALID_MODE] = { "mdcx:invalid_mode", "invalid connection mode in MDCX command." }, [MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS] = { "mdcx:conn_opt", "connection options invalid." }, [MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC] = { "mdcx:no_remote_conn_desc", "no opposite end specified for connection." }, [MGCP_MDCX_FAIL_START_RTP] = { "mdcx:start_rtp_failure", "failure to start RTP processing." }, [MGCP_MDCX_FAIL_AVAIL] = { "mdcx:unavailable", "endpoint unavailable." }, }; static const struct rate_ctr_group_desc mgcp_mdcx_ctr_group_desc = { .group_name_prefix = "mdcx", .group_description = "mdcx statistics", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_ctr = ARRAY_SIZE(mgcp_mdcx_ctr_desc), .ctr_desc = mgcp_mdcx_ctr_desc }; static const struct rate_ctr_desc mgcp_dlcx_ctr_desc[] = { [MGCP_DLCX_SUCCESS] = { "dlcx:success", "DLCX command processed successfully." }, [MGCP_DLCX_FAIL_NO_CONN] = { "dlcx:no_conn", "endpoint specified in DLCX command has no active connections." }, [MGCP_DLCX_FAIL_INVALID_CALLID] = { "dlcx:callid", "CallId specified in DLCX command mismatches endpoint's CallId ." }, [MGCP_DLCX_FAIL_INVALID_CONNID] = { "dlcx:connid", "connection ID specified in DLCX command does not exist on endpoint." }, [MGCP_DLCX_FAIL_UNHANDLED_PARAM] = { "dlcx:unhandled_param", "unhandled parameter in DLCX command." }, [MGCP_DLCX_FAIL_AVAIL] = { "dlcx:unavailable", "endpoint unavailable." }, }; static const struct rate_ctr_group_desc mgcp_dlcx_ctr_group_desc = { .group_name_prefix = "dlcx", .group_description = "dlcx statistics", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_ctr = ARRAY_SIZE(mgcp_dlcx_ctr_desc), .ctr_desc = mgcp_dlcx_ctr_desc }; static const struct rate_ctr_desc e1_rate_ctr_desc[] = { [E1_I460_TRAU_RX_FAIL_CTR] = { "e1:rx_fail", "Inbound I.460 TRAU failures." }, [E1_I460_TRAU_TX_FAIL_CTR] = { "e1:tx_fail", "Outbound I.460 TRAU failures." }, [E1_I460_TRAU_MUX_EMPTY_CTR] = { "e1:i460", "Outbound I.460 MUX queue empty." } }; static const struct rate_ctr_group_desc e1_rate_ctr_group_desc = { .group_name_prefix = "e1", .group_description = "e1 statistics", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_ctr = ARRAY_SIZE(e1_rate_ctr_desc), .ctr_desc = e1_rate_ctr_desc }; static const struct rate_ctr_group_desc all_rtp_conn_rate_ctr_group_desc = { .group_name_prefix = "all_rtp_conn", .group_description = "aggregated statistics for all rtp connections", .class_id = 1, .num_ctr = ARRAY_SIZE(all_rtp_conn_rate_ctr_desc), .ctr_desc = all_rtp_conn_rate_ctr_desc }; static const struct rate_ctr_group_desc all_osmux_conn_rate_ctr_group_desc = { .group_name_prefix = "all_osmux_conn", .group_description = "aggregated statistics for all osmux connections", .class_id = 1, .num_ctr = ARRAY_SIZE(all_osmux_conn_rate_ctr_desc), .ctr_desc = all_osmux_conn_rate_ctr_desc }; /*! allocate global rate counters * (called once at startup). * \param[in] cfg mgw configuration for which the rate counters are allocated. * \returns 0 on success, -EINVAL on failure. */ int mgcp_ratectr_global_alloc(struct mgcp_config *cfg) { struct mgcp_ratectr_global *ratectr = &cfg->ratectr; static atomic_uint general_rate_ctr_index = 0; char ctr_name[512]; if (ratectr->mgcp_general_ctr_group == NULL) { ratectr->mgcp_general_ctr_group = rate_ctr_group_alloc(cfg, &mgcp_general_ctr_group_desc, general_rate_ctr_index++); if (!ratectr->mgcp_general_ctr_group) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s:general", cfg->domain); rate_ctr_group_set_name(ratectr->mgcp_general_ctr_group, ctr_name); } return 0; } /*! free global rate counters * (called once at process shutdown). * \param[in] cfg mgw configuration for which the rate counters are allocated. */ void mgcp_ratectr_global_free(struct mgcp_config *cfg) { struct mgcp_ratectr_global *ratectr = &cfg->ratectr; if (ratectr->mgcp_general_ctr_group) { rate_ctr_group_free(ratectr->mgcp_general_ctr_group); ratectr->mgcp_general_ctr_group = NULL; } } /*! allocate trunk specific rate counters * (called once on trunk initialization). * \param[in] trunk mgw trunk for which the rate counters are allocated. * \returns 0 on success, -EINVAL on failure */ int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk) { struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr; static atomic_uint crcx_rate_ctr_index = 0; static atomic_uint mdcx_rate_ctr_index = 0; static atomic_uint dlcx_rate_ctr_index = 0; static atomic_uint all_rtp_conn_rate_ctr_index = 0; static atomic_uint all_osmux_conn_rate_ctr_index = 0; char ctr_name[256]; if (ratectr->mgcp_crcx_ctr_group == NULL) { ratectr->mgcp_crcx_ctr_group = rate_ctr_group_alloc(trunk, &mgcp_crcx_ctr_group_desc, crcx_rate_ctr_index++); if (!ratectr->mgcp_crcx_ctr_group) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s-%u:crcx", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); rate_ctr_group_set_name(ratectr->mgcp_crcx_ctr_group, ctr_name); } if (ratectr->mgcp_mdcx_ctr_group == NULL) { ratectr->mgcp_mdcx_ctr_group = rate_ctr_group_alloc(trunk, &mgcp_mdcx_ctr_group_desc, mdcx_rate_ctr_index++); if (!ratectr->mgcp_mdcx_ctr_group) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s-%u:mdcx", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); rate_ctr_group_set_name(ratectr->mgcp_mdcx_ctr_group, ctr_name); } if (ratectr->mgcp_dlcx_ctr_group == NULL) { ratectr->mgcp_dlcx_ctr_group = rate_ctr_group_alloc(trunk, &mgcp_dlcx_ctr_group_desc, dlcx_rate_ctr_index++); if (!ratectr->mgcp_dlcx_ctr_group) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s-%u:dlcx", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); rate_ctr_group_set_name(ratectr->mgcp_dlcx_ctr_group, ctr_name); } if (ratectr->all_rtp_conn_stats == NULL) { ratectr->all_rtp_conn_stats = rate_ctr_group_alloc(trunk, &all_rtp_conn_rate_ctr_group_desc, all_rtp_conn_rate_ctr_index++); if (!ratectr->all_rtp_conn_stats) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s-%u:rtp_conn", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); rate_ctr_group_set_name(ratectr->all_rtp_conn_stats, ctr_name); } if (ratectr->all_osmux_conn_stats == NULL) { ratectr->all_osmux_conn_stats = rate_ctr_group_alloc(trunk, &all_osmux_conn_rate_ctr_group_desc, all_osmux_conn_rate_ctr_index++); if (!ratectr->all_osmux_conn_stats) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s-%u:osmux_conn", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); rate_ctr_group_set_name(ratectr->all_osmux_conn_stats, ctr_name); } /* E1 specific */ if (trunk->trunk_type == MGCP_TRUNK_E1 && ratectr->e1_stats == NULL) { ratectr->e1_stats = rate_ctr_group_alloc(trunk, &e1_rate_ctr_group_desc, mdcx_rate_ctr_index++); if (!ratectr->e1_stats) return -EINVAL; snprintf(ctr_name, sizeof(ctr_name), "%s-%u:e1", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); rate_ctr_group_set_name(ratectr->e1_stats, ctr_name); } return 0; } /*! free trunk specific rate counters * (called once when trunk is freed). * \param[in] trunk mgw trunk on which the rate counters are allocated. */ void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk) { struct mgcp_ratectr_trunk *ratectr = &trunk->ratectr; if (ratectr->mgcp_crcx_ctr_group) { rate_ctr_group_free(ratectr->mgcp_crcx_ctr_group); ratectr->mgcp_crcx_ctr_group = NULL; } if (ratectr->mgcp_mdcx_ctr_group) { rate_ctr_group_free(ratectr->mgcp_mdcx_ctr_group); ratectr->mgcp_mdcx_ctr_group = NULL; } if (ratectr->mgcp_dlcx_ctr_group) { rate_ctr_group_free(ratectr->mgcp_dlcx_ctr_group); ratectr->mgcp_dlcx_ctr_group = NULL; } if (ratectr->all_rtp_conn_stats) { rate_ctr_group_free(ratectr->all_rtp_conn_stats); ratectr->all_rtp_conn_stats = NULL; } if (ratectr->all_osmux_conn_stats) { rate_ctr_group_free(ratectr->all_osmux_conn_stats); ratectr->all_osmux_conn_stats = NULL; } /* E1 specific */ if (ratectr->e1_stats) { rate_ctr_group_free(ratectr->e1_stats); ratectr->e1_stats = NULL; } } const struct osmo_stat_item_desc trunk_stat_desc[] = { [TRUNK_STAT_ENDPOINTS_TOTAL] = { "endpoints:total", "Number of endpoints that exist on the trunk", "", 60, 0 }, [TRUNK_STAT_ENDPOINTS_USED] = { "endpoints:used", "Number of endpoints in use", "", 60, 0 }, }; const struct osmo_stat_item_group_desc trunk_statg_desc = { .group_name_prefix = "trunk", .group_description = "mgw trunk", .class_id = OSMO_STATS_CLASS_GLOBAL, .num_items = ARRAY_SIZE(trunk_stat_desc), .item_desc = trunk_stat_desc, }; /*! allocate trunk specific stat items * (called once on trunk initialization). * \param[in] trunk for which the stat items are allocated. * \returns 0 on success, -EINVAL on failure. */ int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk) { struct mgcp_stat_trunk *stats = &trunk->stats; static unsigned int common_stat_index = 0; char stat_name[256]; stats->common = osmo_stat_item_group_alloc(trunk, &trunk_statg_desc, common_stat_index); if (!stats->common) return -EINVAL; snprintf(stat_name, sizeof(stat_name), "%s-%u:common", mgcp_trunk_type_strs_str(trunk->trunk_type), trunk->trunk_nr); osmo_stat_item_group_set_name(stats->common, stat_name); common_stat_index++; return 0; } /*! free trunk specific stat items * (called once when trunk is freed). * \param[in] trunk on which the stat items are allocated. */ void mgcp_stat_trunk_free(struct mgcp_trunk *trunk) { struct mgcp_stat_trunk *stats = &trunk->stats; if (stats->common) { osmo_stat_item_group_free(stats->common); stats->common = NULL; } }