falconia has uploaded this change for review.

View Change

mgw: rtp-patch rfc5993hr: convert to each end's respective format

The purpose of 'rtp-patch rfc5993hr' feature is to convert GSM-HR
RTP packets between RFC 5993 format that is expected on AoIP interface
(as prescribed by 3GPP) and TS 101 318 format that is emitted and
perhaps required by some legacy IP-based BTS. However, the original
implementation of this feature was excessively brute: when enabled,
it would unconditionally "flip" the format of each RTP packet handled
by the MGW. The operator therefore had to be careful to enable this
feature only when those old BTS models are in use, and there was no
possibility of mixing both old and new BTS on the same OsmoBSC+OsmoMGW
setup.

Modify this feature to work by autodetecting the format emitted by
each RTP peer, and send packets toward each peer in the same format
as that peer emits.

Related: OS#6059
Change-Id: I6b446ad83c540fb8b7e1aae24b78c27010212d64
---
M include/osmocom/mgcp/mgcp_rtp_end.h
M src/libosmo-mgcp/mgcp_network.c
2 files changed, 152 insertions(+), 23 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/69/39869/1
diff --git a/include/osmocom/mgcp/mgcp_rtp_end.h b/include/osmocom/mgcp/mgcp_rtp_end.h
index df06569..faebf26 100644
--- a/include/osmocom/mgcp/mgcp_rtp_end.h
+++ b/include/osmocom/mgcp/mgcp_rtp_end.h
@@ -33,7 +33,11 @@
bool force_constant_ssrc;
/* should we perform align_rtp_timestamp_offset() (1) or not (0) */
int force_aligned_timing;
+ /* should we perform automatic detection and conversion between
+ * RFC 5993 and TS 101 318 formats for HR? */
bool rfc5993_hr_convert;
+ /* has this end been detected as sending TS 101 318? */
+ bool hr_ts101318_detected;

/* Each end has a separate socket for RTP and RTCP */
struct osmo_io_fd *rtp;
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index abd095b..a52c73e 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -643,37 +643,104 @@
#endif
}

