/* (C) 2021 by sysmocom - s.f.m.c. GmbH * Author: Pau Espin Pedrol * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "config.h" #include #include #include #include #include #include #include #define HNB_IUUP_MSGB_SIZE 4096 static struct osmo_iuup_rnl_prim *llsk_audio_ce_to_iuup_rnl_cfg(struct rtp_conn *conn, const struct hnb_audio_conn_establish_req_param *ce_req) { struct osmo_iuup_rnl_prim *irp; struct osmo_iuup_rnl_config *cfg; unsigned int i; const struct hnb_audio_conn_establish_req_param_v0 *v0 = &ce_req->v0; const struct hnb *hnb = conn->ue->hnb; irp = osmo_iuup_rnl_prim_alloc(conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, HNB_IUUP_MSGB_SIZE); cfg = &irp->u.config; cfg->transparent = !!v0->transparent; cfg->active = true; cfg->data_pdu_type = v0->data_pdu_type; cfg->supported_versions_mask = v0->supported_versions_mask; cfg->num_rfci = v0->num_rfci; cfg->num_subflows = v0->num_subflows; cfg->IPTIs_present = v0->IPTIs_present; OSMO_ASSERT(cfg->num_rfci <= ARRAY_SIZE(cfg->rfci)); OSMO_ASSERT(cfg->num_subflows <= ARRAY_SIZE(cfg->rfci[0].subflow_sizes)); for (i = 0; i < cfg->num_rfci; i++) { cfg->rfci[i].used = true; /* llsk_audio v0 doesn't provide info, assume RFC ID from position: */ cfg->rfci[i].id = (hnb->llsk.sapi_version_audio > 0) ? ce_req->v1.rfci[i] : i; if (cfg->IPTIs_present) cfg->rfci[i].IPTI = v0->IPTIs[i]; if (cfg->num_subflows > 0) memcpy(&cfg->rfci[i].subflow_sizes[0], &v0->subflow_sizes[i][0], cfg->num_subflows*sizeof(uint16_t)); } cfg->t_init = (struct osmo_iuup_rnl_config_timer){ .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT }; cfg->t_ta = (struct osmo_iuup_rnl_config_timer){ .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT }; cfg->t_rc = (struct osmo_iuup_rnl_config_timer){ .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT }; return irp; } static int _iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct rtp_conn *conn = (struct rtp_conn *)ctx; struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph; struct msgb *msg = oph->msg; int rc; switch (OSMO_PRIM_HDR(&irp->oph)) { case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION): rc = llsk_audio_tx_conn_data_ind(conn, irp->u.data.frame_nr, irp->u.data.fqc, irp->u.data.rfci, msgb_l3(msg), msgb_l3len(msg)); break; default: LOGUE(conn->ue, DRTP, LOGL_NOTICE, "Rx Unknown prim=%u op=%u from IuUP layer", irp->oph.primitive, irp->oph.operation); rc = -1; } msgb_free(msg); return rc; } static int _iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { struct rtp_conn *conn = (struct rtp_conn *)ctx; struct msgb *msg = oph->msg; int rc; rc = osmo_rtp_send_frame_ext(conn->socket, msgb_l2(msg), msgb_l2len(msg), GSM_RTP_DURATION, false); if (rc < 0) { LOGUE(conn->ue, DLLSK, LOGL_ERROR, "Rx IuUP Transport UNITDATA.req: Failed sending RTP frame! id=%u data_len=%u\n", conn->id, msgb_l2len(msg)); } msgb_free(msg); return rc; } struct rtp_conn *rtp_conn_alloc(struct hnb_ue *ue) { struct rtp_conn *conn; char iuup_id[64]; conn = talloc_zero(ue, struct rtp_conn); if (!conn) return NULL; conn->ue = ue; snprintf(iuup_id, sizeof(iuup_id), "ue-%u", conn->ue->conn_id); conn->iui = osmo_iuup_instance_alloc(conn, iuup_id); osmo_iuup_instance_set_user_prim_cb(conn->iui, _iuup_user_prim_cb, conn); osmo_iuup_instance_set_transport_prim_cb(conn->iui, _iuup_transport_prim_cb, conn); llist_add(&conn->list, &ue->conn_cs.conn_list); return conn; } void rtp_conn_free(struct rtp_conn *conn) { if (!conn) return; if (conn->socket) { osmo_rtp_socket_free(conn->socket); conn->socket = NULL; } if (conn->iui) { osmo_iuup_instance_free(conn->iui); conn->iui = NULL; } llist_del(&conn->list); talloc_free(conn); } /* Mixture between osmo_rtp_get_bound_addr and osmo_rtp_get_bound_ip_port using osmo_sockaddr */ /*static int rtp_get_bound_addr(struct osmo_rtp_socket *rs, struct osmo_sockaddr *osa) { int rc; socklen_t alen = sizeof(*osa); rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&osa->u.sa, &alen); if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "getsockname(%d) failed: %s\n", rs->rtp_bfd.fd, strerror(errno)); return rc; } LOGP(DRTP, LOGL_DEBUG, "rtp socket: %s\n", osmo_sock_get_name2(rs->rtp_bfd.fd)); return 0; }*/ /* osmo_rtp_socket_connect() is broken, OS#5356 */ static int rtp_get_bound_addr(struct osmo_rtp_socket *rs, const struct osmo_sockaddr *rem_addr, struct osmo_sockaddr *osa) { int rc; uint16_t port; socklen_t alen = sizeof(*osa); /* First, retrieve bound port using getsockname: */ rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&osa->u.sa, &alen); if (rc < 0) return rc; switch (osa->u.sa.sa_family) { case AF_INET6: port = ntohs(osa->u.sin6.sin6_port); break; case AF_INET: port = ntohs(osa->u.sin.sin_port); break; default: return -EINVAL; } /* osmo_rtp_socket_connect() is broken, OS#5356, so we didn't connect() * and hence local_addr may still be unresolved (0.0.0.0) in the socket. * let's get it from OS regular routing: */ rc = osmo_sockaddr_local_ip(osa, rem_addr); if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "osmo_sockaddr_local_ip(%d) failed: err=%d\n", rs->rtp_bfd.fd, -rc); return rc; } /* Copy back the correct port to the returned address: */ switch (osa->u.sa.sa_family) { case AF_INET6: osa->u.sin6.sin6_port = htons(port); break; case AF_INET: osa->u.sin.sin_port = htons(port); break; default: return -EINVAL; } return 0; } static int rtp_bind(struct hnb *hnb, struct osmo_rtp_socket *rs, const char *ip) { int rc; unsigned int i; unsigned int tries; tries = (hnb->rtp.port_range_end - hnb->rtp.port_range_start) / 2; for (i = 0; i < tries; i++) { uint16_t port; if (hnb->rtp.port_range_next >= hnb->rtp.port_range_end) hnb->rtp.port_range_next = hnb->rtp.port_range_start; port = hnb->rtp.port_range_next; rc = osmo_rtp_socket_bind(rs, ip, port); hnb->rtp.port_range_next += 2; if (rc != 0) continue; if (hnb->rtp.ip_dscp != -1) { if (osmo_rtp_socket_set_dscp(rs, hnb->rtp.ip_dscp)) LOGP(DRTP, LOGL_ERROR, "failed to set DSCP=%d: %s\n", hnb->rtp.ip_dscp, strerror(errno)); } if (hnb->rtp.priority != -1) { if (osmo_rtp_socket_set_priority(rs, hnb->rtp.priority)) LOGP(DRTP, LOGL_ERROR, "failed to set socket priority %d: %s\n", hnb->rtp.priority, strerror(errno)); } return port; } return -1; } static void rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl, unsigned int rtp_pl_len, uint16_t seq_number, uint32_t timestamp, bool marker) { struct rtp_conn *conn = (struct rtp_conn *)rs->priv; struct osmo_iuup_tnl_prim *itp; int rc; LOGUE(conn->ue, DRTP, LOGL_DEBUG, "Rx RTP seq=%u ts=%u M=%u pl=%p len=%u\n", seq_number, timestamp, marker, rtp_pl, rtp_pl_len); itp = osmo_iuup_tnl_prim_alloc(conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, HNB_IUUP_MSGB_SIZE); itp->oph.msg->l2h = msgb_put(itp->oph.msg, rtp_pl_len); memcpy(itp->oph.msg->l2h, rtp_pl, rtp_pl_len); rc = osmo_iuup_tnl_prim_up(conn->iui, itp); if (rc < 0) LOGUE(conn->ue, DRTP, LOGL_NOTICE, "Failed passing rx rtp up to IuUP layer: %d\n", rc); } int rtp_conn_setup(struct rtp_conn *conn, const char *local_ipstr, const struct osmo_sockaddr *rem_addr, const struct hnb_audio_conn_establish_req_param *ce_req) { int rc; char cname[256+4]; char name[32]; struct osmo_rtp_socket *rs; char remote_ipstr[INET6_ADDRSTRLEN]; uint16_t remote_port; struct osmo_iuup_rnl_prim *irp; struct hnb_ue *ue = conn->ue; struct hnb *hnb = ue->hnb; if (osmo_sockaddr_to_str_and_uint(remote_ipstr, sizeof(remote_ipstr), &remote_port, &rem_addr->u.sa) == 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Failed parsing remote address!\n"); return -EINVAL; } conn->rem_addr = *rem_addr; rs = conn->socket = osmo_rtp_socket_create(ue, 0); rc = osmo_rtp_socket_set_param(rs, hnb->rtp.jitter_adaptive ? OSMO_RTP_P_JIT_ADAP : OSMO_RTP_P_JITBUF, hnb->rtp.jitter_buf_ms); if (rc < 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Failed to set RTP socket parameters: %s\n", strerror(-rc)); goto free_ret; } /* TS 25.414 Section 5.1.3.3.1.6: A dynamic Payload Type (IETF RFC 1890 * [23]) shall be used. Values in the Range between 96 and 127 shall be * used. The value shall be ignored in the receiving entity. */ rc = osmo_rtp_socket_set_pt(rs, 96); if (rc < 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Failed to set RTP socket Payload-Type 96\n"); /* Continue, the other side is anyway ignoring it... */ } rs->priv = conn; rs->rx_cb = &rtp_rx_cb; rc = rtp_bind(hnb, rs, local_ipstr); if (rc < 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Failed to bind RTP/RTCP sockets\n"); goto free_ret; } conn->id = rc; /* We use local port as rtp conn ID */ osmo_sockaddr_from_str_and_uint(&conn->loc_addr, local_ipstr, rc); /* Ensure RTCP SDES contains some useful information */ snprintf(cname, sizeof(cname), "hnb@%s", local_ipstr); snprintf(name, sizeof(name), "ue@%u-%u", conn->ue->conn_id, conn->id); osmo_rtp_set_source_desc(rs, cname, name, NULL, NULL, NULL, "OsmoHNodeB-" PACKAGE_VERSION, NULL); rc = osmo_rtp_socket_connect(rs, remote_ipstr, remote_port); if (rc < 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Failed to connect RTP/RTCP sockets\n"); goto free_ret; } if (osmo_sockaddr_is_any(&conn->loc_addr)) { /* osmo_rtp_socket_connect() is broken, OS#5356 */ //rc = rtp_get_bound_addr(rs, &conn->loc_addr); rc = rtp_get_bound_addr(rs, rem_addr, &conn->loc_addr); if (rc < 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Cannot obtain locally bound IP/port: %d\n", rc); goto free_ret; } } /* Now configure the IuUP layer: */ irp = llsk_audio_ce_to_iuup_rnl_cfg(conn, ce_req); rc = osmo_iuup_rnl_prim_down(conn->iui, irp); if (rc < 0) { LOGUE(ue, DRTP, LOGL_ERROR, "Failed setting up IuUP layer: %d\n", rc); goto free_ret; } return rc; free_ret: osmo_rtp_socket_free(conn->socket); conn->socket = NULL; return rc; } int rtp_conn_tx_data(struct rtp_conn *conn, uint8_t frame_nr, uint8_t fqc, uint8_t rfci, const uint8_t *data, unsigned int data_len) { struct osmo_iuup_rnl_prim *irp; irp = osmo_iuup_rnl_prim_alloc(conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, HNB_IUUP_MSGB_SIZE); irp->u.data.rfci = rfci; irp->u.data.frame_nr = frame_nr; irp->u.data.fqc = fqc; irp->oph.msg->l3h = msgb_put(irp->oph.msg, data_len); memcpy(irp->oph.msg->l3h, data, data_len); return osmo_iuup_rnl_prim_down(conn->iui, irp); }