Support forwarding proto IPAC_PROTO_EXT_PCU BSC<->PCU

This new extension protocol is used to forward Osmocom PCUIF messages
BSC<->BTS<->PCU.
It will be sent re-using the IPA multiplex of the OML link between
BSC and BTS. BTS is responsible for forwarding the message over the unix
socket to the PCU.

PCUIF existing RX path needs to be reworked in order to accept
variable-size messages, in order to be able to transparently forward
messages without knowing about them (the new container message is
variable-length).

Related: SYS#5303
Change-Id: I73fdb17107494ade9263a62d1f729e67303fce87
This commit is contained in:
Pau Espin 2021-06-08 18:38:23 +02:00 committed by laforge
parent 80ce85295b
commit 8bf5cbea49
9 changed files with 201 additions and 10 deletions

View File

@ -1,5 +1,6 @@
noinst_HEADERS = \
abis.h \
abis_osmo.h \
bts.h \
bts_model.h \
bts_shutdown_fsm.h \

View File

@ -0,0 +1,10 @@
#pragma once
#include <osmocom/core/msgb.h>
#include <osmo-bts/pcuif_proto.h>
struct gsm_bts;
int down_osmo(struct gsm_bts *bts, struct msgb *msg);
int abis_osmo_pcu_tx_container(struct gsm_bts *bts, const struct gsm_pcu_if_container *container);

View File

