Compare commits

..

5 Commits

Author SHA1 Message Date
Oliver Smith
c9e2984643 Inactive connection cleanup (disabled by default)
Add a watchdog timer to connections, and close these connections when
the watchdog timer expires. Kick the watchdog whenever RTP messages or
the relevant MGCP messages arrive.

This feature is disabled by default, as it is incompatible with LCLS
(connections in LCLS state appear to be inactive). Enable it with the
new "conn-timeout" VTY setting. In general, this feature can be used to
work around interoperability problems causing connections to stay open
forever, and slowly exhausting all available ports. This happened for
various reasons already.

MDCX is the only relevant MGCP message:
- CRCX creates the conn and timer
- DLCX deletes the conn and timer
- MDCX is the only remaining supported MGCP message that indicates a CI
- Can't easily generically parse a CI for all MGCP messages, parsing is
  done in handle_modify_con().

Related: OS#3429
Change-Id: I18886052e090466f73829133c24f011806cc1fe0
2019-01-31 16:37:01 +01:00
Oliver Smith
622dd61764 Cosmetic: fix spaces/tabs in mgcp_requests[]
Change-Id: I2e8ef83b231b7d7b71e533d0699afea433d635aa
2019-01-30 15:27:21 +01:00
Harald Welte
e81c1176aa Bump version: 1.4.0.90-ed0c-dirty → 1.5.0
Change-Id: I8f7c7b75f38ebd1ee48605596424af48cc1ad53d
2019-01-20 15:02:19 +01:00
Neels Hofmeyr
ed0c1aa9d5 mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
Change-Id: Ie4ecb4b82a7a1e476c58d0a6056525733254adbb
2019-01-03 02:12:55 +01:00
Neels Hofmeyr
ac69ea9cdf mgcp_client: make domain part of endpoint configurable
So far, both osmo-msc and osmo-bsc always pass endpoint names of the form
'...@mgw' to osmo-mgw. Allow configuring the 'mgw' part.

Note that the actual way to pass a differing name is to pass a composed
'rtpbridge/*@foo' to mgcp_msg_gen() in the struct mgcp_msg. So this merely adds
a common VTY config for the domain name part, changes to clients are necessary.

- add mgcp_client_rtpbridge_wildcard() (useful for AoIP endpoints)
- add mgcp_client_endpoint_domain() (useful for SCCPlite endpoints)
- add mgcp client vty cfg 'mgw endpoint-domain NAME'

Rationale: reading pcaps becomes so much easier when each of osmo-bsc and
osmo-msc address their MGW with differing domain names. Otherwise, both will
have a '0@mgw' endpoint and it gets really confusing.

Also: our MGCP clients osmo-bsc and osmo-msc use code dup to compose the
initial 'rtpbridge/*@mgw' rtpbridge wildcard. It should be defined by this API
instead.

This will be used by:
* osmo-msc I87ac11847d1a6d165ee9a2b5d8a4978e7ac73433
* osmo-bsc I492023e9dca0233ec0a077032455d9f2e3880f78

After these, with according configuration, there can be a '0@bsc' and a '0@msc'
endpoint on two separate osmo-mgw instances:

osmo-mgw-for-bsc.cfg:
 mgcp
  domain bsc

osmo-bsc.cfg:
 msc 0
  mgw endpoint-domain bsc

osmo-mgw-for-msc.cfg:
 mgcp
  domain msc

osmo-msc.cfg:
 msc
  mgw endpoint-domain msc

There can also be '0@bsc' and '1@msc' endpoints on one single osmo-mgw instance with:

osmo-mgw.cfg:
 mgcp
  domain *

and same osmo-{bsc,msc}.cfg as above.

(By default, everything will still use '@mgw')

Change-Id: Ia662016f29dd8727d9c4626d726729641e21e1f8
2019-01-03 02:11:23 +01:00
32 changed files with 386 additions and 1229 deletions

View File

@@ -24,4 +24,3 @@
# If any interfaces have been removed or changed since the last public release, a=0.
#
#library what description / commit summary line
libosmo-mgcp-client various Drop legacy API functions and mgcp_client_conf members

View File

@@ -39,10 +39,10 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
AC_SUBST(LIBRARY_DL)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.12.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.4.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -206,7 +206,6 @@ AC_OUTPUT(
tests/atlocal
tests/mgcp_client/Makefile
tests/mgcp/Makefile
tests/iuup/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile

View File

@@ -6,7 +6,6 @@ Type=simple
Restart=always
ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg
RestartSec=2
ExecStopPost=/usr/local/bin/save_log_tail osmo-mgw
[Install]
WantedBy=multi-user.target

107
debian/changelog vendored
View File

@@ -1,3 +1,110 @@
osmo-mgw (1.5.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* gitignore: Filter *.pc
* configure: Find correct libgsm's gsm.h header
* vty: Fix typo writing bts-jitter-buffer-delay-{min,max}
* Remove libosmo-legacy-mgcp and osmo-bsc-mgcp
* debian: Remove dangling symlink to osmo-bsc-mgcp.service
* Install systemd services with autotools
* Install sample cfg file to /etc/osmocom
* mgcp: Fix osmux_cid_bitmap static array size calculation
* mgcp_osmux: Use define to calculate rtp_ssrc_winlen
* osmux: Avoid initing output without enabling osmux
* mgcp: Log endpoint nr consistently as hex
* osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled
* osmux: Don't process regular osmux frames if disabled by cfg
* osmux: Move parse_cid of legacy dummy frames to own function
* osmux: Make func handling dummy frames independent of endp type
* osmux: allow enabling osmux only on correct activating state
* osmux: Improve checks around activating and using enabled osmux
* osmux.h: Document enum osmux_state
* osmux: Avoid processing further frames if conn not found
[ Philipp Maier ]
* mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well
* mgcp_network: translate payload type numbers in RTP packets
* mgcp_client: use IETF source port as for MGCP
* mgcp_client: increment local port number when port is in use
* mgcp_test: release endpoints after use
* network: do not patch PT of RTCP packets
* network: check packets before further processing
* Cosmetic: remove misplaced line break
* mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap)
* mgcp_sdp: restructure mgcp_write_response_sdp() (audio)
* mgcp_client: check local port only once
* mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio()
* mgcp_protocol: increase buffer space for codec name in LCO
* osmo-mgw: Add vty reference manual
[ Neels Hofmeyr ]
* fix handling of "Wrong domain name" error
* interpret domain '*' as 'allow any domain'
* cosmetic: log: fix "CallIDs does not match"
* fix 3G hack: allow any IP for loopback and 0.0.0.0
* cosmetic: drop code dup in mgcp_client_fsm.c CRCX
* add X-Osmo-IGN MGCP header to ignore CallID
* X-Osmo-IGN: rather parse items by token, not char
* mgcp_test: fix log of conn_id presence
* mgcp_test: fix get_conn_id_from_response() CI length
* mgcp_client_test: cosmetically re-arrange reply_to() args
* mgcp_client_test: use "\r\n\r\n" instead of "\n\n"
* mgcp_client_test: also verify received conn_id
* mgcp_client_test: test long conn_id
* mgcp_client: error on too long conn id
* mgcp_common: rename to MGCP_CONN_ID_MAXLEN
* doc: fix mgcp_verify_ci() return val doc
* mgcp_verify_ci(): return meaningful error codes
* fix mgcp_verify_ci(): off-by-one in max len check
* generate shorter 'I:' conn IDs
* mgcp_conn_get(): compare conn Id ('I:') case insensitively
* mgcp_conn_get(): match conn Id ('I:') despite leading zeros
* cosmetic: mgcp_test: fix get_conn_id_from_response()
* comment: indicate struct type for mgcp_endpoint.conns
* log: avoid logging early media as error
* fix osmo-mgw -s; fixes osmo-mgw.service using -s
* Importing history from osmo-gsm-manuals.git
* OsmoMGW: update VTY reference
* OsmoMGW: document the 'X-Osmo-IGN' MGCP extension
* mgw: update vty reference
* drop/replace very weird logging in mgcp_client.c
* check_rtp: on IP:port errors, log the IP and port
* osmo-mgw: err-log: include expected domain name
* mgcp_client_vty: fix missing talloc_free
* mgcp_client: drop a bunch of dead code
* mgcp_client: logging tweaks
* mgcp_client: make domain part of endpoint configurable
* mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR}
[ Stefan Sperling ]
* add VTY commands which show specific mgcp endpoints
* add MGCP CRCX command statistics to osmo-mgw
* show RTP TX/RX stats in 'mgcp show stats' output
* use local variable for rate counters in handle_create_con()
* add more mgcp crxc error counters
* add MDCX command statistics to osmo-mgw
* add aggregated rtp connection stats to osmo-mgw
* add DLCX command statistics to osmo-mgw
[ Harald Welte ]
* debian/rules: Don't overwrite .tarball-version
* check_rtp_origin(): Don't memcmp sockadd_in and in_addr
* check_rtp_origin(): Avoid using memcmp for comparing integer types
* vty-ref: Update URI of docbook 5.0 schema
[ Daniel Willmann ]
* mgw: Add new VTY reference
* Add initial OsmoMGW manual
[ Oliver Smith ]
* build manuals moved here from osmo-gsm-manuals.git
* jenkins.sh: remove leftover MGCP env variable
* Fix DISTCHECK_CONFIGURE_FLAGS override
* contrib/jenkins.sh: build and publish manuals
* contrib: fix makedistcheck with disabled systemd
-- Harald Welte <laforge@gnumonks.org> Sun, 20 Jan 2019 15:02:18 +0100
osmo-mgw (1.4.0) unstable; urgency=medium
[ Philipp Maier ]

