From b3af38215744379fd1fb0caa7e935502bcabbd14 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Mon, 29 Nov 2021 14:26:24 +0100 Subject: [PATCH] First implementation of the LLSK audio SAPI This first version can send and receive regular RTP, but does not talk IuUP. Support for IuUP will be added in a future patch. This way we can already test the whole LLSK<->RTP path. Change-Id: I9909a7c054ddaabb1bb63d7d06331cc79f642b5d --- configure.ac | 1 + include/osmocom/hnodeb/Makefile.am | 1 + include/osmocom/hnodeb/hnb_prim.h | 69 +++++++ include/osmocom/hnodeb/hnodeb.h | 19 ++ include/osmocom/hnodeb/llsk.h | 12 ++ include/osmocom/hnodeb/rtp.h | 27 +++ src/osmo-hnodeb/Makefile.am | 4 + src/osmo-hnodeb/debug.c | 5 + src/osmo-hnodeb/hnb.c | 21 +- src/osmo-hnodeb/llsk.c | 61 +++++- src/osmo-hnodeb/llsk_audio.c | 296 +++++++++++++++++++++++++++++ src/osmo-hnodeb/main.c | 8 + src/osmo-hnodeb/rtp.c | 212 +++++++++++++++++++++ 13 files changed, 732 insertions(+), 4 deletions(-) create mode 100644 include/osmocom/hnodeb/rtp.h create mode 100644 src/osmo-hnodeb/llsk_audio.c create mode 100644 src/osmo-hnodeb/rtp.c diff --git a/configure.ac b/configure.ac index 8453bc4..e96a6a1 100644 --- a/configure.ac +++ b/configure.ac @@ -61,6 +61,7 @@ PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0) PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0) +PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0) PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0) PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.5.0) PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 0.8.0) diff --git a/include/osmocom/hnodeb/Makefile.am b/include/osmocom/hnodeb/Makefile.am index 62af5bb..dcb3868 100644 --- a/include/osmocom/hnodeb/Makefile.am +++ b/include/osmocom/hnodeb/Makefile.am @@ -6,6 +6,7 @@ noinst_HEADERS = \ iuh.h \ llsk.h \ ranap.h \ + rtp.h \ rua.h \ vty.h \ $(NULL) diff --git a/include/osmocom/hnodeb/hnb_prim.h b/include/osmocom/hnodeb/hnb_prim.h index 6d597c4..dab00c9 100644 --- a/include/osmocom/hnodeb/hnb_prim.h +++ b/include/osmocom/hnodeb/hnb_prim.h @@ -40,6 +40,16 @@ #define HNB_PRIM_SAPI_GTP 2 #define HNB_PRIM_SAPI_AUDIO 3 +enum u_addr_type { + HNB_PRIM_ADDR_TYPE_UNSPEC, + HNB_PRIM_ADDR_TYPE_IPV4, + HNB_PRIM_ADDR_TYPE_IPV6 +}; +union u_addr { + struct in_addr v4; + struct in6_addr v6; +} __attribute__ ((packed)); + /*! \brief HNB_IUH primitives */ enum hnb_iuh_prim_type { HNB_IUH_PRIM_CONFIGURE, @@ -163,3 +173,62 @@ struct hnb_iuh_prim { struct hnb_iuh_unitdata_ind_param unitdata_ind; } u; } __attribute__ ((packed)); + +/**************************** + * AUDIO + ***************************/ +/*! \brief HNB_AUDIO primitives */ +enum hnb_audio_prim_type { + HNB_AUDIO_PRIM_CONN_ESTABLISH, + HNB_AUDIO_PRIM_CONN_RELEASE, + HNB_AUDIO_PRIM_CONN_DATA, + _HNB_AUDIO_PRIM_MAX +}; + +/* HNB_AUDIO_PRIM_CONN_ESTABLISH, UL */ +struct hnb_audio_conn_establish_req_param { + uint32_t context_id; + uint16_t remote_rtp_port; + uint8_t spare1; + uint8_t remote_rtp_address_type; /* enum u_addr_type */ + union u_addr remote_rtp_addr; +} __attribute__ ((packed)); + +/* HNB_AUDIO_PRIM_CONN_ESTABLISH, DL */ +struct hnb_audio_conn_establish_cnf_param { + uint32_t context_id; + uint16_t local_rtp_port; + uint8_t error_code; /* 0 = success, !0 = failure */ + uint8_t local_rtp_address_type; /* enum u_addr_type */ + union u_addr local_rtp_addr; +} __attribute__ ((packed)); + +/* HNB_AUDIO_PRIM_CONN_RELEASE, UL */ +struct hnb_audio_conn_release_req_param { + uint32_t context_id; +} __attribute__ ((packed)); + +/* HNB_AUDIO_PRIM_CONN_DATA, UL */ +struct hnb_audio_conn_data_req_param { + uint32_t context_id; + uint32_t data_len; /* RTP payload length in bytes */ + uint8_t data[0]; /* RTP payload (aka IP packet) */ +} __attribute__ ((packed)); + +/* HNB_AUDIO_PRIM_CONN_DATA, DL */ +struct hnb_audio_conn_data_ind_param { + uint32_t context_id; + uint32_t data_len; /* RTP payload length in bytes */ + uint8_t data[0]; /* RTP payload (aka IP packet) */ +} __attribute__ ((packed)); + +struct hnb_audio_prim { + struct osmo_prim_hdr hdr; + union { + struct hnb_audio_conn_establish_req_param conn_establish_req; + struct hnb_audio_conn_establish_cnf_param conn_establish_cnf; + struct hnb_audio_conn_release_req_param conn_release_req; + struct hnb_audio_conn_data_req_param conn_data_req; + struct hnb_audio_conn_data_ind_param conn_data_ind; + } u; +} __attribute__ ((packed)); diff --git a/include/osmocom/hnodeb/hnodeb.h b/include/osmocom/hnodeb/hnodeb.h index fd7d1e9..15244c4 100644 --- a/include/osmocom/hnodeb/hnodeb.h +++ b/include/osmocom/hnodeb/hnodeb.h @@ -21,11 +21,13 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -42,6 +44,7 @@ enum { DRANAP, DSCTP, DLLSK, + DRTP, }; extern const struct log_info hnb_log_info; @@ -55,6 +58,9 @@ struct hnb_ue { struct hnb_ue_cs_ctx { bool active; /* Is this chan in use? */ bool conn_est_cnf_pending; /* Did we send CONN_ESTABLISH_CNF to lower layers? */ + struct { + struct osmo_rtp_socket *socket; + } rtp; } conn_cs; struct hnb_ue_ps_ctx { bool active; /* Is this chan in use? */ @@ -86,6 +92,17 @@ struct hnb { uint8_t llsk_valid_sapi_mask; struct osmo_timer_list llsk_defer_configure_ind_timer; + struct { + unsigned int jitter_buf_ms; + bool jitter_adaptive; + + uint16_t port_range_start; + uint16_t port_range_end; + uint16_t port_range_next; + int ip_dscp; + int priority; + } rtp; + uint16_t rnc_id; bool registered; /* Set to true once HnbRegisterAccept was received from Iuh. rnc_id is valid iif registered==true */ @@ -104,3 +121,5 @@ struct hnb_ue *hnb_find_ue_by_imsi(const struct hnb *hnb, char *imsi); extern void *tall_hnb_ctx; extern struct hnb *g_hnb; + +#define LOGUE(ue, ss, lvl, fmt, args...) LOGP(ss, lvl, "UE(%" PRIu32 ") " fmt, (ue)->conn_id, ## args) diff --git a/include/osmocom/hnodeb/llsk.h b/include/osmocom/hnodeb/llsk.h index ab39439..0636a5f 100644 --- a/include/osmocom/hnodeb/llsk.h +++ b/include/osmocom/hnodeb/llsk.h @@ -22,15 +22,22 @@ #include #include +#include #include #include struct hnb; +struct hnb_ue; int hnb_llsk_alloc(struct hnb *hnb); bool hnb_llsk_connected(const struct hnb *hnb); bool hnb_llsk_can_be_configured(struct hnb *hnb); +int ll_addr_type2af(enum u_addr_type t); +int ll_addr2osa(enum u_addr_type t, const union u_addr *uaddr, uint16_t port, struct osmo_sockaddr *osa); +enum u_addr_type osa2_ll_addr(const struct osmo_sockaddr *osa, union u_addr *uaddr, uint16_t *port); + + extern const struct value_string hnb_iuh_prim_type_names[]; int llsk_rx_iuh(struct hnb *hnb, struct osmo_prim_hdr *oph); @@ -42,3 +49,8 @@ struct hnb_iuh_prim *hnb_iuh_makeprim_conn_data_ind(uint32_t context_id, const uint8_t *data, uint32_t data_len); struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_ind(const uint8_t *data, uint32_t data_len); + + +extern const struct value_string hnb_audio_prim_type_names[]; +int llsk_rx_audio(struct hnb *hnb, struct osmo_prim_hdr *oph); +int llsk_audio_tx_conn_data_ind(struct hnb_ue *ue, const uint8_t *payload, uint32_t len); diff --git a/include/osmocom/hnodeb/rtp.h b/include/osmocom/hnodeb/rtp.h new file mode 100644 index 0000000..01ec1c4 --- /dev/null +++ b/include/osmocom/hnodeb/rtp.h @@ -0,0 +1,27 @@ +/* (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 . + * + */ +#pragma once + +#include + +struct hnb; +struct hnb_ue; + +int hnb_ue_voicecall_setup(struct hnb_ue *ue, const struct osmo_sockaddr *rem_addr, struct osmo_sockaddr *loc_addr); +int hnb_ue_voicecall_release(struct hnb_ue *ue); diff --git a/src/osmo-hnodeb/Makefile.am b/src/osmo-hnodeb/Makefile.am index b6d2d49..4182814 100644 --- a/src/osmo-hnodeb/Makefile.am +++ b/src/osmo-hnodeb/Makefile.am @@ -14,6 +14,7 @@ AM_CFLAGS = \ $(LIBOSMONETIF_CFLAGS) \ $(COVERAGE_CFLAGS) \ $(LIBOSMOABIS_CFLAGS) \ + $(LIBOSMOTRAU_CFLAGS) \ $(LIBOSMOSIGTRAN_CFLAGS) \ $(LIBOSMORUA_CFLAGS) \ $(LIBOSMORANAP_CFLAGS) \ @@ -36,8 +37,10 @@ osmo_hnodeb_SOURCES = \ hnb_shutdown_fsm.c \ iuh.c \ llsk.c \ + llsk_audio.c \ llsk_iuh.c \ ranap.c \ + rtp.c \ rua.c \ vty.c \ $(NULL) @@ -51,6 +54,7 @@ osmo_hnodeb_LDADD = \ $(LIBOSMONETIF_LIBS) \ $(COVERAGE_LDFLAGS) \ $(LIBOSMOABIS_LIBS) \ + $(LIBOSMOTRAU_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBOSMORUA_LIBS) \ $(LIBOSMORANAP_LIBS) \ diff --git a/src/osmo-hnodeb/debug.c b/src/osmo-hnodeb/debug.c index f34851b..5d0dabd 100644 --- a/src/osmo-hnodeb/debug.c +++ b/src/osmo-hnodeb/debug.c @@ -52,6 +52,11 @@ static const struct log_info_cat log_cat[] = { .color = "\033[1;31m", .description = "Lower Layer Unix Domain Socket", }, + [DRTP] = { + .name = "DRTP", .loglevel = LOGL_NOTICE, .enabled = 1, + .color = "\033[1;32m", + .description = "RTP Core Network side", + }, }; const struct log_info hnb_log_info = { diff --git a/src/osmo-hnodeb/hnb.c b/src/osmo-hnodeb/hnb.c index 1ac2efc..4c949f5 100644 --- a/src/osmo-hnodeb/hnb.c +++ b/src/osmo-hnodeb/hnb.c @@ -20,15 +20,20 @@ #include "config.h" +#include + #include #include #include #include +#include + #include #include #include #include +#include struct hnb *hnb_alloc(void *tall_ctx) @@ -46,6 +51,14 @@ struct hnb *hnb_alloc(void *tall_ctx) .mcc = 1, .mnc = 1, }; + + hnb->rtp.jitter_adaptive = false; + hnb->rtp.port_range_start = 16384; + hnb->rtp.port_range_end = 17407; + hnb->rtp.port_range_next = hnb->rtp.port_range_start; + hnb->rtp.ip_dscp = -1; + hnb->rtp.priority = -1; + hnb->shutdown_fi = osmo_fsm_inst_alloc(&hnb_shutdown_fsm, hnb, hnb, LOGL_INFO, NULL); @@ -94,16 +107,20 @@ struct hnb_ue *hnb_ue_alloc(struct hnb *hnb, uint32_t conn_id) void hnb_ue_free(struct hnb_ue *ue) { + hnb_ue_reset_chan(ue, true); + hnb_ue_reset_chan(ue, false); llist_del(&ue->list); talloc_free(ue); } void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps) { - if (is_ps) + if (is_ps) { ue->conn_ps = (struct hnb_ue_ps_ctx){0}; - else + } else { + hnb_ue_voicecall_release(ue); ue->conn_cs = (struct hnb_ue_cs_ctx){0}; + } } struct hnb_ue *hnb_find_ue_by_id(const struct hnb *hnb, uint32_t conn_id) diff --git a/src/osmo-hnodeb/llsk.c b/src/osmo-hnodeb/llsk.c index 5f0f9dc..8c93318 100644 --- a/src/osmo-hnodeb/llsk.c +++ b/src/osmo-hnodeb/llsk.c @@ -18,6 +18,9 @@ */ #include +#include +#include +#include #include #include @@ -27,6 +30,58 @@ #include #include +int ll_addr_type2af(enum u_addr_type t) +{ + switch (t) { + case HNB_PRIM_ADDR_TYPE_IPV4: + return AF_INET; + case HNB_PRIM_ADDR_TYPE_IPV6: + return AF_INET6; + default: + LOGP(DLLSK, LOGL_ERROR, "Rx Unknwon address type %u\n", (unsigned)t); + return -1; + } +} + +int ll_addr2osa(enum u_addr_type t, const union u_addr *uaddr, uint16_t port, struct osmo_sockaddr *osa) +{ + int af = ll_addr_type2af(t); + + osa->u.sa.sa_family = af; + + switch (af) { + case AF_INET6: + memcpy(&osa->u.sin6.sin6_addr, &uaddr->v6, sizeof(osa->u.sin6.sin6_addr)); + osa->u.sin6.sin6_port = htons(port); + break; + case AF_INET: + memcpy(&osa->u.sin.sin_addr, &uaddr->v4, sizeof(osa->u.sin.sin_addr)); + osa->u.sin.sin_port = htons(port); + break; + default: + return -1; + } + return 0; +} + +enum u_addr_type osa2_ll_addr(const struct osmo_sockaddr *osa, union u_addr *uaddr, uint16_t *port) +{ + switch (osa->u.sa.sa_family) { + case AF_INET6: + memcpy(&uaddr->v6, &osa->u.sin6.sin6_addr, sizeof(osa->u.sin6.sin6_addr)); + if (port) + *port = ntohs(osa->u.sin6.sin6_port); + return HNB_PRIM_ADDR_TYPE_IPV6; + case AF_INET: + memcpy(&uaddr->v4, &osa->u.sin.sin_addr, sizeof(osa->u.sin.sin_addr)); + if (port) + *port = ntohs(osa->u.sin.sin_port); + return HNB_PRIM_ADDR_TYPE_IPV4; + default: + return HNB_PRIM_ADDR_TYPE_UNSPEC; + } +} + static int llsk_opened_cb(struct osmo_prim_srv *srv) { struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv); @@ -73,7 +128,8 @@ bool hnb_llsk_can_be_configured(struct hnb *hnb) if (!hnb->llsk) return false; - if (hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_IUH)) + if (hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_IUH) && + hnb->llsk_valid_sapi_mask & (1 << HNB_PRIM_SAPI_AUDIO)) return true; return false; } @@ -108,10 +164,11 @@ static int llsk_rx_cb(struct osmo_prim_srv *srv, struct osmo_prim_hdr *oph) case HNB_PRIM_SAPI_IUH: return llsk_rx_iuh(hnb, oph); case HNB_PRIM_SAPI_GTP: - case HNB_PRIM_SAPI_AUDIO: LOGP(DLLSK, LOGL_ERROR, "Rx SAPI %u not yet implemented (len=%u)\n", oph->sap, msgb_length(oph->msg)); return -EINVAL; + case HNB_PRIM_SAPI_AUDIO: + return llsk_rx_audio(hnb, oph); default: LOGP(DLLSK, LOGL_ERROR, "Rx for unknwon SAPI %u (len=%u)\n", oph->sap, msgb_length(oph->msg)); diff --git a/src/osmo-hnodeb/llsk_audio.c b/src/osmo-hnodeb/llsk_audio.c new file mode 100644 index 0000000..4fa8079 --- /dev/null +++ b/src/osmo-hnodeb/llsk_audio.c @@ -0,0 +1,296 @@ +/* (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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +static size_t llsk_audio_prim_size_tbl[4][_HNB_AUDIO_PRIM_MAX] = { + [PRIM_OP_REQUEST] = { + [HNB_AUDIO_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_audio_conn_establish_req_param), + [HNB_AUDIO_PRIM_CONN_RELEASE] = sizeof(struct hnb_audio_conn_release_req_param), + [HNB_AUDIO_PRIM_CONN_DATA] = sizeof(struct hnb_audio_conn_data_req_param), + }, + [PRIM_OP_RESPONSE] = {}, + [PRIM_OP_INDICATION] = { + [HNB_AUDIO_PRIM_CONN_DATA] = sizeof(struct hnb_audio_conn_data_ind_param), + }, + [PRIM_OP_CONFIRM] = { + [HNB_AUDIO_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_audio_conn_establish_cnf_param), + }, +}; +static inline size_t llsk_audio_prim_size(enum hnb_audio_prim_type ptype, enum osmo_prim_operation op) +{ + size_t val = llsk_audio_prim_size_tbl[op][ptype]; + if (val == 0) { + LOGP(DLLSK, LOGL_FATAL, "Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op); + osmo_panic("Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op); + } + return val; +} + +const struct value_string hnb_audio_prim_type_names[] = { + OSMO_VALUE_STRING(HNB_AUDIO_PRIM_CONN_ESTABLISH), + OSMO_VALUE_STRING(HNB_AUDIO_PRIM_CONN_RELEASE), + OSMO_VALUE_STRING(HNB_AUDIO_PRIM_CONN_DATA), + { 0, NULL } +}; + +static struct hnb_audio_prim *hnb_audio_prim_alloc(enum hnb_audio_prim_type ptype, enum osmo_prim_operation op, size_t extra_len) +{ + struct osmo_prim_hdr *oph; + size_t len = llsk_audio_prim_size(ptype, op); + + oph = osmo_prim_msgb_alloc(HNB_PRIM_SAPI_AUDIO, ptype, op, sizeof(*oph) + len + extra_len); + if (!oph) + return NULL; + msgb_put(oph->msg, len); + + return (struct hnb_audio_prim *)oph; +} + +static struct hnb_audio_prim *hnb_audio_makeprim_conn_establish_cnf(uint32_t context_id, uint8_t error_code, + uint16_t local_rtp_port, uint8_t local_rtp_address_type, + const union u_addr *local_rtp_addr) +{ + struct hnb_audio_prim *audio_prim; + + audio_prim = hnb_audio_prim_alloc(HNB_AUDIO_PRIM_CONN_ESTABLISH, PRIM_OP_CONFIRM, 0); + audio_prim->u.conn_establish_cnf.context_id = context_id; + audio_prim->u.conn_establish_cnf.local_rtp_port = local_rtp_port; + audio_prim->u.conn_establish_cnf.error_code = error_code; + audio_prim->u.conn_establish_cnf.local_rtp_address_type = local_rtp_address_type; + if (local_rtp_addr) + audio_prim->u.conn_establish_cnf.local_rtp_addr = *local_rtp_addr; + + return audio_prim; +} + +static struct hnb_audio_prim *hnb_audio_makeprim_conn_data_ind(uint32_t context_id, + uint8_t domain, + const uint8_t *data, + uint32_t data_len) +{ + struct hnb_audio_prim *audio_prim; + + audio_prim = hnb_audio_prim_alloc(HNB_AUDIO_PRIM_CONN_DATA, PRIM_OP_INDICATION, data_len); + audio_prim->u.conn_data_ind.context_id = context_id; + audio_prim->u.conn_data_ind.data_len = data_len; + if (data_len) { + msgb_put(audio_prim->hdr.msg, data_len); + memcpy(audio_prim->u.conn_data_ind.data, data, data_len); + } + + return audio_prim; +} + +int llsk_audio_tx_conn_data_ind(struct hnb_ue *ue, const uint8_t *payload, uint32_t len) +{ + struct hnb_audio_prim *audio_prim; + int rc; + + LOGUE(ue, DLLSK, LOGL_INFO, "Tx AUDIO-CONN_DATA.ind\n"); + audio_prim = hnb_audio_makeprim_conn_data_ind(ue->conn_id, 0 /* CS */, payload, len); + if ((rc = osmo_prim_srv_send(ue->hnb->llsk, audio_prim->hdr.msg)) < 0) + LOGUE(ue, DLLSK, LOGL_ERROR, "Failed sending AUDIO-CONN_DATA.ind\n"); + return rc; +} + +static int _send_conn_establish_cnf_failed(struct hnb *hnb, uint32_t context_id, uint8_t error_code) +{ + struct hnb_audio_prim *audio_prim; + int rc; + LOGP(DLLSK, LOGL_ERROR, "Tx AUDIO-CONN_ESTABLISH.cnf: ctx=%u error_code=%u\n", + context_id, error_code); + audio_prim = hnb_audio_makeprim_conn_establish_cnf(context_id, error_code, 0, HNB_PRIM_ADDR_TYPE_UNSPEC, NULL); + if ((rc = osmo_prim_srv_send(hnb->llsk, audio_prim->hdr.msg)) < 0) { + LOGP(DLLSK, LOGL_ERROR, "Failed sending AUDIO-CONN_ESTABLISH.cnf context_id=%u error_code=%u\n", + context_id, error_code); + } + return rc; +} + +static int llsk_rx_audio_conn_establish_req(struct hnb *hnb, struct hnb_audio_conn_establish_req_param *ce_req) +{ + struct hnb_ue *ue; + int rc = 0; + struct hnb_audio_prim *audio_prim; + int af; + char rem_addrstr[INET6_ADDRSTRLEN+32]; + struct osmo_sockaddr rem_osa = {0}; + struct osmo_sockaddr loc_osa = {0}; + union u_addr loc_uaddr = {0}; + uint16_t loc_port; + + rc = ll_addr2osa(ce_req->remote_rtp_address_type, &ce_req->remote_rtp_addr, ce_req->remote_rtp_port, &rem_osa); + if (rc < 0) { + LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n", + ce_req->context_id, ce_req->remote_rtp_address_type); + return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 1); + } + osmo_sockaddr_to_str_buf(rem_addrstr, sizeof(rem_addrstr), &rem_osa); + + LOGP(DLLSK, LOGL_INFO, "Rx AUDIO-CONN_ESTABLISH.req ctx=%u rem_addr=%s\n", + ce_req->context_id, rem_addrstr); + + if ((af = ll_addr_type2af(ce_req->remote_rtp_address_type)) < 0) { + LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n", + ce_req->context_id, ce_req->remote_rtp_address_type); + return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 1); + } + + ue = hnb_find_ue_by_id(hnb, ce_req->context_id); + if (!ue) { + LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: UE not found! ctx=%u rem_addr=%s\n", + ce_req->context_id, rem_addrstr); + return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 2); + } + if (!ue->conn_cs.active) { + LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: CS chan not active! rem_addr=%s\n", + rem_addrstr); + return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 3); + } + + /* Create the socket: */ + if ((rc = hnb_ue_voicecall_setup(ue, &rem_osa, &loc_osa)) < 0) { + LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: Failed to set up audio socket rem_addr=%s\n", + rem_addrstr); + return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4); + } + + /* Convert resulting local address back to LLSK format: */ + if (osa2_ll_addr(&loc_osa, &loc_uaddr, &loc_port) != ce_req->remote_rtp_address_type) { + LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: Failed to provide proper local address rem_addr=%s\n", + rem_addrstr); + rc = _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4); + goto release_sock; + } + + /* Submit successful confirmation */ + LOGUE(ue, DLLSK, LOGL_INFO, "Tx AUDIO-CONN_ESTABLISH.cnf: error_code=0 rem_addr=%s loc_addr=%s\n", + rem_addrstr, osmo_sockaddr_to_str(&loc_osa)); + audio_prim = hnb_audio_makeprim_conn_establish_cnf(ce_req->context_id, 0, loc_port, + ce_req->remote_rtp_address_type, &loc_uaddr); + if ((rc = osmo_prim_srv_send(hnb->llsk, audio_prim->hdr.msg)) < 0) { + LOGUE(ue, DLLSK, LOGL_ERROR, "Failed sending AUDIO-CONN_ESTABLISH.cnf error_code=0\n"); + goto release_sock; + } + + return rc; +release_sock: + hnb_ue_voicecall_release(ue); + return rc; +} + +static int llsk_rx_audio_conn_release_req(struct hnb *hnb, struct hnb_audio_conn_release_req_param *rel_req) +{ + struct hnb_ue *ue; + + LOGP(DLLSK, LOGL_DEBUG, "Rx AUDIO-CONN_RELEASE.req ctx=%u\n", rel_req->context_id); + + ue = hnb_find_ue_by_id(hnb, rel_req->context_id); + if (!ue) { + LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_RELEASE.req: UE not found! ctx=%u\n", + rel_req->context_id); + return -EINVAL; + } + /* Release RTP socket: */ + return hnb_ue_voicecall_release(ue); +} + +static int llsk_rx_audio_conn_data_req(struct hnb *hnb, struct hnb_audio_conn_data_req_param *data_req) +{ + struct hnb_ue *ue; + int rc = 0; + + LOGP(DLLSK, LOGL_DEBUG, "Rx AUDIO-CONN_DATA.req ctx=%u data_len=%u\n", + data_req->context_id, data_req->data_len); + + ue = hnb_find_ue_by_id(hnb, data_req->context_id); + if (!ue) { + LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_DATA.req: UE not found! ctx=%u data_len=%u\n", + data_req->context_id, data_req->data_len); + return -EINVAL; + } + + /* TODO: transmit data_req->data through RTP/Iu-UP socket */ + rc = osmo_rtp_send_frame_ext(ue->conn_cs.rtp.socket, data_req->data, + data_req->data_len, GSM_RTP_DURATION, false); + if (rc < 0) { + LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_DATA.req: UE not found! ctx=%u data_len=%u\n", + data_req->context_id, data_req->data_len); + } + return rc; +} + +int llsk_rx_audio(struct hnb *hnb, struct osmo_prim_hdr *oph) +{ + size_t prim_size = llsk_audio_prim_size(oph->primitive, oph->operation); + + if (msgb_length(oph->msg) < prim_size) { + LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-%s.%s with length %u < %zu\n", + get_value_string(hnb_audio_prim_type_names, oph->primitive), + get_value_string(osmo_prim_op_names, oph->operation), + msgb_length(oph->msg), prim_size); + return -EINVAL; + } + + switch (oph->operation) { + case PRIM_OP_REQUEST: + switch (oph->primitive) { + case HNB_AUDIO_PRIM_CONN_ESTABLISH: + return llsk_rx_audio_conn_establish_req(hnb, (struct hnb_audio_conn_establish_req_param *)msgb_data(oph->msg)); + case HNB_AUDIO_PRIM_CONN_RELEASE: + return llsk_rx_audio_conn_release_req(hnb, (struct hnb_audio_conn_release_req_param *)msgb_data(oph->msg)); + case HNB_AUDIO_PRIM_CONN_DATA: + return llsk_rx_audio_conn_data_req(hnb, (struct hnb_audio_conn_data_req_param *)msgb_data(oph->msg)); + default: + LOGP(DLLSK, LOGL_ERROR, "Rx llsk-audio unknown primitive %u (len=%u)\n", + oph->primitive, msgb_length(oph->msg)); + return -EINVAL; + } + break; + + case PRIM_OP_RESPONSE: + case PRIM_OP_INDICATION: + case PRIM_OP_CONFIRM: + default: + LOGP(DLLSK, LOGL_ERROR, "Rx llsk-audio unexpected primitive operation %s::%s (len=%u)\n", + get_value_string(hnb_audio_prim_type_names, oph->primitive), + get_value_string(osmo_prim_op_names, oph->operation), + msgb_length(oph->msg)); + return -EINVAL; + } +} diff --git a/src/osmo-hnodeb/main.c b/src/osmo-hnodeb/main.c index 6728a07..9af83ea 100644 --- a/src/osmo-hnodeb/main.c +++ b/src/osmo-hnodeb/main.c @@ -41,6 +41,8 @@ #include #include +#include + #include /* ranap_set_log_area() */ #include @@ -224,6 +226,7 @@ static void signal_handler(int signum) int main(int argc, char **argv) { int rc; + void *tall_rtp_ctx; /* Track the use of talloc NULL memory contexts */ talloc_enable_null_tracking(); @@ -246,6 +249,11 @@ int main(int argc, char **argv) osmo_talloc_vty_add_cmds(); osmo_cpu_sched_vty_init(tall_hnb_ctx); + /* allocate a talloc pool for ORTP to ensure it doesn't have to go back + * to the libc malloc all the time */ + tall_rtp_ctx = talloc_pool(tall_hnb_ctx, 262144); + osmo_rtp_init(tall_rtp_ctx); + g_hnb = hnb_alloc(tall_hnb_ctx); hnb_vty_init(); diff --git a/src/osmo-hnodeb/rtp.c b/src/osmo-hnodeb/rtp.c new file mode 100644 index 0000000..cc03c6c --- /dev/null +++ b/src/osmo-hnodeb/rtp.c @@ -0,0 +1,212 @@ +/* (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 + + +/* 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++) { + + if (hnb->rtp.port_range_next >= hnb->rtp.port_range_end) + hnb->rtp.port_range_next = hnb->rtp.port_range_start; + + rc = osmo_rtp_socket_bind(rs, ip, hnb->rtp.port_range_next); + + 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 0; + } + + 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 hnb_ue *ue = (struct hnb_ue *)rs->priv; + + LOGUE(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); + llsk_audio_tx_conn_data_ind(ue, rtp_pl, rtp_pl_len); +} + +int hnb_ue_voicecall_setup(struct hnb_ue *ue, const struct osmo_sockaddr *rem_addr, struct osmo_sockaddr *loc_addr) +{ + int rc; + char cname[256+4]; + char name[32]; + struct osmo_rtp_socket *rs; + const char *local_wildcard_ipstr = "0.0.0.0"; + char remote_ipstr[INET6_ADDRSTRLEN]; + uint16_t remote_port; + 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; + } + + if (ue->conn_cs.rtp.socket) { + LOGUE(ue, DRTP, LOGL_ERROR, "Setting up rtp socket but it already exists!\n"); + return -EINVAL; + } + + rs = ue->conn_cs.rtp.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; + } + rs->priv = ue; + rs->rx_cb = &rtp_rx_cb; + + rc = rtp_bind(hnb, rs, local_wildcard_ipstr); + if (rc < 0) { + LOGUE(ue, DRTP, LOGL_ERROR, "Failed to bind RTP/RTCP sockets\n"); + goto free_ret; + } + + /* Ensure RTCP SDES contains some useful information */ + snprintf(cname, sizeof(cname), "hnb@%s", local_wildcard_ipstr); + snprintf(name, sizeof(name), "ue@%u", ue->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; + } + + /* osmo_rtp_socket_connect() is broken, OS#5356 */ + //rc = rtp_get_bound_addr(rs, loc_addr); + rc = rtp_get_bound_addr(rs, rem_addr, loc_addr); + if (rc < 0) { + LOGUE(ue, DRTP, LOGL_ERROR, "Cannot obtain locally bound IP/port: %d\n", rc); + goto free_ret; + } + + return rc; +free_ret: + osmo_rtp_socket_free(ue->conn_cs.rtp.socket); + ue->conn_cs.rtp.socket = NULL; + return rc; +} + +int hnb_ue_voicecall_release(struct hnb_ue *ue) +{ + if (!ue->conn_cs.rtp.socket) + return -EINVAL; + osmo_rtp_socket_free(ue->conn_cs.rtp.socket); + ue->conn_cs.rtp.socket = NULL; + return 0; +}