diff --git a/include/osmocom/mgcp/mgcp_codec.h b/include/osmocom/mgcp/mgcp_codec.h index 97650e40d..a45bd0c95 100644 --- a/include/osmocom/mgcp/mgcp_codec.h +++ b/include/osmocom/mgcp/mgcp_codec.h @@ -8,7 +8,11 @@ #define PTYPE_UNDEFINED (-1) +#include + struct mgcp_conn_rtp; +struct mgcp_codec_param; +struct mgcp_rtp_codec; void mgcp_codec_summary(struct mgcp_conn_rtp *conn); void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn); @@ -17,3 +21,4 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn); int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type); const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn, const char *subtype_name, unsigned int match_nr); +bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec); diff --git a/include/osmocom/mgcp/mgcp_conn.h b/include/osmocom/mgcp/mgcp_conn.h index 06879b1b2..6923f5e1b 100644 --- a/include/osmocom/mgcp/mgcp_conn.h +++ b/include/osmocom/mgcp/mgcp_conn.h @@ -99,6 +99,7 @@ struct mgcp_conn_rtp { struct osmo_iuup_instance *iui; bool active_init; /* true: Send IuUP Init */ bool first_rtp_pkt_received; + uint8_t frame_num; /* shouldn't this part of the IUUP layer? */ } iuup; struct rate_ctr_group *rate_ctr_group; diff --git a/include/osmocom/mgcp/mgcp_iuup.h b/include/osmocom/mgcp/mgcp_iuup.h index 041265a14..38262e361 100644 --- a/include/osmocom/mgcp/mgcp_iuup.h +++ b/include/osmocom/mgcp/mgcp_iuup.h @@ -29,4 +29,4 @@ struct mgcp_conn_rtp; int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp); void mgcp_conn_iuup_cleanup(struct mgcp_conn_rtp *conn_rtp); int mgcp_conn_iuup_dispatch_rtp(struct msgb *msg); -int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg); +int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg); diff --git a/src/libosmo-mgcp-client/mgcp_client.c b/src/libosmo-mgcp-client/mgcp_client.c index 5dcedd205..a5d4427b9 100644 --- a/src/libosmo-mgcp-client/mgcp_client.c +++ b/src/libosmo-mgcp-client/mgcp_client.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -98,7 +99,7 @@ enum mgcp_codecs map_str_to_codec(const char *str) codec_name = extract_codec_name(osmo_mgcpc_codec_names[i].str); if (!codec_name) continue; - if (strcmp(codec_name, str_buf) == 0) + if (strcasecmp(codec_name, str_buf) == 0) return osmo_mgcpc_codec_names[i].value; } diff --git a/src/libosmo-mgcp/mgcp_codec.c b/src/libosmo-mgcp/mgcp_codec.c index 7ab2a1716..c7f712e50 100644 --- a/src/libosmo-mgcp/mgcp_codec.c +++ b/src/libosmo-mgcp/mgcp_codec.c @@ -25,6 +25,7 @@ #include #include #include +#include /* Helper function to dump codec information of a specified codec to a printable * string, used by dump_codec_summary() */ @@ -241,6 +242,8 @@ int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *aud codec->payload_type = 112; else if (!strcmp(codec->subtype_name, "AMR-WB")) codec->payload_type = 113; + else if (!strcasecmp(codec->subtype_name, "VND.3GPP.IuFP")) + codec->payload_type = 97; } /* If we could not determine a payload type we assume that @@ -355,7 +358,7 @@ int mgcp_codec_decide(struct mgcp_conn_rtp *conn) * * https://tools.ietf.org/html/rfc4867 */ -static bool amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) +bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) { if (!codec->param_present) return false; @@ -379,7 +382,7 @@ static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *c if (strcmp(codec_a->subtype_name, codec_b->subtype_name)) return false; if (!strcmp(codec_a->subtype_name, "AMR")) { - if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b)) + if (mgcp_codec_amr_is_octet_aligned(codec_a) != mgcp_codec_amr_is_octet_aligned(codec_b)) return false; } @@ -417,7 +420,7 @@ int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp if (!codec_src) return -EINVAL; - /* Use the codec infrmation from the source and try to find the + /* Use the codec information from the source and try to find the * equivalent of it on the destination side */ codecs_assigned = rtp_dst->codecs_assigned; OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); diff --git a/src/libosmo-mgcp/mgcp_iuup.c b/src/libosmo-mgcp/mgcp_iuup.c index 4903462f9..0f4cdf99a 100644 --- a/src/libosmo-mgcp/mgcp_iuup.c +++ b/src/libosmo-mgcp/mgcp_iuup.c @@ -14,8 +14,10 @@ #include +#include #include +#include #include #include #include @@ -104,9 +106,16 @@ static int check_rtp_iuup(const struct mgcp_conn_rtp *conn_rtp, struct msgb *msg static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osmo_iuup_rnl_prim *irp) { - struct mgcp_conn *conn_dst; - struct mgcp_conn_rtp *conn_rtp_dst; + struct mgcp_conn *conn_src, *conn_dst; + struct mgcp_conn_rtp *conn_src_rtp, *conn_rtp_dst; int rc; + uint8_t rfci, frame_nr, fqc; + int ft; + struct msgb *msg; + struct amr_hdr *amr_hdr; + uint8_t *amr_data; + ssize_t amr_length = 0; + struct rtp_hdr *rtp_hdr; conn_dst = _find_dst_conn(conn_rtp_src->conn); @@ -138,6 +147,65 @@ static int _conn_iuup_rx_rnl_data(struct mgcp_conn_rtp *conn_rtp_src, struct osm break; case MGCP_RTP_DEFAULT: /* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */ + rfci = irp->u.data.rfci; + frame_nr = irp->u.data.frame_nr; + fqc = irp->u.data.fqc; + msg = irp->oph.msg; + ft = osmo_amr_bytes_to_ft(msgb_l3len(msg)); + if (ft < 0) { + /* FIXME LOGP */ + return ft; + } + msgb_pull_to_l3(msg); + LOGP(DLMGCP, LOGL_ERROR, "Convert Iuup -> AMR: ft %d, len %d\n", ft, msgb_l3len(msg)); + + if (mgcp_codec_amr_is_octet_aligned(conn_rtp_dst->end.codec)) { + amr_hdr = (struct amr_hdr *) msgb_push(msg, sizeof(struct amr_hdr)); + amr_hdr->cmr = 15; /* no change */ + amr_hdr->f = 0; + amr_hdr->q = !fqc; + amr_hdr->ft = ft & 0xff; + amr_hdr->pad1 = 0; + amr_hdr->pad2 = 0; + } else { + if (msgb_tailroom(msg) < 2) { + /* FIXME not enought tailroom */ + return -4; + } + msgb_put(msg, 2); + osmo_amr_iuup_to_bwe(msgb_data(msg), msgb_length(msg) - 2, msgb_length(msg) + 2); + /* fill bwe header */ + amr_data = msgb_data(msg); + /* CMR no change | follow bit | ft (3 of 4 bits) */ + amr_data[0] = 15 << 4 | (0 << 3) | (ft >> 1); + LOGP(DLMGCP, LOGL_ERROR, "Convert Iuup -> AMR bwe: amr[0] 0x%x 0x%x\n", amr_data[0], amr_data[1]); + amr_data[1] |= ((ft & 0x1) << 7) | (((!fqc) & 0x1) << 6); + LOGP(DLMGCP, LOGL_ERROR, "Convert Iuup -> AMR bwe: amr[0] 0x%x 0x%x\n", amr_data[0], amr_data[1]); + amr_length = (osmo_amr_bits(ft) + 10 + 7) / 8; + msgb_trim(msg, amr_length); + } + rtp_hdr = (struct rtp_hdr *) msgb_push(msg, sizeof(struct rtp_hdr)); + *rtp_hdr = (struct rtp_hdr){ + .csrc_count = 0, + .extension = 0, + .padding = 0, + .version = 0, + .payload_type = 0, + .marker = 0, + .sequence = 0, + .timestamp = 0, + .ssrc = 0 + }; + + conn_src = _find_dst_conn(conn_dst); + if (conn_src) + conn_src_rtp = &conn_src->u.rtp; + else + conn_src_rtp = &conn_dst->u.rtp; + + rc = mgcp_send(conn_dst->endp, 1, + NULL, msg, conn_src_rtp, conn_rtp_dst); + break; case MGCP_OSMUX_BSC: case MGCP_OSMUX_BSC_NAT: default: @@ -222,7 +290,7 @@ static int _conn_iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { - struct mgcp_conn_rtp *conn_rtp_dst = ctx; + struct mgcp_conn_rtp *conn_src_rtp = NULL, *conn_rtp_dst = ctx; struct mgcp_conn *conn_dst = conn_rtp_dst->conn; struct osmo_iuup_tnl_prim *itp = (struct osmo_iuup_tnl_prim *)oph; struct mgcp_conn *conn_src; @@ -230,6 +298,7 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) struct rtp_hdr *rtph; struct osmo_sockaddr from_addr = {0}; /* FIXME: what to do with this one? */ + OSMO_ASSERT(OSMO_PRIM_HDR(&itp->oph) == OSMO_PRIM(OSMO_IUUP_TNL_UNITDATA, PRIM_OP_REQUEST)); msg = oph->msg; @@ -242,8 +311,8 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) .csrc_count = 0, .extension = 0, .padding = 0, - .version = 2, - .payload_type = 96, + .version = 0, + .payload_type = conn_rtp_dst->end.codec->payload_type, .marker = 0, .sequence = 0, .timestamp = 0, @@ -251,12 +320,20 @@ static int _conn_iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) }; /* TODO: mgcp_send_rtp() expects msg to have OSMO_RTP_MSG_CTX filled */ + // struct osmo_rtp_msg_ctx *mc = OSMO_RTP_MSG_CTX(msg); + // how to handle an early init packet which need to be answered correct! */ + /* The destination of the destination conn is the source conn, right? */ conn_src = _find_dst_conn(conn_dst); + if (conn_src) + conn_src_rtp = &conn_src->u.rtp; + else + conn_src_rtp = &conn_dst->u.rtp; + /* FIXME: set from_addr = NULL */ return mgcp_send(conn_dst->endp, 1, - &from_addr, msg, &conn_src->u.rtp, conn_rtp_dst); + &from_addr, msg, conn_src_rtp, conn_rtp_dst); } int mgcp_conn_iuup_init(struct mgcp_conn_rtp *conn_rtp) @@ -320,25 +397,49 @@ free_ret: /* Build IuUP RNL Data primitive from msg containing an incoming RTP pkt from * peer and send it down the IuUP layer towards the destination as IuUP/RTP: */ -int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_rtp, struct msgb *msg) +int mgcp_conn_iuup_send_rtp(struct mgcp_conn_rtp *conn_src_rtp, struct mgcp_conn_rtp *conn_dest_rtp, struct msgb *msg) { struct osmo_iuup_rnl_prim *irp; struct rtp_hdr *rtph; - int rc; + struct amr_hdr *amr_hdr; + int rc = -1; + int iuup_length = 0; /* Tx RNL-DATA.req */ rtph = (struct rtp_hdr *)msgb_data(msg); msgb_pull(msg, sizeof(*rtph)); - irp = osmo_iuup_rnl_prim_alloc(conn_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); + /* FIXME: validate amr packets */ + irp = osmo_iuup_rnl_prim_alloc(conn_dest_rtp->conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, MGW_IUUP_MSGB_SIZE); + /* FIXME: We probably need transcoding here?! Or at least look up AMR modes and translate to related RFCI */ - irp->u.data.rfci = 0; - irp->u.data.frame_nr = rtph->sequence % 4; - irp->u.data.fqc = IUUP_FQC_FRAME_GOOD; + irp->u.data.frame_nr = htons(rtph->sequence) % 16; + + if (msgb_length(msg) < (sizeof(struct amr_hdr))) { + return -1; + } + + amr_hdr = (struct amr_hdr *) msgb_data(msg); + if (mgcp_codec_amr_is_octet_aligned(conn_src_rtp->end.codec)) { + /* FIXME: CMR handling & multiple frames handling */ + irp->u.data.fqc = IUUP_FQC_FRAME_GOOD; + irp->u.data.rfci = amr_hdr->ft < 8 ? 0 : 1; + msgb_pull(msg, 2); + } else { + irp->u.data.fqc = amr_hdr->q; + irp->u.data.rfci = amr_hdr->ft < 8 ? 0 : 1; + rc = iuup_length = osmo_amr_bwe_to_iuup(msgb_data(msg), msgb_length(msg)); + if (rc < 0) { + LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Failed convert the RTP/AMR to IuUP payload\n"); + return rc; + } + msgb_trim(msg, iuup_length); + } + irp->oph.msg->l3h = msgb_put(irp->oph.msg, msgb_length(msg)); memcpy(irp->oph.msg->l3h, msgb_data(msg), msgb_length(msg)); - if ((rc = osmo_iuup_rnl_prim_down(conn_rtp->iuup.iui, irp)) != 0) { - LOG_CONN_RTP(conn_rtp, LOGL_ERROR, "Failed Tx RTP payload down the IuUP layer\n"); + if ((rc = osmo_iuup_rnl_prim_down(conn_dest_rtp->iuup.iui, irp)) != 0) { + LOG_CONN_RTP(conn_dest_rtp, LOGL_ERROR, "Failed Tx RTP payload down the IuUP layer\n"); return rc; } diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c index 76eba3c5a..d4631bc3d 100644 --- a/src/libosmo-mgcp/mgcp_network.c +++ b/src/libosmo-mgcp/mgcp_network.c @@ -768,25 +768,6 @@ static bool amr_oa_bwe_convert_indicated(struct mgcp_rtp_codec *codec) } -/* Return whether an RTP packet with AMR payload is in octet-aligned mode. - * Return 0 if in bandwidth-efficient mode, 1 for octet-aligned mode, and negative if the RTP data is invalid. */ -static int amr_oa_check(char *data, int len) -{ - struct rtp_hdr *rtp_hdr; - unsigned int payload_len; - - if (len < sizeof(struct rtp_hdr)) - return -EINVAL; - - rtp_hdr = (struct rtp_hdr *)data; - - payload_len = len - sizeof(struct rtp_hdr); - if (payload_len < sizeof(struct amr_hdr)) - return -EINVAL; - - return osmo_amr_is_oa(rtp_hdr->data, payload_len) ? 1 : 0; -} - /* 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) @@ -1018,7 +999,7 @@ static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg) LOGPENDP(endp, DRTP, LOGL_DEBUG, "endpoint type is MGCP_RTP_IUUP, " "using mgcp_conn_iuup_send_rtp() to forward data over IuUP\n"); - return mgcp_conn_iuup_send_rtp(conn_dst, msg); + return mgcp_conn_iuup_send_rtp(conn_src, conn_dst, msg); } /* If the data has not been handled/forwarded until here, it will @@ -1145,10 +1126,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *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); - if (rc < 0) { - LOGPENDP(endp, DRTP, LOGL_DEBUG, - "can not patch PT because no suitable egress codec was found.\n"); + if (conn_dst->type != MGCP_RTP_IUUP) { + rc = mgcp_patch_pt(conn_src, conn_dst, msg); + if (rc < 0) { + LOGPENDP(endp, DRTP, LOGL_DEBUG, + "can not patch PT because no suitable egress codec was found.\n"); + } } } @@ -1191,7 +1174,9 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, msg); - if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) { + if (mgcp_conn_rtp_is_iuup(conn_dst) || mgcp_conn_rtp_is_iuup(conn_src)) { + /* the iuup code will correctly transform to the correct AMR mode */ + } else if (amr_oa_bwe_convert_indicated(conn_dst->end.codec)) { rc = amr_oa_bwe_convert(endp, msg, conn_dst->end.codec->param.amr_octet_aligned); if (rc < 0) { @@ -1279,10 +1264,6 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg) * destination connection is known the RTP packet is sent via * the destination connection. */ - /* If source is IuUP, we need to handle state, forward it rhough specific bridge: */ - if (mgcp_conn_rtp_is_iuup(conn_src)) - return mgcp_conn_iuup_dispatch_rtp(msg); - /* 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) { @@ -1308,9 +1289,18 @@ int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg) osmo_sockaddr_ntop(&from_addr->u.sa, ipbuf), conn->u.rtp.end.rtp_port); } + /* If source is IuUP, we need to handle state, forward it rhough specific bridge: */ + if (mgcp_conn_rtp_is_iuup(conn_src)) + return mgcp_conn_iuup_dispatch_rtp(msg); + return mgcp_send_rtp(conn_src, msg); } + /* If source is IuUP, we need to handle state, forward it rhough specific bridge: */ + if (mgcp_conn_rtp_is_iuup(conn_src)) + return mgcp_conn_iuup_dispatch_rtp(msg); + + /* Find a destination connection. */ /* NOTE: This code path runs every time an RTP packet is received. The * function mgcp_find_dst_conn() we use to determine the detination @@ -1518,18 +1508,6 @@ static int rx_rtp(struct msgb *msg) mgcp_conn_watchdog_kick(conn_src->conn); - /* If AMR is configured for the ingress connection a conversion of the - * framing mode (octet-aligned vs. bandwith-efficient is explicitly - * define, then we check if the incoming payload matches that - * expectation. */ - if (amr_oa_bwe_convert_indicated(conn_src->end.codec)) { - int oa = amr_oa_check((char*)msgb_data(msg), msgb_length(msg)); - if (oa < 0) - return -1; - if (((bool)oa) != conn_src->end.codec->param.amr_octet_aligned) - return -1; - } - /* Check if the origin of the RTP packet seems plausible */ if (!trunk->rtp_accept_all && check_rtp_origin(conn_src, from_addr)) return -1; diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c index b7b80e433..7752b480b 100644 --- a/src/libosmo-mgcp/mgcp_protocol.c +++ b/src/libosmo-mgcp/mgcp_protocol.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -1031,7 +1032,7 @@ mgcp_header_done: } /* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */ /* TODO: "codec" probably needs to be moved from endp to conn */ - if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) { + if (conn->type == MGCP_RTP_DEFAULT && strcasecmp(conn->end.codec->subtype_name, "VND.3GPP.IuFP") == 0) { rc = mgcp_conn_iuup_init(conn); }