4
debian/control vendored
View File

@@ -19,7 +19,7 @@ Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
Package: libosmo-mgcp-client3
Package: libosmo-mgcp-client5
Section: libs
Architecture: any
Multi-Arch: same
@@ -31,5 +31,5 @@ Package: libosmo-mgcp-client-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client3 (= ${binary:Version}), ${misc:Depends}
Depends: libosmo-mgcp-client5 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities

View File

@@ -29,7 +29,6 @@
/* Debug Areas of the code */
enum {
DRTP,
DIUUP,
Debug_LastEntry,
};

View File

@@ -1,41 +0,0 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* IuUP CN node, minimal implementation */
/* _____IuUP_CN_____
* | |
* UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+-> ---+-> rx_payload()
* | | |
* | <-PDU-- tx_msg() <-------------+-- <-+--- osmo_iuup_cn_tx_payload()
* | |
* -----------------
*/
#pragma once
struct osmo_iuup_cn;
struct msgb;
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
struct osmo_iuup_cn_cfg {
void *node_priv;
/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
* callback. */
osmo_iuup_data_cb_t rx_payload;
/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
* It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
* originated the request. */
osmo_iuup_data_cb_t tx_msg;
};
bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
const char *name_fmt, ...);
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);

View File

@@ -1,117 +0,0 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* IuUP protocol handling, minimal implementation */
#pragma once
#include <osmocom/core/endian.h>
#include <osmocom/core/msgb.h>
#define OSMO_IUUP_HEADROOM 32
enum osmo_iuup_pdu_type {
OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
};
enum osmo_iuup_acknack {
OSMO_IUUP_ACKNACK_PROCEDURE = 0,
OSMO_IUUP_ACKNACK_ACK = 1,
OSMO_IUUP_ACKNACK_NACK = 2,
};
enum osmo_iuup_procedure {
OSMO_IUUP_PROC_INITIALIZATION = 0,
OSMO_IUUP_PROC_RATE_CONTROL = 1,
OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
OSMO_IUUP_PROC_ERROR_EVENT = 3,
};
enum osmo_iuup_frame_good {
OSMO_IUUP_FRAME_GOOD = 0,
OSMO_IUUP_FRAME_BAD = 1,
OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
};
struct osmo_iuup_hdr_ctrl {
#if OSMO_IS_BIG_ENDIAN
uint8_t pdu_type:4,
ack_nack:2,
frame_nr:2;
uint8_t mode_version:4,
procedure:4;
uint8_t header_crc:6,
payload_crc_hi:2;
uint8_t payload_crc_lo;
uint8_t payload[0];
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t frame_nr:2,
ack_nack:2,
pdu_type:4;
uint8_t procedure:4,
mode_version:4;
uint8_t payload_crc_hi:2,
header_crc:6;
uint8_t payload_crc_lo;
uint8_t payload[0];
#endif
} __attribute__((packed));
union osmo_iuup_hdr_ctrl_payload {
struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t spare:3,
iptis_present:1,
subflows:3,
chain:1;
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t spare:3,
iptis_present:1,
subflows:3,
chain:1;
#endif
} initialization;
struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t error_distance:2,
error_cause:6;
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t error_cause:6,
error_distance:2;
#endif
} error_event;
};
extern const struct value_string osmo_iuup_error_cause_names[];
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
{ return get_value_string(osmo_iuup_error_cause_names, val); }
struct osmo_iuup_hdr_data {
#if OSMO_IS_BIG_ENDIAN
uint8_t pdu_type:4,
frame_nr:4;
uint8_t frame_good:2,
rfci:6;
uint8_t header_crc:6,
payload_crc_hi:2;
uint8_t payload_crc_lo;
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t frame_nr:4,
pdu_type:4;
uint8_t rfci:6,
frame_good:2;
uint8_t payload_crc_hi:2,
header_crc:6;
uint8_t payload_crc_lo;
#endif
uint8_t payload[0];
} __attribute__((packed));
int osmo_iuup_classify(bool log_errors,
const char *log_label,
struct msgb *pdu,
struct osmo_iuup_hdr_ctrl **is_ctrl,
struct osmo_iuup_hdr_data **is_data);
bool osmo_iuup_is_init(struct msgb *pdu);
void osmo_iuup_make_init_ack(struct msgb *ack);
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);

View File

@@ -274,6 +274,9 @@ struct mgcp_config {
uint16_t osmux_dummy;
/* domain name of the media gateway */
char domain[255+1];
/* time after which inactive connections (CIs) get closed */
unsigned int conn_timeout;
};
/* config management */

View File

