blob: f26ff163ce53a5a0a9ac013d08c7f2622d02d406 [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
Harald Weltee4cbb3f2011-01-01 15:25:50 +01008 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
Harald Welte9ba50052010-03-14 15:45:01 +080010 * (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
Harald Weltee4cbb3f2011-01-01 15:25:50 +010015 * GNU Affero General Public License for more details.
Harald Welte9ba50052010-03-14 15:45:01 +080016 *
Harald Weltee4cbb3f2011-01-01 15:25:50 +010017 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte9ba50052010-03-14 15:45:01 +080019 *
Harald Welte4e5721d2010-05-17 23:41:43 +020020 * TODO:
21 * o properly count incoming BVC-RESET packets in counter group
22 * o set log context as early as possible for outgoing packets
Harald Welte9ba50052010-03-14 15:45:01 +080023 */
24
25#include <errno.h>
Harald Welte8f9a3ee2010-05-02 11:26:34 +020026#include <stdint.h>
Harald Welte9ba50052010-03-14 15:45:01 +080027
28#include <netinet/in.h>
29
Pablo Neira Ayusoff663232011-03-22 16:47:59 +010030#include <osmocom/core/msgb.h>
31#include <osmocom/gsm/tlv.h>
32#include <osmocom/core/talloc.h>
33#include <osmocom/core/rate_ctr.h>
Harald Welte6752fa42010-05-02 09:23:16 +020034
Harald Welte9ba50052010-03-14 15:45:01 +080035#include <openbsc/debug.h>
36#include <openbsc/gsm_data.h>
37#include <openbsc/gsm_04_08_gprs.h>
38#include <openbsc/gprs_bssgp.h>
39#include <openbsc/gprs_llc.h>
40#include <openbsc/gprs_ns.h>
Harald Welte2f946832010-05-31 22:12:30 +020041#include <openbsc/gprs_sgsn.h>
Harald Welte313cccf2010-06-09 11:22:47 +020042#include <openbsc/gprs_gmm.h>
Harald Welte9ba50052010-03-14 15:45:01 +080043
Harald Welte6752fa42010-05-02 09:23:16 +020044void *bssgp_tall_ctx = NULL;
45
Harald Welte6752fa42010-05-02 09:23:16 +020046#define BVC_F_BLOCKED 0x0001
47
Harald Welte25de8112010-05-13 21:26:28 +020048enum bssgp_ctr {
Harald Welte16c8dbb2010-05-17 23:30:01 +020049 BSSGP_CTR_PKTS_IN,
50 BSSGP_CTR_PKTS_OUT,
51 BSSGP_CTR_BYTES_IN,
52 BSSGP_CTR_BYTES_OUT,
Harald Welte25de8112010-05-13 21:26:28 +020053 BSSGP_CTR_BLOCKED,
54 BSSGP_CTR_DISCARDED,
55};
56
57static const struct rate_ctr_desc bssgp_ctr_description[] = {
Harald Welte16c8dbb2010-05-17 23:30:01 +020058 { "packets.in", "Packets at BSSGP Level ( In)" },
59 { "packets.out","Packets at BSSGP Level (Out)" },
60 { "bytes.in", "Bytes at BSSGP Level ( In)" },
61 { "bytes.out", "Bytes at BSSGP Level (Out)" },
Harald Welte25de8112010-05-13 21:26:28 +020062 { "blocked", "BVC Blocking count" },
63 { "discarded", "BVC LLC Discarded count" },
64};
65
66static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
67 .group_name_prefix = "bssgp.bss_ctx",
68 .group_description = "BSSGP Peer Statistics",
69 .num_ctr = ARRAY_SIZE(bssgp_ctr_description),
70 .ctr_desc = bssgp_ctr_description,
71};
72
Harald Weltea78b9c22010-05-17 23:02:42 +020073LLIST_HEAD(bssgp_bvc_ctxts);
Harald Welte6752fa42010-05-02 09:23:16 +020074
75/* Find a BTS Context based on parsed RA ID and Cell ID */
Harald Welte8a521132010-05-17 22:59:29 +020076struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
Harald Welte6752fa42010-05-02 09:23:16 +020077{
Harald Welte8a521132010-05-17 22:59:29 +020078 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +020079
Harald Weltea78b9c22010-05-17 23:02:42 +020080 llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
Harald Welte6752fa42010-05-02 09:23:16 +020081 if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) &&
82 bctx->cell_id == cid)
83 return bctx;
84 }
85 return NULL;
86}
87
88/* Find a BTS context based on BVCI+NSEI tuple */
Harald Welte8a521132010-05-17 22:59:29 +020089struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
Harald Welte6752fa42010-05-02 09:23:16 +020090{
Harald Welte8a521132010-05-17 22:59:29 +020091 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +020092
Harald Weltea78b9c22010-05-17 23:02:42 +020093 llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
Harald Welte6752fa42010-05-02 09:23:16 +020094 if (bctx->nsei == nsei && bctx->bvci == bvci)
95 return bctx;
96 }
97 return NULL;
98}
99
Harald Welte8a521132010-05-17 22:59:29 +0200100struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
Harald Welte6752fa42010-05-02 09:23:16 +0200101{
Harald Welte8a521132010-05-17 22:59:29 +0200102 struct bssgp_bvc_ctx *ctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200103
Harald Welte8a521132010-05-17 22:59:29 +0200104 ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx);
Harald Welte6752fa42010-05-02 09:23:16 +0200105 if (!ctx)
106 return NULL;
107 ctx->bvci = bvci;
108 ctx->nsei = nsei;
Harald Welte25de8112010-05-13 21:26:28 +0200109 /* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */
110 ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci);
111
Harald Weltea78b9c22010-05-17 23:02:42 +0200112 llist_add(&ctx->list, &bssgp_bvc_ctxts);
Harald Welte6752fa42010-05-02 09:23:16 +0200113
114 return ctx;
115}
116
Harald Welte9ba50052010-03-14 15:45:01 +0800117/* Chapter 10.4.5: Flow Control BVC ACK */
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200118static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci)
Harald Welte9ba50052010-03-14 15:45:01 +0800119{
120 struct msgb *msg = bssgp_msgb_alloc();
121 struct bssgp_normal_hdr *bgph =
122 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
123
Harald Welte24a655f2010-04-30 19:54:29 +0200124 msgb_nsei(msg) = nsei;
125 msgb_bvci(msg) = ns_bvci;
126
Harald Welte9ba50052010-03-14 15:45:01 +0800127 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
128 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
129
Harald Welte24a655f2010-04-30 19:54:29 +0200130 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800131}
132
Harald Weltea8aa4df2010-05-30 22:00:53 +0200133/* 10.3.7 SUSPEND-ACK PDU */
134int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli,
135 const struct gprs_ra_id *ra_id, uint8_t suspend_ref)
136{
137 struct msgb *msg = bssgp_msgb_alloc();
138 struct bssgp_normal_hdr *bgph =
139 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
140 uint32_t _tlli;
141 uint8_t ra[6];
142
143 msgb_nsei(msg) = nsei;
144 msgb_bvci(msg) = 0; /* Signalling */
145 bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK;
146
147 _tlli = htonl(tlli);
148 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
149 gsm48_construct_ra(ra, ra_id);
150 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
151 msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
152
153 return gprs_ns_sendmsg(bssgp_nsi, msg);
154}
155
156/* 10.3.8 SUSPEND-NACK PDU */
157int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli,
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100158 const struct gprs_ra_id *ra_id,
Harald Weltea8aa4df2010-05-30 22:00:53 +0200159 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;
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100165 uint8_t ra[6];
Harald Weltea8aa4df2010-05-30 22:00:53 +0200166
167 msgb_nsei(msg) = nsei;
168 msgb_bvci(msg) = 0; /* Signalling */
169 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
170
171 _tlli = htonl(tlli);
172 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100173 gsm48_construct_ra(ra, ra_id);
174 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200175 if (cause)
176 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
177
178 return gprs_ns_sendmsg(bssgp_nsi, msg);
179}
180
181/* 10.3.10 RESUME-ACK PDU */
182int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
183 const struct gprs_ra_id *ra_id)
184{
185 struct msgb *msg = bssgp_msgb_alloc();
186 struct bssgp_normal_hdr *bgph =
187 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
188 uint32_t _tlli;
189 uint8_t ra[6];
190
191 msgb_nsei(msg) = nsei;
192 msgb_bvci(msg) = 0; /* Signalling */
193 bgph->pdu_type = BSSGP_PDUT_RESUME_ACK;
194
195 _tlli = htonl(tlli);
196 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
197 gsm48_construct_ra(ra, ra_id);
198 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
199
200 return gprs_ns_sendmsg(bssgp_nsi, msg);
201}
202
203/* 10.3.11 RESUME-NACK PDU */
204int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
205 const struct gprs_ra_id *ra_id, uint8_t *cause)
206{
207 struct msgb *msg = bssgp_msgb_alloc();
208 struct bssgp_normal_hdr *bgph =
209 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
210 uint32_t _tlli;
211 uint8_t ra[6];
212
213 msgb_nsei(msg) = nsei;
214 msgb_bvci(msg) = 0; /* Signalling */
215 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
216
217 _tlli = htonl(tlli);
218 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
219 gsm48_construct_ra(ra, ra_id);
220 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
221 if (cause)
222 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
223
224 return gprs_ns_sendmsg(bssgp_nsi, msg);
225}
226
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200227uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
Harald Welte6752fa42010-05-02 09:23:16 +0200228{
229 /* 6 octets RAC */
230 gsm48_parse_ra(raid, buf);
231 /* 2 octets CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200232 return ntohs(*(uint16_t *) (buf+6));
Harald Welte6752fa42010-05-02 09:23:16 +0200233}
234
Harald Welte28610072011-11-24 21:32:07 +0100235int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
236 uint16_t cid)
237{
238 uint16_t *out_cid = (uint16_t *) (buf + 6);
239 /* 6 octets RAC */
240 gsm48_construct_ra(buf, raid);
241 /* 2 octets CID */
242 *out_cid = htons(cid);
243
244 return 8;
245}
246
Harald Welte3fddf3c2010-05-01 16:48:27 +0200247/* Chapter 8.4 BVC-Reset Procedure */
248static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
249 uint16_t ns_bvci)
250{
Harald Welte8a521132010-05-17 22:59:29 +0200251 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200252 uint16_t nsei = msgb_nsei(msg);
253 uint16_t bvci;
Harald Welte3fddf3c2010-05-01 16:48:27 +0200254 int rc;
255
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200256 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Weltee9686b62010-05-31 18:07:17 +0200257 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
Harald Welte3fddf3c2010-05-01 16:48:27 +0200258 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
259
Harald Welte6752fa42010-05-02 09:23:16 +0200260 /* look-up or create the BTS context for this BVC */
261 bctx = btsctx_by_bvci_nsei(bvci, nsei);
262 if (!bctx)
263 bctx = btsctx_alloc(bvci, nsei);
264
Harald Welte25de8112010-05-13 21:26:28 +0200265 /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
266 bctx->state &= ~BVC_S_BLOCKED;
267
Harald Welte3fddf3c2010-05-01 16:48:27 +0200268 /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
269 * informs us about its RAC + Cell ID, so we can create a mapping */
Harald Welte6752fa42010-05-02 09:23:16 +0200270 if (bvci != 0 && bvci != 1) {
271 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
Harald Welte086fe322011-08-19 16:45:19 +0200272 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
Harald Welte6752fa42010-05-02 09:23:16 +0200273 "missing mandatory IE\n", bvci);
274 return -EINVAL;
275 }
276 /* actually extract RAC / CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200277 bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
278 TLVP_VAL(tp, BSSGP_IE_CELL_ID));
Harald Welteb8a6a832010-05-11 05:54:22 +0200279 LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
Harald Welte6752fa42010-05-02 09:23:16 +0200280 bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
281 bctx->ra_id.rac, bctx->cell_id, bvci);
282 }
Harald Welte3fddf3c2010-05-01 16:48:27 +0200283
Harald Welte6752fa42010-05-02 09:23:16 +0200284 /* Acknowledge the RESET to the BTS */
Harald Welte3fddf3c2010-05-01 16:48:27 +0200285 rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
Harald Welte6752fa42010-05-02 09:23:16 +0200286 nsei, bvci, ns_bvci);
Harald Welte3fddf3c2010-05-01 16:48:27 +0200287 return 0;
288}
289
Harald Welte25de8112010-05-13 21:26:28 +0200290static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
291{
292 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200293 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200294
295 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200296 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200297 /* 8.3.2: Signalling BVC shall never be blocked */
298 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
299 "received block for signalling BVC!?!\n",
300 msgb_nsei(msg), msgb_bvci(msg));
301 return 0;
302 }
Harald Welte25de8112010-05-13 21:26:28 +0200303
Harald Welte086fe322011-08-19 16:45:19 +0200304 LOGP(DBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200305
306 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
307 if (!ptp_ctx)
308 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
309
310 ptp_ctx->state |= BVC_S_BLOCKED;
311 rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
312
313 /* FIXME: Send NM_BVC_BLOCK.ind to NM */
314
315 /* We always acknowledge the BLOCKing */
316 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
317 bvci, msgb_bvci(msg));
318};
319
320static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
321{
322 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200323 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200324
325 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200326 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200327 /* 8.3.2: Signalling BVC shall never be blocked */
328 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
329 "received unblock for signalling BVC!?!\n",
330 msgb_nsei(msg), msgb_bvci(msg));
331 return 0;
332 }
Harald Welte25de8112010-05-13 21:26:28 +0200333
Harald Weltee9686b62010-05-31 18:07:17 +0200334 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200335
336 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
337 if (!ptp_ctx)
338 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
339
340 ptp_ctx->state &= ~BVC_S_BLOCKED;
341
342 /* FIXME: Send NM_BVC_UNBLOCK.ind to NM */
343
344 /* We always acknowledge the unBLOCKing */
345 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
346 bvci, msgb_bvci(msg));
347};
348
Harald Welte9ba50052010-03-14 15:45:01 +0800349/* Uplink unit-data */
Harald Welte25de8112010-05-13 21:26:28 +0200350static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200351 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800352{
Harald Welteec19c102010-05-02 09:50:42 +0200353 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800354
Harald Welte6752fa42010-05-02 09:23:16 +0200355 /* extract TLLI and parse TLV IEs */
Harald Welte510c3922010-04-30 16:33:12 +0200356 msgb_tlli(msg) = ntohl(budh->tlli);
Harald Welte9ba50052010-03-14 15:45:01 +0800357
Harald Welte086fe322011-08-19 16:45:19 +0200358 DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
Harald Weltee9686b62010-05-31 18:07:17 +0200359
Harald Welte9ba50052010-03-14 15:45:01 +0800360 /* Cell ID and LLC_PDU are the only mandatory IE */
Harald Welte25de8112010-05-13 21:26:28 +0200361 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200362 !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
363 LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
364 "missing mandatory IE\n", msgb_tlli(msg));
Harald Welte25de8112010-05-13 21:26:28 +0200365 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200366 }
Harald Welte30bc19a2010-05-02 11:19:37 +0200367
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200368 /* store pointer to LLC header and CELL ID in msgb->cb */
Holger Hans Peter Freytherb6eded82010-05-23 21:11:19 +0800369 msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
370 msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
Harald Welte9ba50052010-03-14 15:45:01 +0800371
Harald Welte25de8112010-05-13 21:26:28 +0200372 return gprs_llc_rcvmsg(msg, tp);
Harald Welte9ba50052010-03-14 15:45:01 +0800373}
374
Harald Welte25de8112010-05-13 21:26:28 +0200375static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200376 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800377{
Harald Welteec19c102010-05-02 09:50:42 +0200378 struct bssgp_normal_hdr *bgph =
379 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200380 struct gprs_ra_id raid;
381 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200382 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800383
Harald Welte25de8112010-05-13 21:26:28 +0200384 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200385 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
386 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
387 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200388 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200389 }
Harald Welte9ba50052010-03-14 15:45:01 +0800390
Harald Weltea8aa4df2010-05-30 22:00:53 +0200391 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200392
Harald Welte17925322010-05-31 20:18:35 +0200393 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200394 ctx->bvci, tlli);
395
Harald Weltea8aa4df2010-05-30 22:00:53 +0200396 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
397
Harald Welte313cccf2010-06-09 11:22:47 +0200398 /* Inform GMM about the SUSPEND request */
399 rc = gprs_gmm_rx_suspend(&raid, tlli);
400 if (rc < 0)
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100401 return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL);
Harald Welte313cccf2010-06-09 11:22:47 +0200402
Harald Weltea8aa4df2010-05-30 22:00:53 +0200403 bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
404
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800405 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800406}
407
Harald Welte25de8112010-05-13 21:26:28 +0200408static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200409 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800410{
Harald Welteec19c102010-05-02 09:50:42 +0200411 struct bssgp_normal_hdr *bgph =
412 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200413 struct gprs_ra_id raid;
414 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200415 uint8_t suspend_ref;
416 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800417
Harald Welte25de8112010-05-13 21:26:28 +0200418 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
419 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200420 !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
421 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
422 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200423 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200424 }
Harald Welte9ba50052010-03-14 15:45:01 +0800425
Harald Weltea8aa4df2010-05-30 22:00:53 +0200426 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Welte313cccf2010-06-09 11:22:47 +0200427 suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
Harald Weltee9686b62010-05-31 18:07:17 +0200428
Harald Welte086fe322011-08-19 16:45:19 +0200429 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ctx->bvci, tlli);
Harald Weltee9686b62010-05-31 18:07:17 +0200430
Harald Weltea8aa4df2010-05-30 22:00:53 +0200431 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
432
Harald Welte313cccf2010-06-09 11:22:47 +0200433 /* Inform GMM about the RESUME request */
434 rc = gprs_gmm_rx_resume(&raid, tlli, suspend_ref);
435 if (rc < 0)
436 return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid,
437 NULL);
438
Harald Weltea8aa4df2010-05-30 22:00:53 +0200439 bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800440 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800441}
442
Harald Weltee9686b62010-05-31 18:07:17 +0200443
444static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
445 struct bssgp_bvc_ctx *ctx)
446{
Harald Welteb7363142010-07-23 21:59:29 +0200447 uint32_t tlli = 0;
Harald Weltee9686b62010-05-31 18:07:17 +0200448
449 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
450 !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
451 !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
452 !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
453 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
454 "missing mandatory IE\n", ctx->bvci);
455 }
456
Harald Welteb7363142010-07-23 21:59:29 +0200457 if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
458 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200459
Harald Welte086fe322011-08-19 16:45:19 +0200460 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200461 ctx->bvci, tlli);
462
463 rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
464
465 /* FIXME: send NM_LLC_DISCARDED to NM */
466 return 0;
467}
468
Harald Welte25de8112010-05-13 21:26:28 +0200469static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200470 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800471{
472
Harald Weltee9686b62010-05-31 18:07:17 +0200473 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
474 bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800475
476 if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
477 !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
478 !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
479 !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200480 !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
481 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
482 "missing mandatory IE\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800483 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200484 }
Harald Welte9ba50052010-03-14 15:45:01 +0800485
Harald Welte30bc19a2010-05-02 11:19:37 +0200486 /* FIXME: actually implement flow control */
487
Harald Welte9ba50052010-03-14 15:45:01 +0800488 /* Send FLOW_CONTROL_BVC_ACK */
Harald Welte24a655f2010-04-30 19:54:29 +0200489 return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
Harald Welte30bc19a2010-05-02 11:19:37 +0200490 msgb_bvci(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800491}
Harald Welte3fddf3c2010-05-01 16:48:27 +0200492
Harald Welte25de8112010-05-13 21:26:28 +0200493/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
494static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200495 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800496{
Harald Welteec19c102010-05-02 09:50:42 +0200497 struct bssgp_normal_hdr *bgph =
498 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Welte30bc19a2010-05-02 11:19:37 +0200499 uint8_t pdu_type = bgph->pdu_type;
Harald Welte9ba50052010-03-14 15:45:01 +0800500 int rc = 0;
501
Harald Welte58e65c92010-05-13 21:45:23 +0200502 /* If traffic is received on a BVC that is marked as blocked, the
503 * received PDU shall not be accepted and a STATUS PDU (Cause value:
504 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
505 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
506 uint16_t bvci = msgb_bvci(msg);
507 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
508 }
509
Harald Welte9ba50052010-03-14 15:45:01 +0800510 switch (pdu_type) {
511 case BSSGP_PDUT_UL_UNITDATA:
512 /* some LLC data from the MS */
Harald Welte25de8112010-05-13 21:26:28 +0200513 rc = bssgp_rx_ul_ud(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800514 break;
515 case BSSGP_PDUT_RA_CAPABILITY:
516 /* BSS requests RA capability or IMSI */
Harald Weltee9686b62010-05-31 18:07:17 +0200517 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
518 bctx->bvci);
Harald Welte6b7cf252010-05-13 19:41:31 +0200519 /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800520 /* FIXME: send RA_CAPA_UPDATE_ACK */
521 break;
522 case BSSGP_PDUT_RADIO_STATUS:
Harald Weltee9686b62010-05-31 18:07:17 +0200523 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800524 /* BSS informs us of some exception */
Harald Welte6b7cf252010-05-13 19:41:31 +0200525 /* FIXME: send GMM_RADIO_STATUS.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800526 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800527 case BSSGP_PDUT_FLOW_CONTROL_BVC:
528 /* BSS informs us of available bandwidth in Gb interface */
Harald Welte25de8112010-05-13 21:26:28 +0200529 rc = bssgp_rx_fc_bvc(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800530 break;
531 case BSSGP_PDUT_FLOW_CONTROL_MS:
532 /* BSS informs us of available bandwidth to one MS */
Harald Weltee9686b62010-05-31 18:07:17 +0200533 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
534 bctx->bvci);
Harald Welte30bc19a2010-05-02 11:19:37 +0200535 /* FIXME: actually implement flow control */
536 /* FIXME: Send FLOW_CONTROL_MS_ACK */
Harald Welte9ba50052010-03-14 15:45:01 +0800537 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800538 case BSSGP_PDUT_STATUS:
539 /* Some exception has occurred */
Harald Welte6b7cf252010-05-13 19:41:31 +0200540 /* FIXME: send NM_STATUS.ind to NM */
Harald Welte9ba50052010-03-14 15:45:01 +0800541 case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
542 case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
543 case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
544 case BSSGP_PDUT_MODIFY_BSS_PFC:
545 case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200546 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
547 "implemented\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200548 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800549 break;
550 /* those only exist in the SGSN -> BSS direction */
551 case BSSGP_PDUT_DL_UNITDATA:
552 case BSSGP_PDUT_PAGING_PS:
553 case BSSGP_PDUT_PAGING_CS:
554 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Harald Welte25de8112010-05-13 21:26:28 +0200555 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
556 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200557 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
558 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200559 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
560 rc = -EINVAL;
561 break;
562 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200563 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
564 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200565 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
566 break;
567 }
568
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800569 return rc;
Harald Welte25de8112010-05-13 21:26:28 +0200570}
571
572/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
573static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200574 struct bssgp_bvc_ctx *bctx)
Harald Welte25de8112010-05-13 21:26:28 +0200575{
576 struct bssgp_normal_hdr *bgph =
577 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
578 uint8_t pdu_type = bgph->pdu_type;
579 int rc = 0;
580 uint16_t ns_bvci = msgb_bvci(msg);
581 uint16_t bvci;
582
583 switch (bgph->pdu_type) {
584 case BSSGP_PDUT_SUSPEND:
585 /* MS wants to suspend */
586 rc = bssgp_rx_suspend(msg, tp, bctx);
587 break;
588 case BSSGP_PDUT_RESUME:
589 /* MS wants to resume */
590 rc = bssgp_rx_resume(msg, tp, bctx);
591 break;
592 case BSSGP_PDUT_FLUSH_LL_ACK:
593 /* BSS informs us it has performed LL FLUSH */
Harald Welte086fe322011-08-19 16:45:19 +0200594 DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200595 /* FIXME: send NM_FLUSH_LL.res to NM */
596 break;
597 case BSSGP_PDUT_LLC_DISCARD:
598 /* BSS informs that some LLC PDU's have been discarded */
Harald Weltee9686b62010-05-31 18:07:17 +0200599 rc = bssgp_rx_llc_disc(msg, tp, bctx);
Harald Welte25de8112010-05-13 21:26:28 +0200600 break;
601 case BSSGP_PDUT_BVC_BLOCK:
602 /* BSS tells us that BVC shall be blocked */
Harald Welte25de8112010-05-13 21:26:28 +0200603 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200604 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
605 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
606 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200607 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200608 }
Harald Welte2677ea52010-05-31 17:16:36 +0200609 rc = bssgp_rx_bvc_block(msg, tp);
Harald Welte25de8112010-05-13 21:26:28 +0200610 break;
611 case BSSGP_PDUT_BVC_UNBLOCK:
612 /* BSS tells us that BVC shall be unblocked */
Harald Weltee9686b62010-05-31 18:07:17 +0200613 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
614 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
615 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200616 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200617 }
Harald Welte25de8112010-05-13 21:26:28 +0200618 rc = bssgp_rx_bvc_unblock(msg, tp);
619 break;
620 case BSSGP_PDUT_BVC_RESET:
621 /* BSS tells us that BVC init is required */
Harald Welte25de8112010-05-13 21:26:28 +0200622 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200623 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
624 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
625 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200626 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200627 }
Harald Welte25de8112010-05-13 21:26:28 +0200628 rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
629 break;
630 case BSSGP_PDUT_STATUS:
631 /* Some exception has occurred */
Harald Weltee9686b62010-05-31 18:07:17 +0200632 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200633 /* FIXME: send NM_STATUS.ind to NM */
634 break;
635 /* those only exist in the SGSN -> BSS direction */
636 case BSSGP_PDUT_PAGING_PS:
637 case BSSGP_PDUT_PAGING_CS:
Harald Welte9ba50052010-03-14 15:45:01 +0800638 case BSSGP_PDUT_SUSPEND_ACK:
639 case BSSGP_PDUT_SUSPEND_NACK:
640 case BSSGP_PDUT_RESUME_ACK:
641 case BSSGP_PDUT_RESUME_NACK:
Harald Welte6b7cf252010-05-13 19:41:31 +0200642 case BSSGP_PDUT_FLUSH_LL:
Harald Welte9ba50052010-03-14 15:45:01 +0800643 case BSSGP_PDUT_BVC_BLOCK_ACK:
644 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
645 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
Harald Weltee9686b62010-05-31 18:07:17 +0200646 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
647 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200648 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800649 rc = -EINVAL;
650 break;
651 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200652 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
653 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200654 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800655 break;
656 }
657
658 return rc;
659err_mand_ie:
660 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
661}
662
Harald Welte25de8112010-05-13 21:26:28 +0200663/* We expect msgb_bssgph() to point to the BSSGP header */
664int gprs_bssgp_rcvmsg(struct msgb *msg)
665{
666 struct bssgp_normal_hdr *bgph =
667 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
668 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
669 struct tlv_parsed tp;
Harald Welte8a521132010-05-17 22:59:29 +0200670 struct bssgp_bvc_ctx *bctx;
Harald Welte25de8112010-05-13 21:26:28 +0200671 uint8_t pdu_type = bgph->pdu_type;
672 uint16_t ns_bvci = msgb_bvci(msg);
673 int data_len;
674 int rc = 0;
675
676 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
677
678 /* UNITDATA BSSGP headers have TLLI in front */
679 if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
680 pdu_type != BSSGP_PDUT_DL_UNITDATA) {
681 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
682 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
683 } else {
684 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
685 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
686 }
687
688 /* look-up or create the BTS context for this BVC */
689 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
690 /* Only a RESET PDU can create a new BVC context */
691 if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
692 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
693 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
694 pdu_type);
695 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
696 }
697
Harald Welte16c8dbb2010-05-17 23:30:01 +0200698 if (bctx) {
Harald Welte4e5721d2010-05-17 23:41:43 +0200699 log_set_context(BSC_CTX_BVC, bctx);
Harald Welte16c8dbb2010-05-17 23:30:01 +0200700 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
701 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
702 msgb_bssgp_len(msg));
703 }
704
Harald Welte61c07842010-05-18 11:57:08 +0200705 if (ns_bvci == BVCI_SIGNALLING)
Harald Welte25de8112010-05-13 21:26:28 +0200706 rc = gprs_bssgp_rx_sign(msg, &tp, bctx);
Harald Welte61c07842010-05-18 11:57:08 +0200707 else if (ns_bvci == BVCI_PTM)
Harald Welte25de8112010-05-13 21:26:28 +0200708 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
709 else
710 rc = gprs_bssgp_rx_ptp(msg, &tp, bctx);
711
712 return rc;
713}
714
Harald Welte6752fa42010-05-02 09:23:16 +0200715/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
Harald Welte30bc19a2010-05-02 11:19:37 +0200716 * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
Harald Welte2f946832010-05-31 22:12:30 +0200717int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800718{
Harald Welte8a521132010-05-17 22:59:29 +0200719 struct bssgp_bvc_ctx *bctx;
Harald Welte9ba50052010-03-14 15:45:01 +0800720 struct bssgp_ud_hdr *budh;
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200721 uint8_t llc_pdu_tlv_hdr_len = 2;
722 uint8_t *llc_pdu_tlv, *qos_profile;
723 uint16_t pdu_lifetime = 1000; /* centi-seconds */
Harald Welte02f73252010-06-01 11:53:01 +0200724 uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 };
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200725 uint16_t msg_len = msg->len;
Harald Welte30bc19a2010-05-02 11:19:37 +0200726 uint16_t bvci = msgb_bvci(msg);
727 uint16_t nsei = msgb_nsei(msg);
Harald Welte2f946832010-05-31 22:12:30 +0200728 uint16_t drx_params;
Harald Welte9ba50052010-03-14 15:45:01 +0800729
Harald Welte30bc19a2010-05-02 11:19:37 +0200730 /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
Harald Welte61c07842010-05-18 11:57:08 +0200731 if (bvci <= BVCI_PTM ) {
Harald Welteb8a6a832010-05-11 05:54:22 +0200732 LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
Harald Welte30bc19a2010-05-02 11:19:37 +0200733 bvci);
734 return -EINVAL;
735 }
736
737 bctx = btsctx_by_bvci_nsei(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200738 if (!bctx) {
739 /* FIXME: don't simply create missing context, but reject message */
Harald Welte30bc19a2010-05-02 11:19:37 +0200740 bctx = btsctx_alloc(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200741 }
Harald Welte9ba50052010-03-14 15:45:01 +0800742
743 if (msg->len > TVLV_MAX_ONEBYTE)
744 llc_pdu_tlv_hdr_len += 1;
745
746 /* prepend the tag and length of the LLC-PDU TLV */
747 llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
748 llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
749 if (llc_pdu_tlv_hdr_len > 2) {
750 llc_pdu_tlv[1] = msg_len >> 8;
751 llc_pdu_tlv[2] = msg_len & 0xff;
752 } else {
Sylvain Munautb00d1ad2010-06-09 21:13:13 +0200753 llc_pdu_tlv[1] = msg_len & 0x7f;
Harald Welte9ba50052010-03-14 15:45:01 +0800754 llc_pdu_tlv[1] |= 0x80;
755 }
756
Harald Welte2f946832010-05-31 22:12:30 +0200757 /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
758
759 if (mmctx) {
760 /* Old TLLI to help BSS map from old->new */
761#if 0
762 if (mmctx->tlli_old)
763 msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old));
764#endif
765
766 /* IMSI */
767 if (strlen(mmctx->imsi)) {
768 uint8_t mi[10];
769 int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi);
770 if (imsi_len > 2)
771 msgb_tvlv_push(msg, BSSGP_IE_IMSI,
772 imsi_len-2, mi+2);
773 }
774
775 /* DRX parameters */
776 drx_params = htons(mmctx->drx_parms);
777 msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
778 (uint8_t *) &drx_params);
779
780 /* FIXME: Priority */
781
782 /* MS Radio Access Capability */
783 if (mmctx->ms_radio_access_capa.len)
784 msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
785 mmctx->ms_radio_access_capa.len,
786 mmctx->ms_radio_access_capa.buf);
787 }
Harald Welte9ba50052010-03-14 15:45:01 +0800788
789 /* prepend the pdu lifetime */
790 pdu_lifetime = htons(pdu_lifetime);
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200791 msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime);
Harald Welte9ba50052010-03-14 15:45:01 +0800792
793 /* prepend the QoS profile, TLLI and pdu type */
794 budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
795 memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default));
Harald Welte510c3922010-04-30 16:33:12 +0200796 budh->tlli = htonl(msgb_tlli(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800797 budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
798
Harald Welte16c8dbb2010-05-17 23:30:01 +0200799 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
800 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
801
Harald Welte30bc19a2010-05-02 11:19:37 +0200802 /* Identifiers down: BVCI, NSEI (in msgb->cb) */
Harald Welte24a655f2010-04-30 19:54:29 +0200803
804 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800805}
Harald Welte68b4f032010-06-09 16:22:28 +0200806
807/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
808int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
809 struct bssgp_paging_info *pinfo)
810{
811 struct msgb *msg = bssgp_msgb_alloc();
812 struct bssgp_normal_hdr *bgph =
813 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
814 uint16_t drx_params = htons(pinfo->drx_params);
815 uint8_t mi[10];
816 int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi);
817 uint8_t ra[6];
818
819 if (imsi_len < 2)
820 return -EINVAL;
821
822 msgb_nsei(msg) = nsei;
823 msgb_bvci(msg) = ns_bvci;
824
825 if (pinfo->mode == BSSGP_PAGING_PS)
826 bgph->pdu_type = BSSGP_PDUT_PAGING_PS;
827 else
828 bgph->pdu_type = BSSGP_PDUT_PAGING_CS;
829 /* IMSI */
830 msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
831 /* DRX Parameters */
832 msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2,
833 (uint8_t *) &drx_params);
834 /* Scope */
835 switch (pinfo->scope) {
836 case BSSGP_PAGING_BSS_AREA:
837 {
838 uint8_t null = 0;
839 msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null);
840 }
841 break;
842 case BSSGP_PAGING_LOCATION_AREA:
843 gsm48_construct_ra(ra, &pinfo->raid);
844 msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra);
845 break;
846 case BSSGP_PAGING_ROUTEING_AREA:
847 gsm48_construct_ra(ra, &pinfo->raid);
848 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
849 break;
850 case BSSGP_PAGING_BVCI:
851 {
852 uint16_t bvci = htons(pinfo->bvci);
853 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci);
854 }
855 break;
856 }
857 /* QoS profile mandatory for PS */
858 if (pinfo->mode == BSSGP_PAGING_PS)
859 msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos);
860
861 /* Optional (P-)TMSI */
862 if (pinfo->ptmsi) {
863 uint32_t ptmsi = htonl(*pinfo->ptmsi);
864 msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
865 }
866
867 return gprs_ns_sendmsg(bssgp_nsi, msg);
868}