/* 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) */
/* (C) 2009-2012 by Harald Welte <laforge@gnumonks.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/*! \addtogroup libgb
* @{
*/
/*! \file gprs_ns.c */
/*!
* 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)
*
* Some introduction into NS: NS is used typically on top of frame relay,
* but in the ip.access world it is encapsulated in UDP packets. It serves
* as an intermediate shim betwen BSSGP and the underlying medium. It doesn't
* do much, apart from providing congestion notification and status indication.
*
* Terms:
* NS Network Service
* NSVC NS Virtual Connection
* NSEI NS Entity Identifier
* NSVL NS Virtual Link
* NSVLI NS Virtual Link Identifier
* BVC BSSGP Virtual Connection
* BVCI BSSGP Virtual Connection Identifier
* NSVCG NS Virtual Connection Goup
* Blocked NS-VC cannot be used for user traffic
* Alive Ability of a NS-VC to provide communication
*
* There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will
* therefore identify the BSSGP virtual connection by a BVCI passed down to NS.
* NS then has to firgure out which NSVC's are responsible for this BVCI.
* Those mappings are administratively configured.
*/
/* This implementation has the following limitations:
* o Only one NS-VC for each NSE: No load-sharing function
* o NSVCI 65535 and 65534 are reserved for internal use
* o Only UDP is supported as of now, no frame relay support
* o The IP Sub-Network-Service (SNS) as specified in 48.016 is not implemented
* o There are no BLOCK and UNBLOCK timers (yet?)
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/signal.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gprs/gprs_ns_frgre.h>
#include "common_vty.h"
static const struct tlv_definition ns_att_tlvdef = {
.def = {
[NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 },
[NS_IE_VCI] = { TLV_TYPE_TvLV, 0 },
[NS_IE_PDU] = { TLV_TYPE_TvLV, 0 },
[NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 },
[NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 },
},
};
enum ns_ctr {
NS_CTR_PKTS_IN,
NS_CTR_PKTS_OUT,
NS_CTR_BYTES_IN,
NS_CTR_BYTES_OUT,
NS_CTR_BLOCKED,
NS_CTR_DEAD,
NS_CTR_REPLACED,
NS_CTR_NSEI_CHG,
NS_CTR_INV_VCI,
NS_CTR_INV_NSEI,
};
static const struct rate_ctr_desc nsvc_ctr_description[] = {
{ "packets.in", "Packets at NS Level ( In)" },
{ "packets.out","Packets at NS Level (Out)" },
{ "bytes.in", "Bytes at NS Level ( In)" },
{ "bytes.out", "Bytes at NS Level (Out)" },
{ "blocked", "NS-VC Block count " },
{ "dead", "NS-VC gone dead count " },
{ "replaced", "NS-VC replaced other count" },
{ "nsei-chg", "NS-VC changed NSEI count " },
{ "inv-nsvci", "NS-VCI was invalid count " },
{ "inv-nsei", "NSEI was invalid count " },
};
static const struct rate_ctr_group_desc nsvc_ctrg_desc = {
.group_name_prefix = "ns.nsvc",
.group_description = "NSVC Peer Statistics",
.num_ctr = ARRAY_SIZE(nsvc_ctr_description),
.ctr_desc = nsvc_ctr_description,
};
#define CHECK_TX_RC(rc, nsvc) \
if (rc < 0) \
LOGP(DNS, LOGL_ERROR, "TX failed (%d) to peer %s\n", \
rc, gprs_ns_ll_str(nsvc));
/*! \brief Lookup struct gprs_nsvc based on NSVCI
* \param[in] nsi NS instance in which to search
* \param[in] nsvci NSVCI to be searched
* \returns gprs_nsvc of respective NSVCI
*/
struct gprs_nsvc *gprs_nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci)
{
struct gprs_nsvc *nsvc;
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
if (nsvc->nsvci == nsvci)
return nsvc;
}
return NULL;
}
/*! \brief Lookup struct gprs_nsvc based on NSEI
* \param[in] nsi NS instance in which to search
* \param[in] nsei NSEI to be searched
* \returns first gprs_nsvc of respective NSEI
*/
struct gprs_nsvc *gprs_nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei)
{
struct gprs_nsvc *nsvc;
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
if (nsvc->nsei == nsei)
return nsvc;
}
return NULL;
}
static struct gprs_nsvc *gprs_active_nsvc_by_nsei(struct gprs_ns_inst *nsi,
uint16_t nsei)
{
struct gprs_nsvc *nsvc;
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
if (nsvc->nsei == nsei) {
if (!(nsvc->state & NSE_S_BLOCKED) &&
nsvc->state & NSE_S_ALIVE)
return nsvc;
}
}
return NULL;
}
/* Lookup struct gprs_nsvc based on remote peer socket addr */
static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi,
struct sockaddr_in *sin)
{
struct gprs_nsvc *nsvc;
llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) {
if (nsvc->ip.bts_addr.sin_addr.s_addr ==
sin->sin_addr.s_addr &&
nsvc->ip.bts_addr.sin_port == sin->sin_port)
return nsvc;
}
return NULL;
}
static void gprs_ns_timer_cb(void *data);
struct gprs_nsvc *gprs_nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci)
{
struct gprs_nsvc *nsvc;
LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci);
nsvc = talloc_zero(nsi, struct gprs_nsvc);
nsvc->nsvci = nsvci;
nsvc->nsvci_is_valid = 1;
/* before RESET procedure: BLOCKED and DEAD */
nsvc->state = NSE_S_BLOCKED;
nsvc->nsi = nsi;
nsvc->timer.cb = gprs_ns_timer_cb;
nsvc->timer.data = nsvc;
nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci);
llist_add(&nsvc->list, &nsi->gprs_nsvcs);
return nsvc;
}
/*! \brief Delete given NS-VC
* \param[in] nsvc gprs_nsvc to be deleted
*/
void gprs_nsvc_delete(struct gprs_nsvc *nsvc)
{
if (osmo_timer_pending(&nsvc->timer))
osmo_timer_del(&nsvc->timer);
llist_del(&nsvc->list);
rate_ctr_group_free(nsvc->ctrg);
talloc_free(nsvc);
}
static void ns_osmo_signal_dispatch(struct gprs_nsvc *nsvc, unsigned int signal,
uint8_t cause)
{
struct ns_signal_data nssd = {0};
nssd.nsvc = nsvc;
nssd.cause = cause;
osmo_signal_dispatch(SS_L_NS, signal, &nssd);
}
static void ns_osmo_signal_dispatch_mismatch(struct gprs_nsvc *nsvc,
struct msgb *msg,
uint8_t pdu_type, uint8_t ie_type)
{
struct ns_signal_data nssd = {0};
nssd.nsvc = nsvc;
nssd.pdu_type = pdu_type;
nssd.ie_type = ie_type;
nssd.msg = msg;
osmo_signal_dispatch(SS_L_NS, S_NS_MISMATCH, &nssd);
}
static void ns_osmo_signal_dispatch_replaced(struct gprs_nsvc *nsvc, struct gprs_nsvc *old_nsvc)
{
struct ns_signal_data nssd = {0};
nssd.nsvc = nsvc;
nssd.old_nsvc = old_nsvc;
osmo_signal_dispatch(SS_L_NS, S_NS_REPLACED, &nssd);
}
/* Section 10.3.2, Table 13 */
static const struct value_string ns_cause_str[] = {
{ NS_CAUSE_TRANSIT_FAIL, "Transit network failure" },
{ NS_CAUSE_OM_INTERVENTION, "O&M intervention" },
{ NS_CAUSE_EQUIP_FAIL, "Equipment failure" },
{ NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" },
{ NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" },
{ NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" },
{ NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" },
{ NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" },
{ NS_CAUSE_PROTO_E
|