@@ -23,28 +23,15 @@
#pragma once
#include <osmocom/core/msgb.h>
struct sockaddr_in;
struct mgcp_conn;
struct mgcp_conn_rtp;
struct mgcp_endpoint;
struct osmo_rtp_msg_ctx {
int proto;
struct mgcp_conn_rtp *conn_src;
struct sockaddr_in *from_addr;
};
#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
#define LOG_ENDP(endp, level, fmt, args...) \
LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
* The fields OSMO_RTP_MSG_PROTO, OSMO_RTP_MSG_CONN_SRC, OSMO_RTP_MSG_FROM_ADDR should be set
* appropriately on the msg. */
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
/* 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()

View File

@@ -37,13 +37,6 @@
#define CONN_ID_BTS "0"
#define CONN_ID_NET "1"
#define LOG_CONN(conn, level, fmt, args...) \
LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
LOG_CONN(conn_rtp->conn, level, fmt, ## args)
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
@@ -207,8 +200,6 @@ struct mgcp_conn_rtp {
} osmux;
struct rate_ctr_group *rate_ctr_group;
struct osmo_iuup_cn *iuup;
};
/*! Connection type, specifies which member of the union "u" in mgcp_conn
@@ -240,6 +231,9 @@ struct mgcp_conn {
/*! human readable name (vty, logging) */
char name[256];
/*! activity tracker (for cleaning up inactive connections) */
struct osmo_timer_list watchdog;
/*! union with connection description */
union {
struct mgcp_conn_rtp rtp;
@@ -268,10 +262,11 @@ struct mgcp_parse_data {
};
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
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);
@@ -336,8 +331,4 @@ enum {
#define PTYPE_UNDEFINED (-1)
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct sockaddr_in *addr, struct msgb *msg);
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);

View File

@@ -25,7 +25,7 @@ struct mgcp_client_conf {
/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
* If this is nonempty, the contained name will be used instead of 'mgw'. */
char endpoint_domain_name[64];
char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN];
};
typedef unsigned int mgcp_trans_id_t;

View File

@@ -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=4:0:1
MGCP_CLIENT_LIBVERSION=5:0:0
lib_LTLIBRARIES = \
libosmo-mgcp-client.la \

View File

@@ -705,8 +705,14 @@ struct mgcp_client *mgcp_client_init(void *ctx,
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
MGCP_CLIENT_REMOTE_PORT_DEFAULT;
osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
sizeof(mgcp->actual.endpoint_domain_name));
if (osmo_strlcpy(mgcp->actual.endpoint_domain_name, conf->endpoint_domain_name,
sizeof(mgcp->actual.endpoint_domain_name))
>= sizeof(mgcp->actual.endpoint_domain_name)) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP client: endpoint domain name is too long, max length is %zu: '%s'\n",
sizeof(mgcp->actual.endpoint_domain_name) - 1, conf->endpoint_domain_name);
talloc_free(mgcp);
return NULL;
}
LOGP(DLMGCP, LOGL_NOTICE, "MGCP client: using endpoint domain '@%s'\n", mgcp_client_endpoint_domain(mgcp));
return mgcp;
@@ -819,7 +825,7 @@ uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
* For example, snprintf("rtpbridge\*@%s", mgcp_client_endpoint_domain(mgcp)). */
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp)
{
return mgcp->actual.endpoint_domain_name[0]? mgcp->actual.endpoint_domain_name : "mgw";
return mgcp->actual.endpoint_domain_name[0] ? mgcp->actual.endpoint_domain_name : "mgw";
}
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
@@ -830,8 +836,8 @@ const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp)
#define RTPBRIDGE_WILDCARD_FMT "rtpbridge/*@%s"
rc = snprintf(endpoint, sizeof(endpoint), RTPBRIDGE_WILDCARD_FMT, mgcp_client_endpoint_domain(mgcp));
if (rc > sizeof(endpoint) - 1) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length ('" RTPBRIDGE_WILDCARD_FMT "')\n",
mgcp_client_endpoint_domain(mgcp));
LOGP(DLMGCP, LOGL_ERROR, "MGCP endpoint exceeds maximum length of %zu: '" RTPBRIDGE_WILDCARD_FMT "'\n",
sizeof(endpoint) - 1, mgcp_client_endpoint_domain(mgcp));
return NULL;
}
if (rc < 1) {

View File

@@ -143,8 +143,13 @@ DEFUN(cfg_mgw_endpoint_domain_name,
MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n"
"Domain name, should be alphanumeric.\n")
{
osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
sizeof(global_mgcp_client_conf->endpoint_domain_name));
if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
sizeof(global_mgcp_client_conf->endpoint_domain_name))
>= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}

View File

@@ -40,6 +40,4 @@ libosmo_mgcp_a_SOURCES = \
mgcp_conn.c \
mgcp_stat.c \
mgcp_endp.c \
iuup_protocol.c \
iuup_cn_node.c \
$(NULL)

View File

@@ -1,224 +0,0 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* IuUP Core Network side protocol handling, minimal implementation */
/*
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr <neels@hofmeyr.de>
*
* 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/>.
*
*/
#include <talloc.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/mgcp/iuup_cn_node.h>
#include <osmocom/mgcp/iuup_protocol.h>
#include <osmocom/mgcp/debug.h>
#define LOG_IUUP_CN(cn, level, fmt, args...) \
LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
struct osmo_iuup_cn {
struct osmo_iuup_cn_cfg cfg;
char *name;
uint8_t next_frame_nr;
int rtp_payload_type;
};
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
const char *name_fmt, ...)
{
va_list ap;
struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
OSMO_ASSERT(cn);
cn->cfg = *cfg;
if (!name_fmt)
name_fmt = "-";
va_start(ap, name_fmt);
cn->name = talloc_vasprintf(cn, name_fmt, ap);
va_end(ap);
LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
if (!osmo_identifier_valid(cn->name)) {
LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
osmo_quote_str(cn->name, -1));
talloc_free(cn);
return NULL;
}
return cn;
}
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
{
talloc_free(cn);
}
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
struct osmo_iuup_hdr_data *hdr)
{
/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
((uint8_t*)hdr)[2] = 0xf0;
((uint8_t*)hdr)[3] = 0x3c;
msgb_pull(pdu, sizeof(*hdr) - 2);
struct rtp_hdr *h = (struct rtp_hdr*)pdu->data;
if (h->payload_type == 96)
h->payload_type = 112;
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
return 0;
}
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
{
/* Send Initialization Ack PDU back to the sender */
int rc;
struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
OSMO_ASSERT(ack);
ack->dst = src_pdu->dst;
/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
osmo_iuup_make_init_ack(ack);
LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
msgb_free(ack);
return rc;
}
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
struct osmo_iuup_hdr_ctrl *hdr)
{
switch (hdr->procedure) {
case OSMO_IUUP_PROC_INITIALIZATION:
switch (hdr->ack_nack) {
case OSMO_IUUP_ACKNACK_PROCEDURE:
LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
return tx_init_ack(cn, pdu);
default:
LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
cn->name, hdr->ack_nack);
break;
}
/* Continue to log "unexpected procedure" below. */
break;
case OSMO_IUUP_PROC_ERROR_EVENT:
{
union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
LOGP(DIUUP, LOGL_ERROR,
"(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
cn->name, p->error_event.error_distance, p->error_event.error_cause,
osmo_iuup_error_cause_name(p->error_event.error_cause));
return 0;
}
default:
break;
}
LOG_IUUP_CN(cn, LOGL_ERROR,
"Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
hdr->procedure, hdr->ack_nack);
return -EINVAL;
}
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
* freed by the caller. */
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
{
struct osmo_iuup_hdr_ctrl *is_ctrl;
struct osmo_iuup_hdr_data *is_data;
int rc;
rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
if (rc)
return rc;
if (is_ctrl)
return rx_control(cn, pdu, is_ctrl);
if (is_data)
return rx_data(cn, pdu, is_data);
return rc;
}
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
{
uint8_t frame_nr = cn->next_frame_nr;
cn->next_frame_nr = (frame_nr + 1) & 0x0f;
return frame_nr;
}
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
* message to the IuUP peer.
* Returns 0 on success, negative on error. */
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
{
struct rtp_hdr *rtp_was, *rtp;
struct osmo_iuup_hdr_data *iuup_hdr;
/* Splice an IuUP header in between RTP header and payload data */
rtp_was = (void*)pdu->data;
/* copy the RTP header part backwards by the size needed for the IuUP header */
/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
memmove(rtp, rtp_was, sizeof(*rtp));
/* Send the same payload type to the peer (erm...) */
rtp->payload_type = cn->rtp_payload_type;
iuup_hdr = (void*)rtp->data;
*iuup_hdr = (struct osmo_iuup_hdr_data){
.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
.frame_nr = next_frame_nr(cn),
.frame_good = OSMO_IUUP_FRAME_GOOD,
};
osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
cn->name, iuup_hdr->frame_nr);
struct rtp_hdr *h = (struct rtp_hdr*)pdu->data;
if (h->payload_type == 112)
h->payload_type = 96;
return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
}

