diff options
49 files changed, 3314 insertions, 1056 deletions
diff --git a/Makefile.am b/Makefile.am index 7b9ef95..8fb6e6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,7 +20,6 @@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = \ libosmo-legacy-mgcp.pc \ libosmo-mgcp-client.pc \ - libosmo-mgcp.pc \ $(NULL) BUILT_SOURCES = $(top_srcdir)/.version diff --git a/TODO-RELEASE b/TODO-RELEASE index d198b97..c5a3b36 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -23,4 +23,4 @@ # If any interfaces have been added since the last public release, a++; # If any interfaces have been removed or changed since the last public release, a=0. # -#library what description / commit summary line +#library what description / commit summary line diff --git a/configure.ac b/configure.ac index dea41b1..0ded288 100644 --- a/configure.ac +++ b/configure.ac @@ -39,9 +39,40 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) AC_SUBST(LIBRARY_DL) -PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.10.0) -PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0) -PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.1.0) +PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0) +PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.11.0) +PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0) +PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.2.0) + +AC_ARG_ENABLE(sanitize, + [AS_HELP_STRING( + [--enable-sanitize], + [Compile with address sanitizer enabled], + )], + [sanitize=$enableval], [sanitize="no"]) +if test x"$sanitize" = x"yes" +then + CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" + CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" +fi + +AC_ARG_ENABLE(werror, + [AS_HELP_STRING( + [--enable-werror], + [Turn all compiler warnings into errors, with exceptions: + a) deprecation (allow upstream to mark deprecation without breaking builds); + b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) + ] + )], + [werror=$enableval], [werror="no"]) +if test x"$werror" = x"yes" +then + WERROR_FLAGS="-Werror" + WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" + WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" + CFLAGS="$CFLAGS $WERROR_FLAGS" + CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" +fi # Enable/disable transcoding within osmo-bsc_mgcp? AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], @@ -113,13 +144,15 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) AC_MSG_RESULT([$enable_ext_tests]) AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") +AC_MSG_RESULT([CFLAGS="$CFLAGS"]) +AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) + dnl Generate the output AM_CONFIG_HEADER(bscconfig.h) AC_OUTPUT( libosmo-legacy-mgcp.pc libosmo-mgcp-client.pc - libosmo-mgcp.pc include/Makefile include/osmocom/Makefile include/osmocom/legacy_mgcp/Makefile diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh index 44152dc..222bd8f 100755 --- a/contrib/jenkins.sh +++ b/contrib/jenkins.sh @@ -37,7 +37,7 @@ set -x cd "$base" autoreconf --install --force -./configure $MGCP --enable-vty-tests --enable-external-tests +./configure $MGCP --enable-vty-tests --enable-external-tests --enable-werror $MAKE $PARALLEL_MAKE LD_LIBRARY_PATH="$inst/lib" $MAKE check \ || cat-testlogs.sh diff --git a/contrib/systemd/osmo-mgw.service b/contrib/systemd/osmo-mgw.service new file mode 100644 index 0000000..f75277e --- /dev/null +++ b/contrib/systemd/osmo-mgw.service @@ -0,0 +1,11 @@ +[Unit] +Description=Osmocom Media Gateway (MGW) + +[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg +RestartSec=2 + +[Install] +WantedBy=multi-user.target diff --git a/doc/examples/osmo-mgw/osmo-mgw.cfg b/doc/examples/osmo-mgw/osmo-mgw.cfg index d8ea3a5..3057369 100644 --- a/doc/examples/osmo-mgw/osmo-mgw.cfg +++ b/doc/examples/osmo-mgw/osmo-mgw.cfg @@ -2,12 +2,17 @@ ! MGCP configuration example ! mgcp - !local ip 10.23.24.2 - !bts ip 10.24.24.1 - !bind ip 10.23.24.1 - bind port 2427 - rtp force-ptime 20 - sdp audio payload number 98 - sdp audio payload name AMR/8000 - number endpoints 31 - no rtcp-omit + bind ip 127.0.0.1 + rtp port-range 4002 16000 + rtp bind-ip 10.9.1.122 + rtp ip-probing + rtp ip-tos 184 + bind port 2427 + sdp audio payload number 98 + sdp audio payload name GSM + number endpoints 31 + loop 0 + force-realloc 1 + rtcp-omit + rtp-patch ssrc + rtp-patch timestamp diff --git a/include/Makefile.am b/include/Makefile.am index b52e5ea..e8fc211 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -7,6 +7,7 @@ nobase_include_HEADERS = \ osmocom/legacy_mgcp/mgcp_internal.h \ osmocom/legacy_mgcp/osmux.h \ osmocom/mgcp_client/mgcp_client.h \ + osmocom/mgcp_client/mgcp_client_fsm.h \ osmocom/mgcp_client/mgcp_common.h \ osmocom/mgcp/mgcp.h \ osmocom/mgcp/mgcp_common.h \ diff --git a/include/osmocom/legacy_mgcp/vty.h b/include/osmocom/legacy_mgcp/vty.h index c277c0a..bf6b7a8 100644 --- a/include/osmocom/legacy_mgcp/vty.h +++ b/include/osmocom/legacy_mgcp/vty.h @@ -1,31 +1,8 @@ -#ifndef OPENBSC_VTY_H -#define OPENBSC_VTY_H +#pragma once -#include <osmocom/vty/vty.h> -#include <osmocom/vty/buffer.h> #include <osmocom/vty/command.h> -struct gsm_network; -struct vty; - -void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); - -struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); - -extern struct cmd_element cfg_description_cmd; -extern struct cmd_element cfg_no_description_cmd; - enum mgcp_vty_node { MGCP_NODE = _LAST_OSMOVTY_NODE + 1, TRUNK_NODE, }; - -struct log_info; -int bsc_vty_init(struct gsm_network *network); -int bsc_vty_init_extra(void); - -void msc_vty_init(struct gsm_network *msc_network); - -struct gsm_network *gsmnet_from_vty(struct vty *vty); - -#endif diff --git a/include/osmocom/mgcp/Makefile.am b/include/osmocom/mgcp/Makefile.am index d706807..7e297e4 100644 --- a/include/osmocom/mgcp/Makefile.am +++ b/include/osmocom/mgcp/Makefile.am @@ -3,7 +3,7 @@ noinst_HEADERS = \ mgcp_msg.h \ mgcp_conn.h \ mgcp_stat.h \ - mgcp_ep.h \ + mgcp_endp.h \ mgcp_sdp.h \ debug.h \ $(NULL) diff --git a/include/osmocom/mgcp/mgcp.h b/include/osmocom/mgcp/mgcp.h index 42a91d8..bdc8f87 100644 --- a/include/osmocom/mgcp/mgcp.h +++ b/include/osmocom/mgcp/mgcp.h @@ -105,12 +105,12 @@ struct mgcp_port_range { }; /* There are up to three modes in which the keep-alive dummy packet can be - * sent. The beviour is controlled viw the keepalive_interval member of the + * sent. The behaviour is controlled via the keepalive_interval member of the * trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy- * packet is sent at all and the timer that sends regular dummy packets * is no longer scheduled. If the keepalive_interval is set to -1, only * one dummy packet is sent when an CRCX or an MDCX is performed. No timer - * is scheduled. For all vales greater 0, the a timer is scheduled and the + * is scheduled. For all vales greater 0, the timer is scheduled and the * value is used as interval. See also mgcp_keepalive_timer_cb(), * handle_modify_con(), and handle_create_con() */ #define MGCP_KEEPALIVE_ONCE (-1) @@ -151,6 +151,7 @@ struct mgcp_trunk_config { int rtp_accept_all; unsigned int number_endpoints; + int vty_number_endpoints; struct mgcp_endpoint *endpoints; }; @@ -211,6 +212,8 @@ struct mgcp_config { * message. */ uint16_t osmux_dummy; + /* domain name of the media gateway */ + char domain[255+1]; }; /* config management */ @@ -219,7 +222,6 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, enum mgcp_role role); int mgcp_vty_init(void); int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); -void mgcp_release_endp(struct mgcp_endpoint *endp); void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); /* diff --git a/include/osmocom/mgcp/mgcp_common.h b/include/osmocom/mgcp/mgcp_common.h index 0eb1388..d23339f 100644 --- a/include/osmocom/mgcp/mgcp_common.h +++ b/include/osmocom/mgcp/mgcp_common.h @@ -68,4 +68,18 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg) return 0; } +/* Maximum length of the comment field */ +#define MGCP_COMMENT_MAXLEN 256 + +/* String length of Connection Identifiers + * (see also RFC3435 2.1.3.2 Names of Connections) */ +#define MGCP_CONN_ID_LENGTH 32+1 + +/* String length of Endpoint Identifiers. +/ (see also RFC3435 section 3.2.1.3) */ +#define MGCP_ENDPOINT_MAXLEN (255*2+1+1) + +/* A prefix to denote the virtual trunk (RTP on both ends) */ +#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/" + #endif diff --git a/include/osmocom/mgcp/mgcp_conn.h b/include/osmocom/mgcp/mgcp_conn.h index e0ae021..e2a423f 100644 --- a/include/osmocom/mgcp/mgcp_conn.h +++ b/include/osmocom/mgcp/mgcp_conn.h @@ -28,12 +28,11 @@ #include <inttypes.h> struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, - uint32_t id, enum mgcp_conn_type type, - char *name); -struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id); + enum mgcp_conn_type type, char *name); +struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id); struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, - uint32_t id); -void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id); + const char *id); +void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id); void mgcp_conn_free_oldest(struct mgcp_endpoint *endp); void mgcp_conn_free_all(struct mgcp_endpoint *endp); char *mgcp_conn_dump(struct mgcp_conn *conn); diff --git a/include/osmocom/mgcp/mgcp_endp.h b/include/osmocom/mgcp/mgcp_endp.h new file mode 100644 index 0000000..a486dcd --- /dev/null +++ b/include/osmocom/mgcp/mgcp_endp.h @@ -0,0 +1,99 @@ +/* Endpoint types */ + +/* + * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Philipp Maier + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#pragma once + +struct sockaddr_in; +struct mgcp_conn; +struct mgcp_endpoint; + +/* Callback type for RTP dispatcher functions + (e.g mgcp_dispatch_rtp_bridge_cb, see below) */ +typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr, + char *buf, unsigned int buf_size, + struct mgcp_conn *conn); + +/* Callback type for endpoint specific cleanup actions. This function + * is automatically executed when a connection is freed (see mgcp_conn_free() + * in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint + * specific things to take care of once a connection has been removed. */ +typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp, + struct mgcp_conn *conn); + +/*! MGCP endpoint properties */ +struct mgcp_endpoint_type { + /*!< maximum number of connections */ + int max_conns; + + /*!< callback that defines how to dispatch incoming RTP data */ + mgcp_dispatch_rtp_cb dispatch_rtp_cb; + + /*!< callback that implements endpoint specific cleanup actions */ + mgcp_cleanup_cp cleanup_cb; +}; + +/*! MGCP endpoint typeset */ +struct mgcp_endpoint_typeset { + struct mgcp_endpoint_type rtp; +}; + +/*! static MGCP endpoint typeset (pre-initalized, read-only) */ +extern const struct mgcp_endpoint_typeset ep_typeset; + +/*! MGCP endpoint model */ +struct mgcp_endpoint { + + /*!< Call identifier string (as supplied by the call agant) */ + char *callid; + + /*!< Local connection options (see mgcp_intermal.h) */ + struct mgcp_lco local_options; + + /*!< List with connections active on this endpoint */ + struct llist_head conns; + + /*!< Backpointer to the MGW configuration */ + struct mgcp_config *cfg; + + /*!< Backpointer to the Trunk specific configuration */ + struct mgcp_trunk_config *tcfg; + + /*!< Endpoint properties (see above) */ + const struct mgcp_endpoint_type *type; + + /*!< Last MGCP transmission (in case re-transmission is required) */ + char *last_trans; + + /*!< Last MGCP response (in case re-transmission is required) */ + char *last_response; + + /*!< Memorize if this endpoint was choosen by the MGW (wildcarded, true) + * or if the user has choosen the particular endpoint explicitly. */ + bool wildcarded_req; +}; + +/*! Extract endpoint number for a given endpoint */ +#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) + +void mgcp_endp_release(struct mgcp_endpoint *endp); + diff --git a/include/osmocom/mgcp/mgcp_ep.h b/include/osmocom/mgcp/mgcp_ep.h deleted file mode 100644 index e477a47..0000000 --- a/include/osmocom/mgcp/mgcp_ep.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Endpoint types */ - -/* - * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * All Rights Reserved - * - * Author: Philipp Maier - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#pragma once - -struct sockaddr_in; -struct mgcp_conn; - -/* Callback type for RTP dispatcher functions - (e.g mgcp_dispatch_rtp_bridge_cb, see below) */ -typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in * addr, - char *buf, unsigned int buf_size, - struct mgcp_conn * conn); - -/*! MGCP endpoint properties */ -struct mgcp_endpoint_type { - /*!< maximum number of connections */ - int max_conns; - - /*!< callback that defines how to dispatch incoming RTP data */ - mgcp_dispatch_rtp_cb dispatch_rtp_cb; -}; - -/*! MGCP endpoint typeset */ -struct mgcp_endpoint_typeset { - struct mgcp_endpoint_type rtp; -}; - -/*! static MGCP endpoint typeset (pre-initalized, read-only) */ -extern const struct mgcp_endpoint_typeset ep_typeset; diff --git a/include/osmocom/mgcp/mgcp_internal.h b/include/osmocom/mgcp/mgcp_internal.h index b9c1731..0da2c56 100644 --- a/include/osmocom/mgcp/mgcp_internal.h +++ b/include/osmocom/mgcp/mgcp_internal.h @@ -27,11 +27,14 @@ #include <osmocom/core/select.h> #include <osmocom/mgcp/mgcp.h> #include <osmocom/core/linuxlist.h> +#include <osmocom/core/counter.h> #define CI_UNUSED 0 -#define CONN_ID_BTS 0 -#define CONN_ID_NET 1 +/* FIXME: This this is only needed to compile the currently + * broken OSMUX support. Remove when fixed */ +#define CONN_ID_BTS "0" +#define CONN_ID_NET "1" enum mgcp_trunk_type { MGCP_TRUNK_VIRTUAL, @@ -48,27 +51,37 @@ struct mgcp_rtp_stream_state { }; struct mgcp_rtp_state { + /* has this state structure been initialized? */ int initialized; - int patch_ssrc; - uint32_t orig_ssrc; - - int seq_offset; - - int32_t timestamp_offset; + struct { + /* are we patching the SSRC value? */ + int patch_ssrc; + /* original SSRC (to which we shall patch any different SSRC) */ + uint32_t orig_ssrc; + /* offset to apply on the sequence number */ + int seq_offset; + /* offset to apply on the timestamp number */ + int32_t timestamp_offset; + } patch; + + /* duration of a packet (FIXME: in which unit?) */ uint32_t packet_duration; struct mgcp_rtp_stream_state in_stream; struct mgcp_rtp_stream_state out_stream; /* jitter and packet loss calculation */ - int stats_initialized; - uint16_t stats_base_seq; - uint16_t stats_max_seq; - uint32_t stats_ssrc; - uint32_t stats_jitter; - int32_t stats_transit; - int stats_cycles; + struct { + int initialized; + uint16_t base_seq; + uint16_t max_seq; + uint32_t ssrc; + uint32_t jitter; + int32_t transit; + int cycles; + } stats; + bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ }; @@ -83,13 +96,18 @@ struct mgcp_rtp_codec { char *subtype_name; }; +/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */ struct mgcp_rtp_end { /* statistics */ - unsigned int packets_rx; - unsigned int octets_rx; - unsigned int packets_tx; - unsigned int octets_tx; - unsigned int dropped_packets; + struct { + unsigned int packets_rx; + unsigned int octets_rx; + unsigned int packets_tx; + unsigned int octets_tx; + unsigned int dropped_packets; + } stats; + + /* local IP address of the RTP socket */ struct in_addr addr; /* in network byte order */ @@ -103,23 +121,30 @@ struct mgcp_rtp_end { int frames_per_packet; uint32_t packet_duration_ms; char *fmtp_extra; + /* are we transmitting packets (1) or dropping (0) outbound packets */ int output_enabled; + /* FIXME: This parameter can be set + printed, but is nowhere used! */ int force_output_ptime; /* RTP patching */ int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ + /* should we perform align_rtp_timestamp_offset() (1) or not (0) */ int force_aligned_timing; + /* FIXME: not used anymore, used to be [external] transcoding related */ void *rtp_process_data; /* Each end has a separate socket for RTP and RTCP */ struct osmo_fd rtp; struct osmo_fd rtcp; + /* local UDP port number of the RTP socket; RTCP is +1 */ int local_port; }; struct mgcp_rtp_tap { + /* is this tap active (1) or not (0) */ int enabled; + /* IP/port to which we're forwarding the tapped data */ struct sockaddr_in forward; }; @@ -155,7 +180,7 @@ struct mgcp_conn_rtp { /* Sequence bits */ struct mgcp_rtp_state state; - /* taps for the rtp connection */ + /* taps for the rtp connection; one per direction */ struct mgcp_rtp_tap tap_in; struct mgcp_rtp_tap tap_out; @@ -202,8 +227,8 @@ struct mgcp_conn { /*!< copy of the mode to restore the original setting (VTY) */ enum mgcp_connection_mode mode_orig; - /*!< connection id to identify the conntion */ - uint32_t id; + /*!< connection id to identify the connection */ + char id[MGCP_CONN_ID_LENGTH]; /*!< human readable name (vty, logging) */ char name[256]; @@ -221,25 +246,9 @@ struct mgcp_conn { struct mgcp_endpoint_type; -struct mgcp_endpoint { - char *callid; - struct mgcp_lco local_options; - struct llist_head conns; - - /* backpointer */ - struct mgcp_config *cfg; - struct mgcp_trunk_config *tcfg; - - const struct mgcp_endpoint_type *type; - - /* fields for re-transmission */ - char *last_trans; - char *last_response; -}; -#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) /** * Internal structure while parsing a request @@ -249,7 +258,6 @@ struct mgcp_parse_data { struct mgcp_endpoint *endp; char *trans; char *save; - int found; }; int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, @@ -258,6 +266,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, unsigned int buf_size, struct mgcp_conn *conn); +void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn); int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, struct mgcp_conn_rtp *conn); void mgcp_free_rtp_port(struct mgcp_rtp_end *end); diff --git a/include/osmocom/mgcp/mgcp_msg.h b/include/osmocom/mgcp/mgcp_msg.h index b7d52bb..7732865 100644 --- a/include/osmocom/mgcp/mgcp_msg.h +++ b/include/osmocom/mgcp/mgcp_msg.h @@ -43,7 +43,7 @@ int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line); int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid); -int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci); +int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id); char *mgcp_strline(char *str, char **saveptr); @@ -54,5 +54,3 @@ char *mgcp_strline(char *str, char **saveptr); #define for_each_non_empty_line(line, save)\ for (line = strtok_r(NULL, "\r\n", &save); line;\ line = strtok_r(NULL, "\r\n", &save)) - -int mgcp_parse_ci(uint32_t *conn_id, const char *ci); diff --git a/include/osmocom/mgcp/vty.h b/include/osmocom/mgcp/vty.h index c277c0a..bf6b7a8 100644 --- a/include/osmocom/mgcp/vty.h +++ b/include/osmocom/mgcp/vty.h @@ -1,31 +1,8 @@ -#ifndef OPENBSC_VTY_H -#define OPENBSC_VTY_H +#pragma once -#include <osmocom/vty/vty.h> -#include <osmocom/vty/buffer.h> #include <osmocom/vty/command.h> -struct gsm_network; -struct vty; - -void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); - -struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); - -extern struct cmd_element cfg_description_cmd; -extern struct cmd_element cfg_no_description_cmd; - enum mgcp_vty_node { MGCP_NODE = _LAST_OSMOVTY_NODE + 1, TRUNK_NODE, }; - -struct log_info; -int bsc_vty_init(struct gsm_network *network); -int bsc_vty_init_extra(void); - -void msc_vty_init(struct gsm_network *msc_network); - -struct gsm_network *gsmnet_from_vty(struct vty *vty); - -#endif diff --git a/include/osmocom/mgcp_client/mgcp_client.h b/include/osmocom/mgcp_client/mgcp_client.h index e91b190..73f3bba 100644 --- a/include/osmocom/mgcp_client/mgcp_client.h +++ b/include/osmocom/mgcp_client/mgcp_client.h @@ -27,9 +27,11 @@ struct mgcp_client_conf { typedef unsigned int mgcp_trans_id_t; struct mgcp_response_head { - int response_code; - mgcp_trans_id_t trans_id; - const char *comment; + int response_code; + mgcp_trans_id_t trans_id; + char comment[MGCP_COMMENT_MAXLEN]; + char conn_id[MGCP_CONN_ID_LENGTH]; + char endpoint[MGCP_ENDPOINT_MAXLEN]; }; struct mgcp_response { @@ -54,18 +56,15 @@ enum mgcp_verb { #define MGCP_MSG_PRESENCE_AUDIO_PORT 0x0010 #define MGCP_MSG_PRESENCE_CONN_MODE 0x0020 -/* See also RFC3435 section 3.2.1.3 */ -#define MGCP_ENDPOINT_MAXLEN (255*2+1+1) - struct mgcp_msg { enum mgcp_verb verb; /* See MGCP_MSG_PRESENCE_* constants */ uint32_t presence; char endpoint[MGCP_ENDPOINT_MAXLEN]; unsigned int call_id; - uint32_t conn_id; - uint16_t audio_port; - char *audio_ip; + char *conn_id; + uint16_t audio_port; + char *audio_ip; enum mgcp_connection_mode conn_mode; }; @@ -92,6 +91,7 @@ int mgcp_response_parse_params(struct mgcp_response *r); int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg, mgcp_response_cb_t response_cb, void *priv); +int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id); enum mgcp_connection_mode; @@ -110,6 +110,7 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint, OSMO_DEPRECATED("Use mgcp_msg_gen() instead"); struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg); +mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg); extern const struct value_string mgcp_client_connection_mode_strs[]; static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode) diff --git a/include/osmocom/mgcp_client/mgcp_client_fsm.h b/include/osmocom/mgcp_client/mgcp_client_fsm.h new file mode 100644 index 0000000..7d06178 --- /dev/null +++ b/include/osmocom/mgcp_client/mgcp_client_fsm.h @@ -0,0 +1,33 @@ +#pragma once + +#include <osmocom/mgcp_client/mgcp_common.h> +#include <osmocom/mgcp_client/mgcp_client.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> + +/*! This struct organizes the connection infromation one connection side + * (either remote or local). It is used to pass parameters (local) to the FSM + * and get responses (remote) from the FSM as pointer attached to the FSM + * event. + * + * When modifiying a connection, the endpoint and call_id members may be left + * unpopulated. The call_id field is ignored in this case. If an endpoint + * identifier is supplied it is checked against the internal state to make + * sure it is correct. */ +struct mgcp_conn_peer { + /*!< RTP connection IP-Address (optional, string e.g. "127.0.0.1") */ + char addr[INET_ADDRSTRLEN]; + + /*!< RTP connection IP-Port (optional) */ + uint16_t port; + + /*!< RTP endpoint */ + char endpoint[MGCP_ENDPOINT_MAXLEN]; + + /*!< CALL ID (unique per connection) */ + unsigned int call_id; +}; + +struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt, + uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); +int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); +void mgcp_conn_delete(struct osmo_fsm_inst *fi); diff --git a/libosmo-mgcp.pc.in b/libosmo-mgcp.pc.in deleted file mode 100644 index 63b794a..0000000 --- a/libosmo-mgcp.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Osmocom Media Gateway Control Protocol library -Description: C Utility Library -Version: @VERSION@ -Libs: -L${libdir} -losmo-mgcp -Cflags: -I${includedir}/ diff --git a/src/libosmo-legacy-mgcp/Makefile.am b/src/libosmo-legacy-mgcp/Makefile.am index 9160586..4ee4e6a 100644 --- a/src/libosmo-legacy-mgcp/Makefile.am +++ b/src/libosmo-legacy-mgcp/Makefile.am @@ -24,7 +24,7 @@ AM_LDFLAGS = \ # This is not at all related to the release version, but a range of supported # API versions. Read TODO_RELEASE in the source tree's root! -LEGACY_MGCP_LIBVERSION=0:0:0 +LEGACY_MGCP_LIBVERSION=0:1:0 lib_LTLIBRARIES = \ libosmo-legacy-mgcp.la \ diff --git a/src/libosmo-legacy-mgcp/mgcp_protocol.c b/src/libosmo-legacy-mgcp/mgcp_protocol.c index bc1ec0d..4e82233 100644 --- a/src/libosmo-legacy-mgcp/mgcp_protocol.c +++ b/src/libosmo-legacy-mgcp/mgcp_protocol.c @@ -372,8 +372,8 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) display_mgcp_message(msg->l2h, msgb_l2len(msg), "Received message"); - /* attempt to treat it as a response */ - if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { + /* attempt to treat it as a response */ + if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); return NULL; } @@ -1588,24 +1588,26 @@ void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size) msg += nchars; size -= nchars; - /* Error Counter */ - nchars = snprintf(msg, size, - "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u", - endp->net_state.in_stream.err_ts_counter, - endp->net_state.out_stream.err_ts_counter, - endp->bts_state.in_stream.err_ts_counter, - endp->bts_state.out_stream.err_ts_counter); - if (nchars < 0 || nchars >= size) - goto truncate; - - msg += nchars; - size -= nchars; - - if (endp->osmux.state == OSMUX_STATE_ENABLED) { - snprintf(msg, size, - "\r\nX-Osmux-ST: CR=%u, BR=%u", - endp->osmux.stats.chunks, - endp->osmux.stats.octets); + if (endp->cfg->osmux != OSMUX_USAGE_OFF) { + /* Error Counter */ + nchars = snprintf(msg, size, + "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u", + endp->net_state.in_stream.err_ts_counter, + endp->net_state.out_stream.err_ts_counter, + endp->bts_state.in_stream.err_ts_counter, + endp->bts_state.out_stream.err_ts_counter); + if (nchars < 0 || nchars >= size) + goto truncate; + + msg += nchars; + size -= nchars; + + if (endp->osmux.state == OSMUX_STATE_ENABLED) { + snprintf(msg, size, + "\r\nX-Osmux-ST: CR=%u, BR=%u", + endp->osmux.stats.chunks, + endp->osmux.stats.octets); + } } truncate: msg[size - 1] = '\0'; diff --git a/src/libosmo-mgcp-client/Makefile.am b/src/libosmo-mgcp-client/Makefile.am index 1e4e764..01371d7 100644 --- a/src/libosmo-mgcp-client/Makefile.am +++ b/src/libosmo-mgcp-client/Makefile.am @@ -20,7 +20,7 @@ AM_LDFLAGS = \ # This is not at all related to the release version, but a range of supported # API versions. Read TODO_RELEASE in the source tree's root! -MGCP_CLIENT_LIBVERSION=2:0:0 +MGCP_CLIENT_LIBVERSION=3:0:0 lib_LTLIBRARIES = \ libosmo-mgcp-client.la \ @@ -29,6 +29,7 @@ lib_LTLIBRARIES = \ libosmo_mgcp_client_la_SOURCES = \ mgcp_client.c \ mgcp_client_vty.c \ + mgcp_client_fsm.c \ $(NULL) libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c index f8c55ac..e054593 100644 --- a/src/libosmo-mgcp-client/mgcp_client.c +++ b/src/libosmo-mgcp-client/mgcp_client.c @@ -4,16 +4,16 @@ * 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Affero General Public License + * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ @@ -24,6 +24,7 @@ #include <osmocom/core/msgb.h> #include <osmocom/core/logging.h> #include <osmocom/core/byteswap.h> +#include <osmocom/core/socket.h> #include <osmocom/mgcp_client/mgcp_client.h> #include <osmocom/mgcp_client/mgcp_client_internal.h> @@ -35,6 +36,8 @@ #include <unistd.h> #include <string.h> +/*! Initalize MGCP client configuration struct with default values. + * \param[out] conf Client configuration.*/ void mgcp_client_conf_init(struct mgcp_client_conf *conf) { /* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */ @@ -61,7 +64,9 @@ static bool endpoint_in_use(uint16_t id, struct mgcp_client *client) return false; } -/* Find and seize an unsused endpoint id */ +/*! Pick next free endpoint ID. + * \param[in,out] client MGCP client descriptor. + * \returns 0 on success, -EINVAL on error. */ int mgcp_client_next_endpoint(struct mgcp_client *client) { int i; @@ -94,6 +99,9 @@ int mgcp_client_next_endpoint(struct mgcp_client *client) return -EINVAL; } +/*! Release a seized endpoint ID to make it available again for other calls. + * \param[in] id Endpoint ID + * \param[in,out] client MGCP client descriptor. */ /* Release a seized endpoint id to make it available again for other calls */ void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client) { @@ -133,20 +141,17 @@ static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg) r->body = (char *)msg->data; - if (sscanf(r->body, "%3d %u %n", + if (sscanf(r->body, "%3d %u %n", &r->head.response_code, &r->head.trans_id, &comment_pos) != 2) goto response_parse_failure; - r->head.comment = r->body + comment_pos; + osmo_strlcpy(r->head.comment, r->body + comment_pos, sizeof(r->head.comment)); end = strchr(r->head.comment, '\r'); if (!end) goto response_parse_failure; /* Mark the end of the comment */ *end = '\0'; - r->body = end + 1; - if (r->body[0] == '\n') - r->body ++; return 0; response_parse_failure: @@ -176,10 +181,13 @@ static bool mgcp_line_is_valid(const char *line) /* Parse a line like "m=audio 16002 RTP/AVP 98" */ static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line) { - if (sscanf(line, "m=audio %hu", + if (sscanf(line, "m=audio %hu", &r->audio_port) != 1) goto response_parse_failure; + if (r->audio_port == 0) + goto response_parse_failure; + return 0; response_parse_failure: @@ -201,8 +209,7 @@ static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line) goto response_parse_failure; /* Extract IP-Address */ - strncpy(r->audio_ip, line + 9, sizeof(r->audio_ip)); - r->audio_ip[sizeof(r->audio_ip) - 1] = '\0'; + osmo_strlcpy(r->audio_ip, line + 9, sizeof(r->audio_ip)); /* Check IP-Address */ if (inet_aton(r->audio_ip, &ip_test) == 0) @@ -216,26 +223,56 @@ response_parse_failure: return -EINVAL; } +/* A new section is marked by a double line break, check a few more + * patterns as there may be variants */ +static char *mgcp_find_section_end(char *string) +{ + char *rc; + + rc = strstr(string, "\n\n"); + if (rc) + return rc; + + rc = strstr(string, "\n\r\n\r"); + if (rc) + return rc; + + rc = strstr(string, "\r\n\r\n"); + if (rc) + return rc; + + return NULL; +} + +/*! Parse body (SDP) parameters of the MGCP response + * \param[in,out] r Response data + * \returns 0 on success, -EINVAL on error. */ int mgcp_response_parse_params(struct mgcp_response *r) { char *line; int rc; + char *data; + char *data_ptr; + + /* Since this functions performs a destructive parsing, we create a + * local copy of the body data */ OSMO_ASSERT(r->body); - char *data = strstr(r->body, "\n\n"); + data = talloc_strdup(r, r->body); + OSMO_ASSERT(data); - if (!data) { + /* Find beginning of the parameter (SDP) section */ + data_ptr = mgcp_find_section_end(data); + if (!data_ptr) { LOGP(DLMGCP, LOGL_ERROR, - "MGCP response: cannot find start of parameters\n"); - return -EINVAL; + "MGCP response: cannot find start of SDP parameters\n"); + rc = -EINVAL; + goto exit; } - /* Advance to after the \n\n, replace the second \n with \0. That's - * where the parameters start. */ - data ++; - *data = '\0'; - data ++; + /* data_ptr now points to the beginning of the section-end-marker; for_each_non_empty_line() + * skips any \r and \n characters for free, so we don't need to skip the marker. */ - for_each_non_empty_line(line, data) { + for_each_non_empty_line(line, data_ptr) { if (!mgcp_line_is_valid(line)) return -EINVAL; @@ -243,30 +280,122 @@ int mgcp_response_parse_params(struct mgcp_response *r) case 'm': rc = mgcp_parse_audio_port(r, line); if (rc) - return rc; + goto exit; break; case 'c': rc = mgcp_parse_audio_ip(r, line); if (rc) - return rc; + goto exit; break; default: /* skip unhandled parameters */ break; } } + + rc = 0; +exit: + talloc_free(data); + return rc; +} + +/* Parse a line like "X: something" */ +static int mgcp_parse_head_param(char *result, unsigned int result_len, + char label, const char *line) +{ + char label_string[4]; + + /* Detect empty parameters */ + if (strlen(line) < 4) + goto response_parse_failure; + + /* Check if the label matches */ + snprintf(label_string, sizeof(label_string), "%c: ", label); + if (memcmp(label_string, line, 3) != 0) + goto response_parse_failure; + + /* Copy payload part of the string to destinations (the label string + * is always 3 chars long) */ + osmo_strlcpy(result, line + 3, result_len); return 0; + +response_parse_failure: + LOGP(DLMGCP, LOGL_ERROR, + "Failed to parse MGCP response (parameter label: %c)\n", label); + return -EINVAL; +} + +/* Parse MGCP parameters of the response */ +static int parse_head_params(struct mgcp_response *r) +{ + char *line; + int rc = 0; + OSMO_ASSERT(r->body); + char *data; + char *data_ptr; + char *data_end; + + /* Since this functions performs a destructive parsing, we create a + * local copy of the body data */ + data = talloc_zero_size(r, strlen(r->body)+1); + OSMO_ASSERT(data); + data_ptr = data; + osmo_strlcpy(data, r->body, strlen(r->body)); + + /* If there is an SDP body attached, prevent for_each_non_empty_line() + * into running in there, we are not yet interested in the parameters + * stored there. */ + data_end = mgcp_find_section_end(data); + if (data_end) + *data_end = '\0'; + + for_each_non_empty_line(line, data_ptr) { + switch (line[0]) { + case 'Z': + rc = mgcp_parse_head_param(r->head.endpoint, + sizeof(r->head.endpoint), + 'Z', line); + if (rc) + goto exit; + + /* A specific endpoint identifier returned by the MGW + * must not contain any wildcard characters */ + if (strstr(r->head.endpoint, "*") != NULL) { + rc = -EINVAL; + goto exit; + } + + /* A specific endpoint identifier returned by the MGW + * must contain an @ character */ + if (strstr(r->head.endpoint, "@") == NULL) { + rc = -EINVAL; + goto exit; + } + break; + case 'I': + rc = mgcp_parse_head_param(r->head.conn_id, + sizeof(r->head.conn_id), + 'I', line); + if (rc) + goto exit; + break; + default: + /* skip unhandled parameters */ + break; + } + } +exit: + talloc_free(data); + return rc; } static struct mgcp_response_pending *mgcp_client_response_pending_get( struct mgcp_client *mgcp, - struct mgcp_response *r) + mgcp_trans_id_t trans_id) { struct mgcp_response_pending *pending; - if (!r) - return NULL; llist_for_each_entry(pending, &mgcp->responses_pending, entry) { - if (pending->trans_id == r->head.trans_id) { + if (pending->trans_id == trans_id) { llist_del(&pending->entry); return pending; } @@ -282,26 +411,42 @@ static struct mgcp_response_pending *mgcp_client_response_pending_get( */ int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg) { - struct mgcp_response r = { 0 }; + struct mgcp_response *r; struct mgcp_response_pending *pending; int rc; - rc = mgcp_response_parse_head(&r, msg); + r = talloc_zero(mgcp, struct mgcp_response); + OSMO_ASSERT(r); + + rc = mgcp_response_parse_head(r, msg); if (rc) { - LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response\n"); - return -1; + LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head)\n"); + rc = 1; + goto error; } - pending = mgcp_client_response_pending_get(mgcp, &r); + rc = parse_head_params(r); + if (rc) { + LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response (head parameters)\n"); + rc = 1; + goto error; + } + + pending = mgcp_client_response_pending_get(mgcp, r->head.trans_id); if (!pending) { LOGP(DLMGCP, LOGL_ERROR, "Cannot find matching MGCP transaction for trans_id %d\n", - r.head.trans_id); - return -1; + r->head.trans_id); + rc = -ENOENT; + goto error; } - mgcp_client_handle_response(mgcp, pending, &r); - return 0; + mgcp_client_handle_response(mgcp, pending, r); + rc = 0; + +error: + talloc_free(r); + return rc; } static int mgcp_do_read(struct osmo_fd *fd) @@ -325,7 +470,7 @@ static int mgcp_do_read(struct osmo_fd *fd) LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret); msgb_free(msg); return -1; - } + } msg->l2h = msgb_put(msg, ret); ret = mgcp_client_rx(mgcp, msg); @@ -340,7 +485,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf); unsigned int i; - strncpy(strbuf, (const char*)msg->data, l); + osmo_strlcpy(strbuf, (const char*)msg->data, l); for (i = 0; i < sizeof(strbuf); i++) { if (strbuf[i] == '\n' || strbuf[i] == '\r') { strbuf[i] = '\0'; @@ -388,9 +533,11 @@ struct mgcp_client *mgcp_client_init(void *ctx, return mgcp; } +/*! Initalize client connection (opens socket only, no request is sent yet) + * \param[in,out] mgcp MGCP client descriptor. + * \returns 0 on success, -EINVAL on error. */ int mgcp_client_connect(struct mgcp_client *mgcp) { - int on; struct sockaddr_in addr; struct osmo_wqueue *wq; int rc; @@ -402,46 +549,19 @@ int mgcp_client_connect(struct mgcp_client *mgcp) wq = &mgcp->wq; - wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (wq->bfd.fd < 0) { - LOGP(DLMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); - return -errno; - } - - on = 1; - if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { - LOGP(DLMGCP, LOGL_FATAL, - "Failed to initialize socket for MGCP GW: %s\n", - strerror(errno)); - rc = -errno; - goto error_close_fd; - } - - /* bind socket */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - inet_aton(mgcp->actual.local_addr, &addr.sin_addr); - addr.sin_port = htons(mgcp->actual.local_port); - if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + rc = osmo_sock_init2_ofd(&wq->bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, + mgcp->actual.local_addr, mgcp->actual.local_port, + mgcp->actual.remote_addr, mgcp->actual.remote_port, + OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT); + if (rc < 0) { LOGP(DLMGCP, LOGL_FATAL, - "Failed to bind for MGCP GW to %s %u\n", - mgcp->actual.local_addr, mgcp->actual.local_port); - rc = -errno; + "Failed to initialize socket %s:%u -> %s:%u for MGCP GW: %s\n", + mgcp->actual.local_addr, mgcp->actual.local_port, + mgcp->actual.remote_addr, mgcp->actual.remote_port, strerror(errno)); goto error_close_fd; } - /* connect to the remote */ inet_aton(mgcp->actual.remote_addr, &addr.sin_addr); - addr.sin_port = htons(mgcp->actual.remote_port); - if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - LOGP(DLMGCP, LOGL_FATAL, - "Failed to connect to MGCP GW at %s %u: %s\n", - mgcp->actual.remote_addr, mgcp->actual.remote_port, - strerror(errno)); - rc = -errno; - goto error_close_fd; - } - mgcp->remote_addr = htonl(addr.sin_addr.s_addr); osmo_wqueue_init(wq, 10); @@ -450,11 +570,6 @@ int mgcp_client_connect(struct mgcp_client *mgcp) wq->read_cb = mgcp_do_read; wq->write_cb = mgcp_do_write; - if (osmo_fd_register(&wq->bfd) != 0) { - LOGP(DLMGCP, LOGL_FATAL, "Failed to register BFD\n"); - rc = -EIO; - goto error_close_fd; - } LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n", mgcp->actual.local_addr, mgcp->actual.local_port, mgcp->actual.remote_addr, mgcp->actual.remote_port); @@ -466,17 +581,25 @@ error_close_fd: return rc; } +/*! Get the IP-Aaddress of the associated MGW as string. + * \param[in] mgcp MGCP client descriptor. + * \returns a pointer to the address string. */ const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp) { return mgcp->actual.remote_addr; } +/*! Get the IP-Port of the associated MGW. + * \param[in] mgcp MGCP client descriptor. + * \returns port number. */ uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp) { return mgcp->actual.remote_port; } -/* Return the MGCP GW binary IPv4 address in network byte order. */ +/*! Get the IP-Aaddress of the associated MGW as its numeric representation. + * \param[in] mgcp MGCP client descriptor. + * \returns IP-Address as 32 bit integer (network byte order) */ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp) { return mgcp->remote_addr; @@ -503,7 +626,10 @@ struct mgcp_response_pending * mgcp_client_pending_add( * response_cb. NOTE: the response_cb still needs to call * mgcp_response_parse_params(response) to get the parsed parameters -- to * potentially save some CPU cycles, only the head line has been parsed when - * the response_cb is invoked. */ + * the response_cb is invoked. + * Before the priv pointer becomes invalid, e.g. due to transaction timeout, + * mgcp_client_cancel() needs to be called for this transaction. + */ int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg, mgcp_response_cb_t response_cb, void *priv) { @@ -546,6 +672,35 @@ mgcp_tx_error: return -1; } +/*! Cancel a pending transaction. + * \param[in] mgcp MGCP client descriptor. + * \param[in,out] trans_id Transaction id. + * \returns 0 on success, -ENOENT on error. + * + * Should a priv pointer passed to mgcp_client_tx() become invalid, this function must be called. In + * practical terms, if the caller of mgcp_client_tx() wishes to tear down a transaction without having + * received a response this function must be called. The trans_id can be obtained by calling + * mgcp_msg_trans_id() on the msgb produced by mgcp_msg_gen(). */ +int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id) +{ + struct mgcp_response_pending *pending = mgcp_client_response_pending_get(mgcp, trans_id); + if (!pending) { + /*! Note: it is not harmful to cancel a transaction twice. */ + LOGP(DLMGCP, LOGL_INFO, "Cannot cancel, no such transaction: %u\n", trans_id); + return -ENOENT; + } + LOGP(DLMGCP, LOGL_INFO, "Canceled transaction %u\n", trans_id); + talloc_free(pending); + return 0; + /*! We don't really need to clean up the wqueue: In all sane cases, the msgb has already been sent + * out and is no longer in the wqueue. If it still is in the wqueue, then sending MGCP messages + * per se is broken and the program should notice so by a full wqueue. Even if this was called + * before we had a chance to send out the message and it is still going to be sent, we will just + * ignore the reply to it later. Removing a msgb from the wqueue here would just introduce more + * bug surface in terms of failing to update wqueue API's counters or some such. + */ +} + static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id, const char *buf, int len) { @@ -650,20 +805,25 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint, #define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \ MGCP_MSG_PRESENCE_CALL_ID | \ - MGCP_MSG_PRESENCE_CONN_ID | \ MGCP_MSG_PRESENCE_CONN_MODE) #define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \ + MGCP_MSG_PRESENCE_CALL_ID | \ MGCP_MSG_PRESENCE_CONN_ID) #define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT) #define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT) #define MGCP_RSIP_MANDATORY 0 /* none */ +/*! Generate an MGCP message + * \param[in] mgcp MGCP client descriptor. + * \param[in] mgcp_msg Message description + * \returns message buffer on success, NULL on error. */ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) { mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp); uint32_t mandatory_mask; struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx"); int rc = 0; + char local_ip[INET_ADDRSTRLEN]; msg->l2h = msg->data; msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id; @@ -706,8 +866,23 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) } /* Add endpoint name */ - if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) + if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT) { + if (strlen(mgcp_msg->endpoint) <= 0) { + LOGP(DLMGCP, LOGL_ERROR, + "Empty endpoint name, can not generate MGCP message\n"); + msgb_free(msg); + return NULL; + } + + if (strstr(mgcp_msg->endpoint, "@") == NULL) { + LOGP(DLMGCP, LOGL_ERROR, + "Endpoint name (%s) lacks separator (@), can not generate MGCP message\n", + mgcp_msg->endpoint); + msgb_free(msg); + } + rc += msgb_printf(msg, " %s", mgcp_msg->endpoint); + } /* Add protocol version */ rc += msgb_printf(msg, " MGCP 1.0\r\n"); @@ -717,12 +892,18 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id); /* Add connection id */ - if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) - rc += msgb_printf(msg, "I: %u\r\n", mgcp_msg->conn_id); + if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID) { + if (strlen(mgcp_msg->conn_id) <= 0) { + LOGP(DLMGCP, LOGL_ERROR, + "Empty connection id, can not generate MGCP message\n"); + msgb_free(msg); + return NULL; + } + rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id); + } /* Add local connection options */ - if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID - && mgcp_msg->verb == MGCP_VERB_CRCX) + if (mgcp_msg->verb == MGCP_VERB_CRCX) rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n"); /* Add mode */ @@ -731,11 +912,49 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) msgb_printf(msg, "M: %s\r\n", mgcp_client_cmode_name(mgcp_msg->conn_mode)); - /* Add RTP address and port (SDP) */ + /* Add SDP body */ if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) { + + /* Add separator to mark the beginning of the SDP block */ rc += msgb_printf(msg, "\r\n"); + + /* Add SDP protocol version */ + rc += msgb_printf(msg, "v=0\r\n"); + + /* Determine local IP-Address */ + if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) { + LOGP(DLMGCP, LOGL_ERROR, + "Could not determine local IP-Address!\n"); + msgb_free(msg); + return NULL; + } + + /* Add owner/creator (SDP) */ + rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n", + mgcp_msg->call_id, local_ip); + + /* Add session name (none) */ + rc += msgb_printf(msg, "s=-\r\n"); + + /* Add RTP address and port */ + if (mgcp_msg->audio_port == 0) { + LOGP(DLMGCP, LOGL_ERROR, + "Invalid port number, can not generate MGCP message\n"); + msgb_free(msg); + return NULL; + } + if (strlen(mgcp_msg->audio_ip) <= 0) { + LOGP(DLMGCP, LOGL_ERROR, + "Empty ip address, can not generate MGCP message\n"); + msgb_free(msg); + return NULL; + } rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip); + + /* Add time description, active time (SDP) */ + rc += msgb_printf(msg, "t=0 0\r\n"); + rc += msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n", mgcp_msg->audio_port); @@ -751,6 +970,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg) return msg; } +/*! Retrieve the MGCP transaction ID from a msgb generated by mgcp_msg_gen() + * \param[in] msg message buffer + * \returns Transaction id. */ +mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg) +{ + return (mgcp_trans_id_t)msg->cb[MSGB_CB_MGCP_TRANS_ID]; +} + +/*! Get the configuration parameters a given MGCP client instance + * \param[in] mgcp MGCP client descriptor. + * \returns configuration */ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp) { return &mgcp->actual; diff --git a/src/libosmo-mgcp-client/mgcp_client_fsm.c b/src/libosmo-mgcp-client/mgcp_client_fsm.c new file mode 100644 index 0000000..3c755be --- /dev/null +++ b/src/libosmo-mgcp-client/mgcp_client_fsm.c @@ -0,0 +1,669 @@ +/* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/mgcp_client/mgcp_client.h> +#include <osmocom/mgcp_client/mgcp_client_fsm.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/fsm.h> +#include <osmocom/core/byteswap.h> +#include <arpa/inet.h> +#include <osmocom/core/logging.h> + +/* Context information, this is attached to the priv pointer of the FSM and + * is also handed back when dispatcheing events to the parent FSM. This is + * purly intened and not meant to be accessible for the API user */ +struct mgcp_ctx { + /* MGCP client instance that is used to interact with the MGW */ + struct mgcp_client *mgcp; + + /* The ID of the last pending transaction. This is used internally + * to cancel the transaction in case of an error */ + mgcp_trans_id_t mgw_pending_trans; + + /* Flag to mark that there is a pending transaction */ + bool mgw_trans_pending; + + /* Connection ID which has been assigned by he MGW */ + char conn_id[MGCP_CONN_ID_LENGTH]; + + /* Local RTP connection info, the MGW will send outgoing traffic to the + * ip/port specified here. The Address does not have to be choosen right + * on the creation of a connection. It can always be modified later by + * the user. */ + struct mgcp_conn_peer conn_peer_local; + + /* Remote RTP connection info, the ip/port specified here is the address + * where the MGW expects the RTP data to be sent. This address is + * defined by soly by the MGW and can not be influenced by the user. */ + struct mgcp_conn_peer conn_peer_remote; + + /* The terminate flag is a way to handle cornercase sitations that + * might occur when the user runs into an error situation and sends + * a DLCX command while the FSM is waiting for a response. In this + * case the DLCX command is not executed immediately. Instead the + * terminate flag is set. When the response to from the previous + * operation is received, we know that there is a DLCX event is + * pending. The FSM then generates the EV_DLCX by itsself before + * it enters ST_READY to cause the immediate execution of the + * DLCX procedure. (If normal operations are executed too fast, + * the API functions will return an error. In general, the user + * should synchronize using the callback events) */ + bool terminate; + + /* Event that is sent when the current operation is completed (except + * for DLCX, there the specified parent_term_evt is sent instead) */ + uint32_t parent_evt; +}; + +#define S(x) (1 << (x)) + +#define MGCP_MGW_TIMEOUT 4 /* in seconds */ +#define MGCP_MGW_TIMEOUT_TIMER_NR 1 + +enum fsm_mgcp_client_states { + ST_CRCX, + ST_CRCX_RESP, + ST_READY, + ST_MDCX_RESP, + ST_DLCX_RESP, +}; + +enum fsm_mgcp_client_evt { + EV_CRCX, + EV_CRCX_RESP, + EV_MDCX, + EV_MDCX_RESP, + EV_DLCX, + EV_DLCX_RESP, +}; + +static const struct value_string fsm_mgcp_client_evt_names[] = { + OSMO_VALUE_STRING(EV_CRCX), + OSMO_VALUE_STRING(EV_CRCX_RESP), + OSMO_VALUE_STRING(EV_MDCX), + OSMO_VALUE_STRING(EV_MDCX_RESP), + OSMO_VALUE_STRING(EV_DLCX), + OSMO_VALUE_STRING(EV_DLCX_RESP), + {0, NULL} +}; + +static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx) +{ + struct mgcp_msg mgcp_msg; + + mgcp_msg = (struct mgcp_msg) { + .verb = MGCP_VERB_CRCX, + .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE), + .call_id = mgcp_ctx->conn_peer_local.call_id, + .conn_mode = MGCP_CONN_LOOPBACK, + }; + osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN); + + return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); +} + +static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx) +{ + struct mgcp_msg mgcp_msg; + + mgcp_msg = (struct mgcp_msg) { + .verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | + MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | + MGCP_MSG_PRESENCE_AUDIO_PORT), + .call_id = mgcp_ctx->conn_peer_local.call_id, + .conn_mode = MGCP_CONN_RECV_SEND, + .audio_ip = mgcp_ctx->conn_peer_local.addr, + .audio_port = mgcp_ctx->conn_peer_local.port, + }; + osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN); + + return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); +} + +static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx) +{ + struct mgcp_msg mgcp_msg; + + mgcp_msg = (struct mgcp_msg) { + .verb = MGCP_VERB_MDCX, + .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID | + MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT), + .call_id = mgcp_ctx->conn_peer_remote.call_id, + .conn_id = mgcp_ctx->conn_id, + .conn_mode = MGCP_CONN_RECV_SEND, + .audio_ip = mgcp_ctx->conn_peer_local.addr, + .audio_port = mgcp_ctx->conn_peer_local.port, + }; + osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); + + /* Note: We take the endpoint and the call_id from the remote + * connection info, because we can be confident that the + * information there is valid. For the local info, we explicitly + * allow endpoint and call_id to be optional */ + return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); +} + +struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx) +{ + struct mgcp_msg mgcp_msg; + + mgcp_msg = (struct mgcp_msg) { + .verb = MGCP_VERB_DLCX, + .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID), + .call_id = mgcp_ctx->conn_peer_remote.call_id, + .conn_id = mgcp_ctx->conn_id, + }; + osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); + + return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); +} + +static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv); + +static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct mgcp_ctx *mgcp_ctx = data; + struct mgcp_client *mgcp; + struct msgb *msg; + int rc; + + OSMO_ASSERT(mgcp_ctx); + mgcp = mgcp_ctx->mgcp; + OSMO_ASSERT(mgcp); + + switch (event) { + case EV_CRCX: + LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n", + mgcp_ctx->conn_peer_local.endpoint); + + if (mgcp_ctx->conn_peer_local.port) + msg = make_crcx_msg_bind_connect(mgcp_ctx); + else + msg = make_crcx_msg_bind(mgcp_ctx); + OSMO_ASSERT(msg); + + mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); + mgcp_ctx->mgw_trans_pending = true; + rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi); + if (rc < 0) { + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + + osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); + break; + default: + OSMO_ASSERT(false); + break; + } +} + +static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv) +{ + struct osmo_fsm_inst *fi = priv; + struct mgcp_ctx *mgcp_ctx; + int rc; + + OSMO_ASSERT(fi); + mgcp_ctx = fi->priv; + OSMO_ASSERT(mgcp_ctx); + + mgcp_ctx->mgw_trans_pending = false; + + if (r->head.response_code != 200) { + LOGPFSML(fi, LOGL_ERROR, + "MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + + osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id)); + LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id); + + rc = mgcp_response_parse_params(r); + if (rc) { + LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n"); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); + + osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); + mgcp_ctx->conn_peer_remote.port = r->audio_port; + + if (strlen(r->head.endpoint) > 0) { + /* If we get an endpoint identifier back from the MGW, take it */ + osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint, + sizeof(mgcp_ctx->conn_peer_remote.endpoint)); + } else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) { + /* If we do not get an endpoint identifier back and the + * identifier we used to create the connection is not a + * wildcarded one, we take the local endpoint identifier + * instead */ + osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint, + sizeof(mgcp_ctx->conn_peer_local.endpoint)); + } else { + LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n"); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + + mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id; + + osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx); +} + +static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct mgcp_ctx *mgcp_ctx = data; + OSMO_ASSERT(mgcp_ctx); + + switch (event) { + case EV_CRCX_RESP: + osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); + if (mgcp_ctx->terminate) { + /* Trigger immediate DLCX if DLCX was requested while the FSM was + * busy with the previous operation */ + LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n"); + osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); + } else + osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); + break; + default: + OSMO_ASSERT(false); + break; + } +} + +static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv); +static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv); + +static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct mgcp_ctx *mgcp_ctx = data; + struct msgb *msg; + struct mgcp_client *mgcp; + uint32_t new_state; + int rc; + + OSMO_ASSERT(mgcp_ctx); + mgcp = mgcp_ctx->mgcp; + OSMO_ASSERT(mgcp); + + switch (event) { + case EV_MDCX: + msg = make_mdcx_msg(mgcp_ctx); + OSMO_ASSERT(msg); + rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi); + new_state = ST_MDCX_RESP; + break; + case EV_DLCX: + msg = make_dlcx_msg(mgcp_ctx); + OSMO_ASSERT(msg); + rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi); + new_state = ST_DLCX_RESP; + break; + default: + OSMO_ASSERT(false); + break; + } + + mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); + mgcp_ctx->mgw_trans_pending = true; + + if (rc < 0) { + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + + osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); +} + +static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv) +{ + struct osmo_fsm_inst *fi = priv; + struct mgcp_ctx *mgcp_ctx; + int rc; + + OSMO_ASSERT(fi); + mgcp_ctx = fi->priv; + OSMO_ASSERT(mgcp_ctx); + + mgcp_ctx->mgw_trans_pending = false; + + if (r->head.response_code != 200) { + LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code, + r->head.comment); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + + rc = mgcp_response_parse_params(r); + if (rc) { + LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n"); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); + + osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); + mgcp_ctx->conn_peer_remote.port = r->audio_port; + + osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx); +} + +static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct mgcp_ctx *mgcp_ctx = data; + OSMO_ASSERT(mgcp_ctx); + + switch (event) { + case EV_MDCX_RESP: + osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); + if (mgcp_ctx->terminate) { + /* Trigger immediate DLCX if DLCX was requested while the FSM was + * busy with the previous operation */ + LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n"); + osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); + } else + osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); + break; + default: + OSMO_ASSERT(false); + break; + } +} + +static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv) +{ + struct osmo_fsm_inst *fi = priv; + struct mgcp_ctx *mgcp_ctx; + + OSMO_ASSERT(fi); + mgcp_ctx = fi->priv; + OSMO_ASSERT(mgcp_ctx); + + mgcp_ctx->mgw_trans_pending = false; + + if (r->head.response_code != 250) { + LOGPFSML(fi, LOGL_ERROR, + "MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return; + } + + osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx); +} + +static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct mgcp_ctx *mgcp_ctx = data; + OSMO_ASSERT(mgcp_ctx); + + switch (event) { + case EV_DLCX_RESP: + /* Rub out the connection identifier, since the connection + * is no longer present and we will use the connection id + * to know in error cases if the connection is still present + * or not */ + memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id)); + + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); + break; + default: + OSMO_ASSERT(false); + break; + } +} + +static int fsm_timeout_cb(struct osmo_fsm_inst *fi) +{ + struct mgcp_ctx *mgcp_ctx = fi->priv; + struct mgcp_client *mgcp; + + OSMO_ASSERT(mgcp_ctx); + mgcp = mgcp_ctx->mgcp; + OSMO_ASSERT(mgcp); + + if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) { + /* Note: We were unable to communicate with the MGW, + * unfortunately there is no meaningful action we can take + * now other than giving up. */ + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); + } else { + /* Note: Ther must not be any unsolicited timers + * in this FSM. If so, we have serious problem. */ + OSMO_ASSERT(false); + } + + return 0; +} + +static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) +{ + struct mgcp_ctx *mgcp_ctx = fi->priv; + struct mgcp_client *mgcp; + struct msgb *msg; + + OSMO_ASSERT(mgcp_ctx); + mgcp = mgcp_ctx->mgcp; + OSMO_ASSERT(mgcp); + + /* If there is still a transaction pending, cancel it now. */ + if (mgcp_ctx->mgw_trans_pending) + mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans); + + /* Should the FSM be terminated while there are still open connections + * on the MGW, we send an unconditional DLCX to terminate the + * connection. This is not the normal case. The user should always use + * mgcp_conn_delete() to instruct the FSM to perform a graceful exit */ + if (strlen(mgcp_ctx->conn_id)) { + LOGPFSML(fi, LOGL_ERROR, + "MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n"); + msg = make_dlcx_msg(mgcp_ctx); + OSMO_ASSERT(msg); + mgcp_client_tx(mgcp, msg, NULL, NULL); + } + + talloc_free(mgcp_ctx); +} + +static struct osmo_fsm_state fsm_mgcp_client_states[] = { + + /* Initial CRCX state. This state is immediately entered and executed + * when the FSM is started. The rationale is that we first have to + * create a connectin before we can execute other operations on that + * connection. */ + [ST_CRCX] = { + .in_event_mask = S(EV_CRCX), + .out_state_mask = S(ST_CRCX_RESP), + .name = OSMO_STRINGIFY(ST_CRCX), + .action = fsm_crcx_cb, + }, + + /* Wait for the response to a CRCX operation, check and process the + * results, change to ST_READY afterwards. */ + [ST_CRCX_RESP] = { + .in_event_mask = S(EV_CRCX_RESP), + .out_state_mask = S(ST_READY), + .name = OSMO_STRINGIFY(ST_CRCX_RESP), + .action = fsm_crcx_resp_cb, + }, + + /* In this idle state we wait for further operations (e.g. MDCX) that + * can be executed by the user using the API. There is no timeout in + * this state. The connection lives on until the user decides to + * terminate it (DLCX). */ + [ST_READY] = { + .in_event_mask = S(EV_MDCX) | S(EV_DLCX), + .out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP), + .name = OSMO_STRINGIFY(ST_READY), + .action = fsm_ready_cb, + }, + + /* Wait for the response of a MDCX operation, check and process the + * results, change to ST_READY afterwards. */ + [ST_MDCX_RESP] = { + .in_event_mask = S(EV_MDCX_RESP), + .out_state_mask = S(ST_READY), + .name = OSMO_STRINGIFY(ST_MDCX_RESP), + .action = fsm_mdcx_resp_cb, + }, + + /* Wait for the response of a DLCX operation and terminate the FSM + * normally. */ + [ST_DLCX_RESP] = { + .in_event_mask = S(EV_DLCX_RESP), + .out_state_mask = 0, + .name = OSMO_STRINGIFY(ST_DLCX_RESP), + .action = fsm_dlcx_resp_cb, + }, +}; + +static struct osmo_fsm fsm_mgcp_client = { + .name = "MGCP_CONN", + .states = fsm_mgcp_client_states, + .num_states = ARRAY_SIZE(fsm_mgcp_client_states), + .timer_cb = fsm_timeout_cb, + .cleanup = fsm_cleanup_cb, + .event_names = fsm_mgcp_client_evt_names, +}; + +/*! allocate FSM, and create a new connection on the MGW. + * \param[in] mgcp MGCP client descriptor. + * \param[in] mgcpparent_fi Parent FSM instance. + * \param[in] parent_term_evt Event to be sent to parent when terminating. + * \param[in] parent_evt Event to be sent to parent when operation is done. + * \param[in] conn_peer Connection parameters (ip, port...). + * \returns newly-allocated, initialized and registered FSM instance, NULL on error. */ +struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, + uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) +{ + struct mgcp_ctx *mgcp_ctx; + static bool fsm_registered = false; + struct osmo_fsm_inst *fi; + struct in_addr ip_test; + + OSMO_ASSERT(parent_fi); + OSMO_ASSERT(mgcp); + OSMO_ASSERT(conn_peer); + + /* Check if IP/Port informstaion in conn info makes sense */ + if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0) + return NULL; + + /* Register the fsm description (if not already done) */ + if (fsm_registered == false) { + osmo_fsm_register(&fsm_mgcp_client); + fsm_registered = true; + } + + /* Allocate and configure a new fsm instance */ + fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt); + OSMO_ASSERT(fi); + mgcp_ctx = talloc_zero(fi, struct mgcp_ctx); + OSMO_ASSERT(mgcp_ctx); + mgcp_ctx->mgcp = mgcp; + mgcp_ctx->parent_evt = parent_evt; + + memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); + fi->priv = mgcp_ctx; + + /* start state machine */ + OSMO_ASSERT(fi->state == ST_CRCX); + osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx); + + return fi; +} + +/*! modify an existing connection on the MGW. + * \param[in] fi FSM instance. + * \param[in] parent_evt Event to be sent to parent when operation is done. + * \param[in] conn_peer New connection information (ip, port...). + * \returns 0 on success, -EINVAL on error. */ +int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) +{ + OSMO_ASSERT(fi); + struct mgcp_ctx *mgcp_ctx = fi->priv; + struct in_addr ip_test; + + OSMO_ASSERT(mgcp_ctx); + OSMO_ASSERT(conn_peer); + + /* The user must not issue an MDCX before the CRCX has completed, + * if this happens, it means that the parent FSM has overhead the + * parent_evt (mandatory!) and executed the MDCX without even + * waiting for the results. Another reason could be that the + * parent FSM got messed up */ + OSMO_ASSERT(fi->state != ST_CRCX_RESP); + + /* If the user tries to issue an MDCX while an DLCX operation is + * pending, there must be a serious problem with the paren FSM. + * Eeither the parent_term_evt (mandatory!) has been overheard, + * or the parant FSM got messed so badly that it still assumes + * a live connection although it as killed it. */ + OSMO_ASSERT(fi->state != ST_DLCX_RESP); + + /* Check if IP/Port parameters make sense */ + if (conn_peer->port == 0) + return -EINVAL; + if (inet_aton(conn_peer->addr, &ip_test) == 0) + return -EINVAL; + + /*! The user may supply an endpoint identifier in conn_peer. The + * identifier is then checked. This check is optional. Later steps do + * not depend on the endpoint identifier supplied here because it is + * already implicitly known from the CRCX phase. */ + if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) + return -EINVAL; + + /*! Note: The call-id is implicitly known from the previous CRCX and + * will not be checked even when it is set in conn_peer. */ + + mgcp_ctx->parent_evt = parent_evt; + memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); + osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx); + return 0; +} + +/*! delete existing connection on the MGW, destroy FSM afterwards. + * \param[in] fi FSM instance. */ +void mgcp_conn_delete(struct osmo_fsm_inst *fi) +{ + OSMO_ASSERT(fi); + struct mgcp_ctx *mgcp_ctx = fi->priv; + + OSMO_ASSERT(mgcp_ctx); + + /* Unlink FSM from parent */ + osmo_fsm_inst_unlink_parent(fi, NULL); + + /* An error situation where the parent FSM must be killed immediately + * may lead into a situation where the DLCX can not be executed right + * at that moment because the FSM is still busy with another operation. + * In those cases we postpone the DLCX so that the FSM and the + * connections on the MGW get cleaned up gracefully. */ + if (fi->state != ST_READY) { + LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n"); + mgcp_ctx->terminate = true; + return; + } + osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); +} diff --git a/src/libosmo-mgcp-client/mgcp_client_vty.c b/src/libosmo-mgcp-client/mgcp_client_vty.c index f8129c0..10d078a 100644 --- a/src/libosmo-mgcp-client/mgcp_client_vty.c +++ b/src/libosmo-mgcp-client/mgcp_client_vty.c @@ -6,16 +6,16 @@ * 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 + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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. + * GNU General Public License for more details. * - * You should have received a copy of the GNU Affero General Public License + * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ @@ -25,6 +25,7 @@ #include <talloc.h> #include <osmocom/vty/command.h> +#include <osmocom/vty/misc.h> #include <osmocom/core/utils.h> #include <osmocom/mgcp_client/mgcp_client.h> @@ -203,4 +204,6 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c install_element(node, &cfg_mgcpgw_remote_port_cmd); install_element(node, &cfg_mgcpgw_endpoint_range_cmd); install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd); + + osmo_fsm_vty_add_cmds(); } diff --git a/src/libosmo-mgcp/Makefile.am b/src/libosmo-mgcp/Makefile.am index fce0e1b..a98c391 100644 --- a/src/libosmo-mgcp/Makefile.am +++ b/src/libosmo-mgcp/Makefile.am @@ -7,6 +7,7 @@ AM_CPPFLAGS = \ AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMONETIF_CFLAGS) \ $(COVERAGE_CFLAGS) \ @@ -14,24 +15,21 @@ AM_CFLAGS = \ AM_LDFLAGS = \ $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMONETIF_LIBS) \ $(COVERAGE_LDFLAGS) \ $(NULL) -# This is not at all related to the release version, but a range of supported -# API versions. Read TODO_RELEASE in the source tree's root! -MGCP_LIBVERSION=1:0:0 - -lib_LTLIBRARIES = \ - libosmo-mgcp.la \ +noinst_LIBRARIES = \ + libosmo-mgcp.a \ $(NULL) noinst_HEADERS = \ g711common.h \ $(NULL) -libosmo_mgcp_la_SOURCES = \ +libosmo_mgcp_a_SOURCES = \ mgcp_protocol.c \ mgcp_network.c \ mgcp_vty.c \ @@ -40,7 +38,5 @@ libosmo_mgcp_la_SOURCES = \ mgcp_msg.c \ mgcp_conn.c \ mgcp_stat.c \ - mgcp_ep.c \ + mgcp_endp.c \ $(NULL) - -libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION) diff --git a/src/libosmo-mgcp/mgcp_conn.c b/src/libosmo-mgcp/mgcp_conn.c index e0eec63..998dbc5 100644 --- a/src/libosmo-mgcp/mgcp_conn.c +++ b/src/libosmo-mgcp/mgcp_conn.c @@ -24,10 +24,51 @@ #include <osmocom/mgcp/mgcp_conn.h> #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_common.h> -#include <osmocom/mgcp/mgcp_ep.h> +#include <osmocom/mgcp/mgcp_endp.h> +#include <osmocom/gsm/gsm_utils.h> +#include <ctype.h> + +/* Allocate a new connection identifier. According to RFC3435, they must + * be unique only within the scope of the endpoint. (Caller must provide + * memory for id) */ +static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id) +{ + int i; + int k; + int rc; + uint8_t id_bin[16]; + char *id_hex; + + /* Generate a connection id that is unique for the current endpoint. + * Technically a counter would be sufficient, but in order to + * be able to find a specific connection in large logfiles and to + * prevent unintentional connections we assign the connection + * identifiers randomly from a reasonable large number space */ + for (i = 0; i < 32; i++) { + rc = osmo_get_rand_id(id_bin, sizeof(id_bin)); + if (rc < 0) + return rc; + + id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin)); + for (k = 0; k < strlen(id_hex); k++) + id_hex[k] = toupper(id_hex[k]); + + /* ensure that the generated conn_id is unique + * for this endpoint */ + if (!mgcp_conn_get_rtp(endp, id_hex)) { + osmo_strlcpy(id, id_hex, MGCP_CONN_ID_LENGTH); + return 0; + } + } + + LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x, unable to generate a unique connectionIdentifier\n", + ENDPOINT_NUMBER(endp)); + + return -1; +} /* Reset codec state and free memory */ -static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec) +static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec) { codec->payload_type = -1; codec->subtype_name = NULL; @@ -42,22 +83,20 @@ static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec) talloc_free(codec->audio_name); } -/* Reset states, free memory, set defaults and reset codec state */ -static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn) +/* Initialize rtp connection struct with default values */ +static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn) { - struct mgcp_rtp_end *end = &conn->end; + struct mgcp_rtp_end *end = &conn_rtp->end; - conn->type = MGCP_RTP_DEFAULT; - conn->osmux.allocated_cid = -1; + conn_rtp->type = MGCP_RTP_DEFAULT; + conn_rtp->osmux.allocated_cid = -1; + + /* backpointer to the generic part of the connection */ + conn->u.rtp.conn = conn; end->rtp.fd = -1; end->rtcp.fd = -1; - end->local_port = 0; - end->packets_rx = 0; - end->octets_rx = 0; - end->packets_tx = 0; - end->octets_tx = 0; - end->dropped_packets = 0; + memset(&end->stats, 0, sizeof(end->stats)); end->rtp_port = end->rtcp_port = 0; talloc_free(end->fmtp_extra); end->fmtp_extra = NULL; @@ -67,8 +106,16 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn) end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; end->output_enabled = 0; - mgcp_rtp_codec_reset(&end->codec); - mgcp_rtp_codec_reset(&end->alt_codec); + mgcp_rtp_codec_init(&end->codec); + mgcp_rtp_codec_init(&end->alt_codec); +} + +/* Cleanup rtp connection struct */ +static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) +{ + osmux_disable_conn(conn_rtp); + osmux_release_cid(conn_rtp); + mgcp_free_rtp_port(&conn_rtp->end); } /*! allocate a new connection list entry. @@ -78,22 +125,15 @@ static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn) * \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP) * \returns pointer to allocated connection, NULL on error */ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, - uint32_t id, enum mgcp_conn_type type, - char *name) + enum mgcp_conn_type type, char *name) { struct mgcp_conn *conn; - OSMO_ASSERT(endp); - OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); - OSMO_ASSERT(strlen(name) < sizeof(conn->name)); + int rc; /* Do not allow more then two connections */ if (llist_count(&endp->conns) >= endp->type->max_conns) return NULL; - /* Prevent duplicate connection IDs */ - if (mgcp_conn_get(endp, id)) - return NULL; - /* Create new connection and add it to the list */ conn = talloc_zero(ctx, struct mgcp_conn); if (!conn) @@ -102,13 +142,16 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, conn->type = type; conn->mode = MGCP_CONN_NONE; conn->mode_orig = MGCP_CONN_NONE; - conn->id = id; - conn->u.rtp.conn = conn; - strcpy(conn->name, name); + osmo_strlcpy(conn->name, name, sizeof(conn->name)); + rc = mgcp_alloc_id(endp, conn->id); + if (rc < 0) { + talloc_free(conn); + return NULL; + } switch (type) { case MGCP_CONN_TYPE_RTP: - mgcp_rtp_conn_reset(&conn->u.rtp); + mgcp_rtp_conn_init(&conn->u.rtp, conn); break; default: /* NOTE: This should never be called with an @@ -126,15 +169,12 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, * \param[in] endp associated endpoint * \param[in] id identification number of the connection * \returns pointer to allocated connection, NULL if not found */ -struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id) +struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id) { - OSMO_ASSERT(endp); - OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); - struct mgcp_conn *conn; llist_for_each_entry(conn, &endp->conns, entry) { - if (conn->id == id) + if (strncmp(conn->id, id, sizeof(conn->id)) == 0) return conn; } @@ -145,11 +185,9 @@ struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id) * \param[in] endp associated endpoint * \param[in] id identification number of the connection * \returns pointer to allocated connection, NULL if not found */ -struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id) +struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, + const char *id) { - OSMO_ASSERT(endp); - OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); - struct mgcp_conn *conn; conn = mgcp_conn_get(endp, id); @@ -165,22 +203,23 @@ struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id) /*! free a connection by its ID. * \param[in] endp associated endpoint * \param[in] id identification number of the connection */ -void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id) +void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id) { - OSMO_ASSERT(endp); - OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); - struct mgcp_conn *conn; conn = mgcp_conn_get(endp, id); if (!conn) return; + /* Run endpoint cleanup action. By this we inform the endpoint about + * the removal of the connection and allow it to clean up its inner + * state accordingly */ + if (endp->type->cleanup_cb) + endp->type->cleanup_cb(endp, conn); + switch (conn->type) { case MGCP_CONN_TYPE_RTP: - osmux_disable_conn(&conn->u.rtp); - osmux_release_cid(&conn->u.rtp); - mgcp_free_rtp_port(&conn->u.rtp.end); + mgcp_rtp_conn_cleanup(&conn->u.rtp); break; default: /* NOTE: This should never be called with an @@ -197,9 +236,6 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id) * \param[in] endp associated endpoint */ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) { - OSMO_ASSERT(endp); - OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); - struct mgcp_conn *conn; if (llist_empty(&endp->conns)) @@ -216,9 +252,6 @@ void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) * \param[in] endp associated endpoint */ void mgcp_conn_free_all(struct mgcp_endpoint *endp) { - OSMO_ASSERT(endp); - OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL); - struct mgcp_conn *conn; struct mgcp_conn *conn_tmp; @@ -230,12 +263,12 @@ void mgcp_conn_free_all(struct mgcp_endpoint *endp) return; } -/*! dump basic connection information to human readble string. +/*! dump basic connection information to human readable string. * \param[in] conn to dump - * \returns human readble string */ + * \returns human readable string */ char *mgcp_conn_dump(struct mgcp_conn *conn) { - static char str[256]; + static char str[sizeof(conn->name)+sizeof(conn->id)+256]; if (!conn) { snprintf(str, sizeof(str), "(null connection)"); @@ -245,7 +278,7 @@ char *mgcp_conn_dump(struct mgcp_conn *conn) switch (conn->type) { case MGCP_CONN_TYPE_RTP: /* Dump RTP connection */ - snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, " + snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, " "rtp:%u rtcp:%u)", conn->name, conn->id, diff --git a/src/libosmo-mgcp/mgcp_ep.c b/src/libosmo-mgcp/mgcp_endp.c index 72ca691..fa2dd28 100644 --- a/src/libosmo-mgcp/mgcp_ep.c +++ b/src/libosmo-mgcp/mgcp_endp.c @@ -21,12 +21,36 @@ * */ -#include <osmocom/mgcp/mgcp_ep.h> #include <osmocom/mgcp/mgcp_internal.h> +#include <osmocom/mgcp/mgcp_endp.h> /* Endpoint typeset definition */ const struct mgcp_endpoint_typeset ep_typeset = { /* Specify endpoint properties for RTP endpoint */ .rtp.max_conns = 2, - .rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb + .rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb, + .rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb }; + +/*! release endpoint, all open connections are closed. + * \param[in] endp endpoint to release */ +void mgcp_endp_release(struct mgcp_endpoint *endp) +{ + LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:0x%x\n", + ENDPOINT_NUMBER(endp)); + + /* Normally this function should only be called when + * all connections have been removed already. In case + * that there are still connections open (e.g. when + * RSIP is executed), free them all at once. */ + mgcp_conn_free_all(endp); + + /* Reset endpoint parameters and states */ + talloc_free(endp->callid); + endp->callid = NULL; + talloc_free(endp->local_options.string); + endp->local_options.string = NULL; + talloc_free(endp->local_options.codec); + endp->local_options.codec = NULL; + endp->wildcarded_req = false; +} diff --git a/src/libosmo-mgcp/mgcp_msg.c b/src/libosmo-mgcp/mgcp_msg.c index d686bca..8d22cc5 100644 --- a/src/libosmo-mgcp/mgcp_msg.c +++ b/src/libosmo-mgcp/mgcp_msg.c @@ -28,6 +28,7 @@ #include <osmocom/mgcp/mgcp_common.h> #include <osmocom/mgcp/mgcp_msg.h> #include <osmocom/mgcp/mgcp_conn.h> +#include <osmocom/mgcp/mgcp_endp.h> /*! Display an mgcp message on the log output. * \param[in] message mgcp message string @@ -82,7 +83,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, if (!mode) { LOGP(DLMGCP, LOGL_ERROR, - "endpoint:%x missing connection mode\n", + "endpoint:0x%x missing connection mode\n", ENDPOINT_NUMBER(endp)); return -1; } @@ -101,7 +102,7 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, conn->mode = MGCP_CONN_LOOPBACK; else { LOGP(DLMGCP, LOGL_ERROR, - "endpoint:%x unknown connection mode: '%s'\n", + "endpoint:0x%x unknown connection mode: '%s'\n", ENDPOINT_NUMBER(endp), mode); ret = -1; } @@ -113,16 +114,16 @@ int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp, } LOGP(DLMGCP, LOGL_DEBUG, - "endpoint:%x conn:%s\n", + "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn)); LOGP(DLMGCP, LOGL_DEBUG, - "endpoint:%x connection mode '%s' %d\n", + "endpoint:0x%x connection mode '%s' %d\n", ENDPOINT_NUMBER(endp), mode, conn->mode); /* Special handling für RTP connections */ if (conn->type == MGCP_CONN_TYPE_RTP) { - LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x output_enabled %d\n", + LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x output_enabled %d\n", ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled); } @@ -142,6 +143,7 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, char *rest = NULL; struct mgcp_trunk_config *tcfg; int trunk, endp; + struct mgcp_endpoint *endp_ptr; trunk = strtoul(mgcp + 6, &rest, 10); if (rest == NULL || rest[0] != '/' || trunk < 1) { @@ -178,25 +180,112 @@ static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, return NULL; } - return &tcfg->endpoints[endp]; + endp_ptr = &tcfg->endpoints[endp]; + endp_ptr->wildcarded_req = false; + return endp_ptr; +} + +/* Find an endpoint that is not in use. Do this by going through the endpoint + * array, check the callid. A callid nullpointer indicates that the endpoint + * is free */ +static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints, + unsigned int number_endpoints) +{ + struct mgcp_endpoint *endp; + unsigned int i; + + for (i = 0; i < number_endpoints; i++) { + if (endpoints[i].callid == NULL) { + endp = &endpoints[i]; + LOGP(DLMGCP, LOGL_DEBUG, + "endpoint:0x%x found free endpoint\n", + ENDPOINT_NUMBER(endp)); + endp->wildcarded_req = true; + return endp; + } + } + + LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n"); + return NULL; +} + +/* Check if the domain name, which is supplied with the endpoint name + * matches the configuration. */ +static int check_domain_name(struct mgcp_config *cfg, const char *mgcp) +{ + char *domain_to_check; + + domain_to_check = strstr(mgcp, "@"); + if (!domain_to_check) + return -EINVAL; + + if (strcmp(domain_to_check+1, cfg->domain) != 0) + return -EINVAL; + + return 0; } /* Search the endpoint pool for the endpoint that had been selected via the * MGCP message (helper function for mgcp_analyze_header()) */ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, - const char *mgcp) + const char *mgcp, + int *cause) { char *endptr = NULL; unsigned int gw = INT_MAX; + const char *endpoint_number_str; + struct mgcp_endpoint *endp; + + *cause = 0; + + /* Check if the domainname in the request is correct */ + if (check_domain_name(cfg, mgcp)) { + LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s'\n", mgcp); + return NULL; + } - if (strncmp(mgcp, "ds/e1", 5) == 0) - return find_e1_endpoint(cfg, mgcp); + /* Check if the E1 trunk is requested */ + if (strncmp(mgcp, "ds/e1", 5) == 0) { + endp = find_e1_endpoint(cfg, mgcp); + if (!endp) + *cause = -500; + return endp; + } + + /* Check if the virtual trunk is addressed (new, correct way with prefix) */ + if (strncmp + (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) { + endpoint_number_str = + mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK); + if (endpoint_number_str[0] == '*') { + endp = find_free_endpoint(cfg->trunk.endpoints, + cfg->trunk.number_endpoints); + if (!endp) + *cause = -403; + return endp; + } + gw = strtoul(endpoint_number_str, &endptr, 16); + if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { + endp = &cfg->trunk.endpoints[gw]; + endp->wildcarded_req = false; + return endp; + } + } + /* Deprecated method without prefix */ + LOGP(DLMGCP, LOGL_NOTICE, + "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n", + MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp); gw = strtoul(mgcp, &endptr, 16); - if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@') - return &cfg->trunk.endpoints[gw]; + if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') { + endp = &cfg->trunk.endpoints[gw]; + endp->wildcarded_req = false; + return endp; + } LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); + *cause = -500; return NULL; } @@ -209,6 +298,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) { int i = 0; char *elem, *save = NULL; + int cause; /*! This function will parse the header part of the received * MGCP message. The parsing results are stored in pdata. @@ -226,25 +316,25 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) pdata->trans = elem; break; case 1: - pdata->endp = find_endpoint(pdata->cfg, elem); + pdata->endp = find_endpoint(pdata->cfg, elem, &cause); if (!pdata->endp) { LOGP(DLMGCP, LOGL_ERROR, "Unable to find Endpoint `%s'\n", elem); - return -1; + return cause; } break; case 2: if (strcmp("MGCP", elem)) { LOGP(DLMGCP, LOGL_ERROR, "MGCP header parsing error\n"); - return -1; + return -510; } break; case 3: if (strcmp("1.0", elem)) { LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' " "not supported\n", elem); - return -1; + return -528; } break; } @@ -255,7 +345,7 @@ int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data) LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n"); pdata->trans = "000000"; pdata->endp = NULL; - return -1; + return -510; } return 0; @@ -318,7 +408,7 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid) if (strcmp(endp->callid, callid) != 0) { LOGP(DLMGCP, LOGL_ERROR, - "endpoint:%x CallIDs does not match '%s' != '%s'\n", + "endpoint:0x%x CallIDs does not match '%s' != '%s'\n", ENDPOINT_NUMBER(endp), endp->callid, callid); return -1; } @@ -330,21 +420,39 @@ int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid) * \param[in] endp pointer to endpoint * \param{in] connection id to verify * \returns 1 when connection id seems plausible, 0 on error */ -int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci) +int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id) { - uint32_t id; + /* Check for null identifiers */ + if (!conn_id) { + LOGP(DLMGCP, LOGL_ERROR, + "endpoint:0x%x invalid ConnectionIdentifier (missing)\n", + ENDPOINT_NUMBER(endp)); + return -1; + } - if (!endp) + /* Check for empty connection identifiers */ + if (strlen(conn_id) == 0) { + LOGP(DLMGCP, LOGL_ERROR, + "endpoint:0x%x invalid ConnectionIdentifier (empty)\n", + ENDPOINT_NUMBER(endp)); return -1; + } - id = strtoul(ci, NULL, 10); + /* Check for over long connection identifiers */ + if (strlen(conn_id) > MGCP_CONN_ID_LENGTH) { + LOGP(DLMGCP, LOGL_ERROR, + "endpoint:0x%x invalid ConnectionIdentifier (too long) 0x%s\n", + ENDPOINT_NUMBER(endp), conn_id); + return -1; + } - if (mgcp_conn_get(endp, id)) + /* Check if connection exists */ + if (mgcp_conn_get(endp, conn_id)) return 0; LOGP(DLMGCP, LOGL_ERROR, - "endpoint:%x No connection found under ConnectionIdentifier %u\n", - ENDPOINT_NUMBER(endp), id); + "endpoint:0x%x no connection found under ConnectionIdentifier 0x%s\n", + ENDPOINT_NUMBER(endp), conn_id); return -1; } @@ -386,20 +494,3 @@ char *mgcp_strline(char *str, char **saveptr) return result; } - -/*! Parse CI from a given string. - * \param[out] caller provided memory to store the result - * \param{in] string containing the connection id - * \returns 0 on success, -1 on error */ -int mgcp_parse_ci(uint32_t *conn_id, const char *ci) -{ - - OSMO_ASSERT(conn_id); - - if (!ci) - return -1; - - *conn_id = strtoul(ci, NULL, 10); - - return 0; -} diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c index d51b829..6923b97 100644 --- a/src/libosmo-mgcp/mgcp_network.c +++ b/src/libosmo-mgcp/mgcp_network.c @@ -27,7 +27,6 @@ #include <errno.h> #include <time.h> #include <limits.h> -#include <sys/socket.h> #include <arpa/inet.h> #include <osmocom/core/msgb.h> @@ -40,7 +39,7 @@ #include <osmocom/mgcp/mgcp_stat.h> #include <osmocom/mgcp/osmux.h> #include <osmocom/mgcp/mgcp_conn.h> -#include <osmocom/mgcp/mgcp_ep.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <osmocom/mgcp/debug.h> #define RTP_SEQ_MOD (1 << 16) @@ -73,11 +72,11 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) rc = osmo_sock_local_ip(addr, inet_ntoa(conn->end.addr)); if (rc < 0) LOGP(DRTP, LOGL_ERROR, - "endpoint:%x CI:%i local interface auto detection failed, using configured addresses...\n", + "endpoint:0x%x CI:%s local interface auto detection failed, using configured addresses...\n", ENDPOINT_NUMBER(endp), conn->conn->id); else { LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x CI:%i selected local rtp bind ip %s by probing using remote ip %s\n", + "endpoint:0x%x CI:%s selected local rtp bind ip %s by probing using remote ip %s\n", ENDPOINT_NUMBER(endp), conn->conn->id, addr, inet_ntoa(conn->end.addr)); return; @@ -88,17 +87,17 @@ void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn) if (endp->cfg->net_ports.bind_addr) { /* Check there is a bind IP for the RTP traffic configured, * if so, use that IP-Address */ - strncpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN); + osmo_strlcpy(addr, endp->cfg->net_ports.bind_addr, INET_ADDRSTRLEN); LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x CI:%i using configured rtp bind ip as local bind ip %s\n", + "endpoint:0x%x CI:%s using configured rtp bind ip as local bind ip %s\n", ENDPOINT_NUMBER(endp), conn->conn->id, addr); } else { /* No specific bind IP is configured for the RTP traffic, so * assume the IP where we listen for incoming MGCP messages * as bind IP */ - strncpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN); + osmo_strlcpy(addr, endp->cfg->source_addr, INET_ADDRSTRLEN); LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x CI:%i using mgcp bind ip as local rtp bind ip: %s\n", + "endpoint:0x%x CI:%s using mgcp bind ip as local rtp bind ip: %s\n", ENDPOINT_NUMBER(endp), conn->conn->id, addr); } } @@ -162,8 +161,8 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) OSMO_ASSERT(conn); LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x sending dummy packet...\n", ENDPOINT_NUMBER(endp)); - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x conn:%s\n", + "endpoint:0x%x sending dummy packet...\n", ENDPOINT_NUMBER(endp)); + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); rc = mgcp_udp_send(conn->end.rtp.fd, &conn->end.addr, @@ -184,7 +183,7 @@ int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) failed: LOGP(DRTP, LOGL_ERROR, - "endpoint:%x Failed to send dummy %s packet.\n", + "endpoint:0x%x Failed to send dummy %s packet.\n", ENDPOINT_NUMBER(endp), was_rtcp ? "RTCP" : "RTP"); return -1; @@ -231,7 +230,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp, "on 0x%x SSRC: %u timestamp: %u " "from %s:%d\n", text, seq, - state->timestamp_offset, state->seq_offset, + state->patch.timestamp_offset, state->patch.seq_offset, ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } @@ -325,15 +324,15 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta; timestamp_offset = out_timestamp - in_timestamp; - if (state->timestamp_offset != timestamp_offset) { - state->timestamp_offset = timestamp_offset; + if (state->patch.timestamp_offset != timestamp_offset) { + state->patch.timestamp_offset = timestamp_offset; LOGP(DRTP, LOGL_NOTICE, "Timestamp offset change on 0x%x SSRC: %u " "SeqNo delta: %d, TS offset: %d, " "from %s:%d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - delta_seq, state->timestamp_offset, + delta_seq, state->patch.timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } @@ -354,11 +353,11 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, /* Align according to: T + Toffs - Tlast = k * Tptime */ ts_error = ts_alignment_error(&state->out_stream, ptime, - timestamp + state->timestamp_offset); + timestamp + state->patch.timestamp_offset); /* If there is an alignment error, we have to compensate it */ if (ts_error) { - state->timestamp_offset += ptime - ts_error; + state->patch.timestamp_offset += ptime - ts_error; LOGP(DRTP, LOGL_NOTICE, "Corrected timestamp alignment error of %d on 0x%x SSRC: %u " @@ -366,16 +365,16 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, "from %s:%d\n", ts_error, ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->timestamp_offset, inet_ntoa(addr->sin_addr), + state->patch.timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } /* Check we really managed to compensate the timestamp * offset. There should not be any remaining error, failing - * here would point to a serous problem with the alingnment - * error computation fuction */ + * here would point to a serous problem with the alignment + * error computation function */ ts_check = ts_alignment_error(&state->out_stream, ptime, - timestamp + state->timestamp_offset); + timestamp + state->patch.timestamp_offset); OSMO_ASSERT(ts_check == 0); /* Return alignment error before compensation */ @@ -387,13 +386,13 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, * \param[in] destination RTP end * \param[in,out] pointer to buffer with voice data * \param[in] voice data length - * \param[in] maxmimum size of caller provided voice data buffer + * \param[in] maximum size of caller provided voice data buffer * \returns ignores input parameters, return always 0 */ int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size) { - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n", + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n", ENDPOINT_NUMBER(endp)); return 0; } @@ -407,7 +406,7 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, struct mgcp_rtp_end *src_end) { - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x transcoding disabled\n", + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x transcoding disabled\n", ENDPOINT_NUMBER(endp)); return 0; } @@ -419,7 +418,7 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) { LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x conn:%s using format defaults\n", + "endpoint:0x%x conn:%s using format defaults\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); *payload_type = conn->end.codec.payload_type; @@ -434,14 +433,14 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, int32_t d; /* initialize or re-initialize */ - if (!state->stats_initialized || state->stats_ssrc != ssrc) { - state->stats_initialized = 1; - state->stats_base_seq = seq; - state->stats_max_seq = seq - 1; - state->stats_ssrc = ssrc; - state->stats_jitter = 0; - state->stats_transit = transit; - state->stats_cycles = 0; + if (!state->stats.initialized || state->stats.ssrc != ssrc) { + state->stats.initialized = 1; + state->stats.base_seq = seq; + state->stats.max_seq = seq - 1; + state->stats.ssrc = ssrc; + state->stats.jitter = 0; + state->stats.transit = transit; + state->stats.cycles = 0; } else { uint16_t udelta; @@ -452,10 +451,10 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, * It can't wrap during the initialization so let's * skip it here. The Appendix A probably doesn't have * this issue because of the probation. */ - udelta = seq - state->stats_max_seq; + udelta = seq - state->stats.max_seq; if (udelta < RTP_MAX_DROPOUT) { - if (seq < state->stats_max_seq) - state->stats_cycles += RTP_SEQ_MOD; + if (seq < state->stats.max_seq) + state->stats.cycles += RTP_SEQ_MOD; } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) { LOGP(DRTP, LOGL_NOTICE, "RTP seqno made a very large jump on 0x%x delta: %u\n", @@ -467,12 +466,12 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, * taken closer to the read function. This was taken from the * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate * resolution. */ - d = transit - state->stats_transit; - state->stats_transit = transit; + d = transit - state->stats.transit; + state->stats.transit = transit; if (d < 0) d = -d; - state->stats_jitter += d - ((state->stats_jitter + 8) >> 4); - state->stats_max_seq = seq; + state->stats.jitter += d - ((state->stats.jitter + 8) >> 4); + state->stats.max_seq = seq; } /* The RFC 3550 Appendix A assumes there are multiple sources but @@ -508,7 +507,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, if (!state->initialized) { state->initialized = 1; state->in_stream.last_seq = seq - 1; - state->in_stream.ssrc = state->orig_ssrc = ssrc; + state->in_stream.ssrc = state->patch.orig_ssrc = ssrc; state->in_stream.last_tsdelta = 0; state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end); @@ -516,23 +515,23 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, state->out_stream.last_timestamp = timestamp; state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */ LOGP(DRTP, LOGL_INFO, - "endpoint:%x initializing stream, SSRC: %u timestamp: %u " + "endpoint:0x%x initializing stream, SSRC: %u timestamp: %u " "pkt-duration: %d, from %s:%d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, state->packet_duration, + state->patch.seq_offset, state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); if (state->packet_duration == 0) { state->packet_duration = rtp_end->codec.rate * 20 / 1000; LOGP(DRTP, LOGL_NOTICE, - "endpoint:%x fixed packet duration is not available, " + "endpoint:0x%x fixed packet duration is not available, " "using fixed 20ms instead: %d from %s:%d\n", ENDPOINT_NUMBER(endp), state->packet_duration, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } } else if (state->in_stream.ssrc != ssrc) { LOGP(DRTP, LOGL_NOTICE, - "endpoint:%x SSRC changed: %u -> %u " + "endpoint:0x%x SSRC changed: %u -> %u " "from %s:%d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, rtp_hdr->ssrc, @@ -543,7 +542,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, int16_t delta_seq; /* Always increment seqno by 1 */ - state->seq_offset = + state->patch.seq_offset = (state->out_stream.last_seq + 1) - seq; /* Estimate number of packets that would have been sent */ @@ -555,17 +554,17 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, delta_seq, timestamp); - state->patch_ssrc = 1; - ssrc = state->orig_ssrc; + state->patch.patch_ssrc = 1; + ssrc = state->patch.orig_ssrc; if (rtp_end->force_constant_ssrc != -1) rtp_end->force_constant_ssrc -= 1; LOGP(DRTP, LOGL_NOTICE, - "endpoint:%x SSRC patching enabled, SSRC: %u " + "endpoint:0x%x SSRC patching enabled, SSRC: %u " "SeqNo offset: %d, TS offset: %d " "from %s:%d\n", ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, state->timestamp_offset, + state->patch.seq_offset, state->patch.timestamp_offset, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); } @@ -576,8 +575,8 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, addr, seq, timestamp, "input", &state->in_stream.last_tsdelta); - if (state->patch_ssrc) - ssrc = state->orig_ssrc; + if (state->patch.patch_ssrc) + ssrc = state->patch.orig_ssrc; } /* Save before patching */ @@ -592,15 +591,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, timestamp); /* Store the updated SSRC back to the packet */ - if (state->patch_ssrc) + if (state->patch.patch_ssrc) rtp_hdr->ssrc = htonl(ssrc); /* Apply the offset and store it back to the packet. * This won't change anything if the offset is 0, so the conditional is * omitted. */ - seq += state->seq_offset; + seq += state->patch.seq_offset; rtp_hdr->sequence = htons(seq); - timestamp += state->timestamp_offset; + timestamp += state->patch.timestamp_offset; rtp_hdr->timestamp = htonl(timestamp); /* Check again, whether the timestamps are still valid */ @@ -619,7 +618,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, #if 0 DEBUGP(DRTP, - "endpoint:%x payload hdr payload %u -> endp payload %u\n", + "endpoint:0x%x payload hdr payload %u -> endp payload %u\n", ENDPOINT_NUMBER(endp), rtp_hdr->payload_type, payload); rtp_hdr->payload_type = payload; #endif @@ -670,16 +669,16 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, if (is_rtp) { LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x delivering RTP packet...\n", + "endpoint:0x%x delivering RTP packet...\n", ENDPOINT_NUMBER(endp)); } else { LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x delivering RTCP packet...\n", + "endpoint:0x%x delivering RTCP packet...\n", ENDPOINT_NUMBER(endp)); } LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x loop:%d, mode:%d ", + "endpoint:0x%x loop:%d, mode:%d ", ENDPOINT_NUMBER(endp), tcfg->audio_loop, conn_src->conn->mode); if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) LOGPC(DRTP, LOGL_DEBUG, "(loopback)\n"); @@ -693,9 +692,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, dest_name = conn_dst->conn->name; if (!rtp_end->output_enabled) { - rtp_end->dropped_packets += 1; + rtp_end->stats.dropped_packets += 1; LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x output disabled, drop to %s %s " + "endpoint:0x%x output disabled, drop to %s %s " "rtp_port:%u rtcp_port:%u\n", ENDPOINT_NUMBER(endp), dest_name, @@ -718,7 +717,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, buflen); LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x process/send to %s %s " + "endpoint:0x%x process/send to %s %s " "rtp_port:%u rtcp_port:%u\n", ENDPOINT_NUMBER(endp), dest_name, inet_ntoa(rtp_end->addr), ntohs(rtp_end->rtp_port), @@ -748,8 +747,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, if (len <= 0) return len; - conn_dst->end.packets_tx += 1; - conn_dst->end.octets_tx += len; + conn_dst->end.stats.packets_tx += 1; + conn_dst->end.stats.octets_tx += len; nbytes += len; buflen = cont; @@ -757,7 +756,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, return nbytes; } else if (!tcfg->omit_rtcp) { LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x send to %s %s rtp_port:%u rtcp_port:%u\n", + "endpoint:0x%x send to %s %s rtp_port:%u rtcp_port:%u\n", ENDPOINT_NUMBER(endp), dest_name, inet_ntoa(rtp_end->addr), @@ -768,8 +767,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr, &rtp_end->addr, rtp_end->rtcp_port, buf, len); - conn_dst->end.packets_tx += 1; - conn_dst->end.octets_tx += len; + conn_dst->end.stats.packets_tx += 1; + conn_dst->end.stats.octets_tx += len; return len; } @@ -804,13 +803,13 @@ static int receive_from(struct mgcp_endpoint *endp, int fd, if (rc < 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x failed to receive packet, errno: %d/%s\n", + "endpoint:0x%x failed to receive packet, errno: %d/%s\n", ENDPOINT_NUMBER(endp), errno, strerror(errno)); return -1; } if (tossed) { - LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n", + LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp)); } @@ -830,11 +829,11 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, if (memcmp(&addr->sin_addr, &conn->end.addr, sizeof(addr->sin_addr)) != 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x data from wrong address: %s, ", + "endpoint:0x%x data from wrong address: %s, ", ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr)); LOGPC(DRTP, LOGL_ERROR, "expected: %s\n", inet_ntoa(conn->end.addr)); - LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n", + LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp)); return -1; } @@ -846,12 +845,12 @@ static int check_rtp_origin(struct mgcp_conn_rtp *conn, if (conn->end.rtp_port != addr->sin_port && conn->end.rtcp_port != addr->sin_port) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x data from wrong source port: %d, ", + "endpoint:0x%x data from wrong source port: %d, ", ENDPOINT_NUMBER(endp), ntohs(addr->sin_port)); LOGPC(DRTP, LOGL_ERROR, "expected: %d for RTP or %d for RTCP\n", ntohs(conn->end.rtp_port), ntohs(conn->end.rtcp_port)); - LOGP(DRTP, LOGL_ERROR, "endpoint:%x packet tossed\n", + LOGP(DRTP, LOGL_ERROR, "endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp)); return -1; } @@ -868,14 +867,14 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn) if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x destination IP-address is invalid\n", + "endpoint:0x%x destination IP-address is invalid\n", ENDPOINT_NUMBER(endp)); return -1; } if (conn->end.rtp_port == 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x destination rtp port is invalid\n", + "endpoint:0x%x destination rtp port is invalid\n", ENDPOINT_NUMBER(endp)); return -1; } @@ -897,7 +896,7 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf, endp = conn->conn->endp; tcfg = endp->tcfg; - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x receiving RTP/RTCP packet...\n", + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x receiving RTP/RTCP packet...\n", ENDPOINT_NUMBER(endp)); rc = receive_from(endp, fd->fd, addr, buf, buf_size); @@ -905,11 +904,11 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf, return -1; *proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x ", ENDPOINT_NUMBER(endp)); - LOGPC(DRTP, LOGL_DEBUG, "receiveing from %s %s %d\n", + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x ", ENDPOINT_NUMBER(endp)); + LOGPC(DRTP, LOGL_DEBUG, "receiving from %s %s %d\n", conn->conn->name, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x conn:%s\n", ENDPOINT_NUMBER(endp), + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); /* Check if the origin of the RTP packet seems plausible */ @@ -921,16 +920,16 @@ static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf, /* Filter out dummy message */ if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { LOGP(DRTP, LOGL_NOTICE, - "endpoint:%x dummy message received\n", + "endpoint:0x%x dummy message received\n", ENDPOINT_NUMBER(endp)); LOGP(DRTP, LOGL_ERROR, - "endpoint:%x packet tossed\n", ENDPOINT_NUMBER(endp)); + "endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp)); return 0; } /* Increment RX statistics */ - conn->end.packets_rx += 1; - conn->end.octets_rx += rc; + conn->end.stats.packets_rx += 1; + conn->end.stats.octets_rx += rc; /* Forward a copy of the RTP data to a debug ip/port */ forward_data(fd->fd, &conn->tap_in, buf, rc); @@ -948,7 +947,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, struct mgcp_endpoint *endp; endp = conn_src->conn->endp; - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x destin conn:%s\n", + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn)); /* Before we try to deliver the packet, we check if the destination @@ -962,7 +961,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, switch (conn_dst->type) { case MGCP_RTP_DEFAULT: LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x endpoint type is MGCP_RTP_DEFAULT, " + "endpoint:0x%x endpoint type is MGCP_RTP_DEFAULT, " "using mgcp_send() to forward data directly\n", ENDPOINT_NUMBER(endp)); return mgcp_send(endp, proto == MGCP_PROTO_RTP, @@ -970,7 +969,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, case MGCP_OSMUX_BSC_NAT: case MGCP_OSMUX_BSC: LOGP(DRTP, LOGL_DEBUG, - "endpoint:%x endpoint type is MGCP_OSMUX_BSC_NAT, " + "endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, " "using osmux_xfrm_to_osmux() to forward data through OSMUX\n", ENDPOINT_NUMBER(endp)); return osmux_xfrm_to_osmux(buf, buf_size, conn_dst); @@ -980,7 +979,7 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf, * be discarded, this should not happen, normally the MGCP type * should be properly set */ LOGP(DRTP, LOGL_ERROR, - "endpoint:%x bad MGCP type -- data discarded!\n", + "endpoint:0x%x bad MGCP type -- data discarded!\n", ENDPOINT_NUMBER(endp)); return -1; @@ -1025,7 +1024,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, /* There is no destination conn, stop here */ if (!conn_dst) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x unable to find destination conn\n", + "endpoint:0x%x unable to find destination conn\n", ENDPOINT_NUMBER(endp)); return -1; } @@ -1033,7 +1032,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, /* The destination conn is not an RTP connection */ if (conn_dst->type != MGCP_CONN_TYPE_RTP) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x unable to find suitable destination conn\n", + "endpoint:0x%x unable to find suitable destination conn\n", ENDPOINT_NUMBER(endp)); return -1; } @@ -1044,6 +1043,25 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf, } +/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed. + * \param[in] endp Endpoint on which the connection resides. + * \param[in] conn Connection that is about to be removed (ignored). + * \returns 0 on success, -1 on ERROR. */ +void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn) +{ + struct mgcp_conn *conn_cleanup; + + /* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the + * pointer to the destination connection, so that we do not have + * to go through the list every time an RTP packet arrives. To prevent + * a use-after-free situation we invalidate this information for all + * connections present when one connection is removed from the + * endpoint. */ + llist_for_each_entry(conn_cleanup, &endp->conns, entry) { + conn_cleanup->priv = NULL; + } +} + /* Handle incoming RTP data from NET */ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) { @@ -1067,7 +1085,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) endp = conn_src->conn->endp; OSMO_ASSERT(endp); - LOGP(DRTP, LOGL_DEBUG, "endpoint:%x source conn:%s\n", + LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x source conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_src->conn)); /* Receive packet */ @@ -1077,7 +1095,16 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) /* Check if the connection is in loopback mode, if yes, just send the * incoming data back to the origin */ + if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) { + /* When we are in loopback mode, we loop back all incoming + * packets back to their origin. We will use the originating + * address data from the UDP packet header to patch the + * outgoing address in connection on the fly */ + if (conn_src->end.rtp_port == 0) { + conn_src->end.addr = addr.sin_addr; + conn_src->end.rtp_port = addr.sin_port; + } return mgcp_send_rtp(proto, &addr, buf, len, conn_src, conn_src); } @@ -1109,41 +1136,17 @@ int mgcp_set_ip_tos(int fd, int tos) * \returns 0 on success, -1 on ERROR */ int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port) { - struct sockaddr_in addr; - int on = 1; - - fd->fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd->fd < 0) { - LOGP(DRTP, LOGL_ERROR, "failed to create UDP port (%s:%i).\n", - source_addr, port); - return -1; - } else { - LOGP(DRTP, LOGL_DEBUG, - "created UDP port (%s:%i).\n", source_addr, port); - } - - if (setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) { - LOGP(DRTP, LOGL_ERROR, - "failed to set socket options (%s:%i).\n", source_addr, - port); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - inet_aton(source_addr, &addr.sin_addr); + int rc; - if (bind(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - close(fd->fd); - fd->fd = -1; + rc = osmo_sock_init2(AF_INET, SOCK_DGRAM, IPPROTO_UDP, source_addr, port, + NULL, 0, OSMO_SOCK_F_BIND); + if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "failed to bind UDP port (%s:%i).\n", source_addr, port); return -1; - } else { - LOGP(DRTP, LOGL_DEBUG, - "bound UDP port (%s:%i).\n", source_addr, port); } + fd->fd = rc; + LOGP(DRTP, LOGL_DEBUG, "created socket + bound UDP port (%s:%i).\n", source_addr, port); return 0; } @@ -1158,7 +1161,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, if (mgcp_create_bind(source_addr, &rtp_end->rtp, rtp_end->local_port) != 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x failed to create RTP port: %s:%d\n", endpno, + "endpoint:0x%x failed to create RTP port: %s:%d\n", endpno, source_addr, rtp_end->local_port); goto cleanup0; } @@ -1166,7 +1169,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, if (mgcp_create_bind(source_addr, &rtp_end->rtcp, rtp_end->local_port + 1) != 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x failed to create RTCP port: %s:%d\n", endpno, + "endpoint:0x%x failed to create RTCP port: %s:%d\n", endpno, source_addr, rtp_end->local_port + 1); goto cleanup1; } @@ -1178,7 +1181,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, rtp_end->rtp.when = BSC_FD_READ; if (osmo_fd_register(&rtp_end->rtp) != 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x failed to register RTP port %d\n", endpno, + "endpoint:0x%x failed to register RTP port %d\n", endpno, rtp_end->local_port); goto cleanup2; } @@ -1186,7 +1189,7 @@ static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, rtp_end->rtcp.when = BSC_FD_READ; if (osmo_fd_register(&rtp_end->rtcp) != 0) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x failed to register RTCP port %d\n", endpno, + "endpoint:0x%x failed to register RTCP port %d\n", endpno, rtp_end->local_port + 1); goto cleanup3; } @@ -1217,12 +1220,12 @@ int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, struct mgcp_rtp_end *end; char local_ip_addr[INET_ADDRSTRLEN]; - snprintf(name, sizeof(name), "%s-%u", conn->conn->name, conn->conn->id); + snprintf(name, sizeof(name), "%s-%s", conn->conn->name, conn->conn->id); end = &conn->end; if (end->rtp.fd != -1 || end->rtcp.fd != -1) { LOGP(DRTP, LOGL_ERROR, - "endpoint:%x %u was already bound on conn:%s\n", + "endpoint:0x%x %u was already bound on conn:%s\n", ENDPOINT_NUMBER(endp), rtp_port, mgcp_conn_dump(conn->conn)); diff --git a/src/libosmo-mgcp/mgcp_osmux.c b/src/libosmo-mgcp/mgcp_osmux.c index 60ffe06..1d3cab3 100644 --- a/src/libosmo-mgcp/mgcp_osmux.c +++ b/src/libosmo-mgcp/mgcp_osmux.c @@ -24,6 +24,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/osmux.h> #include <osmocom/mgcp/mgcp_conn.h> +#include <osmocom/mgcp/mgcp_endp.h> static struct osmo_fd osmux_fd; @@ -142,7 +143,7 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) } /* Lookup existing handle for a specified address, if the handle can not be - * foud a the function will automatically allocate one */ + * found, the function will automatically allocate one */ static struct osmux_in_handle * osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) { @@ -207,12 +208,18 @@ endpoint_lookup(struct mgcp_config *cfg, int cid, case MGCP_DEST_NET: /* FIXME: Get rid of CONN_ID_XXX! */ conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET); - this = &conn_net->end.addr; + if (conn_net) + this = &conn_net->end.addr; + else + this = NULL; break; case MGCP_DEST_BTS: /* FIXME: Get rid of CONN_ID_XXX! */ conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS); - this = &conn_bts->end.addr; + if (conn_bts) + this = &conn_bts->end.addr; + else + this = NULL; break; default: /* Should not ever happen */ @@ -222,7 +229,8 @@ endpoint_lookup(struct mgcp_config *cfg, int cid, /* FIXME: Get rid of CONN_ID_XXX! */ conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET); - if (conn_net->osmux.cid == cid && this->s_addr == from_addr->s_addr) + if (conn_net && this && conn_net->osmux.cid == cid + && this->s_addr == from_addr->s_addr) return endp; } @@ -248,8 +256,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data) .sin_port = conn_net->end.rtp_port, }; - conn_bts->end.octets_tx += msg->len; - conn_bts->end.packets_tx++; + conn_bts->end.stats.octets_tx += msg->len; + conn_bts->end.stats.packets_tx++; /* Send RTP data to NET */ /* FIXME: Get rid of conn_bts and conn_net! */ @@ -275,8 +283,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data) .sin_port = conn_bts->end.rtp_port, }; - conn_net->end.octets_tx += msg->len; - conn_net->end.packets_tx++; + conn_net->end.stats.octets_tx += msg->len; + conn_net->end.stats.packets_tx++; /* Send RTP data to BTS */ /* FIXME: Get rid of conn_bts and conn_net! */ @@ -566,7 +574,7 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn) if (conn->osmux.state != OSMUX_STATE_ENABLED) return; - LOGP(DLMGCP, LOGL_INFO, "Releasing connection %u using Osmux CID %u\n", + LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n", conn->conn->id, conn->osmux.cid); osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid); conn->osmux.state = OSMUX_STATE_DISABLED; diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c index 8c6bd6e..ea86672 100644 --- a/src/libosmo-mgcp/mgcp_protocol.c +++ b/src/libosmo-mgcp/mgcp_protocol.c @@ -38,7 +38,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_stat.h> #include <osmocom/mgcp/mgcp_msg.h> -#include <osmocom/mgcp/mgcp_ep.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <osmocom/mgcp/mgcp_sdp.h> struct mgcp_request { @@ -192,17 +192,41 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp, return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL); } +/* Add MGCP parameters to a message buffer */ +static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp, + const struct mgcp_conn_rtp *conn) +{ + int rc; + + /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */ + if (endp->wildcarded_req + && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) { + rc = msgb_printf(msg, "Z: %s%x@%s\r\n", + MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, + ENDPOINT_NUMBER(endp), endp->cfg->domain); + if (rc < 0) + return -EINVAL; + } + + rc = msgb_printf(msg, "I: %s\r\n", conn->conn->id); + if (rc < 0) + return -EINVAL; + + return 0; +} + /* Format MGCP response string (with SDP attached) */ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, const char *msg, - const char *trans_id) + const char *trans_id, + bool add_conn_params) { const char *addr = endp->cfg->local_ip; struct msgb *sdp; int rc; struct msgb *result; - char osmux_extension[strlen("\nX-Osmux: 255") + 1]; + char osmux_extension[strlen("X-Osmux: 255") + 1]; char local_ip_addr[INET_ADDRSTRLEN]; sdp = msgb_alloc_headroom(4096, 128, "sdp record"); @@ -215,15 +239,28 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, } if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) { - sprintf(osmux_extension, "\nX-Osmux: %u", conn->osmux.cid); + sprintf(osmux_extension, "X-Osmux: %u", conn->osmux.cid); conn->osmux.state = OSMUX_STATE_ACTIVATING; } else { osmux_extension[0] = '\0'; } - rc = msgb_printf(sdp, "I: %u%s\n\n", conn->conn->id, osmux_extension); - if (rc < 0) - goto error; + /* Attach optional connection parameters */ + if (add_conn_params) { + rc = add_params(sdp, endp, conn); + if (rc < 0) + goto error; + } + + /* Attach optional OSMUX parameters */ + if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) { + rc = msgb_printf(sdp, "%s\r\n", osmux_extension); + if (rc < 0) + goto error; + } + + /* Attach line break to separate the parameters from the SDP block */ + rc = msgb_printf(sdp, "\r\n"); rc = mgcp_write_response_sdp(endp, conn, sdp, addr); if (rc < 0) @@ -252,7 +289,7 @@ static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) { struct mgcp_parse_data pdata; - int i, code, handled = 0; + int rc, i, code, handled = 0; struct msgb *resp = NULL; char *data; @@ -280,13 +317,19 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) memset(&pdata, 0, sizeof(pdata)); pdata.cfg = cfg; data = mgcp_strline((char *)msg->l3h, &pdata.save); - pdata.found = mgcp_parse_header(&pdata, data); + rc = mgcp_parse_header(&pdata, data); if (pdata.endp && pdata.trans && pdata.endp->last_trans && strcmp(pdata.endp->last_trans, pdata.trans) == 0) { return do_retransmission(pdata.endp); } + /* check for general parser failure */ + if (rc < 0) { + LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to find the endpoint\n", msg->l2h); + return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans); + } + for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) { if (strncmp (mgcp_requests[i].name, (const char *)&msg->l2h[0], @@ -308,19 +351,13 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p) { LOGP(DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n"); - - if (p->found != 0) { - LOGP(DLMGCP, LOGL_ERROR, - "AUEP: failed to find the endpoint.\n"); - return create_err_response(NULL, 500, "AUEP", p->trans); - } else - return create_ok_response(p->endp, 200, "AUEP", p->trans); + return create_ok_response(p->endp, 200, "AUEP", p->trans); } -/* Try to find a free port by attemting to bind on it. Also handle the +/* Try to find a free port by attempting to bind on it. Also handle the * counter that points on the next free port. Since we have a pointer - * to the next free port, binding should work on the first attemt, - * neverless, try at least the next 200 ports before giving up */ + * to the next free port, binding should work on the first attempt, + * nevertheless, try at least the next 200 ports before giving up */ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) { int i; @@ -356,10 +393,10 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn) } /* Set the LCO from a string (see RFC 3435). - * The string is stored in the 'string' field. A NULL string is handled excatlyy + * The string is stored in the 'string' field. A NULL string is handled exactly * like an empty string, the 'string' field is never NULL after this function * has been called. */ -static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, +static int set_local_cx_options(void *ctx, struct mgcp_lco *lco, const char *options) { char *p_opt, *a_opt; @@ -383,6 +420,15 @@ static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, LOGP(DLMGCP, LOGL_DEBUG, "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n", lco->pkt_period_max, lco->codec); + + /* Check if the packetization fits the 20ms raster */ + if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) { + LOGP(DLMGCP, LOGL_ERROR, + "local CX options: packetization interval is not a multiple of 20ms!\n"); + return 535; + } + + return 0; } void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, @@ -443,19 +489,16 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p) const char *local_options = NULL; const char *callid = NULL; - const char *ci = NULL; const char *mode = NULL; char *line; int have_sdp = 0, osmux_cid = -1; struct mgcp_conn_rtp *conn = NULL; - uint32_t conn_id; + struct mgcp_conn *_conn = NULL; char conn_name[512]; + int rc; LOGP(DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n"); - if (p->found != 0) - return create_err_response(NULL, 510, "CRCX", p->trans); - /* parse CallID C: and LocalParameters L: */ for_each_line(line, p->save) { if (!mgcp_check_param(endp, line)) @@ -469,7 +512,10 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p) callid = (const char *)line + 3; break; case 'I': - ci = (const char *)line + 3; + /* It is illegal to send a connection identifier + * together with a CRCX, the MGW will assign the + * connection identifier by itself on CRCX */ + return create_err_response(NULL, 523, "CRCX", p->trans); break; case 'M': mode = (const char *)line + 3; @@ -489,6 +535,7 @@ static struct msgb *handle_create_con(struct mgcp_parse_data *p) LOGP(DLMGCP, LOGL_NOTICE, "CRCX: endpoint:%x unhandled option: '%c'/%d\n", ENDPOINT_NUMBER(endp), *line, *line); + return create_err_response(NULL, 539, "CRCX", p->trans); break; } } @@ -501,21 +548,14 @@ mgcp_header_done: LOGP(DLMGCP, LOGL_ERROR, "CRCX: endpoint:%x insufficient parameters, missing callid\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "CRCX", p->trans); + return create_err_response(endp, 516, "CRCX", p->trans); } if (!mode) { LOGP(DLMGCP, LOGL_ERROR, "CRCX: endpoint:%x insufficient parameters, missing mode\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "CRCX", p->trans); - } - - if (!ci) { - LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x insufficient parameters, missing connection id\n", - ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "CRCX", p->trans); + return create_err_response(endp, 517, "CRCX", p->trans); } /* Check if we are able to accept the creation of another connection */ @@ -531,7 +571,7 @@ mgcp_header_done: } else { /* There is no more room for a connection, leave * everything as it is and return with an error */ - return create_err_response(endp, 400, "CRCX", p->trans); + return create_err_response(endp, 540, "CRCX", p->trans); } } @@ -539,12 +579,12 @@ mgcp_header_done: * callids match up so that we are sure that this is our call */ if (endp->callid && mgcp_verify_call_id(endp, callid)) { LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x allready seized by other call (%s)\n", + "CRCX: endpoint:0x%x allready seized by other call (%s)\n", ENDPOINT_NUMBER(endp), endp->callid); if (tcfg->force_realloc) /* This is not our call, toss everything by releasing * the entire endpoint. (rude!) */ - mgcp_release_endp(endp); + mgcp_endp_release(endp); else { /* This is not our call, leave everything as it is and * return with an error. */ @@ -553,47 +593,32 @@ mgcp_header_done: } /* Set the callid, creation of another connection will only be possible - * when the callid matches up. (Connections are distinuished by their + * when the callid matches up. (Connections are distinguished by their * connection ids) */ endp->callid = talloc_strdup(tcfg->endpoints, callid); /* Extract audio codec information */ - set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, - local_options); - - if (mgcp_parse_ci(&conn_id, ci)) { + rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, + local_options); + if (rc != 0) { LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x insufficient parameters, missing ci (connectionIdentifier)\n", + "CRCX: endpoint:%x inavlid local connection options!\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "CRCX", p->trans); - } - - /* Only accept another connection when the connection ID is different. */ - if (mgcp_conn_get_rtp(endp, conn_id)) { - LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x there is already a connection with id %u present!\n", - conn_id, ENDPOINT_NUMBER(endp)); - if (tcfg->force_realloc) { - /* Ignore the existing connection by just freeing it */ - mgcp_conn_free(endp, conn_id); - } else { - /* There is already a connection with that ID present, - * leave everything as it is and return with an error. */ - return create_err_response(endp, 400, "CRCX", p->trans); - } + error_code = rc; + goto error2; } - snprintf(conn_name, sizeof(conn_name), "%s-%u", callid, conn_id); - mgcp_conn_alloc(NULL, endp, conn_id, MGCP_CONN_TYPE_RTP, - conn_name); - conn = mgcp_conn_get_rtp(endp, conn_id); - if (!conn) { + snprintf(conn_name, sizeof(conn_name), "%s", callid); + _conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name); + if (!_conn) { LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x unable to allocate RTP connection\n", + "CRCX: endpoint:0x%x unable to allocate RTP connection\n", ENDPOINT_NUMBER(endp)); goto error2; } + conn = mgcp_conn_get_rtp(endp, _conn->id); + OSMO_ASSERT(conn); if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) { error_code = 517; @@ -608,7 +633,7 @@ mgcp_header_done: conn->osmux.state = OSMUX_STATE_NEGOTIATING; } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) { LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x osmux only and no osmux offered\n", + "CRCX: endpoint:0x%x osmux only and no osmux offered\n", ENDPOINT_NUMBER(endp)); goto error2; } @@ -629,13 +654,24 @@ mgcp_header_done: mgcp_rtp_end_config(endp, 0, &conn->end); + /* check connection mode setting */ + if (conn->conn->mode != MGCP_CONN_LOOPBACK + && conn->conn->mode != MGCP_CONN_RECV_ONLY + && conn->end.rtp_port == 0) { + LOGP(DLMGCP, LOGL_ERROR, + "CRCX: endpoint:%x selected connection mode type requires an opposite end!\n", + ENDPOINT_NUMBER(endp)); + error_code = 527; + goto error2; + } + if (allocate_port(endp, conn) != 0) { goto error2; } if (setup_rtp_processing(endp, conn) != 0) { LOGP(DLMGCP, LOGL_ERROR, - "CRCX: endpoint:%x could not start RTP processing!\n", + "CRCX: endpoint:0x%x could not start RTP processing!\n", ENDPOINT_NUMBER(endp)); goto error2; } @@ -648,9 +684,9 @@ mgcp_header_done: switch (rc) { case MGCP_POLICY_REJECT: LOGP(DLMGCP, LOGL_NOTICE, - "CRCX: endpoint:%x CRCX rejected by policy\n", + "CRCX: endpoint:0x%x CRCX rejected by policy\n", ENDPOINT_NUMBER(endp)); - mgcp_release_endp(endp); + mgcp_endp_release(endp); return create_err_response(endp, 400, "CRCX", p->trans); break; case MGCP_POLICY_DEFER: @@ -664,7 +700,7 @@ mgcp_header_done: } LOGP(DLMGCP, LOGL_DEBUG, - "CRCX: endpoint:%x Creating connection: CI: %u port: %u\n", + "CRCX: endpoint:0x%x Creating connection: CI: %s port: %u\n", ENDPOINT_NUMBER(endp), conn->conn->id, conn->end.local_port); if (p->cfg->change_cb) p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); @@ -676,13 +712,13 @@ mgcp_header_done: send_dummy(endp, conn); LOGP(DLMGCP, LOGL_NOTICE, - "CRCX: endpoint:%x connection successfully created\n", + "CRCX: endpoint:0x%x connection successfully created\n", ENDPOINT_NUMBER(endp)); - return create_response_with_sdp(endp, conn, "CRCX", p->trans); + return create_response_with_sdp(endp, conn, "CRCX", p->trans, true); error2: - mgcp_release_endp(endp); + mgcp_endp_release(endp); LOGP(DLMGCP, LOGL_NOTICE, - "CRCX: endpoint:%x unable to create connection resource error\n", + "CRCX: endpoint:0x%x unable to create connection resource error\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, error_code, "CRCX", p->trans); } @@ -695,20 +731,25 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) int silent = 0; int have_sdp = 0; char *line; - const char *ci = NULL; const char *local_options = NULL; const char *mode = NULL; struct mgcp_conn_rtp *conn = NULL; - uint32_t conn_id; + const char *conn_id = NULL; + int rc; LOGP(DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n"); - if (p->found != 0) - return create_err_response(NULL, 510, "MDCX", p->trans); + /* Prohibit wildcarded requests */ + if (endp->wildcarded_req) { + LOGP(DLMGCP, LOGL_ERROR, + "MDCX: endpoint:0x%x wildcarded endpoint names not supported.\n", + ENDPOINT_NUMBER(endp)); + return create_err_response(endp, 507, "MDCX", p->trans); + } if (llist_count(&endp->conns) <= 0) { LOGP(DLMGCP, LOGL_ERROR, - "MDCX: endpoint:%x endpoint is not holding a connection.\n", + "MDCX: endpoint:0x%x endpoint is not holding a connection.\n", ENDPOINT_NUMBER(endp)); return create_err_response(endp, 400, "MDCX", p->trans); } @@ -719,13 +760,17 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) switch (line[0]) { case 'C': - if (mgcp_verify_call_id(endp, line + 3) != 0) + if (mgcp_verify_call_id(endp, line + 3) != 0) { + error_code = 516; goto error3; + } break; case 'I': - ci = (const char *)line + 3; - if (mgcp_verify_ci(endp, ci) != 0) + conn_id = (const char *)line + 3; + if (mgcp_verify_ci(endp, conn_id) != 0) { + error_code = 515; goto error3; + } break; case 'L': local_options = (const char *)line + 3; @@ -742,18 +787,19 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) break; default: LOGP(DLMGCP, LOGL_NOTICE, - "MDCX: endpoint:%x Unhandled MGCP option: '%c'/%d\n", + "MDCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n", ENDPOINT_NUMBER(endp), line[0], line[0]); + return create_err_response(NULL, 539, "MDCX", p->trans); break; } } mgcp_header_done: - if (mgcp_parse_ci(&conn_id, ci)) { + if (!conn_id) { LOGP(DLMGCP, LOGL_ERROR, - "MDCX: endpoint:%x insufficient parameters, missing ci (connectionIdentifier)\n", + "MDCX: endpoint:0x%x insufficient parameters, missing ci (connectionIdentifier)\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "MDCX", p->trans); + return create_err_response(endp, 515, "MDCX", p->trans); } conn = mgcp_conn_get_rtp(endp, conn_id); @@ -771,13 +817,31 @@ mgcp_header_done: if (have_sdp) mgcp_parse_sdp_data(endp, conn, p); - set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, - local_options); + rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, + local_options); + if (rc != 0) { + LOGP(DLMGCP, LOGL_ERROR, + "MDCX: endpoint:%x inavlid local connection options!\n", + ENDPOINT_NUMBER(endp)); + error_code = rc; + goto error3; + } if (!have_sdp && endp->local_options.codec) mgcp_set_audio_info(p->cfg, &conn->end.codec, PTYPE_UNDEFINED, endp->local_options.codec); + /* check connection mode setting */ + if (conn->conn->mode != MGCP_CONN_LOOPBACK + && conn->conn->mode != MGCP_CONN_RECV_ONLY + && conn->end.rtp_port == 0) { + LOGP(DLMGCP, LOGL_ERROR, + "MDCX: endpoint:%x selected connection mode type requires an opposite end!\n", + ENDPOINT_NUMBER(endp)); + error_code = 527; + goto error3; + } + if (setup_rtp_processing(endp, conn) != 0) goto error3; @@ -790,7 +854,7 @@ mgcp_header_done: switch (rc) { case MGCP_POLICY_REJECT: LOGP(DLMGCP, LOGL_NOTICE, - "MDCX: endpoint:%x rejected by policy\n", + "MDCX: endpoint:0x%x rejected by policy\n", ENDPOINT_NUMBER(endp)); if (silent) goto out_silent; @@ -799,7 +863,7 @@ mgcp_header_done: case MGCP_POLICY_DEFER: /* stop processing */ LOGP(DLMGCP, LOGL_DEBUG, - "MDCX: endpoint:%x defered by policy\n", + "MDCX: endpoint:0x%x defered by policy\n", ENDPOINT_NUMBER(endp)); return NULL; break; @@ -813,7 +877,7 @@ mgcp_header_done: /* modify */ LOGP(DLMGCP, LOGL_DEBUG, - "MDCX: endpoint:%x modified conn:%s\n", + "MDCX: endpoint:0x%x modified conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); if (p->cfg->change_cb) p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), @@ -829,14 +893,14 @@ mgcp_header_done: goto out_silent; LOGP(DLMGCP, LOGL_NOTICE, - "MDCX: endpoint:%x connection successfully modified\n", + "MDCX: endpoint:0x%x connection successfully modified\n", ENDPOINT_NUMBER(endp)); - return create_response_with_sdp(endp, conn, "MDCX", p->trans); + return create_response_with_sdp(endp, conn, "MDCX", p->trans, false); error3: return create_err_response(endp, error_code, "MDCX", p->trans); out_silent: - LOGP(DLMGCP, LOGL_DEBUG, "MDCX: endpoint:%x silent exit\n", + LOGP(DLMGCP, LOGL_DEBUG, "MDCX: endpoint:0x%x silent exit\n", ENDPOINT_NUMBER(endp)); return NULL; } @@ -849,22 +913,26 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) int silent = 0; char *line; char stats[1048]; - const char *ci = NULL; + const char *conn_id = NULL; struct mgcp_conn_rtp *conn = NULL; - uint32_t conn_id; LOGP(DLMGCP, LOGL_NOTICE, - "DLCX: endpoint:%x deleting connection ...\n", + "DLCX: endpoint:0x%x deleting connection ...\n", ENDPOINT_NUMBER(endp)); - if (p->found != 0) - return create_err_response(NULL, error_code, "DLCX", p->trans); + /* Prohibit wildcarded requests */ + if (endp->wildcarded_req) { + LOGP(DLMGCP, LOGL_ERROR, + "DLCX: endpoint:0x%x wildcarded endpoint names not supported.\n", + ENDPOINT_NUMBER(endp)); + return create_err_response(endp, 507, "DLCX", p->trans); + } if (llist_count(&endp->conns) <= 0) { LOGP(DLMGCP, LOGL_ERROR, - "DLCX: endpoint:%x endpoint is not holding a connection.\n", + "DLCX: endpoint:0x%x endpoint is not holding a connection.\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "DLCX", p->trans); + return create_err_response(endp, 515, "DLCX", p->trans); } for_each_line(line, p->save) { @@ -873,21 +941,26 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) switch (line[0]) { case 'C': - if (mgcp_verify_call_id(endp, line + 3) != 0) + if (mgcp_verify_call_id(endp, line + 3) != 0) { + error_code = 516; goto error3; + } break; case 'I': - ci = (const char *)line + 3; - if (mgcp_verify_ci(endp, ci) != 0) + conn_id = (const char *)line + 3; + if (mgcp_verify_ci(endp, conn_id) != 0) { + error_code = 515; goto error3; + } break; case 'Z': silent = strcmp("noanswer", line + 3) == 0; break; default: LOGP(DLMGCP, LOGL_NOTICE, - "DLCX: endpoint:%x Unhandled MGCP option: '%c'/%d\n", + "DLCX: endpoint:0x%x Unhandled MGCP option: '%c'/%d\n", ENDPOINT_NUMBER(endp), line[0], line[0]); + return create_err_response(NULL, 539, "DLCX", p->trans); break; } } @@ -900,7 +973,7 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) switch (rc) { case MGCP_POLICY_REJECT: LOGP(DLMGCP, LOGL_NOTICE, - "DLCX: endpoint:%x rejected by policy\n", + "DLCX: endpoint:0x%x rejected by policy\n", ENDPOINT_NUMBER(endp)); if (silent) goto out_silent; @@ -919,12 +992,12 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) /* When no connection id is supplied, we will interpret this as a * wildcarded DLCX and drop all connections at once. (See also * RFC3435 Section F.7) */ - if (!ci) { + if (!conn_id) { LOGP(DLMGCP, LOGL_NOTICE, - "DLCX: endpoint:%x missing ci (connectionIdentifier), will remove all connections at once\n", + "DLCX: endpoint:0x%x missing ci (connectionIdentifier), will remove all connections at once\n", ENDPOINT_NUMBER(endp)); - mgcp_release_endp(endp); + mgcp_endp_release(endp); /* Note: In this case we do not return any statistics, * as we assume that the client is not interested in @@ -932,14 +1005,6 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) return create_ok_response(endp, 200, "DLCX", p->trans); } - /* Parse the connection id */ - if (mgcp_parse_ci(&conn_id, ci)) { - LOGP(DLMGCP, LOGL_ERROR, - "DLCX: endpoint:%x insufficient parameters, invalid ci (connectionIdentifier)\n", - ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "DLCX", p->trans); - } - /* Find the connection */ conn = mgcp_conn_get_rtp(endp, conn_id); if (!conn) @@ -949,19 +1014,19 @@ static struct msgb *handle_delete_con(struct mgcp_parse_data *p) mgcp_format_stats(stats, sizeof(stats), conn->conn); /* delete connection */ - LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:%x deleting conn:%s\n", + LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:0x%x deleting conn:%s\n", ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn)); mgcp_conn_free(endp, conn_id); LOGP(DLMGCP, LOGL_NOTICE, - "DLCX: endpoint:%x connection successfully deleted\n", + "DLCX: endpoint:0x%x connection successfully deleted\n", ENDPOINT_NUMBER(endp)); /* When all connections are closed, the endpoint will be released * in order to be ready to be used by another call. */ if (llist_count(&endp->conns) <= 0) { - mgcp_release_endp(endp); + mgcp_endp_release(endp); LOGP(DLMGCP, LOGL_DEBUG, - "DLCX: endpoint:%x endpoint released\n", + "DLCX: endpoint:0x%x endpoint released\n", ENDPOINT_NUMBER(endp)); } @@ -977,7 +1042,7 @@ error3: return create_err_response(endp, error_code, "DLCX", p->trans); out_silent: - LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:%x silent exit\n", + LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:0x%x silent exit\n", ENDPOINT_NUMBER(endp)); return NULL; } @@ -995,12 +1060,6 @@ static struct msgb *handle_rsip(struct mgcp_parse_data *p) LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n"); - if (p->found != 0) { - LOGP(DLMGCP, LOGL_ERROR, - "RSIP: failed to find the endpoint.\n"); - return NULL; - } - if (p->cfg->reset_cb) p->cfg->reset_cb(p->endp->tcfg); return NULL; @@ -1026,9 +1085,6 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p) LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n"); - if (p->found != 0) - return create_err_response(NULL, 400, "RQNT", p->trans); - for_each_line(line, p->save) { switch (line[0]) { case 'S': @@ -1050,7 +1106,7 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *p) } /* Connection keepalive timer, will take care that dummy packets are send - * regulary, so that NAT connections stay open */ + * regularly, so that NAT connections stay open */ static void mgcp_keepalive_timer_cb(void *_tcfg) { struct mgcp_trunk_config *tcfg = _tcfg; @@ -1119,6 +1175,8 @@ struct mgcp_config *mgcp_config_alloc(void) return NULL; } + osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain)); + cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START; cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END; cfg->net_ports.last_port = cfg->net_ports.range_start; @@ -1170,7 +1228,7 @@ struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) trunk->audio_payload = 126; trunk->audio_send_ptime = 1; trunk->audio_send_name = 1; - trunk->number_endpoints = 33; + trunk->vty_number_endpoints = 33; trunk->omit_rtcp = 0; mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); llist_add_tail(&trunk->entry, &cfg->trunks); @@ -1202,12 +1260,12 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) tcfg->endpoints = _talloc_zero_array(tcfg->cfg, sizeof(struct mgcp_endpoint), - tcfg->number_endpoints, + tcfg->vty_number_endpoints, "endpoints"); if (!tcfg->endpoints) return -1; - for (i = 0; i < tcfg->number_endpoints; ++i) { + for (i = 0; i < tcfg->vty_number_endpoints; ++i) { INIT_LLIST_HEAD(&tcfg->endpoints[i].conns); tcfg->endpoints[i].cfg = tcfg->cfg; tcfg->endpoints[i].tcfg = tcfg; @@ -1217,31 +1275,10 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) tcfg->endpoints[i].type = &ep_typeset.rtp; } + tcfg->number_endpoints = tcfg->vty_number_endpoints; return 0; } -/*! relase endpoint, all open connections are closed. - * \param[in] endp endpoint to release */ -void mgcp_release_endp(struct mgcp_endpoint *endp) -{ - LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:%x\n", - ENDPOINT_NUMBER(endp)); - - /* Normally this function should only be called wehen - * all connections have been removed already. In case - * that there are still connections open (e.g. when - * RSIP is executed), free them all at once. */ - mgcp_conn_free_all(endp); - - /* Reset endpoint parameters and states */ - talloc_free(endp->callid); - endp->callid = NULL; - talloc_free(endp->local_options.string); - endp->local_options.string = NULL; - talloc_free(endp->local_options.codec); - endp->local_options.codec = NULL; -} - static int send_agent(struct mgcp_config *cfg, const char *buf, int len) { return write(cfg->gw_fd.bfd.fd, buf, len); @@ -1254,13 +1291,16 @@ static int send_agent(struct mgcp_config *cfg, const char *buf, int len) * \returns 0 on success, -1 on error */ int mgcp_send_reset_all(struct mgcp_config *cfg) { + char buf[MGCP_ENDPOINT_MAXLEN + 128]; + int len; int rc; - static const char mgcp_reset[] = { - "RSIP 1 *@mgw MGCP 1.0\r\n" - }; + len = snprintf(buf, sizeof(buf), + "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain); + if (len < 0) + return -1; - rc = send_agent(cfg, mgcp_reset, sizeof mgcp_reset - 1); + rc = send_agent(cfg, buf, len); if (rc <= 0) return -1; @@ -1274,17 +1314,15 @@ int mgcp_send_reset_all(struct mgcp_config *cfg) * \returns 0 on success, -1 on error */ int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint) { - char buf[128]; + char buf[MGCP_ENDPOINT_MAXLEN + 128]; int len; int rc; len = snprintf(buf, sizeof(buf), - "RSIP 39 %x@mgw MGCP 1.0\r\n", endpoint); + "RSIP 39 %x@%s MGCP 1.0\r\n", endpoint, endp->cfg->domain); if (len < 0) return -1; - buf[sizeof(buf) - 1] = '\0'; - rc = send_agent(endp->cfg, buf, len); if (rc <= 0) return -1; diff --git a/src/libosmo-mgcp/mgcp_sdp.c b/src/libosmo-mgcp/mgcp_sdp.c index f45d6e7..52b4df4 100644 --- a/src/libosmo-mgcp/mgcp_sdp.c +++ b/src/libosmo-mgcp/mgcp_sdp.c @@ -24,6 +24,7 @@ #include <osmocom/mgcp/mgcp.h> #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_msg.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <errno.h> @@ -365,7 +366,7 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, rc = msgb_printf(sdp, "v=0\r\n" - "o=- %u 23 IN IP4 %s\r\n" + "o=- %s 23 IN IP4 %s\r\n" "s=-\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n", conn->conn->id, addr, addr); diff --git a/src/libosmo-mgcp/mgcp_stat.c b/src/libosmo-mgcp/mgcp_stat.c index b84f5f2..581130c 100644 --- a/src/libosmo-mgcp/mgcp_stat.c +++ b/src/libosmo-mgcp/mgcp_stat.c @@ -23,6 +23,7 @@ */ #include <osmocom/mgcp/mgcp_stat.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <limits.h> /* Helper function for mgcp_format_stats_rtp() to calculate packet loss */ @@ -30,10 +31,10 @@ void calc_loss(struct mgcp_rtp_state *state, struct mgcp_rtp_end *end, uint32_t *expected, int *loss) { - *expected = state->stats_cycles + state->stats_max_seq; - *expected = *expected - state->stats_base_seq + 1; + *expected = state->stats.cycles + state->stats.max_seq; + *expected = *expected - state->stats.base_seq + 1; - if (!state->stats_initialized) { + if (!state->stats.initialized) { *expected = 0; *loss = 0; return; @@ -43,8 +44,8 @@ void calc_loss(struct mgcp_rtp_state *state, * Make sure the sign is correct and use the biggest * positive/negative number that fits. */ - *loss = *expected - end->packets_rx; - if (*expected < end->packets_rx) { + *loss = *expected - end->stats.packets_rx; + if (*expected < end->stats.packets_rx) { if (*loss > 0) *loss = INT_MIN; } else { @@ -56,9 +57,9 @@ void calc_loss(struct mgcp_rtp_state *state, /* Helper function for mgcp_format_stats_rtp() to calculate jitter */ uint32_t calc_jitter(struct mgcp_rtp_state *state) { - if (!state->stats_initialized) + if (!state->stats.initialized) return 0; - return state->stats_jitter >> 4; + return state->stats.jitter >> 4; } /* Generate statistics for an RTP connection */ @@ -74,8 +75,8 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, nchars = snprintf(str, str_len, "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u", - conn->end.packets_tx, conn->end.octets_tx, - conn->end.packets_rx, conn->end.octets_rx, + conn->end.stats.packets_tx, conn->end.stats.octets_tx, + conn->end.stats.packets_rx, conn->end.stats.octets_rx, ploss, jitter); if (nchars < 0 || nchars >= str_len) goto truncate; @@ -83,21 +84,23 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len, str += nchars; str_len -= nchars; - /* Error Counter */ - nchars = snprintf(str, str_len, - "\r\nX-Osmo-CP: EC TI=%u, TO=%u", - conn->state.in_stream.err_ts_counter, - conn->state.out_stream.err_ts_counter); - if (nchars < 0 || nchars >= str_len) - goto truncate; - - str += nchars; - str_len -= nchars; - - if (conn->osmux.state == OSMUX_STATE_ENABLED) { - snprintf(str, str_len, - "\r\nX-Osmux-ST: CR=%u, BR=%u", - conn->osmux.stats.chunks, conn->osmux.stats.octets); + if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) { + /* Error Counter */ + nchars = snprintf(str, str_len, + "\r\nX-Osmo-CP: EC TI=%u, TO=%u", + conn->state.in_stream.err_ts_counter, + conn->state.out_stream.err_ts_counter); + if (nchars < 0 || nchars >= str_len) + goto truncate; + + str += nchars; + str_len -= nchars; + + if (conn->osmux.state == OSMUX_STATE_ENABLED) { + snprintf(str, str_len, + "\r\nX-Osmux-ST: CR=%u, BR=%u", + conn->osmux.stats.chunks, conn->osmux.stats.octets); + } } truncate: diff --git a/src/libosmo-mgcp/mgcp_vty.c b/src/libosmo-mgcp/mgcp_vty.c index 7ff1fdd..14ecd17 100644 --- a/src/libosmo-mgcp/mgcp_vty.c +++ b/src/libosmo-mgcp/mgcp_vty.c @@ -27,6 +27,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/vty.h> #include <osmocom/mgcp/mgcp_conn.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <string.h> @@ -63,15 +64,16 @@ struct cmd_node trunk_node = { static int config_write_mgcp(struct vty *vty) { vty_out(vty, "mgcp%s", VTY_NEWLINE); + vty_out(vty, " domain %s%s", g_cfg->domain, VTY_NEWLINE); if (g_cfg->local_ip) vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE); vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE); vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE); - vty_out(vty, " rtp net-range %u %u%s", + vty_out(vty, " rtp port-range %u %u%s", g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE); if (g_cfg->net_ports.bind_addr) - vty_out(vty, " rtp net-bind-ip %s%s", + vty_out(vty, " rtp bind-ip %s%s", g_cfg->net_ports.bind_addr, VTY_NEWLINE); if (g_cfg->net_ports.bind_addr_probe) vty_out(vty, " rtp ip-probing%s", VTY_NEWLINE); @@ -115,7 +117,7 @@ static int config_write_mgcp(struct vty *vty) g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE); vty_out(vty, " loop %u%s", ! !g_cfg->trunk.audio_loop, VTY_NEWLINE); vty_out(vty, " number endpoints %u%s", - g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); + g_cfg->trunk.vty_number_endpoints - 1, VTY_NEWLINE); vty_out(vty, " %sallow-transcoding%s", g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE); if (g_cfg->call_agent_addr) @@ -167,7 +169,7 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state, " Output-Enabled: %d Force-PTIME: %d%s", state->in_stream.err_ts_counter, state->out_stream.err_ts_counter, VTY_NEWLINE, - end->dropped_packets, VTY_NEWLINE, + end->stats.dropped_packets, VTY_NEWLINE, codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE, codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE, end->frames_per_packet, end->packet_duration_ms, @@ -292,28 +294,37 @@ static void parse_range(struct mgcp_port_range *range, const char **argv) #define RANGE_START_STR "Start of the range of ports\n" #define RANGE_END_STR "End of the range of ports\n" -DEFUN(cfg_mgcp_rtp_net_range, - cfg_mgcp_rtp_net_range_cmd, - "rtp net-range <0-65534> <0-65534>", +DEFUN(cfg_mgcp_rtp_port_range, + cfg_mgcp_rtp_port_range_cmd, + "rtp port-range <0-65534> <0-65534>", RTP_STR "Range of ports to use for the NET side\n" RANGE_START_STR RANGE_END_STR) { parse_range(&g_cfg->net_ports, argv); return CMD_SUCCESS; } +ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range, + cfg_mgcp_rtp_net_range_cmd, + "rtp net-range <0-65534> <0-65534>", + RTP_STR "Range of ports to use for the NET side\n" + RANGE_START_STR RANGE_END_STR) -DEFUN(cfg_mgcp_rtp_net_bind_ip, - cfg_mgcp_rtp_net_bind_ip_cmd, - "rtp net-bind-ip A.B.C.D", +DEFUN(cfg_mgcp_rtp_bind_ip, + cfg_mgcp_rtp_bind_ip_cmd, + "rtp bind-ip A.B.C.D", RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") { osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]); return CMD_SUCCESS; } +ALIAS_DEPRECATED(cfg_mgcp_rtp_bind_ip, + cfg_mgcp_rtp_net_bind_ip_cmd, + "rtp net-bind-ip A.B.C.D", + RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") -DEFUN(cfg_mgcp_rtp_no_net_bind_ip, - cfg_mgcp_rtp_no_net_bind_ip_cmd, - "no rtp net-bind-ip", +DEFUN(cfg_mgcp_rtp_no_bind_ip, + cfg_mgcp_rtp_no_bind_ip_cmd, + "no rtp bind-ip", NO_STR RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") { @@ -321,6 +332,11 @@ DEFUN(cfg_mgcp_rtp_no_net_bind_ip, g_cfg->net_ports.bind_addr = NULL; return CMD_SUCCESS; } +ALIAS_DEPRECATED(cfg_mgcp_rtp_no_bind_ip, + cfg_mgcp_rtp_no_net_bind_ip_cmd, + "no rtp net-bind-ip", + NO_STR RTP_STR "Bind endpoints facing the Network\n" + "Address to bind to\n") DEFUN(cfg_mgcp_rtp_net_bind_ip_probing, cfg_mgcp_rtp_net_bind_ip_probing_cmd, @@ -510,7 +526,7 @@ DEFUN(cfg_mgcp_number_endp, "Number options\n" "Endpoints available\n" "Number endpoints\n") { /* + 1 as we start counting at one */ - g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1; + g_cfg->trunk.vty_number_endpoints = atoi(argv[0]) + 1; return CMD_SUCCESS; } @@ -956,7 +972,7 @@ DEFUN(tap_rtp, struct mgcp_trunk_config *trunk; struct mgcp_endpoint *endp; struct mgcp_conn_rtp *conn; - uint32_t conn_id; + const char *conn_id = NULL; trunk = find_trunk(g_cfg, atoi(argv[0])); if (!trunk) { @@ -980,11 +996,11 @@ DEFUN(tap_rtp, endp = &trunk->endpoints[endp_no]; - conn_id = strtoul(argv[2], NULL, 10); + conn_id = argv[2]; conn = mgcp_conn_get_rtp(endp, conn_id); if (!conn) { - vty_out(vty, "Conn ID %s/%d is invalid.%s", - argv[2], conn_id, VTY_NEWLINE); + vty_out(vty, "Conn ID %s is invalid.%s", + conn_id, VTY_NEWLINE); return CMD_WARNING; } @@ -1032,7 +1048,7 @@ DEFUN(free_endp, free_endp_cmd, } endp = &trunk->endpoints[endp_no]; - mgcp_release_endp(endp); + mgcp_endp_release(endp); return CMD_SUCCESS; } @@ -1102,7 +1118,7 @@ DEFUN(cfg_mgcp_osmux, * allow to turn it on yet. */ vty_out(vty, "OSMUX currently unavailable in this software version.%s", VTY_NEWLINE); return CMD_WARNING; - +#if 0 if (strcmp(argv[0], "on") == 0) g_cfg->osmux = OSMUX_USAGE_ON; else if (strcmp(argv[0], "only") == 0) @@ -1114,6 +1130,7 @@ DEFUN(cfg_mgcp_osmux, } return CMD_SUCCESS; +#endif } DEFUN(cfg_mgcp_osmux_ip, @@ -1164,6 +1181,14 @@ DEFUN(cfg_mgcp_osmux_dummy, return CMD_SUCCESS; } +DEFUN(cfg_mgcp_domain, + cfg_mgcp_domain_cmd, + "domain NAME", "domain\n" "qualified domain name\n") +{ + osmo_strlcpy(g_cfg->domain, argv[0], sizeof(g_cfg->domain)); + return CMD_SUCCESS; +} + int mgcp_vty_init(void) { install_element_ve(&show_mgcp_cmd); @@ -1181,8 +1206,11 @@ int mgcp_vty_init(void) install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_port_range_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bind_ip_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_probing_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_probing_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); @@ -1222,6 +1250,7 @@ int mgcp_vty_init(void) install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd); install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd); + install_element(MGCP_NODE, &cfg_mgcp_domain_cmd); install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); install_node(&trunk_node, config_write_trunk); @@ -1251,18 +1280,6 @@ int mgcp_vty_init(void) return 0; } -static int allocate_trunk(struct mgcp_trunk_config *trunk) -{ - if (mgcp_endpoints_allocate(trunk) != 0) { - LOGP(DLMGCP, LOGL_ERROR, - "Failed to allocate %d endpoints on trunk %d.\n", - trunk->number_endpoints, trunk->trunk_nr); - return -1; - } - - return 0; -} - int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, enum mgcp_role role) { @@ -1286,17 +1303,18 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, return -1; } - if (allocate_trunk(&g_cfg->trunk) != 0) { + if (mgcp_endpoints_allocate(&g_cfg->trunk) != 0) { LOGP(DLMGCP, LOGL_ERROR, - "Failed to initialize the virtual trunk.\n"); + "Failed to initialize the virtual trunk (%d endpoints)\n", + g_cfg->trunk.number_endpoints); return -1; } llist_for_each_entry(trunk, &g_cfg->trunks, entry) { - if (allocate_trunk(trunk) != 0) { + if (mgcp_endpoints_allocate(trunk) != 0) { LOGP(DLMGCP, LOGL_ERROR, - "Failed to initialize E1 trunk %d.\n", - trunk->trunk_nr); + "Failed to initialize trunk %d (%d endpoints)\n", + trunk->trunk_nr, trunk->number_endpoints); return -1; } } diff --git a/src/osmo-bsc_mgcp/Makefile.am b/src/osmo-bsc_mgcp/Makefile.am index 4529a33..e8a6f46 100644 --- a/src/osmo-bsc_mgcp/Makefile.am +++ b/src/osmo-bsc_mgcp/Makefile.am @@ -8,6 +8,7 @@ AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMONETIF_CFLAGS) \ $(LIBBCG729_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) @@ -24,6 +25,7 @@ osmo_bsc_mgcp_LDADD = \ $(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOVTY_LIBS) \ + $(LIBOSMONETIF_LIBS) \ $(LIBBCG729_LIBS) \ $(LIBRARY_GSM) \ $(NULL) diff --git a/src/osmo-bsc_mgcp/mgcp_main.c b/src/osmo-bsc_mgcp/mgcp_main.c index 48241a6..edb19a6 100644 --- a/src/osmo-bsc_mgcp/mgcp_main.c +++ b/src/osmo-bsc_mgcp/mgcp_main.c @@ -189,33 +189,33 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what) int mgcp_vty_is_config_node(struct vty *vty, int node) { - switch (node) { - case CONFIG_NODE: - return 0; + switch (node) { + case CONFIG_NODE: + return 0; - default: - return 1; - } + default: + return 1; + } } int mgcp_vty_go_parent(struct vty *vty) { - switch (vty->node) { - case TRUNK_NODE: - vty->node = MGCP_NODE; - vty->index = NULL; - break; - case MGCP_NODE: - default: - if (mgcp_vty_is_config_node(vty, vty->node)) - vty->node = CONFIG_NODE; - else - vty->node = ENABLE_NODE; - - vty->index = NULL; - } - - return vty->node; + switch (vty->node) { + case TRUNK_NODE: + vty->node = MGCP_NODE; + vty->index = NULL; + break; + case MGCP_NODE: + default: + if (mgcp_vty_is_config_node(vty, vty->node)) + vty->node = CONFIG_NODE; + else + vty->node = ENABLE_NODE; + + vty->index = NULL; + } + + return vty->node; } @@ -231,8 +231,8 @@ static const struct log_info_cat log_categories[] = { }; const struct log_info log_info = { - .cat = log_categories, - .num_cat = ARRAY_SIZE(log_categories), + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) @@ -244,7 +244,7 @@ int main(int argc, char **argv) msgb_talloc_ctx_init(tall_bsc_ctx, 0); osmo_init_ignore_signals(); - osmo_init_logging(&log_info); + osmo_init_logging2(tall_bsc_ctx, &log_info); cfg = mgcp_config_alloc(); if (!cfg) @@ -282,8 +282,8 @@ int main(int argc, char **argv) /* set some callbacks */ cfg->reset_cb = mgcp_rsip_cb; - /* we need to bind a socket */ - if (rc == 0) { + /* we need to bind a socket */ + if (rc == 0) { cfg->gw_fd.bfd.when = BSC_FD_READ; cfg->gw_fd.bfd.cb = read_call_agent; cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); diff --git a/src/osmo-mgw/Makefile.am b/src/osmo-mgw/Makefile.am index 0e12beb..a076d4c 100644 --- a/src/osmo-mgw/Makefile.am +++ b/src/osmo-mgw/Makefile.am @@ -8,6 +8,8 @@ AM_CFLAGS = \ -Wall \ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMONETIF_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) @@ -20,7 +22,9 @@ osmo_mgw_SOURCES = \ $(NULL) osmo_mgw_LDADD = \ - $(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \ + $(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMONETIF_LIBS) \ $(NULL) diff --git a/src/osmo-mgw/mgw_main.c b/src/osmo-mgw/mgw_main.c index ab54e62..de094c1 100644 --- a/src/osmo-mgw/mgw_main.c +++ b/src/osmo-mgw/mgw_main.c @@ -4,6 +4,7 @@ /* * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org> * (C) 2009-2011 by On-Waves + * (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier * All Rights Reserved * * This program is free software; you can redistribute it and/or modify @@ -36,6 +37,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/vty.h> #include <osmocom/mgcp/debug.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <osmocom/core/application.h> #include <osmocom/core/msgb.h> @@ -44,12 +46,14 @@ #include <osmocom/core/stats.h> #include <osmocom/core/rate_ctr.h> #include <osmocom/core/logging.h> +#include <osmocom/core/socket.h> #include <osmocom/vty/telnet_interface.h> #include <osmocom/vty/logging.h> #include <osmocom/vty/ports.h> #include <osmocom/vty/command.h> #include <osmocom/vty/stats.h> +#include <osmocom/vty/misc.h> #include "../../bscconfig.h" @@ -63,16 +67,16 @@ static struct mgcp_trunk_config *reset_trunk; static int reset_endpoints = 0; static int daemonize = 0; -const char *openbsc_copyright = +const char *osmomgw_copyright = "Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n" "Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n" - "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" - "Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n" + "Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n" + "Philipp Maier\r\n\r\n" "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" "This is free software: you are free to change and redistribute it.\r\n" "There is NO WARRANTY, to the extent permitted by law.\r\n"; -static char *config_file = "mgcp.cfg"; +static char *config_file = "osmo-mgw.cfg"; /* used by msgb and mgcp */ void *tall_bsc_ctx = NULL; @@ -187,7 +191,7 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what) /* Walk over all endpoints and trigger a release, this will release all * endpoints, possible open connections are forcefully dropped */ for (i = 1; i < reset_trunk->number_endpoints; ++i) - mgcp_release_endp(&reset_trunk->endpoints[i]); + mgcp_endp_release(&reset_trunk->endpoints[i]); } return 0; @@ -195,33 +199,33 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what) int mgcp_vty_is_config_node(struct vty *vty, int node) { - switch (node) { - case CONFIG_NODE: - return 0; + switch (node) { + case CONFIG_NODE: + return 0; - default: - return 1; - } + default: + return 1; + } } int mgcp_vty_go_parent(struct vty *vty) { - switch (vty->node) { - case TRUNK_NODE: - vty->node = MGCP_NODE; - vty->index = NULL; - break; - case MGCP_NODE: - default: - if (mgcp_vty_is_config_node(vty, vty->node)) - vty->node = CONFIG_NODE; - else - vty->node = ENABLE_NODE; - - vty->index = NULL; - } - - return vty->node; + switch (vty->node) { + case TRUNK_NODE: + vty->node = MGCP_NODE; + vty->index = NULL; + break; + case MGCP_NODE: + default: + if (mgcp_vty_is_config_node(vty, vty->node)) + vty->node = CONFIG_NODE; + else + vty->node = ENABLE_NODE; + + vty->index = NULL; + } + + return vty->node; } @@ -243,28 +247,31 @@ static const struct log_info_cat log_categories[] = { }; const struct log_info log_info = { - .cat = log_categories, - .num_cat = ARRAY_SIZE(log_categories), + .cat = log_categories, + .num_cat = ARRAY_SIZE(log_categories), }; int main(int argc, char **argv) { - struct sockaddr_in addr; - int on = 1, rc; + unsigned int flags; + int rc; tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); + vty_info.tall_ctx = tall_bsc_ctx; + msgb_talloc_ctx_init(tall_bsc_ctx, 0); osmo_init_ignore_signals(); - osmo_init_logging(&log_info); + osmo_init_logging2(tall_bsc_ctx, &log_info); cfg = mgcp_config_alloc(); if (!cfg) return -1; - vty_info.copyright = openbsc_copyright; + vty_info.copyright = osmomgw_copyright; vty_init(&vty_info); logging_vty_add_cmds(NULL); + osmo_talloc_vty_add_cmds(); osmo_stats_vty_add_cmds(&log_info); mgcp_vty_init(); @@ -279,7 +286,7 @@ int main(int argc, char **argv) /* start telnet after reading config for vty_get_bind_addr() */ rc = telnet_init_dynif(tall_bsc_ctx, NULL, - vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP); + vty_get_bind_addr(), OSMO_VTY_PORT_MGW); if (rc < 0) return rc; @@ -287,54 +294,29 @@ int main(int argc, char **argv) * mgcp-command "RSIP" (Reset in Progress) is received */ cfg->reset_cb = mgcp_rsip_cb; - /* we need to bind a socket */ - if (rc == 0) { - cfg->gw_fd.bfd.when = BSC_FD_READ; - cfg->gw_fd.bfd.cb = read_call_agent; - cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (cfg->gw_fd.bfd.fd < 0) { - perror("Gateway failed to listen"); - return -1; - } - - setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(cfg->source_port); - inet_aton(cfg->source_addr, &addr.sin_addr); - - if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("Gateway failed to bind"); - return -1; - } - - cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg"); - if (!cfg->gw_fd.bfd.data) { - fprintf(stderr, "Gateway memory error.\n"); - return -1; - } + /* we need to bind a socket */ + flags = OSMO_SOCK_F_BIND; + if (cfg->call_agent_addr) + flags |= OSMO_SOCK_F_CONNECT; - if (cfg->call_agent_addr) { - addr.sin_port = htons(2727); - inet_aton(cfg->call_agent_addr, &addr.sin_addr); - if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", - cfg->call_agent_addr, errno); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - } - - if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { - LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n"); - return -1; - } + rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, + cfg->source_addr, cfg->source_port, + cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags); + if (rc < 0) { + perror("Gateway failed to bind"); + return -1; + } - LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n"); + cfg->gw_fd.bfd.cb = read_call_agent; + cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg"); + if (!cfg->gw_fd.bfd.data) { + fprintf(stderr, "Gateway memory error.\n"); + return -1; } + LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n", + cfg->source_addr, cfg->source_port); + /* initialisation */ srand(time(NULL)); diff --git a/tests/legacy_mgcp/mgcp_test.c b/tests/legacy_mgcp/mgcp_test.c index 1400893..39400d4 100644 --- a/tests/legacy_mgcp/mgcp_test.c +++ b/tests/legacy_mgcp/mgcp_test.c @@ -268,7 +268,9 @@ static void test_strline(void) "C: 2\r\n" #define DLCX_RET "250 7 OK\r\n" \ - "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \ + "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" + + #define DLCX_RET_OSMUX DLCX_RET \ "X-Osmo-CP: EC TIS=0, TOS=0, TIR=0, TOR=0\r\n" #define RQNT "RQNT 186908780 1@mgw MGCP 1.0\r\n" \ @@ -1212,8 +1214,9 @@ const struct log_info log_info = { int main(int argc, char **argv) { - msgb_talloc_ctx_init(NULL, 0); - osmo_init_logging(&log_info); + void *ctx = talloc_named_const(NULL, 0, "mgcp_test"); + void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0); + osmo_init_logging2(ctx, &log_info); test_strline(); test_values(); @@ -1231,6 +1234,9 @@ int main(int argc, char **argv) test_no_name(); test_osmux_cid(); + OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0); + OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); + talloc_free(msgb_ctx); printf("Done\n"); return EXIT_SUCCESS; } diff --git a/tests/legacy_mgcp/mgcp_transcoding_test.c b/tests/legacy_mgcp/mgcp_transcoding_test.c index 61de25f..d3f94c7 100644 --- a/tests/legacy_mgcp/mgcp_transcoding_test.c +++ b/tests/legacy_mgcp/mgcp_transcoding_test.c @@ -588,7 +588,8 @@ const struct log_info log_info = { int main(int argc, char **argv) { int rc; - osmo_init_logging(&log_info); + void *ctx = talloc_named_const(NULL, 0, "mgcp_transcoding_test"); + osmo_init_logging2(ctx, &log_info); printf("=== Transcoding Good Cases ===\n"); diff --git a/tests/mgcp/Makefile.am b/tests/mgcp/Makefile.am index caccb9f..df2b7c8 100644 --- a/tests/mgcp/Makefile.am +++ b/tests/mgcp/Makefile.am @@ -8,6 +8,8 @@ AM_CFLAGS = \ -Wall \ -ggdb3 \ $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMONETIF_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(NULL) @@ -29,9 +31,10 @@ mgcp_test_SOURCES = \ $(NULL) mgcp_test_LDADD = \ - $(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \ + $(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOGSM_LIBS) \ $(LIBRARY_DL) \ $(LIBOSMONETIF_LIBS) \ -lm \ diff --git a/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c index e14b7ee..f6c421a 100644 --- a/tests/mgcp/mgcp_test.c +++ b/tests/mgcp/mgcp_test.c @@ -25,7 +25,7 @@ #include <osmocom/mgcp/mgcp_internal.h> #include <osmocom/mgcp/mgcp_stat.h> #include <osmocom/mgcp/mgcp_msg.h> -#include <osmocom/mgcp/mgcp_ep.h> +#include <osmocom/mgcp/mgcp_endp.h> #include <osmocom/core/application.h> #include <osmocom/core/talloc.h> @@ -66,43 +66,41 @@ static void test_strline(void) OSMO_ASSERT(counter == EXPECTED_NUMBER_OF_LINES); } -#define AUEP1 "AUEP 158663169 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" +#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n" #define AUEP1_RET "200 158663169 OK\r\n" -#define AUEP2 "AUEP 18983213 ds/e1-2/1@172.16.6.66 MGCP 1.0\r\n" +#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n" #define AUEP2_RET "500 18983213 FAIL\r\n" #define EMPTY "\r\n" #define EMPTY_RET NULL #define SHORT "CRCX \r\n" #define SHORT_RET "510 000000 FAIL\r\n" -#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@172.16.6.66 MGCP 1.0\r\n" -#define MDCX_ERR_RET "510 18983213 FAIL\r\n" -#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@172.16.6.66 MGCP 1.0\r\n" +#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n" +#define MDCX_ERR_RET "500 18983213 FAIL\r\n" +#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n" #define MDCX_RET "400 18983214 FAIL\r\n" #define MDCX3 \ "MDCX 18983215 1@mgw MGCP 1.0\r\n" \ - "I: 1\n" + "I: %s\n" #define MDCX3_RET \ "200 18983215 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ - "m=audio 16002 RTP/AVP 128\r\n" \ - "a=rtpmap:128 GSM-EFR/8000\r\n" \ + "m=audio 16002 RTP/AVP 97\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=ptime:40\r\n" #define MDCX3A_RET \ "200 18983215 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -112,15 +110,14 @@ static void test_strline(void) #define MDCX3_FMTP_RET \ "200 18983215 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ - "m=audio 16006 RTP/AVP 128\r\n" \ - "a=rtpmap:128 GSM-EFR/8000\r\n" \ + "m=audio 16006 RTP/AVP 97\r\n" \ + "a=rtpmap:97 GSM-EFR/8000\r\n" \ "a=fmtp:126 0/1/2\r\n" \ "a=ptime:40\r\n" @@ -128,11 +125,11 @@ static void test_strline(void) "MDCX 18983216 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "L: p:20, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ @@ -141,10 +138,9 @@ static void test_strline(void) #define MDCX4_RET(Ident) \ "200 " Ident " OK\r\n" \ - "I: 1\n" \ - "\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -154,10 +150,9 @@ static void test_strline(void) #define MDCX4_RO_RET(Ident) \ "200 " Ident " OK\r\n" \ - "I: 1\n" \ - "\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -169,11 +164,11 @@ static void test_strline(void) "MDCX 18983217 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "L: p:20-40, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ @@ -184,11 +179,11 @@ static void test_strline(void) "MDCX 18983218 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "L: p:20-20, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ @@ -199,11 +194,11 @@ static void test_strline(void) "MDCX 18983219 1@mgw MGCP 1.0\r\n" \ "M: sendrecv\r" \ "C: 2\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "L: a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ @@ -214,11 +209,11 @@ static void test_strline(void) "MDCX 18983220 1@mgw MGCP 1.0\r\n" \ "M: sendonly\r" \ "C: 2\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "L: p:20, a:AMR, nt:IN\r\n" \ "\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ "m=audio 4441 RTP/AVP 99\r\n" \ @@ -229,7 +224,7 @@ static void test_strline(void) "MDCX 18983221 1@mgw MGCP 1.0\r\n" \ "M: recvonly\r" \ "C: 2\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "L: p:20, a:AMR, nt:IN\r\n" #define SHORT2 "CRCX 1" @@ -242,7 +237,6 @@ static void test_strline(void) "CRCX 2 1@mgw MGCP 1.0\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ - "I: 1\r\n" \ "L: p:20\r\n" \ "\r\n" \ "v=0\r\n" \ @@ -253,10 +247,10 @@ static void test_strline(void) #define CRCX_RET \ "200 2 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "I: %s\r\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -266,10 +260,10 @@ static void test_strline(void) #define CRCX_RET_NO_RTPMAP \ "200 2 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "I: %s\r\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -278,10 +272,10 @@ static void test_strline(void) #define CRCX_FMTP_RET \ "200 2 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "I: %s\r\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -294,7 +288,6 @@ static void test_strline(void) "CRCX 2 1@mgw MGCP 1.0\r" \ "M: recvonly\r" \ "C: 2\r\n" \ - "I: 1\n" \ "\n" \ "v=0\r" \ "c=IN IP4 123.12.12.123\r" \ @@ -303,10 +296,10 @@ static void test_strline(void) #define CRCX_ZYN_RET \ "200 2 OK\r\n" \ - "I: 1\n" \ - "\n" \ + "I: %s\r\n" \ + "\r\n" \ "v=0\r\n" \ - "o=- 1 23 IN IP4 0.0.0.0\r\n" \ + "o=- %s 23 IN IP4 0.0.0.0\r\n" \ "s=-\r\n" \ "c=IN IP4 0.0.0.0\r\n" \ "t=0 0\r\n" \ @@ -316,12 +309,14 @@ static void test_strline(void) #define DLCX \ "DLCX 7 1@mgw MGCP 1.0\r\n" \ - "I: 1\r\n" \ + "I: %s\r\n" \ "C: 2\r\n" #define DLCX_RET \ "250 7 OK\r\n" \ - "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" \ + "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n" + + #define DLCX_RET_OSMUX DLCX_RET \ "X-Osmo-CP: EC TI=0, TO=0\r\n" #define RQNT \ @@ -343,7 +338,6 @@ static void test_strline(void) #define CRCX_MULT_1 \ "CRCX 2 1@mgw MGCP 1.0\r\n" \ - "I: 4711\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ @@ -358,7 +352,6 @@ static void test_strline(void) #define CRCX_MULT_2 \ "CRCX 2 2@mgw MGCP 1.0\r\n" \ - "I: 90210\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ @@ -374,7 +367,6 @@ static void test_strline(void) #define CRCX_MULT_3 \ "CRCX 2 3@mgw MGCP 1.0\r\n" \ - "I: 0815\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ @@ -390,7 +382,6 @@ static void test_strline(void) #define CRCX_MULT_4 \ "CRCX 2 4@mgw MGCP 1.0\r\n" \ - "I: 32168\r\n" \ "M: recvonly\r\n" \ "C: 2\r\n" \ "X\r\n" \ @@ -407,7 +398,6 @@ static void test_strline(void) #define CRCX_MULT_GSM_EXACT \ "CRCX 259260421 5@mgw MGCP 1.0\r\n" \ "C: 1355c6041e\r\n" \ - "I: 3\r\n" \ "L: p:20, a:GSM, nt:IN\r\n" \ "M: recvonly\r\n" \ "\r\n" \ @@ -432,7 +422,7 @@ static void test_strline(void) #define MDCX_NAT_DUMMY \ "MDCX 23 5@mgw MGCP 1.0\r\n" \ "C: 1355c6041e\r\n" \ - "I: 3\r\n" \ + "I: %s\r\n" \ "\r\n" \ "c=IN IP4 8.8.8.8\r\n" \ "m=audio 16434 RTP/AVP 255\r\n" @@ -482,12 +472,20 @@ static const struct mgcp_test retransmit[] = { {"DLCX", DLCX, DLCX_RET}, }; -static struct msgb *create_msg(const char *str) +static struct msgb *create_msg(const char *str, const char *conn_id) { struct msgb *msg; + int len; + + printf("creating message from statically defined input:\n"); + printf("---------8<---------\n%s\n---------8<---------\n", str); msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); - int len = sprintf((char *)msg->data, "%s", str); + if (conn_id && strlen(conn_id)) + len = sprintf((char *)msg->data, str, conn_id, conn_id); + else + len = sprintf((char *)msg->data, "%s", str); + msg->l2h = msgb_put(msg, len); return msg; } @@ -554,20 +552,111 @@ static void test_values(void) MGCP_CONN_RECV_SEND); } +/* Extract a connection ID from a response (CRCX) */ +static int get_conn_id_from_response(uint8_t *resp, char *conn_id, + unsigned int conn_id_len) +{ + char *conn_id_ptr; + int i; + bool got_conn_id = false; + + /* First try to get the conn_id from the I: parameter */ + conn_id_ptr = strstr((char *)resp, "I: "); + if (conn_id_ptr) { + memset(conn_id, 0, conn_id_len); + memcpy(conn_id, conn_id_ptr + 3, 32); + got_conn_id = true; + } else { + /* Alternatively try to extract the conn_id from the o=- SDP + * parameter */ + conn_id_ptr = strstr((char *)resp, "o=- "); + if(conn_id_ptr) { + memset(conn_id, 0, conn_id_len); + memcpy(conn_id, conn_id_ptr + 4, 32); + got_conn_id = true; + } + } + + if (got_conn_id) { + for (i = 0; i < conn_id_len; i++) { + if (conn_id[i] == '\n' || conn_id[i] == '\r') + conn_id[i] = '\0'; + } + + /* A valid conn_id must at least contain one digit, and must + * not exceed a length of 32 digits */ + OSMO_ASSERT(strlen(conn_id) <= 32); + OSMO_ASSERT(strlen(conn_id) > 0); + + return 0; + } + return -EINVAL; +} + +/* Check response, automatically patch connection ID if needed */ +static int check_response(uint8_t *resp, const char *exp_resp) +{ + char exp_resp_patched[4096]; + const char *exp_resp_ptr; + char conn_id[256]; + + printf("checking response:\n"); + + /* If the expected response is intened to be patched + * (%s placeholder inside) we will patch it with the + * connection identifier we just received from the + * real response. This is necessary because the CI + * is generated by the mgcp code on CRCX and we can + * not know it in advance */ + if (strstr(exp_resp, "%s")) { + if (get_conn_id_from_response(resp, conn_id, sizeof(conn_id)) == + 0) { + sprintf(exp_resp_patched, exp_resp, conn_id, conn_id); + exp_resp_ptr = exp_resp_patched; + printf + ("using message with patched conn_id for comparison\n"); + } else { + printf + ("patching conn_id failed, using message as statically defined for comparison\n"); + exp_resp_ptr = exp_resp; + } + } else { + printf("using message as statically defined for comparison\n"); + exp_resp_ptr = exp_resp; + } + + if (strcmp((char *)resp, exp_resp_ptr) != 0) { + printf("Unexpected response, please check!\n"); + printf + ("Got:\n---------8<---------\n%s\n---------8<---------\n\n", + resp); + printf + ("Expected:\n---------8<---------\n%s\n---------8<---------\n", + exp_resp_ptr); + return -EINVAL; + } + + printf("Response matches our expectations.\n"); + return 0; +} + static void test_messages(void) { struct mgcp_config *cfg; struct mgcp_endpoint *endp; int i; struct mgcp_conn_rtp *conn = NULL; + char last_conn_id[256]; + int rc; cfg = mgcp_config_alloc(); - cfg->trunk.number_endpoints = 64; + cfg->trunk.vty_number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); - cfg->policy_cb = mgcp_test_policy_cb; + memset(last_conn_id, 0, sizeof(last_conn_id)); + mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); for (i = 0; i < ARRAY_SIZE(tests); i++) { @@ -575,6 +664,7 @@ static void test_messages(void) struct msgb *inp; struct msgb *msg; + printf("\n================================================\n"); printf("Testing %s\n", t->name); last_endpoint = -1; @@ -583,7 +673,7 @@ static void test_messages(void) osmo_talloc_replace_string(cfg, &cfg->trunk.audio_fmtp_extra, t->extra_fmtp); - inp = create_msg(t->req); + inp = create_msg(t->req, last_conn_id); msg = mgcp_handle_message(cfg, inp); msgb_free(inp); if (!t->exp_resp) { @@ -592,12 +682,22 @@ static void test_messages(void) (char *)msg->data); OSMO_ASSERT(false); } - } else if (strcmp((char *)msg->data, t->exp_resp) != 0) { - printf("%s failed.\nExpected:\n%s\nGot:\n%s\n", - t->name, t->exp_resp, (char *) msg->data); + } else if (check_response(msg->data, t->exp_resp) != 0) { + printf("%s failed.\n", t->name); OSMO_ASSERT(false); } - msgb_free(msg); + + if (msg) { + rc = get_conn_id_from_response(msg->data, last_conn_id, + sizeof(last_conn_id)); + if (rc) + printf("(response contains a connection id)\n"); + else + printf("(response does not contain a connection id)\n"); + } + + if (msg) + msgb_free(msg); if (dummy_packets) printf("Dummy packets: %d\n", dummy_packets); @@ -605,7 +705,7 @@ static void test_messages(void) if (last_endpoint != -1) { endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 1); + conn = mgcp_conn_get_rtp(endp, "1"); if (conn) { OSMO_ASSERT(conn); @@ -657,7 +757,7 @@ static void test_messages(void) } /* Check detected payload type */ - if (t->ptype != PTYPE_IGNORE) { + if (conn && t->ptype != PTYPE_IGNORE) { OSMO_ASSERT(last_endpoint != -1); endp = &cfg->trunk.endpoints[last_endpoint]; @@ -682,12 +782,16 @@ static void test_retransmission(void) { struct mgcp_config *cfg; int i; + char last_conn_id[256]; + int rc; cfg = mgcp_config_alloc(); - cfg->trunk.number_endpoints = 64; + cfg->trunk.vty_number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); + memset(last_conn_id, 0, sizeof(last_conn_id)); + mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); for (i = 0; i < ARRAY_SIZE(retransmit); i++) { @@ -695,24 +799,33 @@ static void test_retransmission(void) struct msgb *inp; struct msgb *msg; + printf("\n================================================\n"); printf("Testing %s\n", t->name); - inp = create_msg(t->req); + inp = create_msg(t->req, last_conn_id); msg = mgcp_handle_message(cfg, inp); msgb_free(inp); - if (strcmp((char *)msg->data, t->exp_resp) != 0) { + if (msg && check_response(msg->data, t->exp_resp) != 0) { printf("%s failed '%s'\n", t->name, (char *)msg->data); OSMO_ASSERT(false); } - msgb_free(msg); + + if (msg && strcmp(t->name, "CRCX") == 0) { + rc = get_conn_id_from_response(msg->data, last_conn_id, + sizeof(last_conn_id)); + OSMO_ASSERT(rc == 0); + } + + if (msg) + msgb_free(msg); /* Retransmit... */ printf("Re-transmitting %s\n", t->name); - inp = create_msg(t->req); + inp = create_msg(t->req, last_conn_id); msg = mgcp_handle_message(cfg, inp); msgb_free(inp); - if (strcmp((char *)msg->data, t->exp_resp) != 0) { + if (check_response(msg->data, t->exp_resp) != 0) { printf("%s failed '%s'\n", t->name, (char *)msg->data); OSMO_ASSERT(false); } @@ -733,21 +846,26 @@ static void test_rqnt_cb(void) { struct mgcp_config *cfg; struct msgb *inp, *msg; + char conn_id[256]; cfg = mgcp_config_alloc(); cfg->rqnt_cb = rqnt_cb; - cfg->trunk.number_endpoints = 64; + cfg->trunk.vty_number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); - inp = create_msg(CRCX); - msgb_free(mgcp_handle_message(cfg, inp)); + inp = create_msg(CRCX, NULL); + msg = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(msg); + OSMO_ASSERT(get_conn_id_from_response(msg->data, conn_id, + sizeof(conn_id)) == 0); + msgb_free(msg); msgb_free(inp); /* send the RQNT and check for the CB */ - inp = create_msg(RQNT); + inp = create_msg(RQNT, conn_id); msg = mgcp_handle_message(cfg, inp); if (strncmp((const char *)msg->l2h, "200", 3) != 0) { printf("FAILED: message is not 200. '%s'\n", msg->l2h); @@ -762,7 +880,7 @@ static void test_rqnt_cb(void) msgb_free(msg); msgb_free(inp); - inp = create_msg(DLCX); + inp = create_msg(DLCX, conn_id); msgb_free(mgcp_handle_message(cfg, inp)); msgb_free(inp); talloc_free(cfg); @@ -809,12 +927,12 @@ static void test_packet_loss_calc(void) memset(&state, 0, sizeof(state)); memset(&rtp, 0, sizeof(rtp)); - state.stats_initialized = 1; - state.stats_base_seq = pl_test_dat[i].base_seq; - state.stats_max_seq = pl_test_dat[i].max_seq; - state.stats_cycles = pl_test_dat[i].cycles; + state.stats.initialized = 1; + state.stats.base_seq = pl_test_dat[i].base_seq; + state.stats.max_seq = pl_test_dat[i].max_seq; + state.stats.cycles = pl_test_dat[i].cycles; - rtp.packets_rx = pl_test_dat[i].packets; + rtp.stats.packets_rx = pl_test_dat[i].packets; calc_loss(&state, &rtp, &expected, &loss); if (loss != pl_test_dat[i].loss @@ -865,7 +983,7 @@ static void test_mgcp_stats(void) int loss; int rc; - msg = create_msg(DLCX_RET); + msg = create_msg(DLCX_RET, NULL); rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); printf("Parsing result: %d\n", rc); if (bps != 0 || bos != 0 || pr != 0 || _or != 0 || loss != 0 @@ -875,7 +993,7 @@ static void test_mgcp_stats(void) msg = create_msg - ("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n"); + ("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n", NULL); rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter); printf("Parsing result: %d\n", rc); if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3 @@ -1014,6 +1132,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) int last_in_ts_err_cnt = 0; int last_out_ts_err_cnt = 0; struct mgcp_conn_rtp *conn = NULL; + struct mgcp_conn *_conn = NULL; printf("Testing packet error detection%s%s.\n", patch_ssrc ? ", patch SSRC" : "", @@ -1025,7 +1144,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) endp.type = &ep_typeset.rtp; - trunk.number_endpoints = 1; + trunk.vty_number_endpoints = 1; trunk.endpoints = &endp; trunk.force_constant_ssrc = patch_ssrc; trunk.force_aligned_timing = patch_ts; @@ -1033,9 +1152,10 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) endp.tcfg = &trunk; INIT_LLIST_HEAD(&endp.conns); - mgcp_conn_alloc(NULL, &endp, 4711, MGCP_CONN_TYPE_RTP, - "test-connection"); - conn = mgcp_conn_get_rtp(&endp, 4711); + _conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP, + "test-connection"); + OSMO_ASSERT(_conn); + conn = mgcp_conn_get_rtp(&endp, _conn->id); OSMO_ASSERT(conn); rtp = &conn->end; @@ -1074,7 +1194,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) state.out_stream.err_ts_counter - last_out_ts_err_cnt); printf("Stats: Jitter = %u, Transit = %d\n", - calc_jitter(&state), state.stats_transit); + calc_jitter(&state), state.stats.transit); last_in_ts_err_cnt = state.in_stream.err_ts_counter; last_out_ts_err_cnt = state.out_stream.err_ts_counter; @@ -1083,6 +1203,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) } force_monotonic_time_us = -1; + mgcp_conn_free_all(&endp); } static void test_multilple_codec(void) @@ -1092,96 +1213,107 @@ static void test_multilple_codec(void) struct msgb *inp, *resp; struct in_addr addr; struct mgcp_conn_rtp *conn = NULL; + char conn_id[256]; printf("Testing multiple payload types\n"); cfg = mgcp_config_alloc(); - cfg->trunk.number_endpoints = 64; + cfg->trunk.vty_number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); cfg->policy_cb = mgcp_test_policy_cb; mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); /* Allocate endpoint 1@mgw with two codecs */ last_endpoint = -1; - inp = create_msg(CRCX_MULT_1); + inp = create_msg(CRCX_MULT_1, NULL); resp = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, + sizeof(conn_id)) == 0); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 1); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 4711); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == 18); OSMO_ASSERT(conn->end.alt_codec.payload_type == 97); /* Allocate 2@mgw with three codecs, last one ignored */ last_endpoint = -1; - inp = create_msg(CRCX_MULT_2); + inp = create_msg(CRCX_MULT_2, NULL); resp = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, + sizeof(conn_id)) == 0); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 2); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 90210); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == 18); OSMO_ASSERT(conn->end.alt_codec.payload_type == 97); /* Allocate 3@mgw with no codecs, check for PT == -1 */ last_endpoint = -1; - inp = create_msg(CRCX_MULT_3); + inp = create_msg(CRCX_MULT_3, NULL); resp = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, + sizeof(conn_id)) == 0); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 3); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 815); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == -1); OSMO_ASSERT(conn->end.alt_codec.payload_type == -1); /* Allocate 4@mgw with a single codec */ last_endpoint = -1; - inp = create_msg(CRCX_MULT_4); + inp = create_msg(CRCX_MULT_4, NULL); resp = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, + sizeof(conn_id)) == 0); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 4); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 32168); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == 18); OSMO_ASSERT(conn->end.alt_codec.payload_type == -1); /* Allocate 5@mgw at select GSM.. */ last_endpoint = -1; - inp = create_msg(CRCX_MULT_GSM_EXACT); + inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); talloc_free(cfg->trunk.audio_name); cfg->trunk.audio_name = "GSM/8000"; cfg->trunk.no_audio_transcoding = 1; resp = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, + sizeof(conn_id)) == 0); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 3); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == 3); OSMO_ASSERT(conn->end.alt_codec.payload_type == -1); - inp = create_msg(MDCX_NAT_DUMMY); + inp = create_msg(MDCX_NAT_DUMMY, conn_id); last_endpoint = -1; resp = mgcp_handle_message(cfg, inp); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 3); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == 3); OSMO_ASSERT(conn->end.alt_codec.payload_type == -1); @@ -1194,23 +1326,25 @@ static void test_multilple_codec(void) /* Free the previous endpoint and the data and * check if the connection really vanished... */ - mgcp_release_endp(endp); + mgcp_endp_release(endp); talloc_free(endp->last_response); talloc_free(endp->last_trans); endp->last_response = endp->last_trans = NULL; - conn = mgcp_conn_get_rtp(endp, 3); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(!conn); last_endpoint = -1; - inp = create_msg(CRCX_MULT_GSM_EXACT); + inp = create_msg(CRCX_MULT_GSM_EXACT, NULL); cfg->trunk.no_audio_transcoding = 0; resp = mgcp_handle_message(cfg, inp); + OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id, + sizeof(conn_id)) == 0); msgb_free(inp); msgb_free(resp); OSMO_ASSERT(last_endpoint == 5); endp = &cfg->trunk.endpoints[last_endpoint]; - conn = mgcp_conn_get_rtp(endp, 3); + conn = mgcp_conn_get_rtp(endp, conn_id); OSMO_ASSERT(conn); OSMO_ASSERT(conn->end.codec.payload_type == 255); OSMO_ASSERT(conn->end.alt_codec.payload_type == 0); @@ -1223,44 +1357,47 @@ static void test_no_cycle(void) struct mgcp_config *cfg; struct mgcp_endpoint *endp; struct mgcp_conn_rtp *conn = NULL; + struct mgcp_conn *_conn = NULL; printf("Testing no sequence flow on initial packet\n"); cfg = mgcp_config_alloc(); - cfg->trunk.number_endpoints = 64; + cfg->trunk.vty_number_endpoints = 64; mgcp_endpoints_allocate(&cfg->trunk); endp = &cfg->trunk.endpoints[1]; - mgcp_conn_alloc(NULL, endp, 4711, MGCP_CONN_TYPE_RTP, - "test-connection"); - conn = mgcp_conn_get_rtp(endp, 4711); + _conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, + "test-connection"); + OSMO_ASSERT(_conn); + conn = mgcp_conn_get_rtp(endp, _conn->id); OSMO_ASSERT(conn); - OSMO_ASSERT(conn->state.stats_initialized == 0); + OSMO_ASSERT(conn->state.stats.initialized == 0); mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342); - OSMO_ASSERT(conn->state.stats_initialized == 1); - OSMO_ASSERT(conn->state.stats_cycles == 0); - OSMO_ASSERT(conn->state.stats_max_seq == 0); + OSMO_ASSERT(conn->state.stats.initialized == 1); + OSMO_ASSERT(conn->state.stats.cycles == 0); + OSMO_ASSERT(conn->state.stats.max_seq == 0); mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342); - OSMO_ASSERT(conn->state.stats_initialized == 1); - OSMO_ASSERT(conn->state.stats_cycles == 0); - OSMO_ASSERT(conn->state.stats_max_seq == 1); + OSMO_ASSERT(conn->state.stats.initialized == 1); + OSMO_ASSERT(conn->state.stats.cycles == 0); + OSMO_ASSERT(conn->state.stats.max_seq == 1); /* now jump.. */ mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342); - OSMO_ASSERT(conn->state.stats_initialized == 1); - OSMO_ASSERT(conn->state.stats_cycles == 0); - OSMO_ASSERT(conn->state.stats_max_seq == UINT16_MAX); + OSMO_ASSERT(conn->state.stats.initialized == 1); + OSMO_ASSERT(conn->state.stats.cycles == 0); + OSMO_ASSERT(conn->state.stats.max_seq == UINT16_MAX); /* and wrap */ mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342); - OSMO_ASSERT(conn->state.stats_initialized == 1); - OSMO_ASSERT(conn->state.stats_cycles == UINT16_MAX + 1); - OSMO_ASSERT(conn->state.stats_max_seq == 0); + OSMO_ASSERT(conn->state.stats.initialized == 1); + OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1); + OSMO_ASSERT(conn->state.stats.max_seq == 0); + mgcp_endp_release(endp); talloc_free(cfg); } @@ -1272,7 +1409,7 @@ static void test_no_name(void) printf("Testing no rtpmap name\n"); cfg = mgcp_config_alloc(); - cfg->trunk.number_endpoints = 64; + cfg->trunk.vty_number_endpoints = 64; cfg->trunk.audio_send_name = 0; mgcp_endpoints_allocate(&cfg->trunk); @@ -1280,9 +1417,10 @@ static void test_no_name(void) mgcp_endpoints_allocate(mgcp_trunk_alloc(cfg, 1)); - inp = create_msg(CRCX); + inp = create_msg(CRCX, NULL); msg = mgcp_handle_message(cfg, inp); - if (strcmp((char *)msg->data, CRCX_RET_NO_RTPMAP) != 0) { + + if (check_response(msg->data, CRCX_RET_NO_RTPMAP) != 0) { printf("FAILED: there should not be a RTPMAP: %s\n", (char *)msg->data); OSMO_ASSERT(false); @@ -1290,7 +1428,7 @@ static void test_no_name(void) msgb_free(inp); msgb_free(msg); - mgcp_release_endp(&cfg->trunk.endpoints[1]); + mgcp_endp_release(&cfg->trunk.endpoints[1]); talloc_free(cfg); } @@ -1329,8 +1467,9 @@ const struct log_info log_info = { int main(int argc, char **argv) { - msgb_talloc_ctx_init(NULL, 0); - osmo_init_logging(&log_info); + void *ctx = talloc_named_const(NULL, 0, "mgcp_test"); + void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0); + osmo_init_logging2(ctx, &log_info); test_strline(); test_values(); @@ -1348,6 +1487,9 @@ int main(int argc, char **argv) test_no_name(); test_osmux_cid(); + OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0); + OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1); + talloc_free(msgb_ctx); printf("Done\n"); return EXIT_SUCCESS; } diff --git a/tests/mgcp/mgcp_test.ok b/tests/mgcp/mgcp_test.ok index 7376930..d2879ad 100644 --- a/tests/mgcp/mgcp_test.ok +++ b/tests/mgcp/mgcp_test.ok @@ -11,87 +11,573 @@ line: 'mixed (4 lines)' line: '' line: '' line: '' + +================================================ Testing AUEP1 +creating message from statically defined input: +---------8<--------- +AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing AUEP2 +creating message from statically defined input: +---------8<--------- +AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing MDCX1 +creating message from statically defined input: +---------8<--------- +MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing MDCX2 +creating message from statically defined input: +---------8<--------- +MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing CRCX +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 97
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetetization period: 20-20 -Connection mode: 1: RECV + +================================================ Testing MDCX3 +creating message from statically defined input: +---------8<--------- +MDCX 18983215 1@mgw MGCP 1.0
+I: %s + +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetization period not set -Connection mode: 1: RECV + +================================================ Testing MDCX4 +creating message from statically defined input: +---------8<--------- +MDCX 18983216 1@mgw MGCP 1.0
+M: sendrecv
C: 2
+I: %s
+L: p:20, a:AMR, nt:IN
+ +v=0
+o=- %s 23 IN IP4 0.0.0.0
+c=IN IP4 0.0.0.0
+t=0 0
+m=audio 4441 RTP/AVP 99
+a=rtpmap:99 AMR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetetization period: 20-20 -Connection mode: 3: SEND RECV + +================================================ Testing MDCX4_PT1 +creating message from statically defined input: +---------8<--------- +MDCX 18983217 1@mgw MGCP 1.0
+M: sendrecv
C: 2
+I: %s
+L: p:20-40, a:AMR, nt:IN
+ +v=0
+o=- %s 23 IN IP4 0.0.0.0
+c=IN IP4 0.0.0.0
+t=0 0
+m=audio 4441 RTP/AVP 99
+a=rtpmap:99 AMR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetetization period: 20-40 -Connection mode: 3: SEND RECV + +================================================ Testing MDCX4_PT2 +creating message from statically defined input: +---------8<--------- +MDCX 18983218 1@mgw MGCP 1.0
+M: sendrecv
C: 2
+I: %s
+L: p:20-20, a:AMR, nt:IN
+ +v=0
+o=- %s 23 IN IP4 0.0.0.0
+c=IN IP4 0.0.0.0
+t=0 0
+m=audio 4441 RTP/AVP 99
+a=rtpmap:99 AMR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetetization period: 20-20 -Connection mode: 3: SEND RECV + +================================================ Testing MDCX4_PT3 +creating message from statically defined input: +---------8<--------- +MDCX 18983219 1@mgw MGCP 1.0
+M: sendrecv
C: 2
+I: %s
+L: a:AMR, nt:IN
+ +v=0
+o=- %s 23 IN IP4 0.0.0.0
+c=IN IP4 0.0.0.0
+t=0 0
+m=audio 4441 RTP/AVP 99
+a=rtpmap:99 AMR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetization period not set -Connection mode: 3: SEND RECV + +================================================ Testing MDCX4_SO -Detected packet duration: 40 -Requested packetetization period: 20-20 -Connection mode: 2: SEND +creating message from statically defined input: +---------8<--------- +MDCX 18983220 1@mgw MGCP 1.0
+M: sendonly
C: 2
+I: %s
+L: p:20, a:AMR, nt:IN
+ +v=0
+o=- %s 23 IN IP4 0.0.0.0
+c=IN IP4 0.0.0.0
+t=0 0
+m=audio 4441 RTP/AVP 99
+a=rtpmap:99 AMR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) + +================================================ Testing MDCX4_RO +creating message from statically defined input: +---------8<--------- +MDCX 18983221 1@mgw MGCP 1.0
+M: recvonly
C: 2
+I: %s
+L: p:20, a:AMR, nt:IN
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetetization period: 20-20 -Connection mode: 1: RECV + +================================================ Testing DLCX +creating message from statically defined input: +---------8<--------- +DLCX 7 1@mgw MGCP 1.0
+I: %s
+C: 2
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing CRCX_ZYN +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
M: recvonly
C: 2
+ +v=0
c=IN IP4 123.12.12.123
m=audio 5904 RTP/AVP 97
a=rtpmap:97 GSM-EFR/8000
+---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 20 -Requested packetization period not set -Connection mode: 1: RECV + +================================================ Testing EMPTY +creating message from statically defined input: +---------8<--------- +
+ +---------8<--------- + +================================================ Testing SHORT1 +creating message from statically defined input: +---------8<--------- +CRCX
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing SHORT2 +creating message from statically defined input: +---------8<--------- +CRCX 1 +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing SHORT3 +creating message from statically defined input: +---------8<--------- +CRCX 1 1@mgw +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing SHORT4 +creating message from statically defined input: +---------8<--------- +CRCX 1 1@mgw MGCP +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing RQNT1 +creating message from statically defined input: +---------8<--------- +RQNT 186908780 1@mgw MGCP 1.0
+X: B244F267488
+S: D/9
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing RQNT2 +creating message from statically defined input: +---------8<--------- +RQNT 186908781 1@mgw MGCP 1.0
+X: ADD4F26746F
+R: D/[0-9#*](N), G/ft, fxr/t38
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing DLCX +creating message from statically defined input: +---------8<--------- +DLCX 7 1@mgw MGCP 1.0
+I: %s
+C: 2
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing CRCX +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 97
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetetization period: 20-20 -Connection mode: 1: RECV + +================================================ Testing MDCX3 +creating message from statically defined input: +---------8<--------- +MDCX 18983215 1@mgw MGCP 1.0
+I: %s + +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. +(response does not contain a connection id) Dummy packets: 2 -Detected packet duration: 40 -Requested packetization period not set -Connection mode: 1: RECV + +================================================ Testing DLCX +creating message from statically defined input: +---------8<--------- +DLCX 7 1@mgw MGCP 1.0
+I: %s
+C: 2
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. +(response contains a connection id) + +================================================ Testing CRCX +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 97
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. Re-transmitting CRCX +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 97
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. + +================================================ Testing RQNT1 +creating message from statically defined input: +---------8<--------- +RQNT 186908780 1@mgw MGCP 1.0
+X: B244F267488
+S: D/9
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. Re-transmitting RQNT1 +creating message from statically defined input: +---------8<--------- +RQNT 186908780 1@mgw MGCP 1.0
+X: B244F267488
+S: D/9
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. + +================================================ Testing RQNT2 +creating message from statically defined input: +---------8<--------- +RQNT 186908781 1@mgw MGCP 1.0
+X: ADD4F26746F
+R: D/[0-9#*](N), G/ft, fxr/t38
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. Re-transmitting RQNT2 +creating message from statically defined input: +---------8<--------- +RQNT 186908781 1@mgw MGCP 1.0
+X: ADD4F26746F
+R: D/[0-9#*](N), G/ft, fxr/t38
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. + +================================================ Testing MDCX3 +creating message from statically defined input: +---------8<--------- +MDCX 18983215 1@mgw MGCP 1.0
+I: %s + +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. Re-transmitting MDCX3 +creating message from statically defined input: +---------8<--------- +MDCX 18983215 1@mgw MGCP 1.0
+I: %s + +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. + +================================================ Testing DLCX +creating message from statically defined input: +---------8<--------- +DLCX 7 1@mgw MGCP 1.0
+I: %s
+C: 2
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. Re-transmitting DLCX +creating message from statically defined input: +---------8<--------- +DLCX 7 1@mgw MGCP 1.0
+I: %s
+C: 2
+ +---------8<--------- +checking response: +using message as statically defined for comparison +Response matches our expectations. Testing packet loss calculation. +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 97
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +RQNT 186908780 1@mgw MGCP 1.0
+X: B244F267488
+S: D/9
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +DLCX 7 1@mgw MGCP 1.0
+I: %s
+C: 2
+ +---------8<--------- Testing stat parsing +creating message from statically defined input: +---------8<--------- +250 7 OK
+P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0
+ +---------8<--------- Parsing result: 0 +creating message from statically defined input: +---------8<--------- +250 7 OK
+P: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40
+ +---------8<--------- Parsing result: 0 Testing packet error detection, patch SSRC. Output SSRC changed to 11223344 @@ -466,6 +952,156 @@ In TS: 160320, dTS: 160, Seq: 1002 Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0 Stats: Jitter = 0, Transit = -144000 Testing multiple payload types +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+X
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 18 97
+a=rtpmap:18 G729/8000
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +CRCX 2 2@mgw MGCP 1.0
+M: recvonly
+C: 2
+X
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 18 97 101
+a=rtpmap:18 G729/8000
+a=rtpmap:97 GSM-EFR/8000
+a=rtpmap:101 FOO/8000
+a=ptime:40
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +CRCX 2 3@mgw MGCP 1.0
+M: recvonly
+C: 2
+X
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP
+a=rtpmap:18 G729/8000
+a=rtpmap:97 GSM-EFR/8000
+a=rtpmap:101 FOO/8000
+a=ptime:40
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +CRCX 2 4@mgw MGCP 1.0
+M: recvonly
+C: 2
+X
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 18
+a=rtpmap:18 G729/8000
+a=rtpmap:97 GSM-EFR/8000
+a=rtpmap:101 FOO/8000
+a=ptime:40
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +CRCX 259260421 5@mgw MGCP 1.0
+C: 1355c6041e
+L: p:20, a:GSM, nt:IN
+M: recvonly
+
+v=0
+o=- 1439038275 1439038275 IN IP4 192.168.181.247
+s=-
+c=IN IP4 192.168.181.247
+t=0 0
+m=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:3 gsm/8000
+a=rtpmap:18 G729/8000
+a=fmtp:18 annexb=no
+a=rtpmap:4 G723/8000
+a=rtpmap:96 iLBC/8000
+a=fmtp:96 mode=20
+a=rtpmap:97 iLBC/8000
+a=fmtp:97 mode=30
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=recvonly
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +MDCX 23 5@mgw MGCP 1.0
+C: 1355c6041e
+I: %s
+
+c=IN IP4 8.8.8.8
+m=audio 16434 RTP/AVP 255
+ +---------8<--------- +creating message from statically defined input: +---------8<--------- +CRCX 259260421 5@mgw MGCP 1.0
+C: 1355c6041e
+L: p:20, a:GSM, nt:IN
+M: recvonly
+
+v=0
+o=- 1439038275 1439038275 IN IP4 192.168.181.247
+s=-
+c=IN IP4 192.168.181.247
+t=0 0
+m=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101
+a=rtpmap:0 PCMU/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:3 gsm/8000
+a=rtpmap:18 G729/8000
+a=fmtp:18 annexb=no
+a=rtpmap:4 G723/8000
+a=rtpmap:96 iLBC/8000
+a=fmtp:96 mode=20
+a=rtpmap:97 iLBC/8000
+a=fmtp:97 mode=30
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+a=recvonly
+ +---------8<--------- Testing no sequence flow on initial packet Testing no rtpmap name +creating message from statically defined input: +---------8<--------- +CRCX 2 1@mgw MGCP 1.0
+M: recvonly
+C: 2
+L: p:20
+
+v=0
+c=IN IP4 123.12.12.123
+m=audio 5904 RTP/AVP 97
+a=rtpmap:97 GSM-EFR/8000
+a=ptime:40
+ +---------8<--------- +checking response: +using message with patched conn_id for comparison +Response matches our expectations. Done diff --git a/tests/mgcp_client/Makefile.am b/tests/mgcp_client/Makefile.am index 5cd0d8a..5ce9538 100644 --- a/tests/mgcp_client/Makefile.am +++ b/tests/mgcp_client/Makefile.am @@ -37,3 +37,6 @@ mgcp_client_test_LDADD = \ $(LIBRARY_DL) \ $(LIBOSMONETIF_LIBS) \ $(NULL) + +update_exp: + $(builddir)/mgcp_client_test >$(srcdir)/mgcp_client_test.ok 2>$(srcdir)/mgcp_client_test.err diff --git a/tests/mgcp_client/mgcp_client_test.c b/tests/mgcp_client/mgcp_client_test.c index 513f345..007b90c 100644 --- a/tests/mgcp_client/mgcp_client_test.c +++ b/tests/mgcp_client/mgcp_client_test.c @@ -18,12 +18,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #include <string.h> #include <osmocom/core/msgb.h> #include <osmocom/core/application.h> #include <osmocom/mgcp_client/mgcp_client.h> #include <osmocom/mgcp_client/mgcp_client_internal.h> +#include <errno.h> void *ctx; @@ -46,14 +49,14 @@ static struct msgb *mgcp_from_str(const char *head, const char *params) l = strlen(head); msg->l2h = msgb_put(msg, l); data = (char*)msgb_l2(msg); - strncpy(data, head, l); + osmo_strlcpy(data, head, l); data = (char*)msgb_put(msg, 1); *data = '\n'; l = strlen(params); data = (char*)msgb_put(msg, l); - strncpy(data, params, l); + osmo_strlcpy(data, params, l); return msg; } @@ -66,14 +69,14 @@ static struct msgb *from_str(const char *str) char *data; msg->l2h = msgb_put(msg, l); data = (char*)msgb_l2(msg); - strncpy(data, str, l); + osmo_strlcpy(data, str, l); return msg; } static struct mgcp_client_conf conf; struct mgcp_client *mgcp = NULL; -static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, +static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, int conn_id, const char *params) { static char compose[4096 - 128]; @@ -87,7 +90,7 @@ static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment, printf("composed response:\n-----\n%s\n-----\n", compose); - mgcp_client_rx(mgcp, from_str(compose)); + return mgcp_client_rx(mgcp, from_str(compose)); } void test_response_cb(struct mgcp_response *response, void *priv) @@ -162,7 +165,7 @@ void test_mgcp_msg(void) .endpoint = "23@mgw", .audio_port = 1234, .call_id = 47, - .conn_id = 11, + .conn_id = "11", .conn_mode = MGCP_CONN_RECV_SEND }; @@ -225,6 +228,154 @@ void test_mgcp_msg(void) msgb_free(msg); } +void test_mgcp_client_cancel() +{ + mgcp_trans_id_t trans_id; + struct msgb *msg; + struct mgcp_msg mgcp_msg = { + .verb = MGCP_VERB_CRCX, + .audio_ip = "192.168.100.23", + .endpoint = "23@mgw", + .audio_port = 1234, + .call_id = 47, + .conn_id = "11", + .conn_mode = MGCP_CONN_RECV_SEND, + .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID + | MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE), + }; + + printf("\n%s():\n", __func__); + fprintf(stderr, "\n%s():\n", __func__); + + if (mgcp) + talloc_free(mgcp); + mgcp = mgcp_client_init(ctx, &conf); + + msg = mgcp_msg_gen(mgcp, &mgcp_msg); + trans_id = mgcp_msg_trans_id(msg); + fprintf(stderr, "- composed msg with trans_id=%u\n", trans_id); + + fprintf(stderr, "- not in queue yet, cannot cancel yet\n"); + OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT); + + fprintf(stderr, "- enqueue\n"); + dummy_mgcp_send(msg); + + fprintf(stderr, "- cancel succeeds\n"); + OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == 0); + + fprintf(stderr, "- late response gets discarded\n"); + OSMO_ASSERT(reply_to(trans_id, 200, "OK", 1, "v=0\r\n") == -ENOENT); + + fprintf(stderr, "- canceling again does nothing\n"); + OSMO_ASSERT(mgcp_client_cancel(mgcp, trans_id) == -ENOENT); + + fprintf(stderr, "%s() done\n", __func__); +} + +struct sdp_section_start_test { + const char *body; + int expect_rc; + struct mgcp_response expect_params; +}; + +static struct sdp_section_start_test sdp_section_start_tests[] = { + { + .body = "", + .expect_rc = -EINVAL, + }, + { + .body = "\n\n", + }, + { + .body = "\r\n\r\n", + }, + { + .body = "\n\r\n\r", + }, + { + .body = "some mgcp header data\r\nand header params" + "\n\n" + "m=audio 23\r\n", + .expect_params = { + .audio_port = 23, + }, + }, + { + .body = "some mgcp header data\r\nand header params" + "\r\n\r\n" + "m=audio 23\r\n", + .expect_params = { + .audio_port = 23, + }, + }, + { + .body = "some mgcp header data\r\nand header params" + "\n\r\n\r" + "m=audio 23\r\n", + .expect_params = { + .audio_port = 23, + }, + }, + { + .body = "some mgcp header data\r\nand header params" + "\n\r\n" + "m=audio 23\r\n", + .expect_rc = -EINVAL, + }, + { + .body = "some mgcp header data\r\nand header params" + "\r\n\r" + "m=audio 23\r\n", + .expect_rc = -EINVAL, + }, + { + .body = "some mgcp header data\r\nand header params" + "\n\r\r" + "m=audio 23\r\n", + .expect_rc = -EINVAL, + }, +}; + +void test_sdp_section_start() +{ + int i; + int failures = 0; + + for (i = 0; i < ARRAY_SIZE(sdp_section_start_tests); i++) { + int rc; + struct sdp_section_start_test *t = &sdp_section_start_tests[i]; + struct mgcp_response *r = talloc_zero(ctx, struct mgcp_response); + + r->body = talloc_strdup(r, t->body); + + printf("\n%s() test [%d]:\n", __func__, i); + fprintf(stderr, "\n%s() test [%d]:\n", __func__, i); + fprintf(stderr, "body: \"%s\"\n", osmo_escape_str(r->body, -1)); + + rc = mgcp_response_parse_params(r); + + fprintf(stderr, "got rc=%d\n", rc); + if (rc != t->expect_rc) { + fprintf(stderr, "FAIL: Expected rc=%d\n", t->expect_rc); + failures++; + } + if (rc) { + talloc_free(r); + continue; + } + + fprintf(stderr, "got audio_port=%u\n", t->expect_params.audio_port); + if (r->audio_port != t->expect_params.audio_port) { + fprintf(stderr, "FAIL: Expected audio_port=%u\n", t->expect_params.audio_port); + failures++; + } + talloc_free(r); + } + + OSMO_ASSERT(!failures); +} + static const struct log_info_cat log_categories[] = { }; @@ -238,16 +389,20 @@ int main(int argc, char **argv) { ctx = talloc_named_const(NULL, 1, "mgcp_client_test"); msgb_talloc_ctx_init(ctx, 0); - osmo_init_logging(&log_info); + osmo_init_logging2(ctx, &log_info); log_set_print_filename(osmo_stderr_target, 0); log_set_print_timestamp(osmo_stderr_target, 0); log_set_use_color(osmo_stderr_target, 0); log_set_print_category(osmo_stderr_target, 1); + log_set_category_filter(osmo_stderr_target, DLMGCP, 1, LOGL_DEBUG); + mgcp_client_conf_init(&conf); test_crcx(); test_mgcp_msg(); + test_mgcp_client_cancel(); + test_sdp_section_start(); printf("Done\n"); fprintf(stderr, "Done\n"); diff --git a/tests/mgcp_client/mgcp_client_test.err b/tests/mgcp_client/mgcp_client_test.err index 24151ee..7309242 100644 --- a/tests/mgcp_client/mgcp_client_test.err +++ b/tests/mgcp_client/mgcp_client_test.err @@ -1,2 +1,65 @@ DLMGCP message buffer to small, can not generate MGCP message + +test_mgcp_client_cancel(): +- composed msg with trans_id=1 +- not in queue yet, cannot cancel yet +DLMGCP Cannot cancel, no such transaction: 1 +- enqueue +- cancel succeeds +DLMGCP Canceled transaction 1 +- late response gets discarded +DLMGCP Cannot find matching MGCP transaction for trans_id 1 +- canceling again does nothing +DLMGCP Cannot cancel, no such transaction: 1 +test_mgcp_client_cancel() done + +test_sdp_section_start() test [0]: +body: "" +DLMGCP MGCP response: cannot find start of SDP parameters +got rc=-22 + +test_sdp_section_start() test [1]: +body: "\n\n" +got rc=0 +got audio_port=0 + +test_sdp_section_start() test [2]: +body: "\r\n\r\n" +got rc=0 +got audio_port=0 + +test_sdp_section_start() test [3]: +body: "\n\r\n\r" +got rc=0 +got audio_port=0 + +test_sdp_section_start() test [4]: +body: "some mgcp header data\r\nand header params\n\nm=audio 23\r\n" +got rc=0 +got audio_port=23 + +test_sdp_section_start() test [5]: +body: "some mgcp header data\r\nand header params\r\n\r\nm=audio 23\r\n" +got rc=0 +got audio_port=23 + +test_sdp_section_start() test [6]: +body: "some mgcp header data\r\nand header params\n\r\n\rm=audio 23\r\n" +got rc=0 +got audio_port=23 + +test_sdp_section_start() test [7]: +body: "some mgcp header data\r\nand header params\n\r\nm=audio 23\r\n" +DLMGCP MGCP response: cannot find start of SDP parameters +got rc=-22 + +test_sdp_section_start() test [8]: +body: "some mgcp header data\r\nand header params\r\n\rm=audio 23\r\n" +DLMGCP MGCP response: cannot find start of SDP parameters +got rc=-22 + +test_sdp_section_start() test [9]: +body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n" +DLMGCP MGCP response: cannot find start of SDP parameters +got rc=-22 Done diff --git a/tests/mgcp_client/mgcp_client_test.ok b/tests/mgcp_client/mgcp_client_test.ok index d4efee4..fc6db30 100644 --- a/tests/mgcp_client/mgcp_client_test.ok +++ b/tests/mgcp_client/mgcp_client_test.ok @@ -43,7 +43,11 @@ C: 2f I: 11
M: sendrecv
+v=0
+o=- 2f 23 IN IP4 127.0.0.1
+s=-
c=IN IP4 192.168.100.23
+t=0 0
m=audio 1234 RTP/AVP 255
Generated DLCX message: @@ -59,4 +63,43 @@ RSIP 5 23@mgw MGCP 1.0 Overfolow test: + +test_mgcp_client_cancel(): +composed: +----- +CRCX 1 23@mgw MGCP 1.0
+C: 2f
+I: 11
+L: p:20, a:AMR, nt:IN
+M: sendrecv
+ +----- +composed response: +----- +200 1 OK
+I: 1 + +v=0
+ +----- + +test_sdp_section_start() test [0]: + +test_sdp_section_start() test [1]: + +test_sdp_section_start() test [2]: + +test_sdp_section_start() test [3]: + +test_sdp_section_start() test [4]: + +test_sdp_section_start() test [5]: + +test_sdp_section_start() test [6]: + +test_sdp_section_start() test [7]: + +test_sdp_section_start() test [8]: + +test_sdp_section_start() test [9]: Done |