gprs_bssgp: add utilities to send and parse BSSGP rim PDUs

At the moment libosmogb offers no convinient way to send RIM PDUs. Also
parsing an incoming RIM messages into destination, source routing
info and RIM container is not available.

Change-Id: I18134fd9938040d2facb6beee3732628b167ce8c
Related: SYS#5103
This commit is contained in:
Philipp Maier 2021-01-07 21:55:48 +01:00
parent aca31b8b2d
commit 7741bc320c
6 changed files with 269 additions and 4 deletions

View File

@ -11,6 +11,7 @@
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/gprs/protocol/gsm_24_301.h>
#include <osmocom/gprs/gprs_bssgp_rim.h>
/* gprs_bssgp_util.c */
@ -41,6 +42,39 @@ int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei,
/* Chapter 10.4.14: Status */
int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg);
/* Chapter 10.6.1: RAN-INFORMATION-REQUEST */
struct bssgp_ran_information_pdu {
struct bssgp_rim_routing_info routing_info_dest;
struct bssgp_rim_routing_info routing_info_src;
/* Encoded variant of the RIM container */
uint8_t rim_cont_iei;
const uint8_t *rim_cont;
unsigned int rim_cont_len;
/* Decoded variant of the RIM container */
bool decoded_present;
union {
struct bssgp_ran_inf_req_rim_cont req_rim_cont;
struct bssgp_ran_inf_rim_cont rim_cont;
struct bssgp_ran_inf_ack_rim_cont ack_rim_cont;
struct bssgp_ran_inf_err_rim_cont err_rim_cont;
struct bssgp_ran_inf_app_err_rim_cont app_err_rim_cont;
} decoded;
/* When receiving a PDU from BSSGP the encoded variant of the RIM
* container will always be present. The decoded variant will be
* present in addition whenever BSSGP was able to decode the container.
*
* When sending a PDU to BSSGP, then the decoded variant is used when
* it is available. The encoded variant (if present) will be ignored
* then. */
};
int bssgp_tx_rim(const struct bssgp_ran_information_pdu *pdu, uint16_t nsei);
int bssgp_parse_rim_pdu(struct bssgp_ran_information_pdu *pdu, const struct msgb *msg);
struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu);
enum bssgp_prim {
PRIM_BSSGP_DL_UD,
PRIM_BSSGP_UL_UD,
@ -75,6 +109,7 @@ struct osmo_bssgp_prim {
struct {
uint8_t suspend_ref;
} resume;
struct bssgp_ran_information_pdu rim_pdu;
} u;
};

View File

@ -54,6 +54,10 @@ struct bssgp_rim_routing_info {
};
};
/* The encoded result of the rim routing information is, depending on the
* address type (discr) of variable length. */
#define BSSGP_RIM_ROUTING_INFO_MAXLEN 14
int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf, unsigned int len);
int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri);

View File

@ -675,6 +675,8 @@ static int bssgp_rx_rim(struct msgb *msg, struct tlv_parsed *tp, uint16_t bvci)
nmp.nsei = nsei;
nmp.bvci = bvci;
nmp.tp = tp;
if (bssgp_parse_rim_pdu(&nmp.u.rim_pdu, msg) < 0)
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
osmo_prim_init(&nmp.oph, SAP_BSSGP_RIM, prim, PRIM_OP_INDICATION, msg);
bssgp_prim_cb(&nmp.oph, NULL);
@ -1111,7 +1113,7 @@ static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
case BSSGP_PDUT_RAN_INFO_ACK:
case BSSGP_PDUT_RAN_INFO_ERROR:
case BSSGP_PDUT_RAN_INFO_APP_ERROR:
bssgp_rx_rim(msg, tp, bvci);
rc = bssgp_rx_rim(msg, tp, bvci);
break;
/* those only exist in the SGSN -> BSS direction */

View File