View File

@@ -1,286 +0,0 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* IuUP Core Network side protocol, minimal implementation */
/*
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr <neels@hofmeyr.de>
*
* 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/>.
*
*/
#include <errno.h>
#include <osmocom/mgcp/iuup_protocol.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/netif/rtp.h>
/* Calculating two bytes of CRC is ok to do by a loop */
static uint8_t header_crc6(const uint8_t *hdr)
{
int bit;
/* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
* that's 1101111 or 0x6f;
* align its lowest bit with a uint16_t's highest bit: */
uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
for (bit = 15; bit >= 0; bit--)
{
if (remainder & (0x40 << bit))
remainder ^= polynomial;
polynomial >>= 1;
}
return remainder;
}
/*
* Charles Michael Heard's CRC-10 code, from
*
* http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
*
* with the CRC table initialized with values computed by
* his "gen_byte_crc10_table()" routine, rather than by calling that
* routine at run time, and with various data type cleanups.
*/
static const uint16_t byte_crc10_table[256] = {
0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
};
static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
{
int i;
for (i = 0; i < payload_len; i++) {
crc10_accum = ((crc10_accum << 8) & 0x300)
^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
^ payload[i];
}
return crc10_accum;
}
/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
* CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
* table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
static uint16_t crc10_remainder(uint16_t crc10_accum)
{
int bit;
/* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
* that's 11000110011 or 0x633;
* align its lowest bit with a 10bit value's highest bit: */
uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
uint32_t remainder = ((uint32_t)crc10_accum) << 10;
/* Run on 10 bits */
for (bit = 9; bit >= 0; bit--)
{
if (remainder & ((1 << 10) << bit))
remainder ^= polynomial;
polynomial >>= 1;
}
return remainder & 0x3ff;
}
static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
{
uint16_t crc10_accum = crc10(0, payload, payload_len);
return crc10_remainder(crc10_accum);
}
/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
*/
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
{
/* For both data and ctrl, the checksums and payload are at the same offset */
struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
uint16_t crc;
unsigned int payload_len;
hdr->header_crc = header_crc6(iuup_header_and_payload);
payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
crc = payload_crc10(hdr->payload, payload_len);
hdr->payload_crc_hi = (crc >> 8) & 0x3;
hdr->payload_crc_lo = crc & 0xff;
}
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
* Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
* in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
* both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
* valid RTP PDU containing an IuUP part. */
int osmo_iuup_classify(bool log_errors,
const char *log_label,
struct msgb *pdu,
struct osmo_iuup_hdr_ctrl **is_ctrl,
struct osmo_iuup_hdr_data **is_data)
{
struct rtp_hdr *rtp = (void*)pdu->data;
struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
unsigned int payload_len;
uint16_t crc_calculated;
uint16_t crc_from_peer;
#define ERR(fmt, args...) do { \
if (log_errors) \
LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
return -EINVAL; \
} while (0)
if (is_ctrl)
*is_ctrl = NULL;
if (is_data)
*is_data = NULL;
/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
* payload, so use osmo_iuup_hdr_data to check the minimum here. */
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
ERR("IuUP PDU too short: %u\n", pdu->len);
/* Let's not validate checksums if the header type isn't sane */
switch (hdr->pdu_type) {
case OSMO_IUUP_PDU_DATA_WITH_CRC:
/* If the caller isn't interested in data PDUs, cut short here. */
if (!is_data)
return 0;
break;
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
/* If the caller isn't interested in control PDUs, cut short here. */
if (!is_ctrl)
return 0;
if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
ERR("IuUP control PDU too short: %u\n", pdu->len);
break;
default:
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
}
/* For both data and ctrl, the checksums and payload are at the same offset */
crc_calculated = header_crc6((uint8_t*)hdr);
if (crc_calculated != hdr->header_crc)
ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
hdr->header_crc, crc_calculated);
payload_len = pdu->tail - hdr->payload;
crc_calculated = payload_crc10(hdr->payload, payload_len);
crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
if (crc_from_peer != crc_calculated)
ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
crc_from_peer, crc_calculated);
switch (hdr->pdu_type) {
case OSMO_IUUP_PDU_DATA_WITH_CRC:
if (is_data)
*is_data = (void*)hdr;
return 0;
case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
if (is_ctrl)
*is_ctrl = hdr;
return 0;
default:
ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
}
#undef ERR
}
/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
bool osmo_iuup_is_init(struct msgb *pdu)
{
struct osmo_iuup_hdr_ctrl *is_ctrl;
osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
return is_ctrl
&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
}
/* Append an IuUP Initialization ACK message */
void osmo_iuup_make_init_ack(struct msgb *ack)
{
/* Send Initialization Ack PDU back to the sender */
struct osmo_iuup_hdr_ctrl *hdr;
OSMO_ASSERT(ack);
hdr = (void*)msgb_put(ack, sizeof(*hdr));
*hdr = (struct osmo_iuup_hdr_ctrl){
.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
.ack_nack = OSMO_IUUP_ACKNACK_ACK,
.procedure = OSMO_IUUP_PROC_INITIALIZATION,
};
osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
}
const struct value_string osmo_iuup_error_cause_names[] = {
{ 0, "CRC error of frame header" },
{ 1, "CRC error of frame payload" },
{ 2, "Unexpected frame number" },
{ 3, "Frame loss" },
{ 4, "PDU type unknown" },
{ 5, "Unknown procedure" },
{ 6, "Unknown reserved value" },
{ 7, "Unknown field" },
{ 8, "Frame too short" },
{ 9, "Missing fields" },
{ 16, "Unexpected PDU type" },
{ 17, "spare" },
{ 18, "Unexpected procedure" },
{ 19, "Unexpected RFCI" },
{ 20, "Unexpected value" },
{ 42, "Initialisation failure" },
{ 43, "Initialisation failure (network error, timer expiry)" },
{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
{ 45, "Rate control failure" },
{ 46, "Error event failure" },
{ 47, "Time Alignment not supported" },
{ 48, "Requested Time Alignment not possible" },
{ 49, "Iu UP Mode version not supported" },
{}
};

View File

@@ -29,6 +29,7 @@
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/timer.h>
#include <ctype.h>
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
@@ -125,6 +126,23 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
rate_ctr_group_free(conn_rtp->rate_ctr_group);
}
void mgcp_conn_watchdog_cb(void *data)
{
struct mgcp_conn *conn = data;
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x CI:%s connection timed out!\n", ENDPOINT_NUMBER(conn->endp), conn->id);
mgcp_conn_free(conn->endp, conn->id);
}
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn)
{
unsigned long int timeout = conn->endp->cfg->conn_timeout;
if (!timeout)
return;
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x CI:%s watchdog kicked\n", ENDPOINT_NUMBER(conn->endp), conn->id);
osmo_timer_schedule(&conn->watchdog, timeout, 0);
}
/*! allocate a new connection list entry.
* \param[in] ctx talloc context
* \param[in] endp associated endpoint
@@ -167,6 +185,9 @@ struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
OSMO_ASSERT(false);
}
/* Initialize watchdog */
osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn);
mgcp_conn_watchdog_kick(conn);
llist_add(&conn->entry, &endp->conns);
return conn;
@@ -274,6 +295,7 @@ void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id)
OSMO_ASSERT(false);
}
osmo_timer_del(&conn->watchdog);
llist_del(&conn->entry);
talloc_free(conn);
}

