2010-03-14 07:45:01 +00:00
|
|
|
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
|
|
|
|
|
2012-06-16 08:09:52 +00:00
|
|
|
/* (C) 2009-2012 by Harald Welte <laforge@gnumonks.org>
|
2010-03-14 07:45:01 +00:00
|
|
|
*
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
2011-01-01 14:25:50 +00:00
|
|
|
* 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
|
2010-03-14 07:45:01 +00:00
|
|
|
* (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
|
2011-01-01 14:25:50 +00:00
|
|
|
* GNU Affero General Public License for more details.
|
2010-03-14 07:45:01 +00:00
|
|
|
*
|
2011-01-01 14:25:50 +00:00
|
|
|
* 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/>.
|
2010-03-14 07:45:01 +00:00
|
|
|
*
|
2010-05-17 21:41:43 +00:00
|
|
|
* TODO:
|
|
|
|
* o properly count incoming BVC-RESET packets in counter group
|
|
|
|
* o set log context as early as possible for outgoing packets
|
2010-03-14 07:45:01 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2010-05-02 09:26:34 +00:00
|
|
|
#include <stdint.h>
|
2010-03-14 07:45:01 +00:00
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
|
2011-03-22 15:47:59 +00:00
|
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/gsm/tlv.h>
|
|
|
|
#include <osmocom/core/talloc.h>
|
|
|
|
#include <osmocom/core/rate_ctr.h>
|
2010-05-02 07:23:16 +00:00
|
|
|
|
2012-06-16 06:59:56 +00:00
|
|
|
#include <osmocom/gprs/gprs_bssgp.h>
|
|
|
|
#include <osmocom/gprs/gprs_ns.h>
|
|
|
|
|
2012-06-16 09:45:59 +00:00
|
|
|
#include "common_vty.h"
|
|
|
|
|
2010-05-02 07:23:16 +00:00
|
|
|
void *bssgp_tall_ctx = NULL;
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
static const struct rate_ctr_desc bssgp_ctr_description[] = {
|
2010-05-17 21:30:01 +00:00
|
|
|
{ "packets.in", "Packets at BSSGP Level ( In)" },
|
|
|
|
{ "packets.out","Packets at BSSGP Level (Out)" },
|
|
|
|
{ "bytes.in", "Bytes at BSSGP Level ( In)" },
|
|
|
|
{ "bytes.out", "Bytes at BSSGP Level (Out)" },
|
2010-05-13 19:26:28 +00:00
|
|
|
{ "blocked", "BVC Blocking count" },
|
|
|
|
{ "discarded", "BVC LLC Discarded count" },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
|
|
|
|
.group_name_prefix = "bssgp.bss_ctx",
|
|
|
|
.group_description = "BSSGP Peer Statistics",
|
|
|
|
.num_ctr = ARRAY_SIZE(bssgp_ctr_description),
|
|
|
|
.ctr_desc = bssgp_ctr_description,
|
|
|
|
};
|
|
|
|
|
2010-05-17 21:02:42 +00:00
|
|
|
LLIST_HEAD(bssgp_bvc_ctxts);
|
2010-05-02 07:23:16 +00:00
|
|
|
|
2012-09-06 19:57:11 +00:00
|
|
|
static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg,
|
|
|
|
uint32_t llc_pdu_len, void *priv);
|
|
|
|
|
2010-05-02 07:23:16 +00:00
|
|
|
/* Find a BTS Context based on parsed RA ID and Cell ID */
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
|
2010-05-02 07:23:16 +00:00
|
|
|
{
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *bctx;
|
2010-05-02 07:23:16 +00:00
|
|
|
|
2010-05-17 21:02:42 +00:00
|
|
|
llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
|
2010-05-02 07:23:16 +00:00
|
|
|
if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) &&
|
|
|
|
bctx->cell_id == cid)
|
|
|
|
return bctx;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find a BTS context based on BVCI+NSEI tuple */
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
|
2010-05-02 07:23:16 +00:00
|
|
|
{
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *bctx;
|
2010-05-02 07:23:16 +00:00
|
|
|
|
2010-05-17 21:02:42 +00:00
|
|
|
llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
|
2010-05-02 07:23:16 +00:00
|
|
|
if (bctx->nsei == nsei && bctx->bvci == bvci)
|
|
|
|
return bctx;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
|
2010-05-02 07:23:16 +00:00
|
|
|
{
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *ctx;
|
2010-05-02 07:23:16 +00:00
|
|
|
|
2010-05-17 20:59:29 +00:00
|
|
|
ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx);
|
2010-05-02 07:23:16 +00:00
|
|
|
if (!ctx)
|
|
|
|
return NULL;
|
|
|
|
ctx->bvci = bvci;
|
|
|
|
ctx->nsei = nsei;
|
2010-05-13 19:26:28 +00:00
|
|
|
/* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */
|
|
|
|
ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci);
|
2012-09-07 09:29:32 +00:00
|
|
|
ctx->fc = talloc_zero(ctx, struct bssgp_flow_control);
|
|
|
|
/* cofigure for 2Mbit, 30 packets in queue */
|
|
|
|
bssgp_fc_init(ctx->fc, 100000, 2*1024*1024/8, 30, &_bssgp_tx_dl_ud);
|
2010-05-13 19:26:28 +00:00
|
|
|
|
2010-05-17 21:02:42 +00:00
|
|
|
llist_add(&ctx->list, &bssgp_bvc_ctxts);
|
2010-05-02 07:23:16 +00:00
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
2010-03-14 07:45:01 +00:00
|
|
|
/* Chapter 10.4.5: Flow Control BVC ACK */
|
2010-05-02 09:26:34 +00:00
|
|
|
static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg = bssgp_msgb_alloc();
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
|
|
|
|
|
2010-04-30 17:54:29 +00:00
|
|
|
msgb_nsei(msg) = nsei;
|
|
|
|
msgb_bvci(msg) = ns_bvci;
|
|
|
|
|
2010-03-14 07:45:01 +00:00
|
|
|
bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
|
|
|
|
|
2010-04-30 17:54:29 +00:00
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
2010-03-14 07:45:01 +00:00
|
|
|
}
|
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
/* 10.3.7 SUSPEND-ACK PDU */
|
|
|
|
int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli,
|
|
|
|
const struct gprs_ra_id *ra_id, uint8_t suspend_ref)
|
|
|
|
{
|
|
|
|
struct msgb *msg = bssgp_msgb_alloc();
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
|
|
|
|
uint32_t _tlli;
|
|
|
|
uint8_t ra[6];
|
|
|
|
|
|
|
|
msgb_nsei(msg) = nsei;
|
|
|
|
msgb_bvci(msg) = 0; /* Signalling */
|
|
|
|
bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK;
|
|
|
|
|
|
|
|
_tlli = htonl(tlli);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
|
|
|
|
gsm48_construct_ra(ra, ra_id);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
|
|
|
|
|
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 10.3.8 SUSPEND-NACK PDU */
|
|
|
|
int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli,
|
2010-12-12 11:45:08 +00:00
|
|
|
const struct gprs_ra_id *ra_id,
|
2010-05-30 20:00:53 +00:00
|
|
|
uint8_t *cause)
|
|
|
|
{
|
|
|
|
struct msgb *msg = bssgp_msgb_alloc();
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
|
|
|
|
uint32_t _tlli;
|
2010-12-12 11:45:08 +00:00
|
|
|
uint8_t ra[6];
|
2010-05-30 20:00:53 +00:00
|
|
|
|
|
|
|
msgb_nsei(msg) = nsei;
|
|
|
|
msgb_bvci(msg) = 0; /* Signalling */
|
|
|
|
bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
|
|
|
|
|
|
|
|
_tlli = htonl(tlli);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
|
2010-12-12 11:45:08 +00:00
|
|
|
gsm48_construct_ra(ra, ra_id);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
|
2010-05-30 20:00:53 +00:00
|
|
|
if (cause)
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
|
|
|
|
|
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 10.3.10 RESUME-ACK PDU */
|
|
|
|
int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
|
|
|
|
const struct gprs_ra_id *ra_id)
|
|
|
|
{
|
|
|
|
struct msgb *msg = bssgp_msgb_alloc();
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
|
|
|
|
uint32_t _tlli;
|
|
|
|
uint8_t ra[6];
|
|
|
|
|
|
|
|
msgb_nsei(msg) = nsei;
|
|
|
|
msgb_bvci(msg) = 0; /* Signalling */
|
|
|
|
bgph->pdu_type = BSSGP_PDUT_RESUME_ACK;
|
|
|
|
|
|
|
|
_tlli = htonl(tlli);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
|
|
|
|
gsm48_construct_ra(ra, ra_id);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
|
|
|
|
|
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 10.3.11 RESUME-NACK PDU */
|
|
|
|
int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
|
|
|
|
const struct gprs_ra_id *ra_id, uint8_t *cause)
|
|
|
|
{
|
|
|
|
struct msgb *msg = bssgp_msgb_alloc();
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
|
|
|
|
uint32_t _tlli;
|
|
|
|
uint8_t ra[6];
|
|
|
|
|
|
|
|
msgb_nsei(msg) = nsei;
|
|
|
|
msgb_bvci(msg) = 0; /* Signalling */
|
|
|
|
bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
|
|
|
|
|
|
|
|
_tlli = htonl(tlli);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
|
|
|
|
gsm48_construct_ra(ra, ra_id);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
|
|
|
|
if (cause)
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
|
|
|
|
|
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
|
|
|
}
|
|
|
|
|
2010-05-02 09:54:55 +00:00
|
|
|
uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
|
2010-05-02 07:23:16 +00:00
|
|
|
{
|
|
|
|
/* 6 octets RAC */
|
|
|
|
gsm48_parse_ra(raid, buf);
|
|
|
|
/* 2 octets CID */
|
2010-05-02 09:54:55 +00:00
|
|
|
return ntohs(*(uint16_t *) (buf+6));
|
2010-05-02 07:23:16 +00:00
|
|
|
}
|
|
|
|
|
2011-11-24 20:32:07 +00:00
|
|
|
int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
|
|
|
|
uint16_t cid)
|
|
|
|
{
|
|
|
|
uint16_t *out_cid = (uint16_t *) (buf + 6);
|
|
|
|
/* 6 octets RAC */
|
|
|
|
gsm48_construct_ra(buf, raid);
|
|
|
|
/* 2 octets CID */
|
|
|
|
*out_cid = htons(cid);
|
|
|
|
|
|
|
|
return 8;
|
|
|
|
}
|
|
|
|
|
2010-05-01 14:48:27 +00:00
|
|
|
/* Chapter 8.4 BVC-Reset Procedure */
|
|
|
|
static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
|
|
|
|
uint16_t ns_bvci)
|
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim nmp;
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *bctx;
|
2010-05-02 07:23:16 +00:00
|
|
|
uint16_t nsei = msgb_nsei(msg);
|
|
|
|
uint16_t bvci;
|
2010-05-01 14:48:27 +00:00
|
|
|
|
2010-05-02 09:26:34 +00:00
|
|
|
bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
|
2010-05-01 14:48:27 +00:00
|
|
|
bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
|
|
|
|
|
2010-05-02 07:23:16 +00:00
|
|
|
/* look-up or create the BTS context for this BVC */
|
|
|
|
bctx = btsctx_by_bvci_nsei(bvci, nsei);
|
|
|
|
if (!bctx)
|
|
|
|
bctx = btsctx_alloc(bvci, nsei);
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
/* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
|
|
|
|
bctx->state &= ~BVC_S_BLOCKED;
|
|
|
|
|
2010-05-01 14:48:27 +00:00
|
|
|
/* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
|
|
|
|
* informs us about its RAC + Cell ID, so we can create a mapping */
|
2010-05-02 07:23:16 +00:00
|
|
|
if (bvci != 0 && bvci != 1) {
|
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
|
2011-08-19 14:45:19 +00:00
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
|
2010-05-02 07:23:16 +00:00
|
|
|
"missing mandatory IE\n", bvci);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* actually extract RAC / CID */
|
2010-05-02 09:54:55 +00:00
|
|
|
bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
|
|
|
|
TLVP_VAL(tp, BSSGP_IE_CELL_ID));
|
2010-05-11 03:54:22 +00:00
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
|
2010-05-02 07:23:16 +00:00
|
|
|
bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
|
|
|
|
bctx->ra_id.rac, bctx->cell_id, bvci);
|
|
|
|
}
|
2010-05-01 14:48:27 +00:00
|
|
|
|
2012-06-17 04:16:31 +00:00
|
|
|
/* Send NM_BVC_RESET.ind to NM */
|
|
|
|
memset(&nmp, 0, sizeof(nmp));
|
|
|
|
nmp.nsei = nsei;
|
|
|
|
nmp.bvci = bvci;
|
|
|
|
nmp.tp = tp;
|
|
|
|
nmp.ra_id = &bctx->ra_id;
|
|
|
|
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_RESET,
|
|
|
|
PRIM_OP_INDICATION, msg);
|
|
|
|
bssgp_prim_cb(&nmp.oph, NULL);
|
|
|
|
|
2010-05-02 07:23:16 +00:00
|
|
|
/* Acknowledge the RESET to the BTS */
|
2012-09-07 10:03:40 +00:00
|
|
|
bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
|
|
|
|
nsei, bvci, ns_bvci);
|
2010-05-01 14:48:27 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
|
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim nmp;
|
2010-05-13 19:26:28 +00:00
|
|
|
uint16_t bvci;
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *ptp_ctx;
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
|
2010-05-18 09:57:08 +00:00
|
|
|
if (bvci == BVCI_SIGNALLING) {
|
2010-05-13 19:45:23 +00:00
|
|
|
/* 8.3.2: Signalling BVC shall never be blocked */
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
|
|
|
|
"received block for signalling BVC!?!\n",
|
|
|
|
msgb_nsei(msg), msgb_bvci(msg));
|
|
|
|
return 0;
|
|
|
|
}
|
2010-05-13 19:26:28 +00:00
|
|
|
|
2011-08-19 14:45:19 +00:00
|
|
|
LOGP(DBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci);
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
|
|
|
|
if (!ptp_ctx)
|
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
|
|
|
|
|
|
|
|
ptp_ctx->state |= BVC_S_BLOCKED;
|
|
|
|
rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
|
|
|
|
|
2012-06-17 04:16:31 +00:00
|
|
|
/* Send NM_BVC_BLOCK.ind to NM */
|
|
|
|
memset(&nmp, 0, sizeof(nmp));
|
|
|
|
nmp.nsei = msgb_nsei(msg);
|
|
|
|
nmp.bvci = bvci;
|
|
|
|
nmp.tp = tp;
|
|
|
|
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_BLOCK,
|
|
|
|
PRIM_OP_INDICATION, msg);
|
|
|
|
bssgp_prim_cb(&nmp.oph, NULL);
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
/* We always acknowledge the BLOCKing */
|
|
|
|
return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
|
|
|
|
bvci, msgb_bvci(msg));
|
|
|
|
};
|
|
|
|
|
|
|
|
static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
|
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim nmp;
|
2010-05-13 19:26:28 +00:00
|
|
|
uint16_t bvci;
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *ptp_ctx;
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
|
2010-05-18 09:57:08 +00:00
|
|
|
if (bvci == BVCI_SIGNALLING) {
|
2010-05-13 19:45:23 +00:00
|
|
|
/* 8.3.2: Signalling BVC shall never be blocked */
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
|
|
|
|
"received unblock for signalling BVC!?!\n",
|
|
|
|
msgb_nsei(msg), msgb_bvci(msg));
|
|
|
|
return 0;
|
|
|
|
}
|
2010-05-13 19:26:28 +00:00
|
|
|
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
|
|
|
|
if (!ptp_ctx)
|
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
|
|
|
|
|
|
|
|
ptp_ctx->state &= ~BVC_S_BLOCKED;
|
|
|
|
|
2012-06-17 04:16:31 +00:00
|
|
|
/* Send NM_BVC_UNBLOCK.ind to NM */
|
|
|
|
memset(&nmp, 0, sizeof(nmp));
|
|
|
|
nmp.nsei = msgb_nsei(msg);
|
|
|
|
nmp.bvci = bvci;
|
|
|
|
nmp.tp = tp;
|
|
|
|
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_UNBLOCK,
|
|
|
|
PRIM_OP_INDICATION, msg);
|
|
|
|
bssgp_prim_cb(&nmp.oph, NULL);
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
/* We always acknowledge the unBLOCKing */
|
|
|
|
return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
|
|
|
|
bvci, msgb_bvci(msg));
|
|
|
|
};
|
|
|
|
|
2010-03-14 07:45:01 +00:00
|
|
|
/* Uplink unit-data */
|
2010-05-13 19:26:28 +00:00
|
|
|
static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *ctx)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim gbp;
|
2010-05-02 07:50:42 +00:00
|
|
|
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-02 07:23:16 +00:00
|
|
|
/* extract TLLI and parse TLV IEs */
|
2010-04-30 14:33:12 +00:00
|
|
|
msgb_tlli(msg) = ntohl(budh->tlli);
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2011-08-19 14:45:19 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
|
2010-05-31 16:07:17 +00:00
|
|
|
|
2010-03-14 07:45:01 +00:00
|
|
|
/* Cell ID and LLC_PDU are the only mandatory IE */
|
2010-05-13 19:26:28 +00:00
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
|
2010-05-31 16:07:17 +00:00
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
|
|
|
|
"missing mandatory IE\n", msgb_tlli(msg));
|
2010-05-13 19:26:28 +00:00
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-05-02 09:19:37 +00:00
|
|
|
|
2010-05-02 09:54:55 +00:00
|
|
|
/* store pointer to LLC header and CELL ID in msgb->cb */
|
2010-05-23 13:11:19 +00:00
|
|
|
msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
|
|
|
|
msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2012-06-17 04:16:31 +00:00
|
|
|
/* Send BSSGP_UL_UD.ind to NM */
|
|
|
|
memset(&gbp, 0, sizeof(gbp));
|
|
|
|
gbp.nsei = ctx->nsei;
|
|
|
|
gbp.bvci = ctx->bvci;
|
|
|
|
gbp.tlli = msgb_tlli(msg);
|
|
|
|
gbp.tp = tp;
|
|
|
|
osmo_prim_init(&gbp.oph, SAP_BSSGP_LL, PRIM_BSSGP_UL_UD,
|
|
|
|
PRIM_OP_INDICATION, msg);
|
|
|
|
return bssgp_prim_cb(&gbp.oph, NULL);
|
2010-03-14 07:45:01 +00:00
|
|
|
}
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *ctx)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim gbp;
|
2010-05-30 20:00:53 +00:00
|
|
|
struct gprs_ra_id raid;
|
|
|
|
uint32_t tlli;
|
2010-06-09 09:22:47 +00:00
|
|
|
int rc;
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
|
2010-05-31 16:07:17 +00:00
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
|
|
|
|
"missing mandatory IE\n", ctx->bvci);
|
2010-05-13 19:26:28 +00:00
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
|
2010-05-31 16:07:17 +00:00
|
|
|
|
2010-05-31 18:18:35 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
|
2010-05-31 16:07:17 +00:00
|
|
|
ctx->bvci, tlli);
|
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
|
|
|
|
|
2010-06-09 09:22:47 +00:00
|
|
|
/* Inform GMM about the SUSPEND request */
|
2012-06-17 04:16:31 +00:00
|
|
|
memset(&gbp, 0, sizeof(gbp));
|
|
|
|
gbp.nsei = msgb_nsei(msg);
|
|
|
|
gbp.bvci = ctx->bvci;
|
|
|
|
gbp.tlli = tlli;
|
|
|
|
gbp.ra_id = &raid;
|
|
|
|
osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_SUSPEND,
|
|
|
|
PRIM_OP_REQUEST, msg);
|
|
|
|
|
|
|
|
rc = bssgp_prim_cb(&gbp.oph, NULL);
|
2010-06-09 09:22:47 +00:00
|
|
|
if (rc < 0)
|
2010-12-12 11:45:08 +00:00
|
|
|
return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL);
|
2010-06-09 09:22:47 +00:00
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
|
|
|
|
|
2010-05-23 13:12:15 +00:00
|
|
|
return 0;
|
2010-03-14 07:45:01 +00:00
|
|
|
}
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *ctx)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim gbp;
|
2010-05-30 20:00:53 +00:00
|
|
|
struct gprs_ra_id raid;
|
|
|
|
uint32_t tlli;
|
2010-06-09 09:22:47 +00:00
|
|
|
uint8_t suspend_ref;
|
|
|
|
int rc;
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
|
2010-05-31 16:07:17 +00:00
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
|
|
|
|
"missing mandatory IE\n", ctx->bvci);
|
2010-05-13 19:26:28 +00:00
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
|
2010-06-09 09:22:47 +00:00
|
|
|
suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
|
2010-05-31 16:07:17 +00:00
|
|
|
|
2011-08-19 14:45:19 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ctx->bvci, tlli);
|
2010-05-31 16:07:17 +00:00
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
|
|
|
|
|
2010-06-09 09:22:47 +00:00
|
|
|
/* Inform GMM about the RESUME request */
|
2012-06-17 04:16:31 +00:00
|
|
|
memset(&gbp, 0, sizeof(gbp));
|
|
|
|
gbp.nsei = msgb_nsei(msg);
|
|
|
|
gbp.bvci = ctx->bvci;
|
|
|
|
gbp.tlli = tlli;
|
|
|
|
gbp.ra_id = &raid;
|
|
|
|
gbp.u.resume.suspend_ref = suspend_ref;
|
|
|
|
osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_RESUME,
|
|
|
|
PRIM_OP_REQUEST, msg);
|
|
|
|
|
|
|
|
rc = bssgp_prim_cb(&gbp.oph, NULL);
|
2010-06-09 09:22:47 +00:00
|
|
|
if (rc < 0)
|
|
|
|
return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid,
|
|
|
|
NULL);
|
|
|
|
|
2010-05-30 20:00:53 +00:00
|
|
|
bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
|
2010-05-23 13:12:15 +00:00
|
|
|
return 0;
|
2010-03-14 07:45:01 +00:00
|
|
|
}
|
|
|
|
|
2010-05-31 16:07:17 +00:00
|
|
|
|
|
|
|
static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
|
|
|
|
struct bssgp_bvc_ctx *ctx)
|
|
|
|
{
|
2012-06-17 04:16:31 +00:00
|
|
|
struct osmo_bssgp_prim nmp;
|
2010-07-23 19:59:29 +00:00
|
|
|
uint32_t tlli = 0;
|
2010-05-31 16:07:17 +00:00
|
|
|
|
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
|
|
|
|
"missing mandatory IE\n", ctx->bvci);
|
|
|
|
}
|
|
|
|
|
2010-07-23 19:59:29 +00:00
|
|
|
if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
|
|
|
|
tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
|
2010-05-31 16:07:17 +00:00
|
|
|
|
2011-08-19 14:45:19 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
|
2010-05-31 16:07:17 +00:00
|
|
|
ctx->bvci, tlli);
|
|
|
|
|
|
|
|
rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
|
|
|
|
|
2012-06-17 04:16:31 +00:00
|
|
|
/* send NM_LLC_DISCARDED to NM */
|
|
|
|
memset(&nmp, 0, sizeof(nmp));
|
|
|
|
nmp.nsei = msgb_nsei(msg);
|
|
|
|
nmp.bvci = ctx->bvci;
|
|
|
|
nmp.tlli = tlli;
|
|
|
|
nmp.tp = tp;
|
|
|
|
osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_LLC_DISCARDED,
|
|
|
|
PRIM_OP_INDICATION, msg);
|
|
|
|
|
|
|
|
return bssgp_prim_cb(&nmp.oph, NULL);
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
|
|
|
|
2012-09-06 19:57:11 +00:00
|
|
|
/* One element (msgb) in a BSSGP Flow Control queue */
|
|
|
|
struct bssgp_fc_queue_element {
|
|
|
|
/* linked list of queue elements */
|
|
|
|
struct llist_head list;
|
|
|
|
/* The message that we have enqueued */
|
|
|
|
struct msgb *msg;
|
|
|
|
/* Length of the LLC PDU part of the contained message */
|
|
|
|
uint32_t llc_pdu_len;
|
|
|
|
/* private pointer passed to the flow control out_cb function */
|
|
|
|
void *priv;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int fc_queue_timer_cfg(struct bssgp_flow_control *fc);
|
|
|
|
static int bssgp_fc_needs_queueing(struct bssgp_flow_control *fc, uint32_t pdu_len);
|
|
|
|
|
|
|
|
static void fc_timer_cb(void *data)
|
|
|
|
{
|
|
|
|
struct bssgp_flow_control *fc = data;
|
|
|
|
struct bssgp_fc_queue_element *fcqe;
|
|
|
|
struct timeval time_now;
|
|
|
|
|
|
|
|
/* if the queue is empty, we return without sending something
|
|
|
|
* and without re-starting the timer */
|
|
|
|
if (llist_empty(&fc->queue))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* get the first entry from the queue */
|
|
|
|
fcqe = llist_entry(fc->queue.next, struct bssgp_fc_queue_element,
|
|
|
|
list);
|
|
|
|
|
|
|
|
if (bssgp_fc_needs_queueing(fc, fcqe->llc_pdu_len)) {
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP-FC: fc_timer_cb() but still "
|
|
|
|
"not able to send PDU of %u bytes\n", fcqe->llc_pdu_len);
|
|
|
|
/* make sure we re-start the timer */
|
|
|
|
fc_queue_timer_cfg(fc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove from the queue */
|
|
|
|
llist_del(&fcqe->list);
|
|
|
|
|
|
|
|
fc->queue_depth--;
|
|
|
|
|
|
|
|
/* record the time we transmitted this PDU */
|
|
|
|
gettimeofday(&time_now, NULL);
|
|
|
|
fc->time_last_pdu = time_now;
|
|
|
|
|
|
|
|
/* call the output callback for this FC instance */
|
|
|
|
fc->out_cb(fcqe->priv, fcqe->msg, fcqe->llc_pdu_len, NULL);
|
|
|
|
|
|
|
|
/* we expect that out_cb will in the end free the msgb once
|
|
|
|
* it is no longer needed */
|
|
|
|
|
|
|
|
/* but we have to free the queue element ourselves */
|
|
|
|
talloc_free(fcqe);
|
|
|
|
|
|
|
|
/* re-configure the timer for the next PDU */
|
|
|
|
fc_queue_timer_cfg(fc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* configure/schedule the flow control timer to expire once the bucket
|
|
|
|
* will have leaked a sufficient number of bytes to transmit the next
|
|
|
|
* PDU in the queue */
|
|
|
|
static int fc_queue_timer_cfg(struct bssgp_flow_control *fc)
|
|
|
|
{
|
|
|
|
struct bssgp_fc_queue_element *fcqe;
|
|
|
|
uint32_t msecs;
|
|
|
|
|
|
|
|
if (llist_empty(&fc->queue))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fcqe = llist_entry(&fc->queue.next, struct bssgp_fc_queue_element,
|
|
|
|
list);
|
|
|
|
|
2013-06-22 07:44:00 +00:00
|
|
|
if (fc->bucket_leak_rate != 0) {
|
|
|
|
/* Calculate the point in time at which we will have leaked
|
|
|
|
* a sufficient number of bytes from the bucket to transmit
|
|
|
|
* the first PDU in the queue */
|
|
|
|
msecs = (fcqe->llc_pdu_len * 1000) / fc->bucket_leak_rate;
|
|
|
|
/* FIXME: add that time to fc->time_last_pdu and subtract it from
|
|
|
|
* current time */
|
|
|
|
fc->timer.data = fc;
|
|
|
|
fc->timer.cb = &fc_timer_cb;
|
|
|
|
osmo_timer_schedule(&fc->timer, msecs / 1000, (msecs % 1000) * 1000);
|
|
|
|
} else {
|
|
|
|
/* If the PCU is telling us to not send any more data at all,
|
|
|
|
* there's no point starting a timer. */
|
|
|
|
}
|
2012-09-06 19:57:11 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enqueue a PDU in the flow control queue for delayed transmission */
|
|
|
|
static int fc_enqueue(struct bssgp_flow_control *fc, struct msgb *msg,
|
|
|
|
uint32_t llc_pdu_len, void *priv)
|
|
|
|
{
|
|
|
|
struct bssgp_fc_queue_element *fcqe;
|
|
|
|
|
|
|
|
if (fc->queue_depth >= fc->max_queue_depth)
|
|
|
|
return -ENOSPC;
|
|
|
|
|
|
|
|
fcqe = talloc_zero(fc, struct bssgp_fc_queue_element);
|
|
|
|
if (!fcqe)
|
|
|
|
return -ENOMEM;
|
|
|
|
fcqe->msg = msg;
|
|
|
|
fcqe->llc_pdu_len = llc_pdu_len;
|
|
|
|
fcqe->priv = priv;
|
|
|
|
|
|
|
|
llist_add_tail(&fcqe->list, &fc->queue);
|
|
|
|
|
|
|
|
fc->queue_depth++;
|
|
|
|
|
|
|
|
/* re-configure the timer for dequeueing the pdu */
|
|
|
|
fc_queue_timer_cfg(fc);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* According to Section 8.2 */
|
|
|
|
static int bssgp_fc_needs_queueing(struct bssgp_flow_control *fc, uint32_t pdu_len)
|
|
|
|
{
|
|
|
|
struct timeval time_now, time_diff;
|
|
|
|
int64_t bucket_predicted;
|
|
|
|
uint32_t csecs_elapsed, leaked;
|
|
|
|
|
|
|
|
/* B' = B + L(p) - (Tc - Tp)*R */
|
|
|
|
|
|
|
|
/* compute number of centi-seconds that have elapsed since transmitting
|
|
|
|
* the last PDU (Tc - Tp) */
|
|
|
|
gettimeofday(&time_now, NULL);
|
|
|
|
timersub(&time_now, &fc->time_last_pdu, &time_diff);
|
|
|
|
csecs_elapsed = time_diff.tv_sec*100 + time_diff.tv_usec/10000;
|
|
|
|
|
|
|
|
/* compute number of bytes that have leaked in the elapsed number
|
|
|
|
* of centi-seconds */
|
|
|
|
leaked = csecs_elapsed * (fc->bucket_leak_rate / 100);
|
|
|
|
/* add the current PDU length to the last bucket level */
|
|
|
|
bucket_predicted = fc->bucket_counter + pdu_len;
|
|
|
|
/* ... and subtract the number of leaked bytes */
|
|
|
|
bucket_predicted -= leaked;
|
|
|
|
|
|
|
|
if (bucket_predicted < pdu_len) {
|
|
|
|
/* this is just to make sure the bucket doesn't underflow */
|
|
|
|
bucket_predicted = pdu_len;
|
|
|
|
goto pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bucket_predicted <= fc->bucket_size_max) {
|
|
|
|
/* the bucket is not full yet, we can pass the packet */
|
|
|
|
fc->bucket_counter = bucket_predicted;
|
|
|
|
goto pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bucket is full, PDU needs to be delayed */
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
pass:
|
|
|
|
/* if we reach here, the PDU can pass */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* output callback for BVC flow control */
|
|
|
|
static int _bssgp_tx_dl_ud(struct bssgp_flow_control *fc, struct msgb *msg,
|
|
|
|
uint32_t llc_pdu_len, void *priv)
|
|
|
|
{
|
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* input function of the flow control implementation, called first
|
|
|
|
* for the MM flow control, and then as the MM flow control output
|
|
|
|
* callback in order to perform BVC flow control */
|
|
|
|
int bssgp_fc_in(struct bssgp_flow_control *fc, struct msgb *msg,
|
|
|
|
uint32_t llc_pdu_len, void *priv)
|
|
|
|
{
|
|
|
|
struct timeval time_now;
|
|
|
|
|
2012-09-07 08:22:01 +00:00
|
|
|
if (llc_pdu_len > fc->bucket_size_max) {
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "Single PDU (size=%u) is larger "
|
|
|
|
"than maximum bucket size (%u)!\n", llc_pdu_len,
|
|
|
|
fc->bucket_size_max);
|
2014-10-10 15:24:34 +00:00
|
|
|
msgb_free(msg);
|
2012-09-07 08:22:01 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2012-09-06 19:57:11 +00:00
|
|
|
if (bssgp_fc_needs_queueing(fc, llc_pdu_len)) {
|
|
|
|
return fc_enqueue(fc, msg, llc_pdu_len, priv);
|
|
|
|
} else {
|
|
|
|
/* record the time we transmitted this PDU */
|
|
|
|
gettimeofday(&time_now, NULL);
|
|
|
|
fc->time_last_pdu = time_now;
|
|
|
|
return fc->out_cb(priv, msg, llc_pdu_len, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-07 08:22:01 +00:00
|
|
|
|
|
|
|
/* Initialize the Flow Control structure */
|
|
|
|
void bssgp_fc_init(struct bssgp_flow_control *fc,
|
|
|
|
uint32_t bucket_size_max, uint32_t bucket_leak_rate,
|
|
|
|
uint32_t max_queue_depth,
|
|
|
|
int (*out_cb)(struct bssgp_flow_control *fc, struct msgb *msg,
|
|
|
|
uint32_t llc_pdu_len, void *priv))
|
|
|
|
{
|
|
|
|
fc->out_cb = out_cb;
|
|
|
|
fc->bucket_size_max = bucket_size_max;
|
|
|
|
fc->bucket_leak_rate = bucket_leak_rate;
|
|
|
|
fc->max_queue_depth = max_queue_depth;
|
|
|
|
INIT_LLIST_HEAD(&fc->queue);
|
|
|
|
gettimeofday(&fc->time_last_pdu, NULL);
|
|
|
|
}
|
|
|
|
|
2012-09-06 19:57:11 +00:00
|
|
|
/* Initialize the Flow Control parameters for a new MS according to
|
|
|
|
* default values for the BVC specified by BVCI and NSEI */
|
|
|
|
int bssgp_fc_ms_init(struct bssgp_flow_control *fc_ms, uint16_t bvci,
|
2012-09-07 08:22:01 +00:00
|
|
|
uint16_t nsei, uint32_t max_queue_depth)
|
2012-09-06 19:57:11 +00:00
|
|
|
{
|
|
|
|
struct bssgp_bvc_ctx *ctx;
|
|
|
|
|
|
|
|
ctx = btsctx_by_bvci_nsei(bvci, nsei);
|
|
|
|
if (!ctx)
|
|
|
|
return -ENODEV;
|
2012-09-07 08:22:01 +00:00
|
|
|
|
|
|
|
/* output call-back of per-MS FC is per-CTX FC */
|
|
|
|
bssgp_fc_init(fc_ms, ctx->bmax_default_ms, ctx->r_default_ms,
|
|
|
|
max_queue_depth, bssgp_fc_in);
|
2012-09-06 19:57:11 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *bctx)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
2013-06-22 07:44:00 +00:00
|
|
|
uint32_t old_leak_rate = bctx->fc->bucket_leak_rate;
|
|
|
|
uint32_t old_r_def_ms = bctx->r_default_ms;
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
|
|
|
|
bctx->bvci);
|
2010-03-14 07:45:01 +00:00
|
|
|
|
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
|
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
|
2010-05-31 16:07:17 +00:00
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
|
|
|
|
"missing mandatory IE\n", bctx->bvci);
|
2010-03-14 07:45:01 +00:00
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2012-09-07 08:22:01 +00:00
|
|
|
/* 11.3.5 Bucket Size in 100 octets unit */
|
2012-09-07 09:29:32 +00:00
|
|
|
bctx->fc->bucket_size_max = 100 *
|
2012-09-06 19:57:11 +00:00
|
|
|
ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVC_BUCKET_SIZE));
|
2012-09-07 08:22:01 +00:00
|
|
|
/* 11.3.4 Bucket Leak Rate in 100 bits/sec unit */
|
2012-09-07 09:29:32 +00:00
|
|
|
bctx->fc->bucket_leak_rate = 100 *
|
2012-09-07 08:22:01 +00:00
|
|
|
ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BUCKET_LEAK_RATE)) / 8;
|
|
|
|
/* 11.3.2 in octets */
|
|
|
|
bctx->bmax_default_ms =
|
2012-09-06 19:57:11 +00:00
|
|
|
ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BMAX_DEFAULT_MS));
|
2012-09-07 08:22:01 +00:00
|
|
|
/* 11.3.32 Bucket Leak rate in 100bits/sec unit */
|
2012-09-06 19:57:11 +00:00
|
|
|
bctx->r_default_ms = 100 *
|
2012-09-07 08:22:01 +00:00
|
|
|
ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_R_DEFAULT_MS)) / 8;
|
2010-05-02 09:19:37 +00:00
|
|
|
|
2013-06-22 07:44:00 +00:00
|
|
|
if (old_leak_rate != 0 && bctx->fc->bucket_leak_rate == 0)
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
|
|
|
|
"rate of 0, stopping all DL GPRS!\n");
|
|
|
|
else if (old_leak_rate == 0 && bctx->fc->bucket_leak_rate != 0)
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to bucket leak "
|
|
|
|
"rate of != 0, restarting all DL GPRS!\n");
|
|
|
|
|
|
|
|
if (old_r_def_ms != 0 && bctx->r_default_ms == 0)
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
|
|
|
|
"bucket leak rate of 0, stopping DL GPRS!\n");
|
|
|
|
else if (old_r_def_ms == 0 && bctx->r_default_ms != 0)
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "BSS instructs us to MS default "
|
|
|
|
"bucket leak rate != 0, restarting DL GPRS!\n");
|
|
|
|
|
|
|
|
/* reconfigure the timer for flow control based on new values */
|
|
|
|
fc_queue_timer_cfg(bctx->fc);
|
|
|
|
|
2010-03-14 07:45:01 +00:00
|
|
|
/* Send FLOW_CONTROL_BVC_ACK */
|
2010-04-30 17:54:29 +00:00
|
|
|
return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
|
2010-05-02 09:19:37 +00:00
|
|
|
msgb_bvci(msg));
|
2010-03-14 07:45:01 +00:00
|
|
|
}
|
2010-05-01 14:48:27 +00:00
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
|
2012-06-17 05:04:02 +00:00
|
|
|
static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
|
|
|
|
struct bssgp_bvc_ctx *bctx)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
2010-05-02 07:50:42 +00:00
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
2010-05-02 09:19:37 +00:00
|
|
|
uint8_t pdu_type = bgph->pdu_type;
|
2010-03-14 07:45:01 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
2010-05-13 19:45:23 +00:00
|
|
|
/* If traffic is received on a BVC that is marked as blocked, the
|
|
|
|
* received PDU shall not be accepted and a STATUS PDU (Cause value:
|
|
|
|
* BVC Blocked) shall be sent to the peer entity on the signalling BVC */
|
|
|
|
if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
|
|
|
|
uint16_t bvci = msgb_bvci(msg);
|
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
|
|
|
|
}
|
|
|
|
|
2010-03-14 07:45:01 +00:00
|
|
|
switch (pdu_type) {
|
|
|
|
case BSSGP_PDUT_UL_UNITDATA:
|
|
|
|
/* some LLC data from the MS */
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_rx_ul_ud(msg, tp, bctx);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_RA_CAPABILITY:
|
|
|
|
/* BSS requests RA capability or IMSI */
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
|
|
|
|
bctx->bvci);
|
2010-05-13 17:41:31 +00:00
|
|
|
/* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
|
2010-03-14 07:45:01 +00:00
|
|
|
/* FIXME: send RA_CAPA_UPDATE_ACK */
|
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_RADIO_STATUS:
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
|
2010-03-14 07:45:01 +00:00
|
|
|
/* BSS informs us of some exception */
|
2010-05-13 17:41:31 +00:00
|
|
|
/* FIXME: send GMM_RADIO_STATUS.ind to GMM */
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
2010-05-13 19:26:28 +00:00
|
|
|
case BSSGP_PDUT_FLOW_CONTROL_BVC:
|
|
|
|
/* BSS informs us of available bandwidth in Gb interface */
|
|
|
|
rc = bssgp_rx_fc_bvc(msg, tp, bctx);
|
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_FLOW_CONTROL_MS:
|
|
|
|
/* BSS informs us of available bandwidth to one MS */
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
|
|
|
|
bctx->bvci);
|
2010-05-13 19:26:28 +00:00
|
|
|
/* FIXME: actually implement flow control */
|
|
|
|
/* FIXME: Send FLOW_CONTROL_MS_ACK */
|
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_STATUS:
|
|
|
|
/* Some exception has occurred */
|
|
|
|
/* FIXME: send NM_STATUS.ind to NM */
|
|
|
|
case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
|
|
|
|
case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
|
|
|
|
case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
|
|
|
|
case BSSGP_PDUT_MODIFY_BSS_PFC:
|
|
|
|
case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
|
|
|
|
"implemented\n", bctx->bvci, pdu_type);
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
|
|
|
|
break;
|
|
|
|
/* those only exist in the SGSN -> BSS direction */
|
|
|
|
case BSSGP_PDUT_DL_UNITDATA:
|
|
|
|
case BSSGP_PDUT_PAGING_PS:
|
|
|
|
case BSSGP_PDUT_PAGING_CS:
|
|
|
|
case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
|
|
|
|
case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
|
|
|
|
case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
|
|
|
|
"in DL\n", bctx->bvci, pdu_type);
|
2010-05-13 19:26:28 +00:00
|
|
|
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
|
|
|
|
bctx->bvci, pdu_type);
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-05-23 13:12:15 +00:00
|
|
|
return rc;
|
2010-05-13 19:26:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
|
2012-06-17 05:04:02 +00:00
|
|
|
static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
|
|
|
|
struct bssgp_bvc_ctx *bctx)
|
2010-05-13 19:26:28 +00:00
|
|
|
{
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
|
|
|
uint8_t pdu_type = bgph->pdu_type;
|
|
|
|
int rc = 0;
|
|
|
|
uint16_t ns_bvci = msgb_bvci(msg);
|
|
|
|
|
|
|
|
switch (bgph->pdu_type) {
|
2010-03-14 07:45:01 +00:00
|
|
|
case BSSGP_PDUT_SUSPEND:
|
|
|
|
/* MS wants to suspend */
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_rx_suspend(msg, tp, bctx);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_RESUME:
|
|
|
|
/* MS wants to resume */
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_rx_resume(msg, tp, bctx);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
2010-05-13 17:41:31 +00:00
|
|
|
case BSSGP_PDUT_FLUSH_LL_ACK:
|
|
|
|
/* BSS informs us it has performed LL FLUSH */
|
2011-08-19 14:45:19 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bctx->bvci);
|
2010-05-13 17:41:31 +00:00
|
|
|
/* FIXME: send NM_FLUSH_LL.res to NM */
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_LLC_DISCARD:
|
|
|
|
/* BSS informs that some LLC PDU's have been discarded */
|
2010-05-31 16:07:17 +00:00
|
|
|
rc = bssgp_rx_llc_disc(msg, tp, bctx);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_BVC_BLOCK:
|
|
|
|
/* BSS tells us that BVC shall be blocked */
|
2010-05-13 19:26:28 +00:00
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
|
2010-05-31 16:07:17 +00:00
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
|
|
|
|
"missing mandatory IE\n");
|
2010-03-14 07:45:01 +00:00
|
|
|
goto err_mand_ie;
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-05-31 15:16:36 +00:00
|
|
|
rc = bssgp_rx_bvc_block(msg, tp);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_BVC_UNBLOCK:
|
|
|
|
/* BSS tells us that BVC shall be unblocked */
|
2010-05-31 16:07:17 +00:00
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
|
|
|
|
"missing mandatory IE\n");
|
2010-03-14 07:45:01 +00:00
|
|
|
goto err_mand_ie;
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_rx_bvc_unblock(msg, tp);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_BVC_RESET:
|
|
|
|
/* BSS tells us that BVC init is required */
|
2010-05-13 19:26:28 +00:00
|
|
|
if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
|
2010-05-31 16:07:17 +00:00
|
|
|
!TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
|
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
|
|
|
|
"missing mandatory IE\n");
|
2010-03-14 07:45:01 +00:00
|
|
|
goto err_mand_ie;
|
2010-05-31 16:07:17 +00:00
|
|
|
}
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
case BSSGP_PDUT_STATUS:
|
|
|
|
/* Some exception has occurred */
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
|
2010-05-13 17:41:31 +00:00
|
|
|
/* FIXME: send NM_STATUS.ind to NM */
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
/* those only exist in the SGSN -> BSS direction */
|
|
|
|
case BSSGP_PDUT_PAGING_PS:
|
|
|
|
case BSSGP_PDUT_PAGING_CS:
|
|
|
|
case BSSGP_PDUT_SUSPEND_ACK:
|
|
|
|
case BSSGP_PDUT_SUSPEND_NACK:
|
|
|
|
case BSSGP_PDUT_RESUME_ACK:
|
|
|
|
case BSSGP_PDUT_RESUME_NACK:
|
2010-05-13 17:41:31 +00:00
|
|
|
case BSSGP_PDUT_FLUSH_LL:
|
2010-03-14 07:45:01 +00:00
|
|
|
case BSSGP_PDUT_BVC_BLOCK_ACK:
|
|
|
|
case BSSGP_PDUT_BVC_UNBLOCK_ACK:
|
|
|
|
case BSSGP_PDUT_SGSN_INVOKE_TRACE:
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
|
|
|
|
"in DL\n", bctx->bvci, pdu_type);
|
2010-05-13 19:26:28 +00:00
|
|
|
bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2010-03-14 07:45:01 +00:00
|
|
|
rc = -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
2010-05-31 16:07:17 +00:00
|
|
|
DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
|
|
|
|
bctx->bvci, pdu_type);
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
|
2010-03-14 07:45:01 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
err_mand_ie:
|
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
|
|
|
|
}
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
/* We expect msgb_bssgph() to point to the BSSGP header */
|
2012-06-17 05:04:02 +00:00
|
|
|
int bssgp_rcvmsg(struct msgb *msg)
|
2010-05-13 19:26:28 +00:00
|
|
|
{
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_bssgph(msg);
|
|
|
|
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
|
|
|
|
struct tlv_parsed tp;
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *bctx;
|
2010-05-13 19:26:28 +00:00
|
|
|
uint8_t pdu_type = bgph->pdu_type;
|
|
|
|
uint16_t ns_bvci = msgb_bvci(msg);
|
2014-09-23 11:28:22 +00:00
|
|
|
uint16_t bvci = ns_bvci;
|
2010-05-13 19:26:28 +00:00
|
|
|
int data_len;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
|
|
|
|
|
|
|
|
/* UNITDATA BSSGP headers have TLLI in front */
|
|
|
|
if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
|
|
|
|
pdu_type != BSSGP_PDUT_DL_UNITDATA) {
|
|
|
|
data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
|
|
|
|
rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
|
|
|
|
} else {
|
|
|
|
data_len = msgb_bssgp_len(msg) - sizeof(*budh);
|
|
|
|
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
|
|
|
|
}
|
|
|
|
|
2014-09-23 11:28:22 +00:00
|
|
|
if (bvci == BVCI_SIGNALLING && TLVP_PRESENT(&tp, BSSGP_IE_BVCI))
|
|
|
|
bvci = ntohs(*(uint16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
|
|
|
|
|
2010-05-13 19:26:28 +00:00
|
|
|
/* look-up or create the BTS context for this BVC */
|
2014-09-23 11:28:22 +00:00
|
|
|
bctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
|
2010-05-13 19:26:28 +00:00
|
|
|
/* Only a RESET PDU can create a new BVC context */
|
|
|
|
if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
|
|
|
|
LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
|
2014-09-23 11:28:22 +00:00
|
|
|
"type %u for unknown BVCI\n", msgb_nsei(msg), bvci,
|
2010-05-13 19:26:28 +00:00
|
|
|
pdu_type);
|
2014-09-23 11:28:22 +00:00
|
|
|
return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
|
2010-05-13 19:26:28 +00:00
|
|
|
}
|
|
|
|
|
2010-05-17 21:30:01 +00:00
|
|
|
if (bctx) {
|
2012-06-16 09:45:59 +00:00
|
|
|
log_set_context(GPRS_CTX_BVC, bctx);
|
2010-05-17 21:30:01 +00:00
|
|
|
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
|
|
|
|
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
|
|
|
|
msgb_bssgp_len(msg));
|
|
|
|
}
|
|
|
|
|
2010-05-18 09:57:08 +00:00
|
|
|
if (ns_bvci == BVCI_SIGNALLING)
|
2012-06-17 05:04:02 +00:00
|
|
|
rc = bssgp_rx_sign(msg, &tp, bctx);
|
2010-05-18 09:57:08 +00:00
|
|
|
else if (ns_bvci == BVCI_PTM)
|
2010-05-13 19:26:28 +00:00
|
|
|
rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
|
|
|
|
else
|
2012-06-17 05:04:02 +00:00
|
|
|
rc = bssgp_rx_ptp(msg, &tp, bctx);
|
2010-05-13 19:26:28 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2012-06-17 05:04:02 +00:00
|
|
|
int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
|
|
|
|
struct bssgp_dl_ud_par *dup)
|
2010-03-14 07:45:01 +00:00
|
|
|
{
|
2010-05-17 20:59:29 +00:00
|
|
|
struct bssgp_bvc_ctx *bctx;
|
2010-03-14 07:45:01 +00:00
|
|
|
struct bssgp_ud_hdr *budh;
|
2010-05-02 09:26:34 +00:00
|
|
|
uint8_t llc_pdu_tlv_hdr_len = 2;
|
2012-06-17 01:31:16 +00:00
|
|
|
uint8_t *llc_pdu_tlv;
|
2010-05-02 09:26:34 +00:00
|
|
|
uint16_t msg_len = msg->len;
|
2010-05-02 09:19:37 +00:00
|
|
|
uint16_t bvci = msgb_bvci(msg);
|
|
|
|
uint16_t nsei = msgb_nsei(msg);
|
2012-06-17 01:31:16 +00:00
|
|
|
uint16_t _pdu_lifetime = htons(pdu_lifetime); /* centi-seconds */
|
2010-05-31 20:12:30 +00:00
|
|
|
uint16_t drx_params;
|
2010-03-14 07:45:01 +00:00
|
|
|
|
2010-05-02 09:19:37 +00:00
|
|
|
/* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
|
2010-05-18 09:57:08 +00:00
|
|
|
if (bvci <= BVCI_PTM ) {
|
2010-05-11 03:54:22 +00:00
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
|
2010-05-02 09:19:37 +00:00
|
|
|
bvci);
|
2014-10-10 15:24:34 +00:00
|
|
|
msgb_free(msg);
|
2010-05-02 09:19:37 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bctx = btsctx_by_bvci_nsei(bvci, nsei);
|
2010-05-13 19:26:28 +00:00
|
|
|
if (!bctx) {
|
2012-09-06 19:57:11 +00:00
|
|
|
LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to unknown BVCI %u\n",
|
|
|
|
bvci);
|
2014-10-10 15:24:34 +00:00
|
|
|
msgb_free(msg);
|
2012-09-06 19:57:11 +00:00
|
|
|
return -ENODEV;
|
2010-05-13 19:26:28 +00:00
|
|
|
}
|
2010-03-14 07:45:01 +00:00
|
|
|
|
|
|
|
if (msg->len > TVLV_MAX_ONEBYTE)
|
|
|
|
llc_pdu_tlv_hdr_len += 1;
|
|
|
|
|
|
|
|
/* prepend the tag and length of the LLC-PDU TLV */
|
|
|
|
llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
|
|
|
|
llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
|
|
|
|
if (llc_pdu_tlv_hdr_len > 2) {
|
|
|
|
llc_pdu_tlv[1] = msg_len >> 8;
|
|
|
|
llc_pdu_tlv[2] = msg_len & 0xff;
|
|
|
|
} else {
|
2010-06-09 19:13:13 +00:00
|
|
|
llc_pdu_tlv[1] = msg_len & 0x7f;
|
2010-03-14 07:45:01 +00:00
|
|
|
llc_pdu_tlv[1] |= 0x80;
|
|
|
|
}
|
|
|
|
|
2010-05-31 20:12:30 +00:00
|
|
|
/* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
|
|
|
|
|
2012-06-17 01:31:16 +00:00
|
|
|
if (dup) {
|
2010-05-31 20:12:30 +00:00
|
|
|
/* Old TLLI to help BSS map from old->new */
|
2012-06-17 01:31:16 +00:00
|
|
|
if (dup->tlli) {
|
|
|
|
uint32_t tlli = htonl(*dup->tlli);
|
|
|
|
msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &tlli);
|
|
|
|
}
|
2010-05-31 20:12:30 +00:00
|
|
|
|
|
|
|
/* IMSI */
|
2012-07-04 19:55:23 +00:00
|
|
|
if (dup->imsi && strlen(dup->imsi)) {
|
2010-05-31 20:12:30 +00:00
|
|
|
uint8_t mi[10];
|
2012-06-17 01:31:16 +00:00
|
|
|
int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi);
|
2010-05-31 20:12:30 +00:00
|
|
|
if (imsi_len > 2)
|
|
|
|
msgb_tvlv_push(msg, BSSGP_IE_IMSI,
|
2012-06-17 01:31:16 +00:00
|
|
|
imsi_len-2, mi+2);
|
2010-05-31 20:12:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* DRX parameters */
|
2012-06-17 01:31:16 +00:00
|
|
|
drx_params = htons(dup->drx_parms);
|
2010-05-31 20:12:30 +00:00
|
|
|
msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
|
|
|
|
(uint8_t *) &drx_params);
|
|
|
|
|
|
|
|
/* FIXME: Priority */
|
|
|
|
|
|
|
|
/* MS Radio Access Capability */
|
2012-06-17 01:31:16 +00:00
|
|
|
if (dup->ms_ra_cap.len)
|
2010-05-31 20:12:30 +00:00
|
|
|
msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
|
2012-06-17 01:31:16 +00:00
|
|
|
dup->ms_ra_cap.len, dup->ms_ra_cap.v);
|
|
|
|
|
2010-05-31 20:12:30 +00:00
|
|
|
}
|
2010-03-14 07:45:01 +00:00
|
|
|
|
|
|
|
/* prepend the pdu lifetime */
|
2012-06-17 01:31:16 +00:00
|
|
|
msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&_pdu_lifetime);
|
2010-03-14 07:45:01 +00:00
|
|
|
|
|
|
|
/* prepend the QoS profile, TLLI and pdu type */
|
|
|
|
budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
|
2012-06-17 01:31:16 +00:00
|
|
|
memcpy(budh->qos_profile, dup->qos_profile, sizeof(budh->qos_profile));
|
2010-04-30 14:33:12 +00:00
|
|
|
budh->tlli = htonl(msgb_tlli(msg));
|
2010-03-14 07:45:01 +00:00
|
|
|
budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
|
|
|
|
|
2010-05-17 21:30:01 +00:00
|
|
|
rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
|
|
|
|
rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
|
|
|
|
|
2010-05-02 09:19:37 +00:00
|
|
|
/* Identifiers down: BVCI, NSEI (in msgb->cb) */
|
2010-04-30 17:54:29 +00:00
|
|
|
|
2012-09-06 19:57:11 +00:00
|
|
|
/* check if we have to go through per-ms flow control or can go
|
|
|
|
* directly to the per-BSS flow control */
|
|
|
|
if (dup->fc)
|
2012-09-07 09:29:32 +00:00
|
|
|
return bssgp_fc_in(dup->fc, msg, msg_len, bctx->fc);
|
2012-09-06 19:57:11 +00:00
|
|
|
else
|
2012-09-07 09:29:32 +00:00
|
|
|
return bssgp_fc_in(bctx->fc, msg, msg_len, NULL);
|
2010-03-14 07:45:01 +00:00
|
|
|
}
|
2010-06-09 14:22:28 +00:00
|
|
|
|
|
|
|
/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
|
2012-06-17 05:04:02 +00:00
|
|
|
int bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
|
|
|
|
struct bssgp_paging_info *pinfo)
|
2010-06-09 14:22:28 +00:00
|
|
|
{
|
|
|
|
struct msgb *msg = bssgp_msgb_alloc();
|
|
|
|
struct bssgp_normal_hdr *bgph =
|
|
|
|
(struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
|
|
|
|
uint16_t drx_params = htons(pinfo->drx_params);
|
|
|
|
uint8_t mi[10];
|
|
|
|
int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi);
|
|
|
|
uint8_t ra[6];
|
|
|
|
|
|
|
|
if (imsi_len < 2)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
msgb_nsei(msg) = nsei;
|
|
|
|
msgb_bvci(msg) = ns_bvci;
|
|
|
|
|
|
|
|
if (pinfo->mode == BSSGP_PAGING_PS)
|
|
|
|
bgph->pdu_type = BSSGP_PDUT_PAGING_PS;
|
|
|
|
else
|
|
|
|
bgph->pdu_type = BSSGP_PDUT_PAGING_CS;
|
|
|
|
/* IMSI */
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
|
|
|
|
/* DRX Parameters */
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2,
|
|
|
|
(uint8_t *) &drx_params);
|
|
|
|
/* Scope */
|
|
|
|
switch (pinfo->scope) {
|
|
|
|
case BSSGP_PAGING_BSS_AREA:
|
|
|
|
{
|
|
|
|
uint8_t null = 0;
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BSSGP_PAGING_LOCATION_AREA:
|
|
|
|
gsm48_construct_ra(ra, &pinfo->raid);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra);
|
|
|
|
break;
|
|
|
|
case BSSGP_PAGING_ROUTEING_AREA:
|
|
|
|
gsm48_construct_ra(ra, &pinfo->raid);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
|
|
|
|
break;
|
|
|
|
case BSSGP_PAGING_BVCI:
|
|
|
|
{
|
|
|
|
uint16_t bvci = htons(pinfo->bvci);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* QoS profile mandatory for PS */
|
|
|
|
if (pinfo->mode == BSSGP_PAGING_PS)
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos);
|
|
|
|
|
|
|
|
/* Optional (P-)TMSI */
|
|
|
|
if (pinfo->ptmsi) {
|
|
|
|
uint32_t ptmsi = htonl(*pinfo->ptmsi);
|
|
|
|
msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
|
|
|
|
}
|
|
|
|
|
|
|
|
return gprs_ns_sendmsg(bssgp_nsi, msg);
|
|
|
|
}
|
2012-06-16 09:45:59 +00:00
|
|
|
|
2012-06-17 05:04:02 +00:00
|
|
|
void bssgp_set_log_ss(int ss)
|
2012-06-16 09:45:59 +00:00
|
|
|
{
|
|
|
|
DBSSGP = ss;
|
|
|
|
}
|