@ -96,6 +96,7 @@ int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri)
{
int rc;
struct gprs_ra_id raid_temp;
int len;
buf[0] = ri->discr & 0x0f;
buf++;
@ -105,11 +106,13 @@ int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri)
rc = bssgp_create_cell_id(buf, &ri->geran.raid, ri->geran.cid);
if (rc < 0)
return -EINVAL;
return rc + 1;
len = rc + 1;
break;
case BSSGP_RIM_ROUTING_INFO_UTRAN:
gsm48_encode_ra((struct gsm48_ra_id *)buf, &ri->utran.raid);
osmo_store16be(ri->utran.rncid, buf + 6);
return 9;
len = 9;
break;
case BSSGP_RIM_ROUTING_INFO_EUTRAN:
/* Note: 3GPP TS 24.301 Figure 9.9.3.32.1 and 3GPP TS 24.008
* Figure 10.5.130 specify MCC/MNC encoding in the same way,
@ -126,10 +129,14 @@ int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri)
sizeof(ri->eutran.global_enb_id));
memcpy(buf + 5, ri->eutran.global_enb_id,
ri->eutran.global_enb_id_len);
return ri->eutran.global_enb_id_len + 6;
len = ri->eutran.global_enb_id_len + 6;
break;
default:
return -EINVAL;
}
OSMO_ASSERT(len <= BSSGP_RIM_ROUTING_INFO_MAXLEN);
return len;
}
/*! Decode a RAN Information Request Application Container for NACC (3GPP TS 48.018, section 11.3.63.1.1).

View File

@ -588,3 +588,218 @@ int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg)
return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* Chapter 10.6.1: RAN-INFORMATION-REQUEST */
int bssgp_tx_rim(const struct bssgp_ran_information_pdu *pdu, uint16_t nsei)
{
struct msgb *msg;
struct bssgp_normal_hdr *bgph;
/* Encode RIM PDU into mesage buffer */
msg = bssgp_encode_rim_pdu(pdu);
if (!msg) {
LOGP(DLBSSGP, LOGL_ERROR,
"BSSGP RIM (NSEI=%u) unable to encode BSSGP RIM PDU\n", nsei);
return -EINVAL;
}
msgb_nsei(msg) = nsei;
msgb_bvci(msg) = 0; /* Signalling */
bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
DEBUGP(DLBSSGP, "BSSGP BVCI=0 Tx RIM-PDU:%s\n", bssgp_pdu_str(bgph->pdu_type));
return bssgp_ns_send(bssgp_ns_send_data, msg);
}
/* Parse a given message buffer into a rim-pdu struct */
int bssgp_parse_rim_pdu(struct bssgp_ran_information_pdu *pdu, const struct msgb *msg)
{
struct tlv_parsed tp[2];
struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *)msgb_bssgph(msg);
int data_len;
int rc;
uint16_t nsei = msgb_nsei(msg);
memset(pdu, 0, sizeof(*pdu));
data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
if (data_len < 0)
return -EINVAL;
rc = osmo_tlv_prot_parse(&osmo_pdef_bssgp, tp, ARRAY_SIZE(tp), bgph->pdu_type, bgph->data, data_len, 0, 0,
DLBSSGP, __func__);
if (rc < 0)
return -EINVAL;
if (TLVP_PRESENT(&tp[0], BSSGP_IE_RIM_ROUTING_INFO)) {
rc = bssgp_parse_rim_ri(&pdu->routing_info_dest, TLVP_VAL(&tp[0], BSSGP_IE_RIM_ROUTING_INFO),
TLVP_LEN(&tp[0], BSSGP_IE_RIM_ROUTING_INFO));
if (rc < 0) {
LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) invalid Destination Cell Identifier IE\n", nsei);
return -EINVAL;
}
} else {
LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) missing Destination Cell Identifier IE\n", nsei);
return -EINVAL;
}
if (TLVP_PRESENT(&tp[1], BSSGP_IE_RIM_ROUTING_INFO)) {
rc = bssgp_parse_rim_ri(&pdu->routing_info_src, TLVP_VAL(&tp[1], BSSGP_IE_RIM_ROUTING_INFO),
TLVP_LEN(&tp[1], BSSGP_IE_RIM_ROUTING_INFO));
if (rc < 0) {
LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) invalid Destination Cell Identifier IE\n", nsei);
return -EINVAL;
}
} else {
LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) missing Source Cell Identifier IE\n", nsei);
return -EINVAL;
}
if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_REQ_RIM_CONTAINER))
pdu->rim_cont_iei = BSSGP_IE_RI_REQ_RIM_CONTAINER;
else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_RIM_CONTAINER))
pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER;
else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_APP_ERROR_RIM_CONT))
pdu->rim_cont_iei = BSSGP_IE_RI_APP_ERROR_RIM_CONT;
else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_ACK_RIM_CONTAINER))
pdu->rim_cont_iei = BSSGP_IE_RI_ACK_RIM_CONTAINER;
else if (TLVP_PRESENT(&tp[0], BSSGP_IE_RI_ERROR_RIM_COINTAINER))
pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER;
else {
LOGP(DLBSSGP, LOGL_ERROR, "BSSGP RIM (NSEI=%u) missing or wrong RIM Container IE\n", nsei);
return -EINVAL;
}
pdu->rim_cont = TLVP_VAL(&tp[0], pdu->rim_cont_iei);
pdu->rim_cont_len = TLVP_LEN(&tp[0], pdu->rim_cont_iei);
/* Make sure the rim container field is not empty */
if (pdu->rim_cont_len < 1)
return -EINVAL;
if (!pdu->rim_cont)
return -EINVAL;
/* Note: It is not an error if we fail to parse the RIM container,
* since there are applications where parsing the RIM container
* is not necessary (routing). It is up to the API user to check
* the results. */
switch (pdu->rim_cont_iei) {
case BSSGP_IE_RI_REQ_RIM_CONTAINER:
rc = bssgp_dec_ran_inf_req_rim_cont(&pdu->decoded.req_rim_cont, pdu->rim_cont, pdu->rim_cont_len);
break;
case BSSGP_IE_RI_RIM_CONTAINER:
rc = bssgp_dec_ran_inf_rim_cont(&pdu->decoded.rim_cont, pdu->rim_cont, pdu->rim_cont_len);
break;
case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
rc = bssgp_dec_ran_inf_app_err_rim_cont(&pdu->decoded.app_err_rim_cont, pdu->rim_cont,
pdu->rim_cont_len);
break;
case BSSGP_IE_RI_ACK_RIM_CONTAINER:
rc = bssgp_dec_ran_inf_ack_rim_cont(&pdu->decoded.ack_rim_cont, pdu->rim_cont, pdu->rim_cont_len);
break;
case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
rc = bssgp_dec_ran_inf_err_rim_cont(&pdu->decoded.err_rim_cont, pdu->rim_cont, pdu->rim_cont_len);
break;
default:
LOGP(DLBSSGP, LOGL_DEBUG, "BSSGP RIM (NSEI=%u) cannot parse unknown RIM container.\n", nsei);
return 0;
}
if (rc < 0) {
LOGP(DLBSSGP, LOGL_DEBUG, "BSSGP RIM (NSEI=%u) unable to parse RIM container.\n", nsei);
return 0;
}
pdu->decoded_present = true;
return 0;
}
/* Encode a given rim-pdu struct into a message buffer */
struct msgb *bssgp_encode_rim_pdu(const struct bssgp_ran_information_pdu *pdu)
{
struct msgb *msg = bssgp_msgb_alloc();
struct bssgp_normal_hdr *bgph;
uint8_t rim_ri_buf[BSSGP_RIM_ROUTING_INFO_MAXLEN];
uint8_t *rim_cont_buf;
int rc;
if (!msg)
return NULL;
bgph = (struct bssgp_normal_hdr *)msgb_put(msg, sizeof(*bgph));
/* Set PDU type based on RIM container type */
switch (pdu->rim_cont_iei) {
case BSSGP_IE_RI_REQ_RIM_CONTAINER:
bgph->pdu_type = BSSGP_PDUT_RAN_INFO_REQ;
break;
case BSSGP_IE_RI_RIM_CONTAINER:
bgph->pdu_type = BSSGP_PDUT_RAN_INFO;
break;
case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
bgph->pdu_type = BSSGP_PDUT_RAN_INFO_APP_ERROR;
break;
case BSSGP_IE_RI_ACK_RIM_CONTAINER:
bgph->pdu_type = BSSGP_PDUT_RAN_INFO_ACK;
break;
case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
bgph->pdu_type = BSSGP_PDUT_RAN_INFO_ERROR;
break;
default:
/* The caller must correctly specify the container type! */
OSMO_ASSERT(false);
}
/* Put RIM routing information */
rc = bssgp_create_rim_ri(rim_ri_buf, &pdu->routing_info_dest);
if (rc < 0 || rc > BSSGP_RIM_ROUTING_INFO_MAXLEN)
goto error;
msgb_tvlv_put(msg, BSSGP_IE_RIM_ROUTING_INFO, rc, rim_ri_buf);
rc = bssgp_create_rim_ri(rim_ri_buf, &pdu->routing_info_src);
if (rc < 0 || rc > BSSGP_RIM_ROUTING_INFO_MAXLEN)
goto error;
msgb_tvlv_put(msg, BSSGP_IE_RIM_ROUTING_INFO, rc, rim_ri_buf);
/* Put RIM container */
if (pdu->decoded_present) {
rim_cont_buf = talloc_zero_size(msg, msg->data_len);
if (!rim_cont_buf)
goto error;
switch (pdu->rim_cont_iei) {
case BSSGP_IE_RI_REQ_RIM_CONTAINER:
rc = bssgp_enc_ran_inf_req_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.req_rim_cont);
break;
case BSSGP_IE_RI_RIM_CONTAINER:
rc = bssgp_enc_ran_inf_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.rim_cont);
break;
case BSSGP_IE_RI_APP_ERROR_RIM_CONT:
rc = bssgp_enc_ran_inf_app_err_rim_cont(rim_cont_buf, msg->data_len,
&pdu->decoded.app_err_rim_cont);
break;
case BSSGP_IE_RI_ACK_RIM_CONTAINER:
rc = bssgp_enc_ran_inf_ack_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.ack_rim_cont);
break;
case BSSGP_IE_RI_ERROR_RIM_COINTAINER:
rc = bssgp_enc_ran_inf_err_rim_cont(rim_cont_buf, msg->data_len, &pdu->decoded.err_rim_cont);
break;
default:
/* The API user must set the iei properly! */
OSMO_ASSERT(false);
}
if (rc < 0)
goto error;
msgb_tvlv_put(msg, pdu->rim_cont_iei, rc, rim_cont_buf);
talloc_free(rim_cont_buf);
} else {
/* Make sure the RIM container is actually present. */
OSMO_ASSERT(pdu->rim_cont_iei != 0 && pdu->rim_cont_len > 0 && pdu->rim_cont);
msgb_tvlv_put(msg, pdu->rim_cont_iei, pdu->rim_cont_len, pdu->rim_cont);
}
return msg;
error:
talloc_free(rim_cont_buf);
msgb_free(msg);
return 0;
}

View File

@ -30,6 +30,7 @@ bssgp_msgb_copy;
bssgp_msgb_tlli_put;
bssgp_msgb_ra_put;
bssgp_parse_cell_id;
bssgp_parse_rim_pdu;
bssgp_parse_rim_ri;
bssgp_set_bssgp_callback;
bssgp_tx_bvc_block;
@ -48,6 +49,7 @@ bssgp_tx_radio_status_tmsi;
bssgp_tx_resume;
bssgp_tx_resume_ack;
bssgp_tx_resume_nack;
bssgp_tx_rim;
bssgp_tx_simple_bvci;
bssgp_tx_status;
bssgp_tx_suspend;