View File

@@ -43,8 +43,6 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/debug.h>
#include <osmocom/mgcp/iuup_cn_node.h>
#include <osmocom/mgcp/iuup_protocol.h>
#define RTP_SEQ_MOD (1 << 16)
@@ -52,13 +50,11 @@
#define RTP_MAX_MISORDER 100
#define RTP_BUF_SIZE 4096
enum rtp_proto {
enum {
MGCP_PROTO_RTP,
MGCP_PROTO_RTCP,
};
static int rx_rtp(struct msgb *msg);
/*! Determine the local rtp bind IP-address.
* \param[out] addr caller provided memory to store the resulting IP-Address
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
@@ -485,14 +481,14 @@ void mgcp_rtp_annex_count(struct mgcp_endpoint *endp,
* Patch the payload type of an RTP packet so that it uses the payload type
* that is valid for the destination connection (conn_dst) */
static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
struct mgcp_conn_rtp *conn_dst, char *data, int len)
{
struct rtp_hdr *rtp_hdr;
uint8_t pt_in;
int pt_out;
OSMO_ASSERT(msg->len >= sizeof(struct rtp_hdr));
rtp_hdr = (struct rtp_hdr *)msg->data;
OSMO_ASSERT(len >= sizeof(struct rtp_hdr));
rtp_hdr = (struct rtp_hdr *)data;
pt_in = rtp_hdr->payload_type;
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
@@ -512,7 +508,7 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct sockaddr_in *addr, struct msgb *msg)
struct sockaddr_in *addr, char *data, int len)
{
uint32_t arrival_time;
int32_t transit;
@@ -520,12 +516,11 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
uint32_t timestamp, ssrc;
struct rtp_hdr *rtp_hdr;
int payload = rtp_end->codec->payload_type;
unsigned int len = msg->len;
if (len < sizeof(*rtp_hdr))
return;
rtp_hdr = (struct rtp_hdr *)msg->data;
rtp_hdr = (struct rtp_hdr *)data;
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
arrival_time = get_current_ts(rtp_end->codec->rate);
@@ -658,14 +653,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
/* Forward data to a debug tap. This is debug function that is intended for
* debugging the voice traffic with tools like gstreamer */
static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
static void forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf,
int len)
{
int rc;
if (!tap->enabled)
return;
rc = sendto(fd, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
rc = sendto(fd, buf, len, 0, (struct sockaddr *)&tap->forward,
sizeof(tap->forward));
if (rc < 0)
@@ -683,7 +679,7 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
* \param[in] conn_dst associated destination connection
* \returns 0 on success, -1 on ERROR */
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
char *buf, int len, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst)
{
/*! When no destination connection is available (e.g. when only one
@@ -695,7 +691,6 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
struct mgcp_rtp_state *rtp_state;
char *dest_name;
int rc;
int len;
OSMO_ASSERT(conn_src);
OSMO_ASSERT(conn_dst);
@@ -724,7 +719,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
* should not occur if transcoding is consequently avoided. Until
* we have transcoding support in osmo-mgw we can not resolve this. */
if (is_rtp) {
rc = mgcp_patch_pt(conn_src, conn_dst, msg);
rc = mgcp_patch_pt(conn_src, conn_dst, buf, len);
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR,
"endpoint:0x%x can not patch PT because no suitable egress codec was found.\n",
@@ -751,18 +746,18 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
} else if (is_rtp) {
int cont;
int nbytes = 0;
int buflen = msg->len;
int buflen = len;
do {
/* Run transcoder */
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
(char*)msg->data, &buflen,
buf, &buflen,
RTP_BUF_SIZE);
if (cont < 0)
break;
if (addr)
mgcp_patch_and_count(endp, rtp_state, rtp_end,
addr, msg);
addr, buf, buflen);
LOGP(DRTP, LOGL_DEBUG,
"endpoint:0x%x process/send to %s %s "
"rtp_port:%u rtcp_port:%u\n",
@@ -773,9 +768,8 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
buf, buflen);
#if 0
/* FIXME: HACK HACK HACK. See OS#2459.
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
@@ -783,7 +777,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
*/
if (!rtp_state->patched_first_rtp_payload
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
uint8_t *data = msg->data + 12;
uint8_t *data = (uint8_t *) & buf[12];
if (data[0] == 0xe0) {
data[0] = 0xe4;
data[1] = 0x00;
@@ -794,13 +788,10 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
ENDPOINT_NUMBER(endp));
}
}
#endif
if (conn_dst->iuup)
len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
else
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
(char*)msg->data, msg->len);
len = mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port, buf, buflen);
if (len <= 0)
return len;
@@ -823,7 +814,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
len = mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
rtp_end->rtcp_port, (char*)msg->data, msg->len);
rtp_end->rtcp_port, buf, len);
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
@@ -834,6 +825,46 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
return 0;
}
/* Helper function for mgcp_recv(),
Receive one RTP Packet + Originating address from file descriptor */
static int receive_from(struct mgcp_endpoint *endp, int fd,
struct sockaddr_in *addr, char *buf, int bufsize)
{
int rc;
socklen_t slen = sizeof(*addr);
struct sockaddr_in addr_sink;
char buf_sink[RTP_BUF_SIZE];
bool tossed = false;
if (!addr)
addr = &addr_sink;
if (!buf) {
tossed = true;
buf = buf_sink;
bufsize = sizeof(buf_sink);
}
rc = recvfrom(fd, buf, bufsize, 0, (struct sockaddr *)addr, &slen);
LOGP(DRTP, LOGL_DEBUG,
"receiving %u bytes length packet from %s:%u ...\n",
rc, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
if (rc < 0) {
LOGP(DRTP, LOGL_ERROR,
"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:0x%x packet tossed\n",
ENDPOINT_NUMBER(endp));
}
return rc;
}
/* Check if the origin (addr) matches the address/port data of the RTP
* connections. */
static int check_rtp_origin(struct mgcp_conn_rtp *conn,
@@ -938,7 +969,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
/* Do some basic checks to make sure that the RTCP packets we are going to
* process are not complete garbage */
static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
static int check_rtcp(char *buf, unsigned int buf_size)
{
struct rtcp_hdr *hdr;
unsigned int len;
@@ -946,45 +977,33 @@ static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
/* RTPC packets that are just a header without data do not make
* any sense. */
if (msg->len < sizeof(struct rtcp_hdr)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
msg->len, sizeof(struct rtcp_hdr));
if (buf_size < sizeof(struct rtcp_hdr))
return -EINVAL;
}
/* Make sure that the length of the received packet does not exceed
* the available buffer size */
hdr = (struct rtcp_hdr *)msg->data;
hdr = (struct rtcp_hdr *)buf;
len = (osmo_ntohs(hdr->length) + 1) * 4;
if (len > msg->len) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
len, msg->len);
if (len > buf_size)
return -EINVAL;
}
/* Make sure we accept only packets that have a proper packet type set
* See also: http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
type = hdr->type;
if ((type < 192 || type > 195) && (type < 200 || type > 213)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header: invalid type: %u\n", type);
if ((type < 192 || type > 195) && (type < 200 || type > 213))
return -EINVAL;
}
return 0;
}
/* Do some basic checks to make sure that the RTP packets we are going to
* process are not complete garbage */
static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
static int check_rtp(char *buf, unsigned int buf_size)
{
size_t min_size = sizeof(struct rtp_hdr);
if (conn_src->iuup)
min_size += sizeof(struct osmo_iuup_hdr_data);
if (msg->len < min_size) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
msg->len, min_size);
return -1;
}
/* RTP packets that are just a header without data do not make
* any sense. */
if (buf_size < sizeof(struct rtp_hdr))
return -EINVAL;
/* FIXME: Add more checks, the reason why we do not check more than
* the length is because we currently handle IUUP packets as RTP
@@ -995,14 +1014,91 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
return 0;
}
/* Receive RTP data from a specified source connection and dispatch it to a
* destination connection. */
static int mgcp_recv(int *proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct osmo_fd *fd)
{
struct mgcp_endpoint *endp;
struct mgcp_conn_rtp *conn;
struct mgcp_trunk_config *tcfg;
int rc;
conn = (struct mgcp_conn_rtp*) fd->data;
endp = conn->conn->endp;
tcfg = endp->tcfg;
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);
if (rc <= 0)
return -1;
/* FIXME: The way how we detect the protocol looks odd. We should look
* into the packet header. Also we should introduce a packet type
* MGCP_PROTO_IUUP because currently we handle IUUP packets like RTP
* packets which is problematic. */
*proto = fd == &conn->end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
if (*proto == MGCP_PROTO_RTP) {
if (check_rtp(buf, rc) < 0) {
LOGP(DRTP, LOGL_ERROR,
"endpoint:0x%x invalid RTP packet received -- packet tossed\n",
ENDPOINT_NUMBER(endp));
return -1;
}
} else if (*proto == MGCP_PROTO_RTCP) {
if (check_rtcp(buf, rc) < 0) {
LOGP(DRTP, LOGL_ERROR,
"endpoint:0x%x invalid RTCP packet received -- packet tossed\n",
ENDPOINT_NUMBER(endp));
return -1;
}
}
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:0x%x conn:%s\n", ENDPOINT_NUMBER(endp),
mgcp_conn_dump(conn->conn));
/* Check if the origin of the RTP packet seems plausible */
if (tcfg->rtp_accept_all == 0) {
if (check_rtp_origin(conn, addr) != 0)
return -1;
}
/* Filter out dummy message */
if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) {
LOGP(DRTP, LOGL_NOTICE,
"endpoint:0x%x dummy message received\n",
ENDPOINT_NUMBER(endp));
LOGP(DRTP, LOGL_ERROR,
"endpoint:0x%x packet tossed\n", ENDPOINT_NUMBER(endp));
return 0;
}
/* Increment RX statistics */
rate_ctr_inc(&conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
rate_ctr_add(&conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], rc);
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(fd->fd, &conn->tap_in, buf, rc);
return rc;
}
/* Send RTP data. Possible options are standard RTP packet
* transmission or trsmission via an osmux connection */
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size,
struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst)
{
enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
struct mgcp_endpoint *endp = conn_src->conn->endp;
struct mgcp_endpoint *endp;
endp = conn_src->conn->endp;
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_dst->conn));
@@ -1022,14 +1118,14 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
"using mgcp_send() to forward data directly\n",
ENDPOINT_NUMBER(endp));
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
from_addr, msg, conn_src, conn_dst);
addr, buf, buf_size, conn_src, conn_dst);
case MGCP_OSMUX_BSC_NAT:
case MGCP_OSMUX_BSC:
LOGP(DRTP, LOGL_DEBUG,
"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((char*)msg->data, msg->len, conn_dst);
return osmux_xfrm_to_osmux(buf, buf_size, conn_dst);
}
/* If the data has not been handled/forwarded until here, it will
@@ -1049,10 +1145,9 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
* \param[in] buf_size size data length of buf
* \param[in] conn originating connection
* \returns 0 on success, -1 on ERROR */
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct mgcp_conn *conn)
{
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct mgcp_conn *conn = conn_src->conn;
struct mgcp_conn *conn_dst;
struct mgcp_endpoint *endp;
endp = conn->endp;
@@ -1096,7 +1191,9 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
}
/* Dispatch RTP packet to destination RTP connection */
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
return mgcp_send_rtp(proto, addr, buf,
buf_size, &conn->u.rtp, &conn_dst->u.rtp);
}
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1118,76 +1215,6 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
}
}
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
{
return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
}
struct pdu_ctx {
struct sockaddr_in *from_addr;
struct mgcp_conn_rtp *conn_src;
};
/* IuUP CN node has stripped an IuUP header and forwards RTP data to distribute to the peers. */
int iuup_rx_payload(struct msgb *msg, void *node_priv)
{
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "iuup_rx_payload(%u bytes)\n", msg->len);
return rx_rtp(msg);
}
/* IuUP CN node has composed a message that contains an IuUP header and asks us to send to the IuUP peer.
*/
int iuup_tx_msg(struct msgb *msg, void *node_priv)
{
const struct in_addr zero_addr = {};
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct mgcp_conn_rtp *conn_dst = node_priv;
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
struct mgcp_rtp_end *rtp_end = &conn_dst->end;
struct in_addr to_addr = rtp_end->addr;
uint16_t to_port = rtp_end->rtp_port;
if (conn_src == conn_dst
&& !memcmp(&zero_addr, &to_addr, sizeof(zero_addr)) && !to_port) {
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(): direct IuUP reply\n");
/* IuUP wants to send a message back to the same peer that sent an RTP package, but there
* is no address configured for that peer yet. It is probably an IuUP Initialization ACK
* reply. Use the sender address to send the reply.
*
* During 3G RAB Assignment, a 3G cell might first probe the MGW and expect an IuUP
* Initialization ACK before it replies to the MSC with a successful RAB Assignment; only
* after that reply does MSC officially know which RTP address+port the 3G cell wants to
* use and can tell this MGW about it, so this "loopback" is, for some 3G cells, the only
* chance we have to get a successful RAB Assignment done (particularly the nano3G does
* this). */
to_addr = from_addr->sin_addr;
to_port = from_addr->sin_port;
}
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(%u bytes) to %s:%u\n", msg->len,
inet_ntoa(to_addr), ntohs(to_port));
return mgcp_udp_send(rtp_end->rtp.fd, &to_addr, to_port, (char*)msg->data, msg->len);
}
static void iuup_init(struct mgcp_conn_rtp *conn_src)
{
struct osmo_iuup_cn_cfg cfg = {
.node_priv = conn_src,
.rx_payload = iuup_rx_payload,
.tx_msg = iuup_tx_msg,
};
if (conn_src->iuup) {
LOG_CONN_RTP(conn_src, LOGL_NOTICE, "Rx IuUP init, but already initialized. Ignoring.\n");
return;
}
conn_src->iuup = osmo_iuup_cn_init(conn_src->conn, &cfg, "endp_%d_conn_%s",
ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
}
/* Handle incoming RTP data from NET */
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
{
@@ -1201,110 +1228,45 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
struct mgcp_conn_rtp *conn_src;
struct mgcp_endpoint *endp;
struct sockaddr_in addr;
socklen_t slen = sizeof(addr);
int ret;
enum rtp_proto proto;
struct osmo_rtp_msg_ctx mc;
struct msgb *msg = msgb_alloc_headroom(RTP_BUF_SIZE + OSMO_IUUP_HEADROOM,
OSMO_IUUP_HEADROOM, "RTP-rx");
int rc;
char buf[RTP_BUF_SIZE];
int proto;
int len;
conn_src = (struct mgcp_conn_rtp *)fd->data;
OSMO_ASSERT(conn_src);
endp = conn_src->conn->endp;
OSMO_ASSERT(endp);
proto = (fd == &conn_src->end.rtp)? MGCP_PROTO_RTP : MGCP_PROTO_RTCP;
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x source conn:%s\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn_src->conn));
ret = recvfrom(fd->fd, msg->data, msg->data_len, 0, (struct sockaddr *)&addr, &slen);
/* Receive packet */
len = mgcp_recv(&proto, &addr, buf, sizeof(buf), fd);
if (len < 0)
return -1;
if (ret <= 0) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "recvfrom error: %s\n", strerror(errno));
rc = -1;
goto out;
}
msgb_put(msg, ret);
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
msg->len, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
/* Logging happened in the two check_ functions */
rc = -1;
goto out;
}
if (is_dummy_msg(proto, msg)) {
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx dummy packet (dropped)\n");
rc = 0;
goto out;
}
mc = (struct osmo_rtp_msg_ctx){
.proto = proto,
.conn_src = conn_src,
.from_addr = &addr,
};
OSMO_RTP_MSG_CTX(msg) = &mc;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
OSMO_RTP_MSG_CTX(msg)->proto,
OSMO_RTP_MSG_CTX(msg)->conn_src,
osmo_hexdump((void*)OSMO_RTP_MSG_CTX(msg)->from_addr, sizeof(struct sockaddr_in)));
/* Increment RX statistics */
rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msg->len);
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(fd->fd, &conn_src->tap_in, msg);
if (proto == MGCP_PROTO_RTP && osmo_iuup_is_init(msg))
iuup_init(conn_src);
if (conn_src->iuup && proto == MGCP_PROTO_RTP)
rc = osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
else
rc = rx_rtp(msg);
out:
msgb_free(msg);
return rc;
}
static int rx_rtp(struct msgb *msg)
{
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
struct mgcp_conn *conn = conn_src->conn;
struct mgcp_trunk_config *tcfg = conn->endp->tcfg;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msg->len);
mgcp_conn_watchdog_kick(conn_src->conn);
/* Check if the connection is in loopback mode, if yes, just send the
* incoming data back to the origin */
if (conn->mode == MGCP_CONN_LOOPBACK) {
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 = from_addr->sin_addr;
conn_src->end.rtp_port = from_addr->sin_port;
conn_src->end.addr = addr.sin_addr;
conn_src->end.rtp_port = addr.sin_port;
}
return mgcp_send_rtp(conn_src, msg);
return mgcp_send_rtp(proto, &addr, buf,
len, conn_src, conn_src);
}
/* Check if the origin of the RTP packet seems plausible */
if (!tcfg->rtp_accept_all && check_rtp_origin(conn_src, from_addr))
return -1;
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */
return conn->endp->type->dispatch_rtp_cb(msg);
return endp->type->dispatch_rtp_cb(proto, &addr, buf, len,
conn_src->conn);
}
/*! set IP Type of Service parameter.

View File

@@ -261,7 +261,8 @@ static void scheduled_tx_net_cb(struct msgb *msg, void *data)
/* Send RTP data to NET */
/* FIXME: Get rid of conn_bts and conn_net! */
mgcp_send(endp, 1, &addr, msg, conn_bts, conn_net);
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_bts, conn_net);
msgb_free(msg);
}
@@ -287,7 +288,8 @@ static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
/* Send RTP data to BTS */
/* FIXME: Get rid of conn_bts and conn_net! */
mgcp_send(endp, 1, &addr, msg, conn_net, conn_bts);
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_net, conn_bts);
msgb_free(msg);
}