@ -167,6 +167,8 @@ struct gsm_bts {
/* how do we talk OML with this TRX? */
struct e1inp_sign_link *oml_link;
struct timespec oml_conn_established_timestamp;
/* OSMO extenion link associated to same line as oml_link: */
struct e1inp_sign_link *osmo_link;
/* Abis network management O&M handle */
struct gsm_abis_mo mo;

View File

@ -1,8 +1,12 @@
#ifndef _PCU_IF_H
#define _PCU_IF_H
#include <osmo-bts/pcuif_proto.h>
extern int pcu_direct;
#define PCUIF_HDR_SIZE ( sizeof(struct gsm_pcu_if) - sizeof(((struct gsm_pcu_if *)0)->u) )
int pcu_tx_info_ind(void);
int pcu_tx_si(const struct gsm_bts *bts, enum osmo_sysinfo_type si_type, bool enable);
int pcu_tx_app_info_req(struct gsm_bts *bts, uint8_t app_type, uint8_t len, const uint8_t *app_data);
@ -20,6 +24,7 @@ int pcu_tx_interf_ind(uint8_t bts_nr, uint8_t trx_nr, uint32_t fn,
int pcu_tx_pag_req(const uint8_t *identity_lv, uint8_t chan_needed);
int pcu_tx_pch_data_cnf(uint32_t fn, uint8_t *data, uint8_t len);
int pcu_tx_susp_req(struct gsm_lchan *lchan, uint32_t tlli, const uint8_t *ra_id, uint8_t cause);
int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
int pcu_sock_init(const char *path);
void pcu_sock_exit(void);

View File

@ -12,6 +12,7 @@ libbts_a_SOURCES = \
sysinfo.c \
logging.c \
abis.c \
abis_osmo.c \
oml.c \
bts.c \
bts_trx.c \

View File

@ -48,6 +48,7 @@
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/abis_osmo.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts_trx.h>
@ -113,6 +114,8 @@ static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
if (clock_gettime(CLOCK_MONOTONIC, &g_bts->oml_conn_established_timestamp) != 0)
memset(&g_bts->oml_conn_established_timestamp, 0,
sizeof(g_bts->oml_conn_established_timestamp));
g_bts->osmo_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OSMO,
g_bts->c0, IPAC_PROTO_OSMO, 0);
drain_oml_queue(g_bts);
bts_link_estab(g_bts);
return g_bts->oml_link;
@ -158,10 +161,15 @@ static void sign_link_down(struct e1inp_line *line)
"A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
(uint64_t)(now.tv_sec - g_bts->oml_conn_established_timestamp.tv_sec));
}
g_bts->oml_link = NULL;
}
g_bts->oml_link = NULL;
memset(&g_bts->oml_conn_established_timestamp, 0, sizeof(g_bts->oml_conn_established_timestamp));
if (g_bts->osmo_link) {
e1inp_sign_link_destroy(g_bts->osmo_link);
g_bts->osmo_link = NULL;
}
/* Then iterate over the RSL signalling links */
llist_for_each_entry(trx, &g_bts->trx_list, list) {
if (trx->rsl_link) {
@ -191,6 +199,9 @@ static int sign_link_cb(struct msgb *msg)
case E1INP_SIGN_RSL:
down_rsl(link->trx, msg);
break;
case E1INP_SIGN_OSMO:
down_osmo(link->trx->bts, msg);
break;
default:
msgb_free(msg);
break;

136
src/common/abis_osmo.c Normal file
View File

@ -0,0 +1,136 @@
/* OSMO extenion link associated to same line as oml_link: */
/* (C) 2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* 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 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/core/msgb.h>
#include <osmocom/gsm/ipa.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/pcuif_proto.h>
extern struct gsm_network bts_gsmnet;
#define OM_HEADROOM_SIZE 128
////////////////////////////////////////
// OSMO ABIS extensions (PCU)
///////////////////////////////////////
static struct msgb *abis_osmo_pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr, size_t extra_size)
{
struct msgb *msg;
struct gsm_pcu_if *pcu_prim;
msg = msgb_alloc_headroom(OM_HEADROOM_SIZE + sizeof(struct gsm_pcu_if) + extra_size,
OM_HEADROOM_SIZE, "IPA/ABIS/OSMO");
/* Only header is filled, caller is responible for reserving + filling
* message type specific contents: */
msgb_put(msg, PCUIF_HDR_SIZE);
pcu_prim = (struct gsm_pcu_if *) msgb_data(msg);
pcu_prim->msg_type = msg_type;
pcu_prim->bts_nr = bts_nr;
return msg;
}
/* Send a OML NM Message from BSC to BTS */
int abis_osmo_sendmsg(struct gsm_bts *bts, struct msgb *msg)
{
msg->dst = bts->osmo_link;
msg->l2h = msg->data;
return abis_sendmsg(msg);
}
/* Send IPA/OSMO/PCU extension Abis message from PCU to BSC */
static int abis_osmo_pcu_sendmsg(struct gsm_bts *bts, struct msgb *msg)
{
ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_PCU);
return abis_osmo_sendmsg(bts, msg);
}
int abis_osmo_pcu_tx_container(struct gsm_bts *bts, const struct gsm_pcu_if_container *container)
{
uint16_t data_length = osmo_load16be(&container->length);
struct msgb *msg = abis_osmo_pcu_msgb_alloc(PCU_IF_MSG_CONTAINER, bts->nr, data_length);
struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msgb_data(msg);
struct gsm_pcu_if_container *tx_cont = &pcu_prim->u.container;
msgb_put(msg, sizeof(*tx_cont) + data_length);
tx_cont->msg_type = container->msg_type;
tx_cont->length = container->length;
if (data_length)
memcpy(tx_cont->data, container->data, data_length);
return abis_osmo_pcu_sendmsg(bts, msg);
}
/* incoming IPA/OSMOEXT/PCU Abis message from BSC */
static int rx_down_osmo_pcu(struct gsm_bts *bts, struct msgb *msg)
{
struct gsm_pcu_if *pcu_prim;
if (msgb_l2len(msg) < PCUIF_HDR_SIZE) {
LOGP(DPCU, LOGL_ERROR, "ABIS_OSMO_PCU message too short\n");
oml_tx_failure_event_rep(&bts->mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_UKWN_MSG,
"ABIS_OSMO_PCU message too short\n");
msgb_free(msg);
return -EIO;
}
pcu_prim = msgb_l2(msg);
LOGP(DPCU, LOGL_INFO, "Rx BSC->BTS%d ABIS_OSMO_PCU msg type %u\n",
pcu_prim->bts_nr, pcu_prim->msg_type);
/* we patch the bts_nr received from BTS with the bts_nr we used to set up in the local PCU */
pcu_prim->bts_nr = bts->nr;
/* Trim Abis lower layers: */
msgb_pull_to_l2(msg);
/* we simply forward it to PCUIF: */
return pcu_sock_send(&bts_gsmnet, msg);
}
/* incoming IPA/OSMO extension Abis message from BSC */
int down_osmo(struct gsm_bts *bts, struct msgb *msg)
{
uint8_t *type;
if (msgb_l2len(msg) < 1) {
oml_tx_failure_event_rep(&bts->mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_UKWN_MSG,
"OSMO message too short\n");
msgb_free(msg);
return -EIO;
}
type = msgb_l2(msg);
msg->l2h = type + 1;
switch (*type) {
case IPAC_PROTO_EXT_PCU:
return rx_down_osmo_pcu(bts, msg);
default:
oml_tx_failure_event_rep(&bts->mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_UKWN_MSG,
"OSMO message unknown extension %u\n", *type);
msgb_free(msg);
return -EIO;
}
}

View File