-/* There are different dialects used to format and transfer voice data. When
- * the receiving end expects GSM-HR data to be formated after RFC 5993, this
- * function is used to convert between RFC 5993 and TS 101318, which we normally
- * use.
- * Return 0 on sucess, negative on errors like invalid data length. */
-static int rfc5993_hr_convert(struct mgcp_endpoint *endp, struct msgb *msg)
+/* There exist 3 different RTP payload formats for GSM-HR codec: in forward
+ * chronological order of invention, they are ETSI TS 101 318, IETF RFC 5993
+ * and Themyscira TW-TS-002. RFC 5993 is the format specified by 3GPP for
+ * AoIP interface, while the other two formats deviate from RFC 5993 in
+ * opposite directions: TW-TS-002 adds more metadata flags and frame types
+ * to restore the full functionality and semantics of GSM 08.61, whereas
+ * TS 101 318 format carries no metadata at all.
+ *
+ * In a perfect world, we would support only RFC 5993 and TW-TS-002: the former
+ * for 3GPP spec compliance and the latter for extended functionality.
+ * However, we also have some legacy factors that force us to deal with
+ * TS 101 318 format in certain cases:
+ *
+ * - Osmocom BSS supports ip.access nanoBTS, which uses TS 101 318 format;
+ *
+ * - Native OsmoBTS always accepts both formats in RTP input and provides
+ * a vty option to select RTP output format; however, for backward
+ * compatibility osmo-bts-{lc15,oc2g,sysmo} default to TS 101 318 output.
+ *
+ * Because OsmoBTS has a vty option to select the RTP output format for HR,
+ * it is not feasible for OsmoBSC to "know" which format is used by each BTS
+ * based on the BTS model. Hence we follow an alternative approach: we
+ * autodetect the format which we receive from each RTP connection, and we
+ * emit packets toward each RTP end in whichever format that end has been
+ * detected to use, converting on the fly if necessary. This function
+ * performs the conversion; it returns 0 on success, negative on errors.
+ *
+ * Note that when TW-TS-002 format is in use (as explicitly communicated to us
+ * in MGCP/SDP), all conversions are disabled. Converting TW-TS-002
+ * (a superset of RFC 5993, would be seen as 5993 by simple checks based
+ * on payload length) to TS 101 318 would discard all metadata and thus
+ * defeat the point of using TW-TS-002 in the first place, hence such
+ * conversion is not a sensible operation. OsmoBSC will tell us to use
+ * TW-TS-002 only if the MSC asked for it *and* the BTS indicates support
+ * for this feature; the BTS will also be commanded to use TW-TS-002 in this
+ * case, thereby eliminating any and all need for conversion in this MGW.
+ *
+ * Another quirk worthy of note: when we are an RTP bridge between A and B,
+ * we will always receive packets in one direction before the other. Even if
+ * both sides start emitting packets at the same time, the very first packet
+ * we receive will be either A->B or B->A. Suppose the very first packet we
+ * receive is in A->B direction. We haven't received anything from B yet -
+ * which format do we emit toward B? In the present implementation, we emit
+ * RFC 5993 format in this case, following the general principle that it is
+ * the preferred format. For the resulting effect on the overall system,
+ * consider the full RTP chain of a single call:
+ *
+ * BTS-A BSC-MGW-A MSC-MGW-A MSC-MGW-B BSC-MGW-B BTS-B
+ * |------->|--------->|--------->|--------->|------->|
+ * |<-------|<---------|<---------|<---------|<-------|
+ *
+ * In a full system like the one depicted above, the RTP format on AoIP
+ * interface will always be RFC 5993 (following 3GPP TS 48.103), irrespective
+ * of which format is used by each BTS. If BTS-A uses or is configured to use
+ * TS 101 318 format, BSC-MGW-A will convert packets to RFC 5993 before sending
+ * them to MSC-MGW-A, and likewise on the other end. The call will always
+ * pass audio in both directions, each BTS will receive whichever RTP format
+ * it emits, while the core network will only see RFC 5993.
+ *
+ * E1 BTS considerations: the present function is invoked in the path from
+ * E1 to RTP, but not in the path from RTP to E1. Our TRAU<->RTP functions
+ * always accept both formats in RTP input and always emit RFC 5993 (or
+ * TW-TS-002, which has already been covered above) in RTP output. The
+ * resulting effect is that the single RTP end connected to the E1 endpoint
+ * will receive RTP in the same format as it emits (if this format is
+ * TS 101 318, the effect will be produced by this function doing the
+ * conversion), while in the other direction both formats will be accepted
+ * natively without any need of conversion.
+ */
+static int rfc5993_hr_convert(struct msgb *msg, struct mgcp_conn_rtp *dst)
{
struct rtp_hdr *rtp_hdr;
- if (msgb_length(msg) < sizeof(struct rtp_hdr)) {
- LOGPENDP(endp, DRTP, LOGL_ERROR, "RTP packet too short (%d < %zu)\n",
- msgb_length(msg), sizeof(struct rtp_hdr));
- return -EINVAL;
+ uint8_t *rtp_pl;
+ uint32_t rtp_pl_len;
+
+ /* Is this connection configured to use TW-TS-002?
+ * If so, leave it alone. */
+ if (dst->end.cset.codec->param_present &&
+ dst->end.cset.codec->param.hr_twts002) {
+ return 0;
}

- rtp_hdr = (struct rtp_hdr *)msgb_data(msg);
+ rtp_hdr = osmo_rtp_get_hdr(msg);
+ if (!rtp_hdr)
+ return -EINVAL;
+ rtp_pl = osmo_rtp_get_payload(rtp_hdr, msg, &rtp_pl_len);
+ if (!rtp_pl)
+ return -EINVAL;

- if (msgb_length(msg) == GSM_HR_BYTES + sizeof(struct rtp_hdr)) {
+ if (rtp_pl_len == GSM_HR_BYTES_RTP_TS101318 && !dst->end.hr_ts101318_detected) {
/* TS 101318 encoding => RFC 5993 encoding */
msgb_put(msg, 1);
- memmove(rtp_hdr->data + 1, rtp_hdr->data, GSM_HR_BYTES);
- rtp_hdr->data[0] = 0x00;
- } else if (msgb_length(msg) == GSM_HR_BYTES + sizeof(struct rtp_hdr) + 1) {
+ memmove(rtp_pl + 1, rtp_pl, GSM_HR_BYTES);
+ rtp_pl[0] = osmo_hr_check_sid(rtp_pl + 1, GSM_HR_BYTES) ? 0x20 : 0x00;
+ } else if (rtp_pl_len == GSM_HR_BYTES_RTP_RFC5993 && dst->end.hr_ts101318_detected) {
/* RFC 5993 encoding => TS 101318 encoding */
- memmove(rtp_hdr->data, rtp_hdr->data + 1, GSM_HR_BYTES);
+ memmove(rtp_pl, rtp_pl + 1, GSM_HR_BYTES);
msgb_trim(msg, msgb_length(msg) - 1);
- } else {
- /* It is possible that multiple payloads occur in one RTP
- * packet. This is not supported yet. */
- LOGPENDP(endp, DRTP, LOGL_ERROR,
- "cannot figure out how to convert RTP packet\n");
- return -ENOTSUP;
}
return 0;
}
@@ -1182,7 +1249,7 @@
}
} else if (rtp_end->rfc5993_hr_convert &&
strcmp(src_codec->subtype_name, "GSM-HR-08") == 0) {
- rc = rfc5993_hr_convert(endp, msg);
+ rc = rfc5993_hr_convert(msg, conn_dst);
if (rc < 0) {
LOGPENDP(endp, DRTP, LOGL_ERROR, "Error while converting to GSM-HR-08\n");
msgb_free(msg);
@@ -1509,6 +1576,55 @@
msgb_free(msg);
}

+/* This internal function examines a received RTP packet; it is invoked when
+ * the active codec is GSM-HR-08 and automatic conversion between TS 101 318
+ * and RFC 5993 formats is enabled. This function determines which format
+ * has been received, and also weeds out invalid RTP packets that are in
+ * neither of the two recognized formats. */
+static int examine_rtp_rx_gsmhr(struct msgb *msg, struct mgcp_conn_rtp *src)
+{
+ struct rtp_hdr *rtp_hdr;
+ const uint8_t *rtp_pl;
+ uint32_t rtp_pl_len;
+
+ rtp_hdr = osmo_rtp_get_hdr(msg);
+ if (!rtp_hdr)
+ return -EINVAL;
+ rtp_pl = osmo_rtp_get_payload(rtp_hdr, msg, &rtp_pl_len);
+ if (!rtp_pl)
+ return -EINVAL;
+
+ /* Is this connection configured to use TW-TS-002? If so, there is
+ * no possibility of TS 101 318, as TW-TS-002 is a superset of
+ * RFC 5993. */
+ if (src->end.cset.codec->param_present &&
+ src->end.cset.codec->param.hr_twts002) {
+ src->end.hr_ts101318_detected = false;
+ return 0;
+ }
+
+ /* It must be either TS 101 318 or RFC 5993, not TW-TS-002.
+ * TS 101 318 and RFC 5993 payloads have different lengths.
+ * In the case of TS 101 318, every possible bit pattern is valid;
+ * in the case of RFC 5993 as it is used in 3GPP AoIP (the only form
+ * we support), the first byte must be either 0x or 2x,
+ * where 'x' means ignore the lower nibble.
+ */
+ if (rtp_pl_len == GSM_HR_BYTES_RTP_TS101318) {
+ src->end.hr_ts101318_detected = true;
+ return 0;
+ }
+ if (rtp_pl_len == GSM_HR_BYTES_RTP_RFC5993 && (rtp_pl[0] & 0xD0) == 0) {
+ src->end.hr_ts101318_detected = false;
+ return 0;
+ }
+
+ LOG_CONN_RTP(src, LOGL_ERROR,
+ "rx_rtp(%u bytes): HR format is neither TS 101 318 nor RFC 5993\n",
+ msgb_length(msg));
+ return -EINVAL;
+}
+
/* Note: This function is able to handle RTP and RTCP. msgb ownership is transferred, so this function or its
* downstream consumers must make sure to [eventually] free the msgb. */
static int rx_rtp(struct msgb *msg)
@@ -1543,6 +1659,15 @@
}
}

+ /* Autodetect between RFC 5993 and TS 101 318, if enabled */
+ if (mc->proto == MGCP_PROTO_RTP && conn_src->end.cset.codec &&
+ strcmp(conn_src->end.cset.codec->subtype_name, "GSM-HR-08") == 0 &&
+ conn_src->end.rfc5993_hr_convert) {
+ int rc = examine_rtp_rx_gsmhr(msg, conn_src);
+ if (rc < 0)
+ goto out_free;
+ }
+
mgcp_conn_watchdog_kick(conn_src->conn);

/* Execute endpoint specific implementation that handles the

To view, visit change 39869. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: I6b446ad83c540fb8b7e1aae24b78c27010212d64
Gerrit-Change-Number: 39869
Gerrit-PatchSet: 1
Gerrit-Owner: falconia <falcon@freecalypso.org>