View File

@@ -177,13 +177,13 @@ static int setup_rtp_processing(struct mgcp_endpoint *endp,
* for performance reasons. */
static const struct mgcp_request mgcp_requests[] = {
MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
/* SPEC extension */
MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
/* SPEC extension */
MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
};
/* Helper function to allocate some memory for responses and retransmissions */
@@ -1141,6 +1141,8 @@ mgcp_header_done:
return create_err_response(endp, 400, "MDCX", p->trans);
}
mgcp_conn_watchdog_kick(conn->conn);
if (mode) {
if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_MODE]);

View File

@@ -154,6 +154,10 @@ static int config_write_mgcp(struct vty *vty)
vty_out(vty, " osmux dummy %s%s",
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
}
if (g_cfg->conn_timeout)
vty_out(vty, " conn-timeout %u%s", g_cfg->conn_timeout, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1327,6 +1331,18 @@ DEFUN(cfg_mgcp_domain,
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_conn_timeout,
cfg_mgcp_conn_timeout_cmd,
"conn-timeout <1-65534>",
"Set a time after which inactive connections (CIs) are closed. This can be used to work around interoperability"
" problems causing connections to stay open forever, and slowly exhausting all available ports. Do not enable"
" when LCLS is used (connections in LCLS state appear to be inactive)!\n"
"Timeout value (sec.)\n")
{
g_cfg->conn_timeout = strtoul(argv[0], NULL, 16);
return CMD_SUCCESS;
}
int mgcp_vty_init(void)
{
install_element_ve(&show_mgcp_cmd);
@@ -1391,6 +1407,7 @@ int mgcp_vty_init(void)
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_conn_timeout_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
install_node(&trunk_node, config_write_trunk);

View File

@@ -244,12 +244,6 @@ static const struct log_info_cat log_categories[] = {
.color = "\033[1;30m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
[DIUUP] = {
.name = "DIUUP",
.description = "IuUP within RTP stream handling",
.color = "\033[1;31m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {

View File

@@ -1,7 +1,6 @@
SUBDIRS = \
mgcp_client \
mgcp \
iuup \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.

View File

@@ -1,45 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_srcdir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
EXTRA_DIST = \
iuup_test.ok \
iuup_test.err \
$(NULL)
noinst_PROGRAMS = \
iuup_test \
$(NULL)
iuup_test_SOURCES = \
iuup_test.c \
$(NULL)
iuup_test_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
-lm \
$(NULL)
update_exp:
$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err

View File

@@ -1,157 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/iuup_cn_node.h>
#include <osmocom/mgcp/iuup_protocol.h>
void *ctx = NULL;
static const char *dump(struct msgb *msg)
{
return osmo_hexdump_nospc(msg->data, msg->len);
}
struct msgb *msgb_from_hex(const char *label, const char *hex)
{
struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
OSMO_IUUP_HEADROOM, label);
unsigned char *rc;
msg->l2h = msg->data;
rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
OSMO_ASSERT(rc == msg->l2h);
return msg;
}
const char *expect_rx_payload = NULL;
int rx_payload(struct msgb *msg, void *node_priv)
{
printf("rx_payload() invoked by iuup_cn!\n");
printf(" [IuUP] -RTP->\n");
printf("%s\n", dump(msg));
printf("node_priv=%p\n", node_priv);
if (!expect_rx_payload) {
printf("ERROR: did not expect rx_payload()\n");
exit(-1);
} else if (strcmp(expect_rx_payload, dump(msg))) {
printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
exit(-1);
} else
printf("ok: matches expected msg\n");
expect_rx_payload = NULL;
return 0;
}
const char *expect_tx_msg = NULL;
int tx_msg(struct msgb *msg, void *node_priv)
{
printf("tx_msg() invoked by iuup_cn!\n");
printf(" <-PDU- [IuUP]\n");
printf("%s\n", dump(msg));
printf("node_priv=%p\n", node_priv);
if (!expect_tx_msg) {
printf("ERROR: did not expect tx_msg()\n");
exit(-1);
} else if (strcmp(expect_tx_msg, dump(msg))) {
printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
exit(-1);
} else
printf("ok: matches expected msg\n");
expect_tx_msg = NULL;
return 0;
}
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
{
int rc;
printf(" -PDU-> [IuUP]\n");
printf("%s\n", dump(msg));
rc = osmo_iuup_cn_rx_pdu(cn, msg);
printf("rc=%d\n", rc);
return rc;
}
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
{
int rc;
printf(" [IuUP] <-RTP-\n");
printf("%s\n", dump(msg));
rc = osmo_iuup_cn_tx_payload(cn, msg);
printf("rc=%d\n", rc);
return rc;
}
void test_cn_session()
{
void *node_priv = (void*)0x2342;
struct osmo_iuup_cn_cfg cfg = {
.node_priv = node_priv,
.rx_payload = rx_payload,
.tx_msg = tx_msg,
};
struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
OSMO_ASSERT(cn);
printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
"e4002400"; /* IuUP Init Ack */
rx_pdu(cn,
msgb_from_hex("IuUP-Init",
"8060dc5219495e3f00010111" /* <- RTP header */
"e000df99" /* <- IuUP header */
"160051673c01270000820000001710000100" /* IuUP params */));
#define RTP_HEADER "8060944c6256042c00010102"
#define RTP_HEADER_PT112 "8070944c6256042c00010102"
#define IUUP_HEADER "0100e2b3"
#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
printf("i.e. should strip away " IUUP_HEADER "\n");
expect_rx_payload = RTP_HEADER_PT112 "f03c" RTP_PAYLOAD;
rx_pdu(cn,
msgb_from_hex("IuUP-Data",
RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
printf("All done.\n");
}
static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(void)
{
ctx = talloc_named_const(NULL, 0, __FILE__);
void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
osmo_init_logging2(ctx, &log_info);
test_cn_session();
talloc_free(msgb_ctx);
return 0;
}

View File

@@ -1,58 +0,0 @@
Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
-PDU-> [IuUP]
8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060dc5219495e3f00010111e4002400
node_priv=0x2342
ok: matches expected msg
rc=0
Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
i.e. should strip away 0100e2b3
-PDU-> [IuUP]
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
rx_payload() invoked by iuup_cn!
[IuUP] -RTP->
8070944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342
ok: matches expected msg
rc=0
Transmit RTP. Expecting tx_msg() with inserted IuUP header
[IuUP] <-RTP-
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342
ok: matches expected msg
rc=0
More RTP, each time the Frame Nr advances, causing a new header CRC.
[IuUP] <-RTP-
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342
ok: matches expected msg
rc=0
[IuUP] <-RTP-
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342
ok: matches expected msg
rc=0
[IuUP] <-RTP-
8060944c6256042c00010102f03c6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342
ok: matches expected msg
rc=0
All done.

View File

@@ -28,7 +28,6 @@
#include <osmocom/mgcp/mgcp_endp.h>
#include <osmocom/mgcp/mgcp_sdp.h>
#include <osmocom/mgcp/mgcp_codec.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
@@ -971,6 +970,7 @@ static void test_packet_loss_calc(void)
{
int i;
struct mgcp_endpoint endp;
struct mgcp_config cfg = {0};
struct mgcp_trunk_config trunk;
printf("Testing packet loss calculation.\n");
@@ -978,6 +978,7 @@ static void test_packet_loss_calc(void)
memset(&endp, 0, sizeof(endp));
memset(&trunk, 0, sizeof(trunk));
endp.cfg = &cfg;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
trunk.endpoints = &endp;
@@ -1190,7 +1191,7 @@ struct rtp_packet_info test_rtp_packets1[] = {
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
struct mgcp_rtp_state *state,
struct mgcp_rtp_end *rtp_end,
struct sockaddr_in *addr, struct msgb *msg);
struct sockaddr_in *addr, char *data, int len);
static void test_packet_error_detection(int patch_ssrc, int patch_ts)
{
@@ -1198,9 +1199,11 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
struct mgcp_trunk_config trunk;
struct mgcp_endpoint endp;
struct mgcp_config cfg = {0};
struct mgcp_rtp_state state;
struct mgcp_rtp_end *rtp;
struct sockaddr_in addr = { 0 };
char buffer[4096];
uint32_t last_ssrc = 0;
uint32_t last_timestamp = 0;
uint32_t last_seqno = 0;
@@ -1224,6 +1227,7 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
state.in_stream.err_ts_ctr = &test_ctr_in;
state.out_stream.err_ts_ctr = &test_ctr_out;
endp.cfg = &cfg;
endp.type = &ep_typeset.rtp;
trunk.vty_number_endpoints = 1;
@@ -1247,17 +1251,16 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
struct rtp_packet_info *info = test_rtp_packets1 + i;
struct msgb *msg = msgb_alloc(4096, __func__);
force_monotonic_time_us = round(1000000.0 * info->txtime);
OSMO_ASSERT(info->len <= msgb_tailroom(msg));
OSMO_ASSERT(info->len <= sizeof(buffer));
OSMO_ASSERT(info->len >= 0);
msg->l3h = msgb_put(msg, info->len);
memcpy((char*)msgb_l3(msg), info->data, info->len);
memmove(buffer, info->data, info->len);
mgcp_rtp_end_config(&endp, 1, rtp);
mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
mgcp_patch_and_count(&endp, &state, rtp, &addr,
buffer, info->len);
if (state.out_stream.ssrc != last_ssrc) {
printf("Output SSRC changed to %08x\n",
@@ -1284,8 +1287,6 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
last_timestamp = state.out_stream.last_timestamp;
last_seqno = state.out_stream.last_seq;
msgb_free(msg);
}
force_monotonic_time_us = -1;

View File

@@ -13,10 +13,3 @@ AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([iuup])
AT_KEYWORDS([iuup])
cat $abs_srcdir/iuup/iuup_test.ok > expout
cat $abs_srcdir/iuup/iuup_test.err > experr
AT_CHECK([$abs_top_builddir/tests/iuup/iuup_test], [], [expout], [experr])
AT_CLEANUP