summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2010-05-02 09:23:16 +0200
committerHarald Welte <laforge@gnumonks.org>2010-05-04 07:20:43 +0200
commit6752fa43b6cdcc2cb33d04b5e6cf3c87c609de68 (patch)
treea85d4f5f9f5b305d1b1a3bbb7aa01468752426be
parent3fddf3c4ef22c5f973843f250c5700814fc465c1 (diff)
[gprs] introduce BSSGP concept of BTS contextx
A BTS context represents the mapping betewen (RA-ID, Cell-ID) and (BVCI, NSEI) as well as the per-BVC local state.
-rw-r--r--openbsc/src/gprs_bssgp.c133
1 files changed, 110 insertions, 23 deletions
diff --git a/openbsc/src/gprs_bssgp.c b/openbsc/src/gprs_bssgp.c
index ab6d1a09..44e0a35c 100644
--- a/openbsc/src/gprs_bssgp.c
+++ b/openbsc/src/gprs_bssgp.c
@@ -1,6 +1,6 @@
/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -27,6 +27,8 @@
#include <osmocore/msgb.h>
#include <osmocore/tlv.h>
+#include <osmocore/talloc.h>
+
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_08_gprs.h>
@@ -39,6 +41,8 @@
extern struct gsm_network *bsc_gsmnet;
struct gprs_ns_inst *bssgp_nsi;
+void *bssgp_tall_ctx = NULL;
+
/* Chapter 11.3.9 / Table 11.10: Cause coding */
static const char *bssgp_cause_strings[] = {
[BSSGP_CAUSE_PROC_OVERLOAD] = "Processor overload",
@@ -74,6 +78,68 @@ static const char *bssgp_cause_str(enum gprs_bssgp_cause cause)
return "undefined";
}
+#define BVC_F_BLOCKED 0x0001
+
+/* The per-BTS context that we keep on the SGSN side of the BSSGP link */
+struct bssgp_bts_ctx {
+ struct llist_head list;
+
+ /* parsed RA ID and Cell ID of the remote BTS */
+ struct gprs_ra_id ra_id;
+ uint16_t cell_id;
+
+ /* NSEI and BVCI of underlying Gb link. Together they
+ * uniquely identify a link to a BTS (5.4.4) */
+ uint16_t bvci;
+ uint16_t nsei;
+
+ uint32_t bvc_state;
+
+ /* we might want to add this as a shortcut later, avoiding the NSVC
+ * lookup for every packet, similar to a routing cache */
+ //struct gprs_nsvc *nsvc;
+};
+LLIST_HEAD(bts_ctxts);
+
+/* Find a BTS Context based on parsed RA ID and Cell ID */
+struct bssgp_bts_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
+{
+ struct bssgp_bts_ctx *bctx;
+
+ llist_for_each_entry(bctx, &bts_ctxts, list) {
+ 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 */
+struct bssgp_bts_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
+{
+ struct bssgp_bts_ctx *bctx;
+
+ llist_for_each_entry(bctx, &bts_ctxts, list) {
+ if (bctx->nsei == nsei && bctx->bvci == bvci)
+ return bctx;
+ }
+ return NULL;
+}
+
+struct bssgp_btx_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
+{
+ struct bssgp_bts_ctx *ctx;
+
+ ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bts_ctx);
+ if (!ctx)
+ return NULL;
+ ctx->bvci = bvci;
+ ctx->nsei = nsei;
+ llist_add(&ctx->list, &bts_ctxts);
+
+ return ctx;
+}
+
static inline struct msgb *bssgp_msgb_alloc(void)
{
return msgb_alloc_headroom(4096, 128, "BSSGP");
@@ -138,22 +204,52 @@ int bssgp_tx_status(u_int8_t cause, u_int16_t *bvci, struct msgb *orig_msg)
return gprs_ns_sendmsg(bssgp_nsi, msg);
}
+static void bssgp_parse_cell_id(struct gprs_ra_id *raid, uint16_t *cid,
+ const uint8_t *buf)
+{
+ /* 6 octets RAC */
+ gsm48_parse_ra(raid, buf);
+ /* 2 octets CID */
+ *cid = ntohs(*(uint16_t *) (buf+6));
+}
+
/* Chapter 8.4 BVC-Reset Procedure */
static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
uint16_t ns_bvci)
{
- uint8_t bvci;
+ struct bssgp_bts_ctx *bctx;
+ uint16_t nsei = msgb_nsei(msg);
+ uint16_t bvci;
int rc;
bvci = ntohs(*(u_int16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci,
bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
+ /* look-up or create the BTS context for this BVC */
+ bctx = btsctx_by_bvci_nsei(bvci, nsei);
+ if (!bctx)
+ bctx = btsctx_alloc(bvci, nsei);
+
/* 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 */
+ if (bvci != 0 && bvci != 1) {
+ if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
+ LOGP(DGPRS, LOGL_ERROR, "BSSGP RESET BVCI=%u "
+ "missing mandatory IE\n", bvci);
+ return -EINVAL;
+ }
+ /* actually extract RAC / CID */
+ bssgp_parse_cell_id(&bctx->ra_id, &bctx->cell_id,
+ TLVP_VAL(tp, BSSGP_IE_CELL_ID));
+ LOGP(DGPRS, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
+ bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
+ bctx->ra_id.rac, bctx->cell_id, bvci);
+ }
+ /* Acknowledge the RESET to the BTS */
rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
- msgb_nsei(msg), bvci, ns_bvci);
+ nsei, bvci, ns_bvci);
return 0;
}
@@ -161,13 +257,13 @@ static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
static int bssgp_rx_ul_ud(struct msgb *msg, u_int16_t bvci)
{
struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msg->l3h;
- struct gsm_bts *bts;
int data_len = msgb_l3len(msg) - sizeof(*budh);
struct tlv_parsed tp;
int rc;
DEBUGP(DGPRS, "BSSGP UL-UD\n");
+ /* extract TLLI and parse TLV IEs */
msgb_tlli(msg) = ntohl(budh->tlli);
rc = bssgp_tlv_parse(&tp, budh->data, data_len);
@@ -176,15 +272,6 @@ static int bssgp_rx_ul_ud(struct msgb *msg, u_int16_t bvci)
!TLVP_PRESENT(&tp, BSSGP_IE_LLC_PDU))
return -EIO;
-#if 0 //FIXME
- /* Determine the BTS based on the Cell ID */
- bts = gsm48_bts_by_ra_id(bsc_gsmnet,
- TLVP_VAL(&tp, BSSGP_IE_CELL_ID),
- TLVP_LEN(&tp, BSSGP_IE_CELL_ID));
-#endif
- if (bts)
- msg->trx = bts->c0;
-
msgb_llch(msg) = TLVP_VAL(&tp, BSSGP_IE_LLC_PDU);
return gprs_llc_rcvmsg(msg, &tp);
@@ -261,6 +348,7 @@ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci)
u_int16_t bvci;
int rc = 0;
+ /* UNITDATA BSSGP headers have TLLI in front */
if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
pdu_type != BSSGP_PDUT_DL_UNITDATA)
rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
@@ -314,6 +402,7 @@ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci)
bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
DEBUGPC(DGPRS, "BVCI=%u, cause=%s\n", bvci,
bssgp_cause_str(*TLVP_VAL(&tp, BSSGP_IE_CAUSE)));
+ /* We always acknowledge the BLOCKing */
rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK,
msgb_nsei(msg), bvci, ns_bvci);
break;
@@ -324,6 +413,7 @@ int gprs_bssgp_rcvmsg(struct msgb *msg, u_int16_t ns_bvci)
goto err_mand_ie;
bvci = ntohs(*(u_int16_t *)TLVP_VAL(&tp, BSSGP_IE_BVCI));
DEBUGPC(DGPRS, "BVCI=%u\n", bvci);
+ /* We always acknowledge the unBLOCKing */
rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK,
msgb_nsei(msg), bvci, ns_bvci);
break;
@@ -374,9 +464,11 @@ err_mand_ie:
return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
}
-int gprs_bssgp_tx_dl_ud(struct msgb *msg)
+/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
+ * to a remote MS (identified by TLLI) at a BTS identified by its RAC and CID */
+int gprs_bssgp_tx_dl_ud(struct msgb *msg, const struct gprs_ra_id *raid, uint16_t cid)
{
- struct gsm_bts *bts;
+ struct bssgp_bts_ctx *bctx;
struct bssgp_ud_hdr *budh;
u_int8_t llc_pdu_tlv_hdr_len = 2;
u_int8_t *llc_pdu_tlv, *qos_profile;
@@ -384,12 +476,7 @@ int gprs_bssgp_tx_dl_ud(struct msgb *msg)
u_int8_t qos_profile_default[3] = { 0x00, 0x00, 0x21 };
u_int16_t msg_len = msg->len;
- if (!msg->trx) {
- DEBUGP(DGPRS, "Cannot transmit DL-UD without TRX assigned\n");
- return -EINVAL;
- }
-
- bts = msg->trx->bts;
+ bctx = btsctx_by_raid_cid(raid, cid);
if (msg->len > TVLV_MAX_ONEBYTE)
llc_pdu_tlv_hdr_len += 1;
@@ -417,8 +504,8 @@ int gprs_bssgp_tx_dl_ud(struct msgb *msg)
budh->tlli = htonl(msgb_tlli(msg));
budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
- msgb_nsei(msg) = bts->gprs.nse.nsei;
- msgb_bvci(msg) = bts->gprs.cell.bvci;
+ msgb_nsei(msg) = bctx->nsei;
+ msgb_bvci(msg) = bctx->bvci;
return gprs_ns_sendmsg(bssgp_nsi, msg);
}