2020-07-12 11:45:50 +00:00
|
|
|
/*! \file gprs_ns2_message.c
|
|
|
|
* NS-over-FR-over-GRE implementation.
|
|
|
|
* GPRS Networks Service (NS) messages on the Gb interface.
|
|
|
|
* 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05)
|
|
|
|
* as well as its successor 3GPP TS 48.016 */
|
|
|
|
|
|
|
|
/* (C) 2020 sysmocom - s.f.m.c. GmbH
|
|
|
|
* Author: Alexander Couzens <lynxis@fe80.eu>
|
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 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 General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/byteswap.h>
|
|
|
|
#include <osmocom/core/rate_ctr.h>
|
|
|
|
#include <osmocom/core/stat_item.h>
|
|
|
|
#include <osmocom/core/stats.h>
|
|
|
|
#include <osmocom/gsm/tlv.h>
|
|
|
|
#include <osmocom/gprs/gprs_msgb.h>
|
|
|
|
#include <osmocom/gprs/gprs_ns2.h>
|
|
|
|
#include <osmocom/gprs/protocol/gsm_08_16.h>
|
|
|
|
|
|
|
|
#include "gprs_ns2_internal.h"
|
|
|
|
|
|
|
|
#define ERR_IF_NSVC_USES_SNS(nsvc, reason) \
|
|
|
|
do { \
|
|
|
|
if (!nsvc->nse->bss_sns_fi) \
|
|
|
|
break; \
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_DEBUG, "invalid packet %s with SNS\n", reason); \
|
2020-07-12 11:45:50 +00:00
|
|
|
} while (0)
|
|
|
|
|
2021-01-25 15:18:21 +00:00
|
|
|
static int ns2_validate_reset(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1) ||
|
|
|
|
!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:18:21 +00:00
|
|
|
static int ns2_validate_reset_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_NSEI, 2)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:18:21 +00:00
|
|
|
static int ns2_validate_block(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2) || !TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:18:21 +00:00
|
|
|
static int ns2_validate_block_ack(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 2)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:18:21 +00:00
|
|
|
static int ns2_validate_status(struct gprs_ns2_vc *nsvc, struct msgb *msg, struct tlv_parsed *tp, uint8_t *cause)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_CAUSE, 1)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-09-06 16:17:15 +00:00
|
|
|
uint8_t _cause = tlvp_val8(tp, NS_IE_CAUSE, 0);
|
2020-07-12 11:45:50 +00:00
|
|
|
switch (_cause) {
|
|
|
|
case NS_CAUSE_NSVC_BLOCKED:
|
|
|
|
case NS_CAUSE_NSVC_UNKNOWN:
|
2021-09-06 16:17:15 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_VCI, 1)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
2021-09-07 13:30:02 +00:00
|
|
|
|
|
|
|
if (nsvc->mode != GPRS_NS2_VC_MODE_BLOCKRESET) {
|
|
|
|
*cause = NS_CAUSE_PDU_INCOMP_PSTATE;
|
|
|
|
return -1;
|
|
|
|
}
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
|
|
|
case NS_CAUSE_SEM_INCORR_PDU:
|
|
|
|
case NS_CAUSE_PDU_INCOMP_PSTATE:
|
|
|
|
case NS_CAUSE_PROTO_ERR_UNSPEC:
|
|
|
|
case NS_CAUSE_INVAL_ESSENT_IE:
|
|
|
|
case NS_CAUSE_MISSING_ESSENT_IE:
|
2021-09-06 16:17:15 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_PDU, 1)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NS_CAUSE_BVCI_UNKNOWN:
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRES_LEN(tp, NS_IE_BVCI, 2)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NS_CAUSE_UNKN_IP_TEST_FAILED:
|
2020-12-03 15:01:02 +00:00
|
|
|
if (!TLVP_PRESENT(tp, NS_IE_IPv4_LIST) && !TLVP_PRESENT(tp, NS_IE_IPv6_LIST)) {
|
2020-07-12 11:45:50 +00:00
|
|
|
*cause = NS_CAUSE_MISSING_ESSENT_IE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
int ns2_validate(struct gprs_ns2_vc *nsvc,
|
|
|
|
uint8_t pdu_type,
|
|
|
|
struct msgb *msg,
|
|
|
|
struct tlv_parsed *tp,
|
|
|
|
uint8_t *cause)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
switch (pdu_type) {
|
|
|
|
case NS_PDUT_RESET:
|
2021-01-25 15:18:21 +00:00
|
|
|
return ns2_validate_reset(nsvc, msg, tp, cause);
|
2020-07-12 11:45:50 +00:00
|
|
|
case NS_PDUT_RESET_ACK:
|
2021-01-25 15:18:21 +00:00
|
|
|
return ns2_validate_reset_ack(nsvc, msg, tp, cause);
|
2020-07-12 11:45:50 +00:00
|
|
|
case NS_PDUT_BLOCK:
|
2021-01-25 15:18:21 +00:00
|
|
|
return ns2_validate_block(nsvc, msg, tp, cause);
|
2020-07-12 11:45:50 +00:00
|
|
|
case NS_PDUT_BLOCK_ACK:
|
2021-01-25 15:18:21 +00:00
|
|
|
return ns2_validate_block_ack(nsvc, msg, tp, cause);
|
2020-07-12 11:45:50 +00:00
|
|
|
case NS_PDUT_STATUS:
|
2021-01-25 15:18:21 +00:00
|
|
|
return ns2_validate_status(nsvc, msg, tp, cause);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
/* following PDUs doesn't have any payloads */
|
|
|
|
case NS_PDUT_ALIVE:
|
|
|
|
case NS_PDUT_ALIVE_ACK:
|
|
|
|
case NS_PDUT_UNBLOCK:
|
|
|
|
case NS_PDUT_UNBLOCK_ACK:
|
|
|
|
if (msgb_l2len(msg) != sizeof(struct gprs_ns_hdr)) {
|
|
|
|
*cause = NS_CAUSE_PROTO_ERR_UNSPEC;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-30 20:49:28 +00:00
|
|
|
static int ns_vc_tx(struct gprs_ns2_vc *nsvc, struct msgb *msg)
|
|
|
|
{
|
2021-01-30 21:01:28 +00:00
|
|
|
unsigned int bytes = msgb_length(msg);
|
|
|
|
int rc;
|
2021-01-30 20:51:15 +00:00
|
|
|
|
2021-01-30 21:01:28 +00:00
|
|
|
|
|
|
|
rc = nsvc->bind->send_vc(nsvc, msg);
|
|
|
|
if (rc < 0) {
|
2021-07-09 18:35:00 +00:00
|
|
|
RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT_DROP);
|
|
|
|
RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT_DROP, bytes);
|
2021-01-30 21:01:28 +00:00
|
|
|
} else {
|
2021-07-09 18:35:00 +00:00
|
|
|
RATE_CTR_INC_NS(nsvc, NS_CTR_PKTS_OUT);
|
|
|
|
RATE_CTR_ADD_NS(nsvc, NS_CTR_BYTES_OUT, bytes);
|
2021-01-30 21:01:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
2021-01-30 20:49:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
/* transmit functions */
|
|
|
|
static int ns2_tx_simple(struct gprs_ns2_vc *nsvc, uint8_t pdu_type)
|
|
|
|
{
|
2021-01-25 15:09:23 +00:00
|
|
|
struct msgb *msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
nsh->pdu_type = pdu_type;
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-BLOCK on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] vc NS-VC on which the NS-BLOCK is to be transmitted
|
|
|
|
* \param[in] cause Numeric NS Cause value
|
2021-09-06 23:10:38 +00:00
|
|
|
* \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2021-09-06 23:10:38 +00:00
|
|
|
int ns2_tx_block(struct gprs_ns2_vc *nsvc, uint8_t cause, uint16_t *nsvci)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
2021-09-06 23:10:38 +00:00
|
|
|
uint16_t encoded_nsvci;
|
|
|
|
|
|
|
|
if (nsvci)
|
|
|
|
encoded_nsvci = osmo_htons(*nsvci);
|
|
|
|
else
|
|
|
|
encoded_nsvci = osmo_htons(nsvc->nsvci);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK");
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2021-06-04 16:17:12 +00:00
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr(nsvc->ctrg, NS_CTR_BLOCKED));
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
nsh->pdu_type = NS_PDUT_BLOCK;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
|
2021-09-06 23:10:38 +00:00
|
|
|
msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &encoded_nsvci);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-BLOCK-ACK on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC on which the NS-BLOCK is to be transmitted
|
2021-09-06 23:10:38 +00:00
|
|
|
* \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2021-09-06 23:10:38 +00:00
|
|
|
int ns2_tx_block_ack(struct gprs_ns2_vc *nsvc, uint16_t *nsvci)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
2021-09-06 23:10:38 +00:00
|
|
|
uint16_t encoded_nsvci;
|
|
|
|
|
|
|
|
if (nsvci)
|
|
|
|
encoded_nsvci = osmo_htons(*nsvci);
|
|
|
|
else
|
|
|
|
encoded_nsvci = osmo_htons(nsvc->nsvci);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS BLOCK ACK");
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
nsh->pdu_type = NS_PDUT_BLOCK_ACK;
|
|
|
|
|
2021-09-06 23:10:38 +00:00
|
|
|
msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &encoded_nsvci);
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-RESET on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC used for transmission
|
|
|
|
* \paam[in] cause Numeric NS cause value
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_reset(struct gprs_ns2_vc *nsvc, uint8_t cause)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsvci = osmo_htons(nsvc->nsvci);
|
|
|
|
uint16_t nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET");
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
nsh->pdu_type = NS_PDUT_RESET;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
|
|
|
|
msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci);
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei);
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-RESET-ACK on a given NS-VC.
|
|
|
|
* \param[in] nsvc NS-VC used for transmission
|
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_reset_ack(struct gprs_ns2_vc *nsvc)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsvci, nsei;
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/* Section 9.2.6 */
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS RESET ACK");
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
nsvci = osmo_htons(nsvc->nsvci);
|
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
|
|
|
|
nsh->pdu_type = NS_PDUT_RESET_ACK;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci);
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-UNBLOCK on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC on which the NS-UNBLOCK is to be transmitted
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_unblock(struct gprs_ns2_vc *nsvc)
|
|
|
|
{
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK");
|
|
|
|
|
|
|
|
return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-UNBLOCK-ACK on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC on which the NS-UNBLOCK-ACK is to be transmitted
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_unblock_ack(struct gprs_ns2_vc *nsvc)
|
|
|
|
{
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
ERR_IF_NSVC_USES_SNS(nsvc, "transmit NS UNBLOCK ACK");
|
|
|
|
|
|
|
|
return ns2_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK);
|
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-ALIVE on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC on which the NS-ALIVE is to be transmitted
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_alive(struct gprs_ns2_vc *nsvc)
|
|
|
|
{
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
return ns2_tx_simple(nsvc, NS_PDUT_ALIVE);
|
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-ALIVE-ACK on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC on which the NS-ALIVE-ACK is to be transmitted
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_alive_ack(struct gprs_ns2_vc *nsvc)
|
|
|
|
{
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
return ns2_tx_simple(nsvc, NS_PDUT_ALIVE_ACK);
|
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit NS-UNITDATA on a given NS-VC.
|
|
|
|
* \param[in] nsvc NS-VC on which the NS-UNITDATA is to be transmitted
|
|
|
|
* \param[in] bvci BVCI to encode in NS-UNITDATA header
|
|
|
|
* \param[in] sducontrol SDU control octet of NS header
|
|
|
|
* \param[in] msg message buffer containing payload
|
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_unit_data(struct gprs_ns2_vc *nsvc,
|
|
|
|
uint16_t bvci, uint8_t sducontrol,
|
|
|
|
struct msgb *msg)
|
|
|
|
{
|
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
msg->l2h = msgb_push(msg, sizeof(*nsh) + 3);
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
if (!nsh) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Not enough headroom for NS header\n");
|
2020-07-12 11:45:50 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsh->pdu_type = NS_PDUT_UNITDATA;
|
|
|
|
nsh->data[0] = sducontrol;
|
|
|
|
nsh->data[1] = bvci >> 8;
|
|
|
|
nsh->data[2] = bvci & 0xff;
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_DATA(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, "\n");
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2020-09-18 20:33:24 +00:00
|
|
|
/*! Transmit a NS-STATUS on a given NS-VC.
|
2020-07-12 11:45:50 +00:00
|
|
|
* \param[in] nsvc NS-VC to be used for transmission
|
|
|
|
* \param[in] cause Numeric NS cause value
|
|
|
|
* \param[in] bvci BVCI to be reset within NSVC
|
2020-09-18 20:33:24 +00:00
|
|
|
* \param[in] orig_msg message causing the STATUS
|
2021-09-23 14:19:32 +00:00
|
|
|
* \param[in] nsvci if given this NSVCI will be encoded. If NULL the nsvc->nsvci will be used.
|
2020-09-18 20:33:24 +00:00
|
|
|
* \returns 0 in case of success */
|
2020-07-12 11:45:50 +00:00
|
|
|
int ns2_tx_status(struct gprs_ns2_vc *nsvc, uint8_t cause,
|
2021-09-23 14:19:32 +00:00
|
|
|
uint16_t bvci, struct msgb *orig_msg, uint16_t *nsvci)
|
2020-07-12 11:45:50 +00:00
|
|
|
{
|
2021-01-25 15:09:23 +00:00
|
|
|
struct msgb *msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns_hdr *nsh;
|
2021-09-23 14:19:32 +00:00
|
|
|
uint16_t encoded_nsvci;
|
2021-02-15 03:51:14 +00:00
|
|
|
unsigned int orig_len, max_orig_len;
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
|
|
|
|
bvci = osmo_htons(bvci);
|
|
|
|
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
nsh->pdu_type = NS_PDUT_STATUS;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause);
|
|
|
|
|
|
|
|
switch (cause) {
|
2021-02-15 03:55:08 +00:00
|
|
|
case NS_CAUSE_NSVC_BLOCKED:
|
|
|
|
case NS_CAUSE_NSVC_UNKNOWN:
|
|
|
|
/* Section 9.2.7.1: Static conditions for NS-VCI */
|
2021-09-23 14:19:32 +00:00
|
|
|
if (nsvci)
|
|
|
|
encoded_nsvci = osmo_htons(*nsvci);
|
|
|
|
else
|
|
|
|
encoded_nsvci = osmo_htons(nsvc->nsvci);
|
|
|
|
msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&encoded_nsvci);
|
2021-02-15 03:55:08 +00:00
|
|
|
break;
|
2020-07-12 11:45:50 +00:00
|
|
|
case NS_CAUSE_SEM_INCORR_PDU:
|
|
|
|
case NS_CAUSE_PDU_INCOMP_PSTATE:
|
|
|
|
case NS_CAUSE_PROTO_ERR_UNSPEC:
|
|
|
|
case NS_CAUSE_INVAL_ESSENT_IE:
|
|
|
|
case NS_CAUSE_MISSING_ESSENT_IE:
|
2021-02-15 03:55:08 +00:00
|
|
|
/* Section 9.2.7.2: Static conditions for NS PDU */
|
2021-02-15 03:51:14 +00:00
|
|
|
/* ensure the PDU doesn't exceed the MTU */
|
|
|
|
orig_len = msgb_l2len(orig_msg);
|
|
|
|
max_orig_len = msgb_length(msg) + TVLV_GROSS_LEN(orig_len);
|
|
|
|
if (max_orig_len > nsvc->bind->mtu)
|
|
|
|
orig_len -= max_orig_len - nsvc->bind->mtu;
|
|
|
|
msgb_tvlv_put(msg, NS_IE_PDU, orig_len, orig_msg->l2h);
|
2020-07-12 11:45:50 +00:00
|
|
|
break;
|
2021-02-15 03:55:08 +00:00
|
|
|
case NS_CAUSE_BVCI_UNKNOWN:
|
|
|
|
/* Section 9.2.7.3: Static conditions for BVCI */
|
|
|
|
msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci);
|
|
|
|
break;
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n", gprs_ns2_cause_str(cause));
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
2021-02-27 00:08:35 +00:00
|
|
|
/*! Encode + Transmit a SNS-ADD/SNS-CHANGE-WEIGHT as per Section 9.3.2/9.3.3.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
|
|
|
|
* \param[in] pdu The PDU type to send out
|
|
|
|
* \param[in] trans_id The transaction id
|
|
|
|
* \param[in] ip4_elems Array of IPv4 Elements
|
|
|
|
* \param[in] num_ip4_elems number of ip4_elems
|
|
|
|
* \param[in] ip6_elems Array of IPv6 Elements
|
|
|
|
* \param[in] num_ip6_elems number of ip6_elems
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
static int ns2_tx_sns_procedure(struct gprs_ns2_vc *nsvc,
|
|
|
|
enum ns_pdu_type pdu,
|
|
|
|
uint8_t trans_id,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4_elems,
|
|
|
|
unsigned int num_ip4_elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6_elems,
|
|
|
|
unsigned int num_ip6_elems)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsei;
|
|
|
|
|
|
|
|
if (!nsvc)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!ip4_elems && !ip6_elems)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
msg = ns2_msgb_alloc();
|
|
|
|
|
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!nsvc->nse->bss_sns_fi) {
|
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
|
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
nsh->pdu_type = pdu;
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
msgb_v_put(msg, trans_id);
|
|
|
|
|
|
|
|
/* List of IP4 Elements 10.3.2c */
|
|
|
|
if (ip4_elems) {
|
|
|
|
msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
|
|
|
|
(const uint8_t *)ip4_elems);
|
|
|
|
} else if (ip6_elems) {
|
|
|
|
/* List of IP6 elements 10.3.2d */
|
|
|
|
msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
|
|
|
|
(const uint8_t *)ip6_elems);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns_vc_tx(nsvc, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-ADD as per Section 9.3.2.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
|
|
|
|
* \param[in] trans_id The transaction id
|
|
|
|
* \param[in] ip4_elems Array of IPv4 Elements
|
|
|
|
* \param[in] num_ip4_elems number of ip4_elems
|
|
|
|
* \param[in] ip6_elems Array of IPv6 Elements
|
|
|
|
* \param[in] num_ip6_elems number of ip6_elems
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_add(struct gprs_ns2_vc *nsvc,
|
|
|
|
uint8_t trans_id,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4_elems,
|
|
|
|
unsigned int num_ip4_elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6_elems,
|
|
|
|
unsigned int num_ip6_elems)
|
|
|
|
{
|
|
|
|
return ns2_tx_sns_procedure(nsvc, SNS_PDUT_ADD, trans_id, ip4_elems, num_ip4_elems, ip6_elems, num_ip6_elems);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-CHANGE-WEIGHT as per Section 9.3.3.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
|
|
|
|
* \param[in] trans_id The transaction id
|
|
|
|
* \param[in] ip4_elems Array of IPv4 Elements
|
|
|
|
* \param[in] num_ip4_elems number of ip4_elems
|
|
|
|
* \param[in] ip6_elems Array of IPv6 Elements
|
|
|
|
* \param[in] num_ip6_elems number of ip6_elems
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_change_weight(struct gprs_ns2_vc *nsvc,
|
|
|
|
uint8_t trans_id,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4_elems,
|
|
|
|
unsigned int num_ip4_elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6_elems,
|
|
|
|
unsigned int num_ip6_elems)
|
|
|
|
{
|
|
|
|
return ns2_tx_sns_procedure(nsvc, SNS_PDUT_CHANGE_WEIGHT, trans_id, ip4_elems, num_ip4_elems, ip6_elems, num_ip6_elems);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-DEL as per Section 9.3.6.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
|
|
|
|
* \param[in] trans_id The transaction id
|
|
|
|
* \param[in] ip4_elems Array of IPv4 Elements
|
|
|
|
* \param[in] num_ip4_elems number of ip4_elems
|
|
|
|
* \param[in] ip6_elems Array of IPv6 Elements
|
|
|
|
* \param[in] num_ip6_elems number of ip6_elems
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_del(struct gprs_ns2_vc *nsvc,
|
|
|
|
uint8_t trans_id,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4_elems,
|
|
|
|
unsigned int num_ip4_elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6_elems,
|
|
|
|
unsigned int num_ip6_elems)
|
|
|
|
{
|
|
|
|
/* TODO: IP Address field */
|
|
|
|
return ns2_tx_sns_procedure(nsvc, SNS_PDUT_DELETE, trans_id, ip4_elems, num_ip4_elems, ip6_elems, num_ip6_elems);
|
|
|
|
}
|
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-ACK as per Section 9.3.1.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the ACK
|
|
|
|
* \param[in] trans_id Transaction ID which to acknowledge
|
|
|
|
* \param[in] cause Pointer to cause value (NULL if no cause to be sent)
|
|
|
|
* \param[in] ip4_elems Array of IPv4 Elements
|
|
|
|
* \param[in] num_ip4_elems number of ip4_elems
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_ack(struct gprs_ns2_vc *nsvc, uint8_t trans_id, uint8_t *cause,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4_elems,
|
|
|
|
unsigned int num_ip4_elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6_elems,
|
|
|
|
unsigned int num_ip6_elems)
|
|
|
|
{
|
2021-01-17 13:20:28 +00:00
|
|
|
struct msgb *msg;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsei;
|
|
|
|
|
|
|
|
if (!nsvc)
|
|
|
|
return -1;
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!nsvc->nse->bss_sns_fi) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
|
2020-07-12 11:45:50 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2021-03-04 06:59:16 +00:00
|
|
|
|
2020-07-12 11:45:50 +00:00
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
|
|
|
|
nsh->pdu_type = SNS_PDUT_ACK;
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
msgb_v_put(msg, trans_id);
|
|
|
|
if (cause)
|
|
|
|
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
|
|
|
|
if (ip4_elems) {
|
|
|
|
/* List of IP4 Elements 10.3.2c */
|
|
|
|
msgb_tvlv_put(msg, NS_IE_IPv4_LIST,
|
|
|
|
num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
|
|
|
|
(const uint8_t *)ip4_elems);
|
|
|
|
}
|
|
|
|
if (ip6_elems) {
|
|
|
|
/* List of IP6 elements 10.3.2d */
|
|
|
|
msgb_tvlv_put(msg, NS_IE_IPv6_LIST,
|
|
|
|
num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
|
|
|
|
(const uint8_t *)ip6_elems);
|
|
|
|
}
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
|
|
|
|
" (trans_id=%u, cause=%s, num_ip4=%u, num_ip6=%u)\n",
|
|
|
|
trans_id, cause ? gprs_ns2_cause_str(*cause) : "NULL", num_ip4_elems, num_ip6_elems);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-CONFIG as per Section 9.3.4.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG
|
|
|
|
* \param[in] end_flag Whether or not this is the last SNS-CONFIG
|
|
|
|
* \param[in] ip4_elems Array of IPv4 Elements
|
|
|
|
* \param[in] num_ip4_elems number of ip4_elems
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_config(struct gprs_ns2_vc *nsvc, bool end_flag,
|
|
|
|
const struct gprs_ns_ie_ip4_elem *ip4_elems,
|
|
|
|
unsigned int num_ip4_elems,
|
|
|
|
const struct gprs_ns_ie_ip6_elem *ip6_elems,
|
|
|
|
unsigned int num_ip6_elems)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsei;
|
|
|
|
|
|
|
|
if (!nsvc)
|
|
|
|
return -1;
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!nsvc->nse->bss_sns_fi) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
|
2020-07-12 11:45:50 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
|
|
|
|
nsh->pdu_type = SNS_PDUT_CONFIG;
|
|
|
|
|
|
|
|
msgb_v_put(msg, end_flag ? 0x01 : 0x00);
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
|
|
|
|
/* List of IP4 Elements 10.3.2c */
|
|
|
|
if (ip4_elems) {
|
|
|
|
msgb_tvlv_put(msg, NS_IE_IPv4_LIST, num_ip4_elems*sizeof(struct gprs_ns_ie_ip4_elem),
|
|
|
|
(const uint8_t *)ip4_elems);
|
|
|
|
} else if (ip6_elems) {
|
|
|
|
/* List of IP6 elements 10.3.2d */
|
|
|
|
msgb_tvlv_put(msg, NS_IE_IPv6_LIST, num_ip6_elems*sizeof(struct gprs_ns_ie_ip6_elem),
|
|
|
|
(const uint8_t *)ip6_elems);
|
|
|
|
}
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
|
|
|
|
" (end_flag=%u, num_ip4=%u, num_ip6=%u)\n",
|
|
|
|
end_flag, num_ip4_elems, num_ip6_elems);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-CONFIG-ACK as per Section 9.3.5.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-CONFIG-ACK
|
|
|
|
* \param[in] cause Pointer to cause value (NULL if no cause to be sent)
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_config_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
|
|
|
|
{
|
|
|
|
struct msgb *msg;
|
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsei;
|
|
|
|
|
|
|
|
if (!nsvc)
|
|
|
|
return -1;
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!nsvc->nse->bss_sns_fi) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
|
2020-07-12 11:45:50 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
|
|
|
|
nsh->pdu_type = SNS_PDUT_CONFIG_ACK;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
if (cause)
|
|
|
|
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_INFO, "Tx SNS-CONFIG-ACK (cause=%s)\n",
|
|
|
|
cause ? gprs_ns2_cause_str(*cause) : "NULL");
|
|
|
|
LOG_NS_TX_SIGNAL(nsvc, nsh->pdu_type);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*! Encode + transmit a SNS-SIZE as per Section 9.3.7.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-SIZE
|
|
|
|
* \param[in] reset_flag Whether or not to add a RESET flag
|
|
|
|
* \param[in] max_nr_nsvc Maximum number of NS-VCs
|
|
|
|
* \param[in] ip4_ep_nr Number of IPv4 endpoints (< 0 will omit the TLV)
|
|
|
|
* \param[in] ip6_ep_nr Number of IPv6 endpoints (< 0 will omit the TLV)
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_size(struct gprs_ns2_vc *nsvc, bool reset_flag, uint16_t max_nr_nsvc,
|
|
|
|
int ip4_ep_nr, int ip6_ep_nr)
|
|
|
|
{
|
2021-01-17 13:20:28 +00:00
|
|
|
struct msgb *msg;
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsei;
|
|
|
|
|
|
|
|
if (!nsvc)
|
|
|
|
return -1;
|
|
|
|
|
2021-01-25 15:09:23 +00:00
|
|
|
msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!nsvc->nse->bss_sns_fi) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
|
2020-07-12 11:45:50 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
|
|
|
|
nsh->pdu_type = SNS_PDUT_SIZE;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
msgb_tv_put(msg, NS_IE_RESET_FLAG, reset_flag ? 0x01 : 0x00);
|
|
|
|
msgb_tv16_put(msg, NS_IE_MAX_NR_NSVC, max_nr_nsvc);
|
|
|
|
if (ip4_ep_nr >= 0)
|
|
|
|
msgb_tv16_put(msg, NS_IE_IPv4_EP_NR, ip4_ep_nr);
|
|
|
|
if (ip6_ep_nr >= 0)
|
|
|
|
msgb_tv16_put(msg, NS_IE_IPv6_EP_NR, ip6_ep_nr);
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO,
|
2021-07-06 09:34:04 +00:00
|
|
|
" (reset=%u, max_nr_nsvc=%u, num_ip4=%d, num_ip6=%d)\n",
|
2021-01-18 16:55:35 +00:00
|
|
|
reset_flag, max_nr_nsvc, ip4_ep_nr, ip6_ep_nr);
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*! Encode + Transmit a SNS-SIZE-ACK as per Section 9.3.8.
|
|
|
|
* \param[in] nsvc NS-VC through which to transmit the SNS-SIZE-ACK
|
|
|
|
* \param[in] cause Pointer to cause value (NULL if no cause to be sent)
|
|
|
|
* \returns 0 on success; negative in case of error */
|
|
|
|
int ns2_tx_sns_size_ack(struct gprs_ns2_vc *nsvc, uint8_t *cause)
|
|
|
|
{
|
2021-01-25 15:09:23 +00:00
|
|
|
struct msgb *msg = ns2_msgb_alloc();
|
2020-07-12 11:45:50 +00:00
|
|
|
struct gprs_ns_hdr *nsh;
|
|
|
|
uint16_t nsei;
|
|
|
|
|
2020-12-02 17:59:44 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSE, nsvc->nse);
|
2020-07-12 11:45:50 +00:00
|
|
|
log_set_context(LOG_CTX_GB_NSVC, nsvc);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (!nsvc->nse->bss_sns_fi) {
|
2021-01-20 13:54:14 +00:00
|
|
|
LOGNSVC(nsvc, LOGL_ERROR, "Cannot transmit SNS on NSVC without SNS active\n");
|
2020-07-12 11:45:50 +00:00
|
|
|
msgb_free(msg);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsei = osmo_htons(nsvc->nse->nsei);
|
|
|
|
|
|
|
|
msg->l2h = msgb_put(msg, sizeof(*nsh));
|
|
|
|
nsh = (struct gprs_ns_hdr *) msg->l2h;
|
|
|
|
|
|
|
|
nsh->pdu_type = SNS_PDUT_SIZE_ACK;
|
|
|
|
|
|
|
|
msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei);
|
|
|
|
if (cause)
|
|
|
|
msgb_tvlv_put(msg, NS_IE_CAUSE, 1, cause);
|
|
|
|
|
2021-01-18 16:55:35 +00:00
|
|
|
LOG_NS_SIGNAL(nsvc, "Tx", nsh->pdu_type, LOGL_INFO, " cause=%s\n",
|
|
|
|
cause ? gprs_ns2_cause_str(*cause) : "NULL");
|
2021-01-30 20:49:28 +00:00
|
|
|
return ns_vc_tx(nsvc, msg);
|
2020-07-12 11:45:50 +00:00
|
|
|
}
|