blob: 4b8c730283f03e744fa1094039fa549f438deeb3 [file] [log] [blame]
Harald Welte9ba50052010-03-14 15:45:01 +08001/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
2
Harald Welte605ac5d2012-06-16 16:09:52 +08003/* (C) 2009-2012 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 Welte73952e32012-06-16 14:59:56 +080035#include <osmocom/gprs/gprs_bssgp.h>
36#include <osmocom/gprs/gprs_ns.h>
37
Harald Weltecca49632012-06-16 17:45:59 +080038#include "common_vty.h"
39
Harald Welte6752fa42010-05-02 09:23:16 +020040void *bssgp_tall_ctx = NULL;
41
Harald Welte25de8112010-05-13 21:26:28 +020042static const struct rate_ctr_desc bssgp_ctr_description[] = {
Harald Welte16c8dbb2010-05-17 23:30:01 +020043 { "packets.in", "Packets at BSSGP Level ( In)" },
44 { "packets.out","Packets at BSSGP Level (Out)" },
45 { "bytes.in", "Bytes at BSSGP Level ( In)" },
46 { "bytes.out", "Bytes at BSSGP Level (Out)" },
Harald Welte25de8112010-05-13 21:26:28 +020047 { "blocked", "BVC Blocking count" },
48 { "discarded", "BVC LLC Discarded count" },
49};
50
51static const struct rate_ctr_group_desc bssgp_ctrg_desc = {
52 .group_name_prefix = "bssgp.bss_ctx",
53 .group_description = "BSSGP Peer Statistics",
54 .num_ctr = ARRAY_SIZE(bssgp_ctr_description),
55 .ctr_desc = bssgp_ctr_description,
56};
57
Harald Weltea78b9c22010-05-17 23:02:42 +020058LLIST_HEAD(bssgp_bvc_ctxts);
Harald Welte6752fa42010-05-02 09:23:16 +020059
60/* Find a BTS Context based on parsed RA ID and Cell ID */
Harald Welte8a521132010-05-17 22:59:29 +020061struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid)
Harald Welte6752fa42010-05-02 09:23:16 +020062{
Harald Welte8a521132010-05-17 22:59:29 +020063 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +020064
Harald Weltea78b9c22010-05-17 23:02:42 +020065 llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
Harald Welte6752fa42010-05-02 09:23:16 +020066 if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) &&
67 bctx->cell_id == cid)
68 return bctx;
69 }
70 return NULL;
71}
72
73/* Find a BTS context based on BVCI+NSEI tuple */
Harald Welte8a521132010-05-17 22:59:29 +020074struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei)
Harald Welte6752fa42010-05-02 09:23:16 +020075{
Harald Welte8a521132010-05-17 22:59:29 +020076 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +020077
Harald Weltea78b9c22010-05-17 23:02:42 +020078 llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) {
Harald Welte6752fa42010-05-02 09:23:16 +020079 if (bctx->nsei == nsei && bctx->bvci == bvci)
80 return bctx;
81 }
82 return NULL;
83}
84
Harald Welte8a521132010-05-17 22:59:29 +020085struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei)
Harald Welte6752fa42010-05-02 09:23:16 +020086{
Harald Welte8a521132010-05-17 22:59:29 +020087 struct bssgp_bvc_ctx *ctx;
Harald Welte6752fa42010-05-02 09:23:16 +020088
Harald Welte8a521132010-05-17 22:59:29 +020089 ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx);
Harald Welte6752fa42010-05-02 09:23:16 +020090 if (!ctx)
91 return NULL;
92 ctx->bvci = bvci;
93 ctx->nsei = nsei;
Harald Welte25de8112010-05-13 21:26:28 +020094 /* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */
95 ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci);
96
Harald Weltea78b9c22010-05-17 23:02:42 +020097 llist_add(&ctx->list, &bssgp_bvc_ctxts);
Harald Welte6752fa42010-05-02 09:23:16 +020098
99 return ctx;
100}
101
Harald Welte9ba50052010-03-14 15:45:01 +0800102/* Chapter 10.4.5: Flow Control BVC ACK */
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200103static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci)
Harald Welte9ba50052010-03-14 15:45:01 +0800104{
105 struct msgb *msg = bssgp_msgb_alloc();
106 struct bssgp_normal_hdr *bgph =
107 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
108
Harald Welte24a655f2010-04-30 19:54:29 +0200109 msgb_nsei(msg) = nsei;
110 msgb_bvci(msg) = ns_bvci;
111
Harald Welte9ba50052010-03-14 15:45:01 +0800112 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
113 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
114
Harald Welte24a655f2010-04-30 19:54:29 +0200115 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800116}
117
Harald Weltea8aa4df2010-05-30 22:00:53 +0200118/* 10.3.7 SUSPEND-ACK PDU */
119int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli,
120 const struct gprs_ra_id *ra_id, uint8_t suspend_ref)
121{
122 struct msgb *msg = bssgp_msgb_alloc();
123 struct bssgp_normal_hdr *bgph =
124 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
125 uint32_t _tlli;
126 uint8_t ra[6];
127
128 msgb_nsei(msg) = nsei;
129 msgb_bvci(msg) = 0; /* Signalling */
130 bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK;
131
132 _tlli = htonl(tlli);
133 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
134 gsm48_construct_ra(ra, ra_id);
135 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
136 msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref);
137
138 return gprs_ns_sendmsg(bssgp_nsi, msg);
139}
140
141/* 10.3.8 SUSPEND-NACK PDU */
142int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli,
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100143 const struct gprs_ra_id *ra_id,
Harald Weltea8aa4df2010-05-30 22:00:53 +0200144 uint8_t *cause)
145{
146 struct msgb *msg = bssgp_msgb_alloc();
147 struct bssgp_normal_hdr *bgph =
148 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
149 uint32_t _tlli;
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100150 uint8_t ra[6];
Harald Weltea8aa4df2010-05-30 22:00:53 +0200151
152 msgb_nsei(msg) = nsei;
153 msgb_bvci(msg) = 0; /* Signalling */
154 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
155
156 _tlli = htonl(tlli);
157 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100158 gsm48_construct_ra(ra, ra_id);
159 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200160 if (cause)
161 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
162
163 return gprs_ns_sendmsg(bssgp_nsi, msg);
164}
165
166/* 10.3.10 RESUME-ACK PDU */
167int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
168 const struct gprs_ra_id *ra_id)
169{
170 struct msgb *msg = bssgp_msgb_alloc();
171 struct bssgp_normal_hdr *bgph =
172 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
173 uint32_t _tlli;
174 uint8_t ra[6];
175
176 msgb_nsei(msg) = nsei;
177 msgb_bvci(msg) = 0; /* Signalling */
178 bgph->pdu_type = BSSGP_PDUT_RESUME_ACK;
179
180 _tlli = htonl(tlli);
181 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
182 gsm48_construct_ra(ra, ra_id);
183 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
184
185 return gprs_ns_sendmsg(bssgp_nsi, msg);
186}
187
188/* 10.3.11 RESUME-NACK PDU */
189int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
190 const struct gprs_ra_id *ra_id, uint8_t *cause)
191{
192 struct msgb *msg = bssgp_msgb_alloc();
193 struct bssgp_normal_hdr *bgph =
194 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
195 uint32_t _tlli;
196 uint8_t ra[6];
197
198 msgb_nsei(msg) = nsei;
199 msgb_bvci(msg) = 0; /* Signalling */
200 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
201
202 _tlli = htonl(tlli);
203 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
204 gsm48_construct_ra(ra, ra_id);
205 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
206 if (cause)
207 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
208
209 return gprs_ns_sendmsg(bssgp_nsi, msg);
210}
211
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200212uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
Harald Welte6752fa42010-05-02 09:23:16 +0200213{
214 /* 6 octets RAC */
215 gsm48_parse_ra(raid, buf);
216 /* 2 octets CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200217 return ntohs(*(uint16_t *) (buf+6));
Harald Welte6752fa42010-05-02 09:23:16 +0200218}
219
Harald Welte28610072011-11-24 21:32:07 +0100220int bssgp_create_cell_id(uint8_t *buf, const struct gprs_ra_id *raid,
221 uint16_t cid)
222{
223 uint16_t *out_cid = (uint16_t *) (buf + 6);
224 /* 6 octets RAC */
225 gsm48_construct_ra(buf, raid);
226 /* 2 octets CID */
227 *out_cid = htons(cid);
228
229 return 8;
230}
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 Welte15a36432012-06-17 12:16:31 +0800236 struct osmo_bssgp_prim nmp;
Harald Welte8a521132010-05-17 22:59:29 +0200237 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200238 uint16_t nsei = msgb_nsei(msg);
239 uint16_t bvci;
Harald Welte3fddf3c2010-05-01 16:48:27 +0200240 int rc;
241
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200242 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Weltee9686b62010-05-31 18:07:17 +0200243 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
Harald Welte3fddf3c2010-05-01 16:48:27 +0200244 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
245
Harald Welte6752fa42010-05-02 09:23:16 +0200246 /* look-up or create the BTS context for this BVC */
247 bctx = btsctx_by_bvci_nsei(bvci, nsei);
248 if (!bctx)
249 bctx = btsctx_alloc(bvci, nsei);
250
Harald Welte25de8112010-05-13 21:26:28 +0200251 /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
252 bctx->state &= ~BVC_S_BLOCKED;
253
Harald Welte3fddf3c2010-05-01 16:48:27 +0200254 /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
255 * informs us about its RAC + Cell ID, so we can create a mapping */
Harald Welte6752fa42010-05-02 09:23:16 +0200256 if (bvci != 0 && bvci != 1) {
257 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
Harald Welte086fe322011-08-19 16:45:19 +0200258 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESET "
Harald Welte6752fa42010-05-02 09:23:16 +0200259 "missing mandatory IE\n", bvci);
260 return -EINVAL;
261 }
262 /* actually extract RAC / CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200263 bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
264 TLVP_VAL(tp, BSSGP_IE_CELL_ID));
Harald Welteb8a6a832010-05-11 05:54:22 +0200265 LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
Harald Welte6752fa42010-05-02 09:23:16 +0200266 bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
267 bctx->ra_id.rac, bctx->cell_id, bvci);
268 }
Harald Welte3fddf3c2010-05-01 16:48:27 +0200269
Harald Welte15a36432012-06-17 12:16:31 +0800270 /* Send NM_BVC_RESET.ind to NM */
271 memset(&nmp, 0, sizeof(nmp));
272 nmp.nsei = nsei;
273 nmp.bvci = bvci;
274 nmp.tp = tp;
275 nmp.ra_id = &bctx->ra_id;
276 osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_RESET,
277 PRIM_OP_INDICATION, msg);
278 bssgp_prim_cb(&nmp.oph, NULL);
279
Harald Welte6752fa42010-05-02 09:23:16 +0200280 /* Acknowledge the RESET to the BTS */
Harald Welte3fddf3c2010-05-01 16:48:27 +0200281 rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
Harald Welte6752fa42010-05-02 09:23:16 +0200282 nsei, bvci, ns_bvci);
Harald Welte3fddf3c2010-05-01 16:48:27 +0200283 return 0;
284}
285
Harald Welte25de8112010-05-13 21:26:28 +0200286static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
287{
Harald Welte15a36432012-06-17 12:16:31 +0800288 struct osmo_bssgp_prim nmp;
Harald Welte25de8112010-05-13 21:26:28 +0200289 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200290 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200291
292 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200293 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200294 /* 8.3.2: Signalling BVC shall never be blocked */
295 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
296 "received block for signalling BVC!?!\n",
297 msgb_nsei(msg), msgb_bvci(msg));
298 return 0;
299 }
Harald Welte25de8112010-05-13 21:26:28 +0200300
Harald Welte086fe322011-08-19 16:45:19 +0200301 LOGP(DBSSGP, LOGL_INFO, "BSSGP Rx BVCI=%u BVC-BLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200302
303 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
304 if (!ptp_ctx)
305 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
306
307 ptp_ctx->state |= BVC_S_BLOCKED;
308 rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
309
Harald Welte15a36432012-06-17 12:16:31 +0800310 /* Send NM_BVC_BLOCK.ind to NM */
311 memset(&nmp, 0, sizeof(nmp));
312 nmp.nsei = msgb_nsei(msg);
313 nmp.bvci = bvci;
314 nmp.tp = tp;
315 osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_BLOCK,
316 PRIM_OP_INDICATION, msg);
317 bssgp_prim_cb(&nmp.oph, NULL);
Harald Welte25de8112010-05-13 21:26:28 +0200318
319 /* We always acknowledge the BLOCKing */
320 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
321 bvci, msgb_bvci(msg));
322};
323
324static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
325{
Harald Welte15a36432012-06-17 12:16:31 +0800326 struct osmo_bssgp_prim nmp;
Harald Welte25de8112010-05-13 21:26:28 +0200327 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200328 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200329
330 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200331 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200332 /* 8.3.2: Signalling BVC shall never be blocked */
333 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
334 "received unblock for signalling BVC!?!\n",
335 msgb_nsei(msg), msgb_bvci(msg));
336 return 0;
337 }
Harald Welte25de8112010-05-13 21:26:28 +0200338
Harald Weltee9686b62010-05-31 18:07:17 +0200339 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200340
341 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
342 if (!ptp_ctx)
343 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
344
345 ptp_ctx->state &= ~BVC_S_BLOCKED;
346
Harald Welte15a36432012-06-17 12:16:31 +0800347 /* Send NM_BVC_UNBLOCK.ind to NM */
348 memset(&nmp, 0, sizeof(nmp));
349 nmp.nsei = msgb_nsei(msg);
350 nmp.bvci = bvci;
351 nmp.tp = tp;
352 osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_BVC_UNBLOCK,
353 PRIM_OP_INDICATION, msg);
354 bssgp_prim_cb(&nmp.oph, NULL);
Harald Welte25de8112010-05-13 21:26:28 +0200355
356 /* We always acknowledge the unBLOCKing */
357 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
358 bvci, msgb_bvci(msg));
359};
360
Harald Welte9ba50052010-03-14 15:45:01 +0800361/* Uplink unit-data */
Harald Welte25de8112010-05-13 21:26:28 +0200362static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200363 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800364{
Harald Welte15a36432012-06-17 12:16:31 +0800365 struct osmo_bssgp_prim gbp;
Harald Welteec19c102010-05-02 09:50:42 +0200366 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800367
Harald Welte6752fa42010-05-02 09:23:16 +0200368 /* extract TLLI and parse TLV IEs */
Harald Welte510c3922010-04-30 16:33:12 +0200369 msgb_tlli(msg) = ntohl(budh->tlli);
Harald Welte9ba50052010-03-14 15:45:01 +0800370
Harald Welte086fe322011-08-19 16:45:19 +0200371 DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x Rx UPLINK-UNITDATA\n", msgb_tlli(msg));
Harald Weltee9686b62010-05-31 18:07:17 +0200372
Harald Welte9ba50052010-03-14 15:45:01 +0800373 /* Cell ID and LLC_PDU are the only mandatory IE */
Harald Welte25de8112010-05-13 21:26:28 +0200374 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200375 !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
376 LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
377 "missing mandatory IE\n", msgb_tlli(msg));
Harald Welte25de8112010-05-13 21:26:28 +0200378 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200379 }
Harald Welte30bc19a2010-05-02 11:19:37 +0200380
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200381 /* store pointer to LLC header and CELL ID in msgb->cb */
Holger Hans Peter Freytherb6eded82010-05-23 21:11:19 +0800382 msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
383 msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
Harald Welte9ba50052010-03-14 15:45:01 +0800384
Harald Welte15a36432012-06-17 12:16:31 +0800385 /* Send BSSGP_UL_UD.ind to NM */
386 memset(&gbp, 0, sizeof(gbp));
387 gbp.nsei = ctx->nsei;
388 gbp.bvci = ctx->bvci;
389 gbp.tlli = msgb_tlli(msg);
390 gbp.tp = tp;
391 osmo_prim_init(&gbp.oph, SAP_BSSGP_LL, PRIM_BSSGP_UL_UD,
392 PRIM_OP_INDICATION, msg);
393 return bssgp_prim_cb(&gbp.oph, NULL);
Harald Welte9ba50052010-03-14 15:45:01 +0800394}
395
Harald Welte25de8112010-05-13 21:26:28 +0200396static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200397 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800398{
Harald Welte15a36432012-06-17 12:16:31 +0800399 struct osmo_bssgp_prim gbp;
Harald Welteec19c102010-05-02 09:50:42 +0200400 struct bssgp_normal_hdr *bgph =
401 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200402 struct gprs_ra_id raid;
403 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200404 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800405
Harald Welte25de8112010-05-13 21:26:28 +0200406 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200407 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
408 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
409 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200410 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200411 }
Harald Welte9ba50052010-03-14 15:45:01 +0800412
Harald Weltea8aa4df2010-05-30 22:00:53 +0200413 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200414
Harald Welte17925322010-05-31 20:18:35 +0200415 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200416 ctx->bvci, tlli);
417
Harald Weltea8aa4df2010-05-30 22:00:53 +0200418 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
419
Harald Welte313cccf2010-06-09 11:22:47 +0200420 /* Inform GMM about the SUSPEND request */
Harald Welte15a36432012-06-17 12:16:31 +0800421 memset(&gbp, 0, sizeof(gbp));
422 gbp.nsei = msgb_nsei(msg);
423 gbp.bvci = ctx->bvci;
424 gbp.tlli = tlli;
425 gbp.ra_id = &raid;
426 osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_SUSPEND,
427 PRIM_OP_REQUEST, msg);
428
429 rc = bssgp_prim_cb(&gbp.oph, NULL);
Harald Welte313cccf2010-06-09 11:22:47 +0200430 if (rc < 0)
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100431 return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL);
Harald Welte313cccf2010-06-09 11:22:47 +0200432
Harald Weltea8aa4df2010-05-30 22:00:53 +0200433 bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
434
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800435 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800436}
437
Harald Welte25de8112010-05-13 21:26:28 +0200438static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200439 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800440{
Harald Welte15a36432012-06-17 12:16:31 +0800441 struct osmo_bssgp_prim gbp;
Harald Welteec19c102010-05-02 09:50:42 +0200442 struct bssgp_normal_hdr *bgph =
443 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200444 struct gprs_ra_id raid;
445 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200446 uint8_t suspend_ref;
447 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800448
Harald Welte25de8112010-05-13 21:26:28 +0200449 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
450 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200451 !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
452 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
453 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200454 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200455 }
Harald Welte9ba50052010-03-14 15:45:01 +0800456
Harald Weltea8aa4df2010-05-30 22:00:53 +0200457 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Welte313cccf2010-06-09 11:22:47 +0200458 suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
Harald Weltee9686b62010-05-31 18:07:17 +0200459
Harald Welte086fe322011-08-19 16:45:19 +0200460 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx RESUME\n", ctx->bvci, tlli);
Harald Weltee9686b62010-05-31 18:07:17 +0200461
Harald Weltea8aa4df2010-05-30 22:00:53 +0200462 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
463
Harald Welte313cccf2010-06-09 11:22:47 +0200464 /* Inform GMM about the RESUME request */
Harald Welte15a36432012-06-17 12:16:31 +0800465 memset(&gbp, 0, sizeof(gbp));
466 gbp.nsei = msgb_nsei(msg);
467 gbp.bvci = ctx->bvci;
468 gbp.tlli = tlli;
469 gbp.ra_id = &raid;
470 gbp.u.resume.suspend_ref = suspend_ref;
471 osmo_prim_init(&gbp.oph, SAP_BSSGP_GMM, PRIM_BSSGP_GMM_RESUME,
472 PRIM_OP_REQUEST, msg);
473
474 rc = bssgp_prim_cb(&gbp.oph, NULL);
Harald Welte313cccf2010-06-09 11:22:47 +0200475 if (rc < 0)
476 return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid,
477 NULL);
478
Harald Weltea8aa4df2010-05-30 22:00:53 +0200479 bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800480 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800481}
482
Harald Weltee9686b62010-05-31 18:07:17 +0200483
484static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
485 struct bssgp_bvc_ctx *ctx)
486{
Harald Welte15a36432012-06-17 12:16:31 +0800487 struct osmo_bssgp_prim nmp;
Harald Welteb7363142010-07-23 21:59:29 +0200488 uint32_t tlli = 0;
Harald Weltee9686b62010-05-31 18:07:17 +0200489
490 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
491 !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
492 !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
493 !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
494 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
495 "missing mandatory IE\n", ctx->bvci);
496 }
497
Harald Welteb7363142010-07-23 21:59:29 +0200498 if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
499 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200500
Harald Welte086fe322011-08-19 16:45:19 +0200501 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x Rx LLC DISCARDED\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200502 ctx->bvci, tlli);
503
504 rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
505
Harald Welte15a36432012-06-17 12:16:31 +0800506 /* send NM_LLC_DISCARDED to NM */
507 memset(&nmp, 0, sizeof(nmp));
508 nmp.nsei = msgb_nsei(msg);
509 nmp.bvci = ctx->bvci;
510 nmp.tlli = tlli;
511 nmp.tp = tp;
512 osmo_prim_init(&nmp.oph, SAP_BSSGP_NM, PRIM_NM_LLC_DISCARDED,
513 PRIM_OP_INDICATION, msg);
514
515 return bssgp_prim_cb(&nmp.oph, NULL);
Harald Weltee9686b62010-05-31 18:07:17 +0200516}
517
Harald Welte25de8112010-05-13 21:26:28 +0200518static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200519 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800520{
521
Harald Weltee9686b62010-05-31 18:07:17 +0200522 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
523 bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800524
525 if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
526 !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
527 !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
528 !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200529 !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
530 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
531 "missing mandatory IE\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800532 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200533 }
Harald Welte9ba50052010-03-14 15:45:01 +0800534
Harald Welte30bc19a2010-05-02 11:19:37 +0200535 /* FIXME: actually implement flow control */
536
Harald Welte9ba50052010-03-14 15:45:01 +0800537 /* Send FLOW_CONTROL_BVC_ACK */
Harald Welte24a655f2010-04-30 19:54:29 +0200538 return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
Harald Welte30bc19a2010-05-02 11:19:37 +0200539 msgb_bvci(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800540}
Harald Welte3fddf3c2010-05-01 16:48:27 +0200541
Harald Welte25de8112010-05-13 21:26:28 +0200542/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
Harald Weltede4599c2012-06-17 13:04:02 +0800543static int bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
544 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800545{
Harald Welteec19c102010-05-02 09:50:42 +0200546 struct bssgp_normal_hdr *bgph =
547 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Welte30bc19a2010-05-02 11:19:37 +0200548 uint8_t pdu_type = bgph->pdu_type;
Harald Welte9ba50052010-03-14 15:45:01 +0800549 int rc = 0;
550
Harald Welte58e65c92010-05-13 21:45:23 +0200551 /* If traffic is received on a BVC that is marked as blocked, the
552 * received PDU shall not be accepted and a STATUS PDU (Cause value:
553 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
554 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
555 uint16_t bvci = msgb_bvci(msg);
556 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
557 }
558
Harald Welte9ba50052010-03-14 15:45:01 +0800559 switch (pdu_type) {
560 case BSSGP_PDUT_UL_UNITDATA:
561 /* some LLC data from the MS */
Harald Welte25de8112010-05-13 21:26:28 +0200562 rc = bssgp_rx_ul_ud(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800563 break;
564 case BSSGP_PDUT_RA_CAPABILITY:
565 /* BSS requests RA capability or IMSI */
Harald Weltee9686b62010-05-31 18:07:17 +0200566 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
567 bctx->bvci);
Harald Welte6b7cf252010-05-13 19:41:31 +0200568 /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800569 /* FIXME: send RA_CAPA_UPDATE_ACK */
570 break;
571 case BSSGP_PDUT_RADIO_STATUS:
Harald Weltee9686b62010-05-31 18:07:17 +0200572 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800573 /* BSS informs us of some exception */
Harald Welte6b7cf252010-05-13 19:41:31 +0200574 /* FIXME: send GMM_RADIO_STATUS.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800575 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800576 case BSSGP_PDUT_FLOW_CONTROL_BVC:
577 /* BSS informs us of available bandwidth in Gb interface */
Harald Welte25de8112010-05-13 21:26:28 +0200578 rc = bssgp_rx_fc_bvc(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800579 break;
580 case BSSGP_PDUT_FLOW_CONTROL_MS:
581 /* BSS informs us of available bandwidth to one MS */
Harald Weltee9686b62010-05-31 18:07:17 +0200582 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
583 bctx->bvci);
Harald Welte30bc19a2010-05-02 11:19:37 +0200584 /* FIXME: actually implement flow control */
585 /* FIXME: Send FLOW_CONTROL_MS_ACK */
Harald Welte9ba50052010-03-14 15:45:01 +0800586 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800587 case BSSGP_PDUT_STATUS:
588 /* Some exception has occurred */
Harald Welte6b7cf252010-05-13 19:41:31 +0200589 /* FIXME: send NM_STATUS.ind to NM */
Harald Welte9ba50052010-03-14 15:45:01 +0800590 case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
591 case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
592 case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
593 case BSSGP_PDUT_MODIFY_BSS_PFC:
594 case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200595 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
596 "implemented\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200597 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800598 break;
599 /* those only exist in the SGSN -> BSS direction */
600 case BSSGP_PDUT_DL_UNITDATA:
601 case BSSGP_PDUT_PAGING_PS:
602 case BSSGP_PDUT_PAGING_CS:
603 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Harald Welte25de8112010-05-13 21:26:28 +0200604 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
605 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200606 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
607 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200608 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
609 rc = -EINVAL;
610 break;
611 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200612 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
613 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200614 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
615 break;
616 }
617
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800618 return rc;
Harald Welte25de8112010-05-13 21:26:28 +0200619}
620
621/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
Harald Weltede4599c2012-06-17 13:04:02 +0800622static int bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
623 struct bssgp_bvc_ctx *bctx)
Harald Welte25de8112010-05-13 21:26:28 +0200624{
625 struct bssgp_normal_hdr *bgph =
626 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
627 uint8_t pdu_type = bgph->pdu_type;
628 int rc = 0;
629 uint16_t ns_bvci = msgb_bvci(msg);
630 uint16_t bvci;
631
632 switch (bgph->pdu_type) {
633 case BSSGP_PDUT_SUSPEND:
634 /* MS wants to suspend */
635 rc = bssgp_rx_suspend(msg, tp, bctx);
636 break;
637 case BSSGP_PDUT_RESUME:
638 /* MS wants to resume */
639 rc = bssgp_rx_resume(msg, tp, bctx);
640 break;
641 case BSSGP_PDUT_FLUSH_LL_ACK:
642 /* BSS informs us it has performed LL FLUSH */
Harald Welte086fe322011-08-19 16:45:19 +0200643 DEBUGP(DBSSGP, "BSSGP Rx BVCI=%u FLUSH LL ACK\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200644 /* FIXME: send NM_FLUSH_LL.res to NM */
645 break;
646 case BSSGP_PDUT_LLC_DISCARD:
647 /* BSS informs that some LLC PDU's have been discarded */
Harald Weltee9686b62010-05-31 18:07:17 +0200648 rc = bssgp_rx_llc_disc(msg, tp, bctx);
Harald Welte25de8112010-05-13 21:26:28 +0200649 break;
650 case BSSGP_PDUT_BVC_BLOCK:
651 /* BSS tells us that BVC shall be blocked */
Harald Welte25de8112010-05-13 21:26:28 +0200652 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200653 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
654 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
655 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200656 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200657 }
Harald Welte2677ea52010-05-31 17:16:36 +0200658 rc = bssgp_rx_bvc_block(msg, tp);
Harald Welte25de8112010-05-13 21:26:28 +0200659 break;
660 case BSSGP_PDUT_BVC_UNBLOCK:
661 /* BSS tells us that BVC shall be unblocked */
Harald Weltee9686b62010-05-31 18:07:17 +0200662 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
663 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
664 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200665 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200666 }
Harald Welte25de8112010-05-13 21:26:28 +0200667 rc = bssgp_rx_bvc_unblock(msg, tp);
668 break;
669 case BSSGP_PDUT_BVC_RESET:
670 /* BSS tells us that BVC init is required */
Harald Welte25de8112010-05-13 21:26:28 +0200671 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200672 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
673 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
674 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200675 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200676 }
Harald Welte25de8112010-05-13 21:26:28 +0200677 rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
678 break;
679 case BSSGP_PDUT_STATUS:
680 /* Some exception has occurred */
Harald Weltee9686b62010-05-31 18:07:17 +0200681 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200682 /* FIXME: send NM_STATUS.ind to NM */
683 break;
684 /* those only exist in the SGSN -> BSS direction */
685 case BSSGP_PDUT_PAGING_PS:
686 case BSSGP_PDUT_PAGING_CS:
Harald Welte9ba50052010-03-14 15:45:01 +0800687 case BSSGP_PDUT_SUSPEND_ACK:
688 case BSSGP_PDUT_SUSPEND_NACK:
689 case BSSGP_PDUT_RESUME_ACK:
690 case BSSGP_PDUT_RESUME_NACK:
Harald Welte6b7cf252010-05-13 19:41:31 +0200691 case BSSGP_PDUT_FLUSH_LL:
Harald Welte9ba50052010-03-14 15:45:01 +0800692 case BSSGP_PDUT_BVC_BLOCK_ACK:
693 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
694 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
Harald Weltee9686b62010-05-31 18:07:17 +0200695 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
696 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200697 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800698 rc = -EINVAL;
699 break;
700 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200701 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
702 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200703 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800704 break;
705 }
706
707 return rc;
708err_mand_ie:
709 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
710}
711
Harald Welte25de8112010-05-13 21:26:28 +0200712/* We expect msgb_bssgph() to point to the BSSGP header */
Harald Weltede4599c2012-06-17 13:04:02 +0800713int bssgp_rcvmsg(struct msgb *msg)
Harald Welte25de8112010-05-13 21:26:28 +0200714{
715 struct bssgp_normal_hdr *bgph =
716 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
717 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
718 struct tlv_parsed tp;
Harald Welte8a521132010-05-17 22:59:29 +0200719 struct bssgp_bvc_ctx *bctx;
Harald Welte25de8112010-05-13 21:26:28 +0200720 uint8_t pdu_type = bgph->pdu_type;
721 uint16_t ns_bvci = msgb_bvci(msg);
722 int data_len;
723 int rc = 0;
724
725 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
726
727 /* UNITDATA BSSGP headers have TLLI in front */
728 if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
729 pdu_type != BSSGP_PDUT_DL_UNITDATA) {
730 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
731 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
732 } else {
733 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
734 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
735 }
736
737 /* look-up or create the BTS context for this BVC */
738 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
739 /* Only a RESET PDU can create a new BVC context */
740 if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
741 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
742 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
743 pdu_type);
744 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
745 }
746
Harald Welte16c8dbb2010-05-17 23:30:01 +0200747 if (bctx) {
Harald Weltecca49632012-06-16 17:45:59 +0800748 log_set_context(GPRS_CTX_BVC, bctx);
Harald Welte16c8dbb2010-05-17 23:30:01 +0200749 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
750 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
751 msgb_bssgp_len(msg));
752 }
753
Harald Welte61c07842010-05-18 11:57:08 +0200754 if (ns_bvci == BVCI_SIGNALLING)
Harald Weltede4599c2012-06-17 13:04:02 +0800755 rc = bssgp_rx_sign(msg, &tp, bctx);
Harald Welte61c07842010-05-18 11:57:08 +0200756 else if (ns_bvci == BVCI_PTM)
Harald Welte25de8112010-05-13 21:26:28 +0200757 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
758 else
Harald Weltede4599c2012-06-17 13:04:02 +0800759 rc = bssgp_rx_ptp(msg, &tp, bctx);
Harald Welte25de8112010-05-13 21:26:28 +0200760
761 return rc;
762}
763
Harald Weltede4599c2012-06-17 13:04:02 +0800764int bssgp_tx_dl_ud(struct msgb *msg, uint16_t pdu_lifetime,
765 struct bssgp_dl_ud_par *dup)
Harald Welte9ba50052010-03-14 15:45:01 +0800766{
Harald Welte8a521132010-05-17 22:59:29 +0200767 struct bssgp_bvc_ctx *bctx;
Harald Welte9ba50052010-03-14 15:45:01 +0800768 struct bssgp_ud_hdr *budh;
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200769 uint8_t llc_pdu_tlv_hdr_len = 2;
Harald Welte8ef54d12012-06-17 09:31:16 +0800770 uint8_t *llc_pdu_tlv;
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200771 uint16_t msg_len = msg->len;
Harald Welte30bc19a2010-05-02 11:19:37 +0200772 uint16_t bvci = msgb_bvci(msg);
773 uint16_t nsei = msgb_nsei(msg);
Harald Welte8ef54d12012-06-17 09:31:16 +0800774 uint16_t _pdu_lifetime = htons(pdu_lifetime); /* centi-seconds */
Harald Welte2f946832010-05-31 22:12:30 +0200775 uint16_t drx_params;
Harald Welte9ba50052010-03-14 15:45:01 +0800776
Harald Welte30bc19a2010-05-02 11:19:37 +0200777 /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
Harald Welte61c07842010-05-18 11:57:08 +0200778 if (bvci <= BVCI_PTM ) {
Harald Welteb8a6a832010-05-11 05:54:22 +0200779 LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
Harald Welte30bc19a2010-05-02 11:19:37 +0200780 bvci);
781 return -EINVAL;
782 }
783
784 bctx = btsctx_by_bvci_nsei(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200785 if (!bctx) {
786 /* FIXME: don't simply create missing context, but reject message */
Harald Welte30bc19a2010-05-02 11:19:37 +0200787 bctx = btsctx_alloc(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200788 }
Harald Welte9ba50052010-03-14 15:45:01 +0800789
790 if (msg->len > TVLV_MAX_ONEBYTE)
791 llc_pdu_tlv_hdr_len += 1;
792
793 /* prepend the tag and length of the LLC-PDU TLV */
794 llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
795 llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
796 if (llc_pdu_tlv_hdr_len > 2) {
797 llc_pdu_tlv[1] = msg_len >> 8;
798 llc_pdu_tlv[2] = msg_len & 0xff;
799 } else {
Sylvain Munautb00d1ad2010-06-09 21:13:13 +0200800 llc_pdu_tlv[1] = msg_len & 0x7f;
Harald Welte9ba50052010-03-14 15:45:01 +0800801 llc_pdu_tlv[1] |= 0x80;
802 }
803
Harald Welte2f946832010-05-31 22:12:30 +0200804 /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
805
Harald Welte8ef54d12012-06-17 09:31:16 +0800806 if (dup) {
Harald Welte2f946832010-05-31 22:12:30 +0200807 /* Old TLLI to help BSS map from old->new */
Harald Welte8ef54d12012-06-17 09:31:16 +0800808 if (dup->tlli) {
809 uint32_t tlli = htonl(*dup->tlli);
810 msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &tlli);
811 }
Harald Welte2f946832010-05-31 22:12:30 +0200812
813 /* IMSI */
Harald Welte8ef54d12012-06-17 09:31:16 +0800814 if (strlen(dup->imsi)) {
Harald Welte2f946832010-05-31 22:12:30 +0200815 uint8_t mi[10];
Harald Welte8ef54d12012-06-17 09:31:16 +0800816 int imsi_len = gsm48_generate_mid_from_imsi(mi, dup->imsi);
Harald Welte2f946832010-05-31 22:12:30 +0200817 if (imsi_len > 2)
818 msgb_tvlv_push(msg, BSSGP_IE_IMSI,
Harald Welte8ef54d12012-06-17 09:31:16 +0800819 imsi_len-2, mi+2);
Harald Welte2f946832010-05-31 22:12:30 +0200820 }
821
822 /* DRX parameters */
Harald Welte8ef54d12012-06-17 09:31:16 +0800823 drx_params = htons(dup->drx_parms);
Harald Welte2f946832010-05-31 22:12:30 +0200824 msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
825 (uint8_t *) &drx_params);
826
827 /* FIXME: Priority */
828
829 /* MS Radio Access Capability */
Harald Welte8ef54d12012-06-17 09:31:16 +0800830 if (dup->ms_ra_cap.len)
Harald Welte2f946832010-05-31 22:12:30 +0200831 msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
Harald Welte8ef54d12012-06-17 09:31:16 +0800832 dup->ms_ra_cap.len, dup->ms_ra_cap.v);
833
Harald Welte2f946832010-05-31 22:12:30 +0200834 }
Harald Welte9ba50052010-03-14 15:45:01 +0800835
836 /* prepend the pdu lifetime */
Harald Welte8ef54d12012-06-17 09:31:16 +0800837 msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&_pdu_lifetime);
Harald Welte9ba50052010-03-14 15:45:01 +0800838
839 /* prepend the QoS profile, TLLI and pdu type */
840 budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
Harald Welte8ef54d12012-06-17 09:31:16 +0800841 memcpy(budh->qos_profile, dup->qos_profile, sizeof(budh->qos_profile));
Harald Welte510c3922010-04-30 16:33:12 +0200842 budh->tlli = htonl(msgb_tlli(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800843 budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
844
Harald Welte16c8dbb2010-05-17 23:30:01 +0200845 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
846 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
847
Harald Welte30bc19a2010-05-02 11:19:37 +0200848 /* Identifiers down: BVCI, NSEI (in msgb->cb) */
Harald Welte24a655f2010-04-30 19:54:29 +0200849
850 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800851}
Harald Welte68b4f032010-06-09 16:22:28 +0200852
853/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
Harald Weltede4599c2012-06-17 13:04:02 +0800854int bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
855 struct bssgp_paging_info *pinfo)
Harald Welte68b4f032010-06-09 16:22:28 +0200856{
857 struct msgb *msg = bssgp_msgb_alloc();
858 struct bssgp_normal_hdr *bgph =
859 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
860 uint16_t drx_params = htons(pinfo->drx_params);
861 uint8_t mi[10];
862 int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi);
863 uint8_t ra[6];
864
865 if (imsi_len < 2)
866 return -EINVAL;
867
868 msgb_nsei(msg) = nsei;
869 msgb_bvci(msg) = ns_bvci;
870
871 if (pinfo->mode == BSSGP_PAGING_PS)
872 bgph->pdu_type = BSSGP_PDUT_PAGING_PS;
873 else
874 bgph->pdu_type = BSSGP_PDUT_PAGING_CS;
875 /* IMSI */
876 msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
877 /* DRX Parameters */
878 msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2,
879 (uint8_t *) &drx_params);
880 /* Scope */
881 switch (pinfo->scope) {
882 case BSSGP_PAGING_BSS_AREA:
883 {
884 uint8_t null = 0;
885 msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null);
886 }
887 break;
888 case BSSGP_PAGING_LOCATION_AREA:
889 gsm48_construct_ra(ra, &pinfo->raid);
890 msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra);
891 break;
892 case BSSGP_PAGING_ROUTEING_AREA:
893 gsm48_construct_ra(ra, &pinfo->raid);
894 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
895 break;
896 case BSSGP_PAGING_BVCI:
897 {
898 uint16_t bvci = htons(pinfo->bvci);
899 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci);
900 }
901 break;
902 }
903 /* QoS profile mandatory for PS */
904 if (pinfo->mode == BSSGP_PAGING_PS)
905 msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos);
906
907 /* Optional (P-)TMSI */
908 if (pinfo->ptmsi) {
909 uint32_t ptmsi = htonl(*pinfo->ptmsi);
910 msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
911 }
912
913 return gprs_ns_sendmsg(bssgp_nsi, msg);
914}
Harald Weltecca49632012-06-16 17:45:59 +0800915
Harald Weltede4599c2012-06-17 13:04:02 +0800916void bssgp_set_log_ss(int ss)
Harald Weltecca49632012-06-16 17:45:59 +0800917{
918 DBSSGP = ss;
919}