@ -371,6 +371,7 @@ int bts_init(struct gsm_bts *bts)
/* features implemented in 'common', available for all models */
osmo_bts_set_feature(bts->features, BTS_FEAT_ETWS_PN);
osmo_bts_set_feature(bts->features, BTS_FEAT_IPV6_NSVC);
osmo_bts_set_feature(bts->features, BTS_FEAT_ABIS_OSMO_PCU);
rc = bts_model_init(bts);
if (rc < 0) {

View File

@ -45,6 +45,7 @@
#include <osmo-bts/signal.h>
#include <osmo-bts/l1sap.h>
#include <osmo-bts/oml.h>
#include <osmo-bts/abis_osmo.h>
uint32_t trx_get_hlayer1(const struct gsm_bts_trx *trx);
@ -62,8 +63,6 @@ static const char *sapi_string[] = {
[PCU_IF_SAPI_PTCCH] = "PTCCH",
};
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg);
/*
* PCU messages
*/
@ -884,11 +883,21 @@ static int pcu_rx_act_req(struct gsm_bts *bts,
return 0;
}
#define CHECK_IF_MSG_SIZE(prim_len, prim_msg) \
do { \
size_t _len = PCUIF_HDR_SIZE + sizeof(prim_msg); \
if (prim_len < _len) { \
LOGP(DPCU, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive %s " \
"size is %zu, discarding\n", prim_len, #prim_msg, _len); \
return -EINVAL; \
} \
} while(0);
static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
struct gsm_pcu_if *pcu_prim)
struct gsm_pcu_if *pcu_prim, size_t prim_len)
{
int rc = 0;
struct gsm_bts *bts;
size_t exp_len;
if ((bts = gsm_bts_num(net, pcu_prim->bts_nr)) == NULL) {
LOGP(DPCU, LOGL_ERROR, "Received PCU Prim for non-existent BTS %u\n", pcu_prim->bts_nr);
@ -897,17 +906,32 @@ static int pcu_rx(struct gsm_network *net, uint8_t msg_type,
switch (msg_type) {
case PCU_IF_MSG_DATA_REQ:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.data_req);
rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req);
break;
case PCU_IF_MSG_PAG_REQ:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.pag_req);
rc = pcu_rx_pag_req(bts, msg_type, &pcu_prim->u.pag_req);
break;
case PCU_IF_MSG_ACT_REQ:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.act_req);
rc = pcu_rx_act_req(bts, &pcu_prim->u.act_req);
break;
case PCU_IF_MSG_TXT_IND:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.txt_ind);
rc = pcu_rx_txt_ind(bts, &pcu_prim->u.txt_ind);
break;
case PCU_IF_MSG_CONTAINER:
CHECK_IF_MSG_SIZE(prim_len, pcu_prim->u.container);
/* ^ check if we can access container fields, v check with container data length */
exp_len = PCUIF_HDR_SIZE + sizeof(pcu_prim->u.container) + osmo_load16be(&pcu_prim->u.container.length);
if (prim_len < exp_len) {
LOGP(DPCU, LOGL_ERROR, "Received %zu bytes on PCU Socket, but primitive "
"container size is %zu, discarding\n", prim_len, exp_len);
return -EINVAL;
}
rc = abis_osmo_pcu_tx_container(bts, &pcu_prim->u.container);
break;
default:
LOGP(DPCU, LOGL_ERROR, "Received unknown PCU msg type %d\n",
msg_type);
@ -928,7 +952,7 @@ struct pcu_sock_state {
struct llist_head upqueue; /* queue for sending messages */
};
static int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
int pcu_sock_send(struct gsm_network *net, struct msgb *msg)
{
struct pcu_sock_state *state = net->pcu_state;
struct osmo_fd *conn_bfd;
@ -1019,7 +1043,7 @@ static int pcu_sock_read(struct osmo_fd *bfd)
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
msg = msgb_alloc(sizeof(*pcu_prim) + 1000, "pcu_sock_rx");
if (!msg)
return -ENOMEM;
@ -1037,14 +1061,14 @@ static int pcu_sock_read(struct osmo_fd *bfd)
goto close;
}
if (rc < sizeof(*pcu_prim)) {
LOGP(DPCU, LOGL_ERROR, "Received %d bytes on PCU Socket, but primitive size "
"is %zu, discarding\n", rc, sizeof(*pcu_prim));
if (rc < PCUIF_HDR_SIZE) {
LOGP(DPCU, LOGL_ERROR, "Received %d bytes on PCU Socket, but primitive hdr size "
"is %zu, discarding\n", rc, PCUIF_HDR_SIZE);
msgb_free(msg);
return 0;
}
rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim);
rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim, rc);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */