blob: 051ec92f83414e48d77c20ef1f53593a8596dc59 [file] [log] [blame]
Harald Welte9ba50052010-03-14 15:45:01 +08001/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
2
Harald Welte6752fa42010-05-02 09:23:16 +02003/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
Harald Welte9ba50052010-03-14 15:45:01 +08004 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
Harald Welte4e5721d2010-05-17 23:41:43 +020021 * TODO:
22 * o properly count incoming BVC-RESET packets in counter group
23 * o set log context as early as possible for outgoing packets
Harald Welte9ba50052010-03-14 15:45:01 +080024 */
25
26#include <errno.h>
Harald Welte8f9a3ee2010-05-02 11:26:34 +020027#include <stdint.h>
Harald Welte9ba50052010-03-14 15:45:01 +080028
29#include <netinet/in.h>
30
31#include <osmocore/msgb.h>
32#include <osmocore/tlv.h>
Harald Welte6752fa42010-05-02 09:23:16 +020033#include <osmocore/talloc.h>
Harald Welte25de8112010-05-13 21:26:28 +020034#include <osmocore/rate_ctr.h>
Harald Welte6752fa42010-05-02 09:23:16 +020035
Harald Welte9ba50052010-03-14 15:45:01 +080036#include <openbsc/debug.h>
37#include <openbsc/gsm_data.h>
38#include <openbsc/gsm_04_08_gprs.h>
39#include <openbsc/gprs_bssgp.h>
40#include <openbsc/gprs_llc.h>
41#include <openbsc/gprs_ns.h>
Harald Welte2f946832010-05-31 22:12:30 +020042#include <openbsc/gprs_sgsn.h>
Harald Welte313cccf2010-06-09 11:22:47 +020043#include <openbsc/gprs_gmm.h>
Harald Welte9ba50052010-03-14 15:45:01 +080044
Harald Welte6752fa42010-05-02 09:23:16 +020045void *bssgp_tall_ctx = NULL;
46
Harald Welte6752fa42010-05-02 09:23:16 +020047#define BVC_F_BLOCKED 0x0001
48
Harald Welte25de8112010-05-13 21:26:28 +020049enum bssgp_ctr {
Harald Welte16c8dbb2010-05-17 23:30:01 +020050 BSSGP_CTR_PKTS_IN,
51 BSSGP_CTR_PKTS_OUT,
52 BSSGP_CTR_BYTES_IN,
53 BSSGP_CTR_BYTES_OUT,
Harald Welte25de8112010-05-13 21:26:28 +020054 BSSGP_CTR_BLOCKED,
55 BSSGP_CTR_DISCARDED,
56};
57
58static const struct rate_ctr_desc bssgp_ctr_description[] = {
Harald Welte16c8dbb2010-05-17 23:30:01 +020059 { "packets.in", "Packets at BSSGP Level ( In)" },
60 { "packets.out","Packets at BSSGP Level (Out)" },
61 { "bytes.in", "Bytes at BSSGP Level ( In)" },
62 { "bytes.out", "Bytes at BSSGP Level (Out)" },
Harald Welte25de8112010-05-13 21:26:28 +020063 { "blocked", "BVC Blocking count" },
64 { "discarded", "BVC LLC Discarded count" },
65};
66
67static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
68 .group_name_prefix = "bssgp.bss_ctx",
69 .group_description = "BSSGP Peer Statistics",
70 .num_ctr = ARRAY_SIZE(bssgp_ctr_description),
71 .ctr_desc = bssgp_ctr_description,
72};
73
Harald Weltea78b9c22010-05-17 23:02:42 +020074LLIST_HEAD(bssgp_bvc_ctxts);
Harald Welte6752fa42010-05-02 09:23:16 +020075
76/* Find a BTS Context based on parsed RA ID and Cell ID */
Harald Welte8a521132010-05-17 22:59:29 +020077struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
Harald Welte6752fa42010-05-02 09:23:16 +020078{
Harald Welte8a521132010-05-17 22:59:29 +020079 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +020080
Harald Weltea78b9c22010-05-17 23:02:42 +020081 llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
Harald Welte6752fa42010-05-02 09:23:16 +020082 if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) &&
83 bctx->cell_id == cid)
84 return bctx;
85 }
86 return NULL;
87}
88
89/* Find a BTS context based on BVCI+NSEI tuple */
Harald Welte8a521132010-05-17 22:59:29 +020090struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
Harald Welte6752fa42010-05-02 09:23:16 +020091{
Harald Welte8a521132010-05-17 22:59:29 +020092 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +020093
Harald Weltea78b9c22010-05-17 23:02:42 +020094 llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
Harald Welte6752fa42010-05-02 09:23:16 +020095 if (bctx->nsei == nsei && bctx->bvci == bvci)
96 return bctx;
97 }
98 return NULL;
99}
100
Harald Welte8a521132010-05-17 22:59:29 +0200101struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
Harald Welte6752fa42010-05-02 09:23:16 +0200102{
Harald Welte8a521132010-05-17 22:59:29 +0200103 struct bssgp_bvc_ctx *ctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200104
Harald Welte8a521132010-05-17 22:59:29 +0200105 ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx);
Harald Welte6752fa42010-05-02 09:23:16 +0200106 if (!ctx)
107 return NULL;
108 ctx->bvci = bvci;
109 ctx->nsei = nsei;
Harald Welte25de8112010-05-13 21:26:28 +0200110 /* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */
111 ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci);
112
Harald Weltea78b9c22010-05-17 23:02:42 +0200113 llist_add(&ctx->list, &bssgp_bvc_ctxts);
Harald Welte6752fa42010-05-02 09:23:16 +0200114
115 return ctx;
116}
117
Harald Welte9ba50052010-03-14 15:45:01 +0800118/* Chapter 10.4.5: Flow Control BVC ACK */
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200119static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci)
Harald Welte9ba50052010-03-14 15:45:01 +0800120{
121 struct msgb *msg = bssgp_msgb_alloc();
122 struct bssgp_normal_hdr *bgph =
123 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
124
Harald Welte24a655f2010-04-30 19:54:29 +0200125 msgb_nsei(msg) = nsei;
126 msgb_bvci(msg) = ns_bvci;
127
Harald Welte9ba50052010-03-14 15:45:01 +0800128 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
129 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
130
Harald Welte24a655f2010-04-30 19:54:29 +0200131 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800132}
133
Harald Weltea8aa4df2010-05-30 22:00:53 +0200134/* 10.3.7 SUSPEND-ACK PDU */
135int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli,
136 const struct gprs_ra_id *ra_id, uint8_t suspend_ref)
137{
138 struct msgb *msg = bssgp_msgb_alloc();
139 struct bssgp_normal_hdr *bgph =
140 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
141 uint32_t _tlli;
142 uint8_t ra[6];
143
144 msgb_nsei(msg) = nsei;
145 msgb_bvci(msg) = 0; /* Signalling */
146 bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK;
147
148 _tlli = htonl(tlli);
149 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
150 gsm48_construct_ra(ra, ra_id);
151 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
152 msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
153
154 return gprs_ns_sendmsg(bssgp_nsi, msg);
155}
156
157/* 10.3.8 SUSPEND-NACK PDU */
158int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli,
159 uint8_t *cause)
160{
161 struct msgb *msg = bssgp_msgb_alloc();
162 struct bssgp_normal_hdr *bgph =
163 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
164 uint32_t _tlli;
165
166 msgb_nsei(msg) = nsei;
167 msgb_bvci(msg) = 0; /* Signalling */
168 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
169
170 _tlli = htonl(tlli);
171 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
172 if (cause)
173 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
174
175 return gprs_ns_sendmsg(bssgp_nsi, msg);
176}
177
178/* 10.3.10 RESUME-ACK PDU */
179int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
180 const struct gprs_ra_id *ra_id)
181{
182 struct msgb *msg = bssgp_msgb_alloc();
183 struct bssgp_normal_hdr *bgph =
184 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
185 uint32_t _tlli;
186 uint8_t ra[6];
187
188 msgb_nsei(msg) = nsei;
189 msgb_bvci(msg) = 0; /* Signalling */
190 bgph->pdu_type = BSSGP_PDUT_RESUME_ACK;
191
192 _tlli = htonl(tlli);
193 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
194 gsm48_construct_ra(ra, ra_id);
195 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
196
197 return gprs_ns_sendmsg(bssgp_nsi, msg);
198}
199
200/* 10.3.11 RESUME-NACK PDU */
201int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
202 const struct gprs_ra_id *ra_id, uint8_t *cause)
203{
204 struct msgb *msg = bssgp_msgb_alloc();
205 struct bssgp_normal_hdr *bgph =
206 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
207 uint32_t _tlli;
208 uint8_t ra[6];
209
210 msgb_nsei(msg) = nsei;
211 msgb_bvci(msg) = 0; /* Signalling */
212 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
213
214 _tlli = htonl(tlli);
215 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
216 gsm48_construct_ra(ra, ra_id);
217 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
218 if (cause)
219 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
220
221 return gprs_ns_sendmsg(bssgp_nsi, msg);
222}
223
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200224uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
Harald Welte6752fa42010-05-02 09:23:16 +0200225{
226 /* 6 octets RAC */
227 gsm48_parse_ra(raid, buf);
228 /* 2 octets CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200229 return ntohs(*(uint16_t *) (buf+6));
Harald Welte6752fa42010-05-02 09:23:16 +0200230}
231
Harald Welte3fddf3c2010-05-01 16:48:27 +0200232/* Chapter 8.4 BVC-Reset Procedure */
233static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
234 uint16_t ns_bvci)
235{
Harald Welte8a521132010-05-17 22:59:29 +0200236 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200237 uint16_t nsei = msgb_nsei(msg);
238 uint16_t bvci;
Harald Welte3fddf3c2010-05-01 16:48:27 +0200239 int rc;
240
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200241 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Weltee9686b62010-05-31 18:07:17 +0200242 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
Harald Welte3fddf3c2010-05-01 16:48:27 +0200243 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
244
Harald Welte6752fa42010-05-02 09:23:16 +0200245 /* look-up or create the BTS context for this BVC */
246 bctx = btsctx_by_bvci_nsei(bvci, nsei);
247 if (!bctx)
248 bctx = btsctx_alloc(bvci, nsei);
249
Harald Welte25de8112010-05-13 21:26:28 +0200250 /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
251 bctx->state &= ~BVC_S_BLOCKED;
252
Harald Welte3fddf3c2010-05-01 16:48:27 +0200253 /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
254 * informs us about its RAC + Cell ID, so we can create a mapping */
Harald Welte6752fa42010-05-02 09:23:16 +0200255 if (bvci != 0 && bvci != 1) {
256 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
Harald Weltee9686b62010-05-31 18:07:17 +0200257 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u RESET "
Harald Welte6752fa42010-05-02 09:23:16 +0200258 "missing mandatory IE\n", bvci);
259 return -EINVAL;
260 }
261 /* actually extract RAC / CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200262 bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
263 TLVP_VAL(tp, BSSGP_IE_CELL_ID));
Harald Welteb8a6a832010-05-11 05:54:22 +0200264 LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
Harald Welte6752fa42010-05-02 09:23:16 +0200265 bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
266 bctx->ra_id.rac, bctx->cell_id, bvci);
267 }
Harald Welte3fddf3c2010-05-01 16:48:27 +0200268
Harald Welte6752fa42010-05-02 09:23:16 +0200269 /* Acknowledge the RESET to the BTS */
Harald Welte3fddf3c2010-05-01 16:48:27 +0200270 rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
Harald Welte6752fa42010-05-02 09:23:16 +0200271 nsei, bvci, ns_bvci);
Harald Welte3fddf3c2010-05-01 16:48:27 +0200272 return 0;
273}
274
Harald Welte25de8112010-05-13 21:26:28 +0200275static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
276{
277 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200278 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200279
280 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200281 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200282 /* 8.3.2: Signalling BVC shall never be blocked */
283 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
284 "received block for signalling BVC!?!\n",
285 msgb_nsei(msg), msgb_bvci(msg));
286 return 0;
287 }
Harald Welte25de8112010-05-13 21:26:28 +0200288
Harald Weltee9686b62010-05-31 18:07:17 +0200289 LOGP(DBSSGP, LOGL_INFO, "BSSGP BVCI=%u BVC-BLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200290
291 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
292 if (!ptp_ctx)
293 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
294
295 ptp_ctx->state |= BVC_S_BLOCKED;
296 rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
297
298 /* FIXME: Send NM_BVC_BLOCK.ind to NM */
299
300 /* We always acknowledge the BLOCKing */
301 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
302 bvci, msgb_bvci(msg));
303};
304
305static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
306{
307 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200308 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200309
310 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200311 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200312 /* 8.3.2: Signalling BVC shall never be blocked */
313 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
314 "received unblock for signalling BVC!?!\n",
315 msgb_nsei(msg), msgb_bvci(msg));
316 return 0;
317 }
Harald Welte25de8112010-05-13 21:26:28 +0200318
Harald Weltee9686b62010-05-31 18:07:17 +0200319 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200320
321 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
322 if (!ptp_ctx)
323 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
324
325 ptp_ctx->state &= ~BVC_S_BLOCKED;
326
327 /* FIXME: Send NM_BVC_UNBLOCK.ind to NM */
328
329 /* We always acknowledge the unBLOCKing */
330 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
331 bvci, msgb_bvci(msg));
332};
333
Harald Welte9ba50052010-03-14 15:45:01 +0800334/* Uplink unit-data */
Harald Welte25de8112010-05-13 21:26:28 +0200335static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200336 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800337{
Harald Welteec19c102010-05-02 09:50:42 +0200338 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800339
Harald Welte6752fa42010-05-02 09:23:16 +0200340 /* extract TLLI and parse TLV IEs */
Harald Welte510c3922010-04-30 16:33:12 +0200341 msgb_tlli(msg) = ntohl(budh->tlli);
Harald Welte9ba50052010-03-14 15:45:01 +0800342
Harald Weltee9686b62010-05-31 18:07:17 +0200343 DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x UPLINK-UNITDATA\n", msgb_tlli(msg));
344
Harald Welte9ba50052010-03-14 15:45:01 +0800345 /* Cell ID and LLC_PDU are the only mandatory IE */
Harald Welte25de8112010-05-13 21:26:28 +0200346 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200347 !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
348 LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
349 "missing mandatory IE\n", msgb_tlli(msg));
Harald Welte25de8112010-05-13 21:26:28 +0200350 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200351 }
Harald Welte30bc19a2010-05-02 11:19:37 +0200352
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200353 /* store pointer to LLC header and CELL ID in msgb->cb */
Holger Hans Peter Freytherb6eded82010-05-23 21:11:19 +0800354 msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
355 msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
Harald Welte9ba50052010-03-14 15:45:01 +0800356
Harald Welte25de8112010-05-13 21:26:28 +0200357 return gprs_llc_rcvmsg(msg, tp);
Harald Welte9ba50052010-03-14 15:45:01 +0800358}
359
Harald Welte25de8112010-05-13 21:26:28 +0200360static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200361 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800362{
Harald Welteec19c102010-05-02 09:50:42 +0200363 struct bssgp_normal_hdr *bgph =
364 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200365 struct gprs_ra_id raid;
366 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200367 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800368
Harald Welte25de8112010-05-13 21:26:28 +0200369 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200370 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
371 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
372 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200373 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200374 }
Harald Welte9ba50052010-03-14 15:45:01 +0800375
Harald Weltea8aa4df2010-05-30 22:00:53 +0200376 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200377
Harald Welte17925322010-05-31 20:18:35 +0200378 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200379 ctx->bvci, tlli);
380
Harald Weltea8aa4df2010-05-30 22:00:53 +0200381 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
382
Harald Welte313cccf2010-06-09 11:22:47 +0200383 /* Inform GMM about the SUSPEND request */
384 rc = gprs_gmm_rx_suspend(&raid, tlli);
385 if (rc < 0)
386 return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, NULL);
387
Harald Weltea8aa4df2010-05-30 22:00:53 +0200388 bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
389
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800390 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800391}
392
Harald Welte25de8112010-05-13 21:26:28 +0200393static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200394 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800395{
Harald Welteec19c102010-05-02 09:50:42 +0200396 struct bssgp_normal_hdr *bgph =
397 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200398 struct gprs_ra_id raid;
399 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200400 uint8_t suspend_ref;
401 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800402
Harald Welte25de8112010-05-13 21:26:28 +0200403 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
404 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200405 !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
406 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
407 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200408 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200409 }
Harald Welte9ba50052010-03-14 15:45:01 +0800410
Harald Weltea8aa4df2010-05-30 22:00:53 +0200411 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Welte313cccf2010-06-09 11:22:47 +0200412 suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
Harald Weltee9686b62010-05-31 18:07:17 +0200413
414 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x RESUME\n", ctx->bvci, tlli);
415
Harald Weltea8aa4df2010-05-30 22:00:53 +0200416 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
417
Harald Welte313cccf2010-06-09 11:22:47 +0200418 /* Inform GMM about the RESUME request */
419 rc = gprs_gmm_rx_resume(&raid, tlli, suspend_ref);
420 if (rc < 0)
421 return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid,
422 NULL);
423
Harald Weltea8aa4df2010-05-30 22:00:53 +0200424 bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800425 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800426}
427
Harald Weltee9686b62010-05-31 18:07:17 +0200428
429static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
430 struct bssgp_bvc_ctx *ctx)
431{
Harald Welteb7363142010-07-23 21:59:29 +0200432 uint32_t tlli = 0;
Harald Weltee9686b62010-05-31 18:07:17 +0200433
434 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
435 !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
436 !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
437 !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
438 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
439 "missing mandatory IE\n", ctx->bvci);
440 }
441
Harald Welteb7363142010-07-23 21:59:29 +0200442 if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
443 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200444
Harald Welte0e430072010-06-29 18:34:26 +0200445 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x LLC DISCARDED\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200446 ctx->bvci, tlli);
447
448 rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
449
450 /* FIXME: send NM_LLC_DISCARDED to NM */
451 return 0;
452}
453
Harald Welte25de8112010-05-13 21:26:28 +0200454static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200455 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800456{
457
Harald Weltee9686b62010-05-31 18:07:17 +0200458 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
459 bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800460
461 if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
462 !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
463 !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
464 !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200465 !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
466 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
467 "missing mandatory IE\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800468 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200469 }
Harald Welte9ba50052010-03-14 15:45:01 +0800470
Harald Welte30bc19a2010-05-02 11:19:37 +0200471 /* FIXME: actually implement flow control */
472
Harald Welte9ba50052010-03-14 15:45:01 +0800473 /* Send FLOW_CONTROL_BVC_ACK */
Harald Welte24a655f2010-04-30 19:54:29 +0200474 return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
Harald Welte30bc19a2010-05-02 11:19:37 +0200475 msgb_bvci(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800476}
Harald Welte3fddf3c2010-05-01 16:48:27 +0200477
Harald Welte25de8112010-05-13 21:26:28 +0200478/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
479static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200480 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800481{
Harald Welteec19c102010-05-02 09:50:42 +0200482 struct bssgp_normal_hdr *bgph =
483 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Welte30bc19a2010-05-02 11:19:37 +0200484 uint8_t pdu_type = bgph->pdu_type;
Harald Welte9ba50052010-03-14 15:45:01 +0800485 int rc = 0;
486
Harald Welte58e65c92010-05-13 21:45:23 +0200487 /* If traffic is received on a BVC that is marked as blocked, the
488 * received PDU shall not be accepted and a STATUS PDU (Cause value:
489 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
490 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
491 uint16_t bvci = msgb_bvci(msg);
492 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
493 }
494
Harald Welte9ba50052010-03-14 15:45:01 +0800495 switch (pdu_type) {
496 case BSSGP_PDUT_UL_UNITDATA:
497 /* some LLC data from the MS */
Harald Welte25de8112010-05-13 21:26:28 +0200498 rc = bssgp_rx_ul_ud(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800499 break;
500 case BSSGP_PDUT_RA_CAPABILITY:
501 /* BSS requests RA capability or IMSI */
Harald Weltee9686b62010-05-31 18:07:17 +0200502 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
503 bctx->bvci);
Harald Welte6b7cf252010-05-13 19:41:31 +0200504 /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800505 /* FIXME: send RA_CAPA_UPDATE_ACK */
506 break;
507 case BSSGP_PDUT_RADIO_STATUS:
Harald Weltee9686b62010-05-31 18:07:17 +0200508 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800509 /* BSS informs us of some exception */
Harald Welte6b7cf252010-05-13 19:41:31 +0200510 /* FIXME: send GMM_RADIO_STATUS.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800511 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800512 case BSSGP_PDUT_FLOW_CONTROL_BVC:
513 /* BSS informs us of available bandwidth in Gb interface */
Harald Welte25de8112010-05-13 21:26:28 +0200514 rc = bssgp_rx_fc_bvc(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800515 break;
516 case BSSGP_PDUT_FLOW_CONTROL_MS:
517 /* BSS informs us of available bandwidth to one MS */
Harald Weltee9686b62010-05-31 18:07:17 +0200518 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
519 bctx->bvci);
Harald Welte30bc19a2010-05-02 11:19:37 +0200520 /* FIXME: actually implement flow control */
521 /* FIXME: Send FLOW_CONTROL_MS_ACK */
Harald Welte9ba50052010-03-14 15:45:01 +0800522 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800523 case BSSGP_PDUT_STATUS:
524 /* Some exception has occurred */
Harald Welte6b7cf252010-05-13 19:41:31 +0200525 /* FIXME: send NM_STATUS.ind to NM */
Harald Welte9ba50052010-03-14 15:45:01 +0800526 case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
527 case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
528 case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
529 case BSSGP_PDUT_MODIFY_BSS_PFC:
530 case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200531 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
532 "implemented\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200533 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800534 break;
535 /* those only exist in the SGSN -> BSS direction */
536 case BSSGP_PDUT_DL_UNITDATA:
537 case BSSGP_PDUT_PAGING_PS:
538 case BSSGP_PDUT_PAGING_CS:
539 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Harald Welte25de8112010-05-13 21:26:28 +0200540 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
541 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200542 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
543 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200544 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
545 rc = -EINVAL;
546 break;
547 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200548 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
549 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200550 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
551 break;
552 }
553
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800554 return rc;
Harald Welte25de8112010-05-13 21:26:28 +0200555}
556
557/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
558static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200559 struct bssgp_bvc_ctx *bctx)
Harald Welte25de8112010-05-13 21:26:28 +0200560{
561 struct bssgp_normal_hdr *bgph =
562 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
563 uint8_t pdu_type = bgph->pdu_type;
564 int rc = 0;
565 uint16_t ns_bvci = msgb_bvci(msg);
566 uint16_t bvci;
567
568 switch (bgph->pdu_type) {
569 case BSSGP_PDUT_SUSPEND:
570 /* MS wants to suspend */
571 rc = bssgp_rx_suspend(msg, tp, bctx);
572 break;
573 case BSSGP_PDUT_RESUME:
574 /* MS wants to resume */
575 rc = bssgp_rx_resume(msg, tp, bctx);
576 break;
577 case BSSGP_PDUT_FLUSH_LL_ACK:
578 /* BSS informs us it has performed LL FLUSH */
Harald Weltee9686b62010-05-31 18:07:17 +0200579 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx FLUSH LL ACK\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200580 /* FIXME: send NM_FLUSH_LL.res to NM */
581 break;
582 case BSSGP_PDUT_LLC_DISCARD:
583 /* BSS informs that some LLC PDU's have been discarded */
Harald Weltee9686b62010-05-31 18:07:17 +0200584 rc = bssgp_rx_llc_disc(msg, tp, bctx);
Harald Welte25de8112010-05-13 21:26:28 +0200585 break;
586 case BSSGP_PDUT_BVC_BLOCK:
587 /* BSS tells us that BVC shall be blocked */
Harald Welte25de8112010-05-13 21:26:28 +0200588 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200589 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
590 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
591 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200592 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200593 }
Harald Welte2677ea52010-05-31 17:16:36 +0200594 rc = bssgp_rx_bvc_block(msg, tp);
Harald Welte25de8112010-05-13 21:26:28 +0200595 break;
596 case BSSGP_PDUT_BVC_UNBLOCK:
597 /* BSS tells us that BVC shall be unblocked */
Harald Weltee9686b62010-05-31 18:07:17 +0200598 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
599 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
600 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200601 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200602 }
Harald Welte25de8112010-05-13 21:26:28 +0200603 rc = bssgp_rx_bvc_unblock(msg, tp);
604 break;
605 case BSSGP_PDUT_BVC_RESET:
606 /* BSS tells us that BVC init is required */
Harald Welte25de8112010-05-13 21:26:28 +0200607 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200608 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
609 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
610 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200611 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200612 }
Harald Welte25de8112010-05-13 21:26:28 +0200613 rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
614 break;
615 case BSSGP_PDUT_STATUS:
616 /* Some exception has occurred */
Harald Weltee9686b62010-05-31 18:07:17 +0200617 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200618 /* FIXME: send NM_STATUS.ind to NM */
619 break;
620 /* those only exist in the SGSN -> BSS direction */
621 case BSSGP_PDUT_PAGING_PS:
622 case BSSGP_PDUT_PAGING_CS:
Harald Welte9ba50052010-03-14 15:45:01 +0800623 case BSSGP_PDUT_SUSPEND_ACK:
624 case BSSGP_PDUT_SUSPEND_NACK:
625 case BSSGP_PDUT_RESUME_ACK:
626 case BSSGP_PDUT_RESUME_NACK:
Harald Welte6b7cf252010-05-13 19:41:31 +0200627 case BSSGP_PDUT_FLUSH_LL:
Harald Welte9ba50052010-03-14 15:45:01 +0800628 case BSSGP_PDUT_BVC_BLOCK_ACK:
629 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
630 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
Harald Weltee9686b62010-05-31 18:07:17 +0200631 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
632 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200633 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800634 rc = -EINVAL;
635 break;
636 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200637 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
638 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200639 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800640 break;
641 }
642
643 return rc;
644err_mand_ie:
645 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
646}
647
Harald Welte25de8112010-05-13 21:26:28 +0200648/* We expect msgb_bssgph() to point to the BSSGP header */
649int gprs_bssgp_rcvmsg(struct msgb *msg)
650{
651 struct bssgp_normal_hdr *bgph =
652 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
653 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
654 struct tlv_parsed tp;
Harald Welte8a521132010-05-17 22:59:29 +0200655 struct bssgp_bvc_ctx *bctx;
Harald Welte25de8112010-05-13 21:26:28 +0200656 uint8_t pdu_type = bgph->pdu_type;
657 uint16_t ns_bvci = msgb_bvci(msg);
658 int data_len;
659 int rc = 0;
660
661 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
662
663 /* UNITDATA BSSGP headers have TLLI in front */
664 if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
665 pdu_type != BSSGP_PDUT_DL_UNITDATA) {
666 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
667 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
668 } else {
669 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
670 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
671 }
672
673 /* look-up or create the BTS context for this BVC */
674 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
675 /* Only a RESET PDU can create a new BVC context */
676 if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
677 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
678 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
679 pdu_type);
680 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
681 }
682
Harald Welte16c8dbb2010-05-17 23:30:01 +0200683 if (bctx) {
Harald Welte4e5721d2010-05-17 23:41:43 +0200684 log_set_context(BSC_CTX_BVC, bctx);
Harald Welte16c8dbb2010-05-17 23:30:01 +0200685 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
686 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
687 msgb_bssgp_len(msg));
688 }
689
Harald Welte61c07842010-05-18 11:57:08 +0200690 if (ns_bvci == BVCI_SIGNALLING)
Harald Welte25de8112010-05-13 21:26:28 +0200691 rc = gprs_bssgp_rx_sign(msg, &tp, bctx);
Harald Welte61c07842010-05-18 11:57:08 +0200692 else if (ns_bvci == BVCI_PTM)
Harald Welte25de8112010-05-13 21:26:28 +0200693 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
694 else
695 rc = gprs_bssgp_rx_ptp(msg, &tp, bctx);
696
697 return rc;
698}
699
Harald Welte6752fa42010-05-02 09:23:16 +0200700/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
Harald Welte30bc19a2010-05-02 11:19:37 +0200701 * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
Harald Welte2f946832010-05-31 22:12:30 +0200702int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800703{
Harald Welte8a521132010-05-17 22:59:29 +0200704 struct bssgp_bvc_ctx *bctx;
Harald Welte9ba50052010-03-14 15:45:01 +0800705 struct bssgp_ud_hdr *budh;
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200706 uint8_t llc_pdu_tlv_hdr_len = 2;
707 uint8_t *llc_pdu_tlv, *qos_profile;
708 uint16_t pdu_lifetime = 1000; /* centi-seconds */
Harald Welte02f73252010-06-01 11:53:01 +0200709 uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 };
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200710 uint16_t msg_len = msg->len;
Harald Welte30bc19a2010-05-02 11:19:37 +0200711 uint16_t bvci = msgb_bvci(msg);
712 uint16_t nsei = msgb_nsei(msg);
Harald Welte2f946832010-05-31 22:12:30 +0200713 uint16_t drx_params;
Harald Welte9ba50052010-03-14 15:45:01 +0800714
Harald Welte30bc19a2010-05-02 11:19:37 +0200715 /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
Harald Welte61c07842010-05-18 11:57:08 +0200716 if (bvci <= BVCI_PTM ) {
Harald Welteb8a6a832010-05-11 05:54:22 +0200717 LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
Harald Welte30bc19a2010-05-02 11:19:37 +0200718 bvci);
719 return -EINVAL;
720 }
721
722 bctx = btsctx_by_bvci_nsei(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200723 if (!bctx) {
724 /* FIXME: don't simply create missing context, but reject message */
Harald Welte30bc19a2010-05-02 11:19:37 +0200725 bctx = btsctx_alloc(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200726 }
Harald Welte9ba50052010-03-14 15:45:01 +0800727
728 if (msg->len > TVLV_MAX_ONEBYTE)
729 llc_pdu_tlv_hdr_len += 1;
730
731 /* prepend the tag and length of the LLC-PDU TLV */
732 llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
733 llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
734 if (llc_pdu_tlv_hdr_len > 2) {
735 llc_pdu_tlv[1] = msg_len >> 8;
736 llc_pdu_tlv[2] = msg_len & 0xff;
737 } else {
Sylvain Munautb00d1ad2010-06-09 21:13:13 +0200738 llc_pdu_tlv[1] = msg_len & 0x7f;
Harald Welte9ba50052010-03-14 15:45:01 +0800739 llc_pdu_tlv[1] |= 0x80;
740 }
741
Harald Welte2f946832010-05-31 22:12:30 +0200742 /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
743
744 if (mmctx) {
745 /* Old TLLI to help BSS map from old->new */
746#if 0
747 if (mmctx->tlli_old)
748 msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old));
749#endif
750
751 /* IMSI */
752 if (strlen(mmctx->imsi)) {
753 uint8_t mi[10];
754 int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi);
755 if (imsi_len > 2)
756 msgb_tvlv_push(msg, BSSGP_IE_IMSI,
757 imsi_len-2, mi+2);
758 }
759
760 /* DRX parameters */
761 drx_params = htons(mmctx->drx_parms);
762 msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
763 (uint8_t *) &drx_params);
764
765 /* FIXME: Priority */
766
767 /* MS Radio Access Capability */
768 if (mmctx->ms_radio_access_capa.len)
769 msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
770 mmctx->ms_radio_access_capa.len,
771 mmctx->ms_radio_access_capa.buf);
772 }
Harald Welte9ba50052010-03-14 15:45:01 +0800773
774 /* prepend the pdu lifetime */
775 pdu_lifetime = htons(pdu_lifetime);
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200776 msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime);
Harald Welte9ba50052010-03-14 15:45:01 +0800777
778 /* prepend the QoS profile, TLLI and pdu type */
779 budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
780 memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default));
Harald Welte510c3922010-04-30 16:33:12 +0200781 budh->tlli = htonl(msgb_tlli(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800782 budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
783
Harald Welte16c8dbb2010-05-17 23:30:01 +0200784 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
785 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
786
Harald Welte30bc19a2010-05-02 11:19:37 +0200787 /* Identifiers down: BVCI, NSEI (in msgb->cb) */
Harald Welte24a655f2010-04-30 19:54:29 +0200788
789 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800790}
Harald Welte68b4f032010-06-09 16:22:28 +0200791
792/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
793int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
794 struct bssgp_paging_info *pinfo)
795{
796 struct msgb *msg = bssgp_msgb_alloc();
797 struct bssgp_normal_hdr *bgph =
798 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
799 uint16_t drx_params = htons(pinfo->drx_params);
800 uint8_t mi[10];
801 int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi);
802 uint8_t ra[6];
803
804 if (imsi_len < 2)
805 return -EINVAL;
806
807 msgb_nsei(msg) = nsei;
808 msgb_bvci(msg) = ns_bvci;
809
810 if (pinfo->mode == BSSGP_PAGING_PS)
811 bgph->pdu_type = BSSGP_PDUT_PAGING_PS;
812 else
813 bgph->pdu_type = BSSGP_PDUT_PAGING_CS;
814 /* IMSI */
815 msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
816 /* DRX Parameters */
817 msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2,
818 (uint8_t *) &drx_params);
819 /* Scope */
820 switch (pinfo->scope) {
821 case BSSGP_PAGING_BSS_AREA:
822 {
823 uint8_t null = 0;
824 msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null);
825 }
826 break;
827 case BSSGP_PAGING_LOCATION_AREA:
828 gsm48_construct_ra(ra, &pinfo->raid);
829 msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra);
830 break;
831 case BSSGP_PAGING_ROUTEING_AREA:
832 gsm48_construct_ra(ra, &pinfo->raid);
833 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
834 break;
835 case BSSGP_PAGING_BVCI:
836 {
837 uint16_t bvci = htons(pinfo->bvci);
838 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci);
839 }
840 break;
841 }
842 /* QoS profile mandatory for PS */
843 if (pinfo->mode == BSSGP_PAGING_PS)
844 msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos);
845
846 /* Optional (P-)TMSI */
847 if (pinfo->ptmsi) {
848 uint32_t ptmsi = htonl(*pinfo->ptmsi);
849 msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
850 }
851
852 return gprs_ns_sendmsg(bssgp_nsi, msg);
853}