blob: eca34b989372cbee48675fc55a3291d0ebf4fa02 [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
30#include <osmocore/msgb.h>
31#include <osmocore/tlv.h>
Harald Welte6752fa42010-05-02 09:23:16 +020032#include <osmocore/talloc.h>
Harald Welte25de8112010-05-13 21:26:28 +020033#include <osmocore/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 Welte3fddf3c2010-05-01 16:48:27 +0200235/* Chapter 8.4 BVC-Reset Procedure */
236static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
237 uint16_t ns_bvci)
238{
Harald Welte8a521132010-05-17 22:59:29 +0200239 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200240 uint16_t nsei = msgb_nsei(msg);
241 uint16_t bvci;
Harald Welte3fddf3c2010-05-01 16:48:27 +0200242 int rc;
243
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200244 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Weltee9686b62010-05-31 18:07:17 +0200245 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
Harald Welte3fddf3c2010-05-01 16:48:27 +0200246 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
247
Harald Welte6752fa42010-05-02 09:23:16 +0200248 /* look-up or create the BTS context for this BVC */
249 bctx = btsctx_by_bvci_nsei(bvci, nsei);
250 if (!bctx)
251 bctx = btsctx_alloc(bvci, nsei);
252
Harald Welte25de8112010-05-13 21:26:28 +0200253 /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
254 bctx->state &= ~BVC_S_BLOCKED;
255
Harald Welte3fddf3c2010-05-01 16:48:27 +0200256 /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
257 * informs us about its RAC + Cell ID, so we can create a mapping */
Harald Welte6752fa42010-05-02 09:23:16 +0200258 if (bvci != 0 && bvci != 1) {
259 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
Harald Weltee9686b62010-05-31 18:07:17 +0200260 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u RESET "
Harald Welte6752fa42010-05-02 09:23:16 +0200261 "missing mandatory IE\n", bvci);
262 return -EINVAL;
263 }
264 /* actually extract RAC / CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200265 bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
266 TLVP_VAL(tp, BSSGP_IE_CELL_ID));
Harald Welteb8a6a832010-05-11 05:54:22 +0200267 LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
Harald Welte6752fa42010-05-02 09:23:16 +0200268 bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
269 bctx->ra_id.rac, bctx->cell_id, bvci);
270 }
Harald Welte3fddf3c2010-05-01 16:48:27 +0200271
Harald Welte6752fa42010-05-02 09:23:16 +0200272 /* Acknowledge the RESET to the BTS */
Harald Welte3fddf3c2010-05-01 16:48:27 +0200273 rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
Harald Welte6752fa42010-05-02 09:23:16 +0200274 nsei, bvci, ns_bvci);
Harald Welte3fddf3c2010-05-01 16:48:27 +0200275 return 0;
276}
277
Harald Welte25de8112010-05-13 21:26:28 +0200278static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
279{
280 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200281 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200282
283 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200284 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200285 /* 8.3.2: Signalling BVC shall never be blocked */
286 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
287 "received block for signalling BVC!?!\n",
288 msgb_nsei(msg), msgb_bvci(msg));
289 return 0;
290 }
Harald Welte25de8112010-05-13 21:26:28 +0200291
Harald Weltee9686b62010-05-31 18:07:17 +0200292 LOGP(DBSSGP, LOGL_INFO, "BSSGP BVCI=%u BVC-BLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200293
294 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
295 if (!ptp_ctx)
296 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
297
298 ptp_ctx->state |= BVC_S_BLOCKED;
299 rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
300
301 /* FIXME: Send NM_BVC_BLOCK.ind to NM */
302
303 /* We always acknowledge the BLOCKing */
304 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
305 bvci, msgb_bvci(msg));
306};
307
308static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
309{
310 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200311 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200312
313 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200314 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200315 /* 8.3.2: Signalling BVC shall never be blocked */
316 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
317 "received unblock for signalling BVC!?!\n",
318 msgb_nsei(msg), msgb_bvci(msg));
319 return 0;
320 }
Harald Welte25de8112010-05-13 21:26:28 +0200321
Harald Weltee9686b62010-05-31 18:07:17 +0200322 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200323
324 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
325 if (!ptp_ctx)
326 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
327
328 ptp_ctx->state &= ~BVC_S_BLOCKED;
329
330 /* FIXME: Send NM_BVC_UNBLOCK.ind to NM */
331
332 /* We always acknowledge the unBLOCKing */
333 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
334 bvci, msgb_bvci(msg));
335};
336
Harald Welte9ba50052010-03-14 15:45:01 +0800337/* Uplink unit-data */
Harald Welte25de8112010-05-13 21:26:28 +0200338static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200339 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800340{
Harald Welteec19c102010-05-02 09:50:42 +0200341 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800342
Harald Welte6752fa42010-05-02 09:23:16 +0200343 /* extract TLLI and parse TLV IEs */
Harald Welte510c3922010-04-30 16:33:12 +0200344 msgb_tlli(msg) = ntohl(budh->tlli);
Harald Welte9ba50052010-03-14 15:45:01 +0800345
Harald Weltee9686b62010-05-31 18:07:17 +0200346 DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x UPLINK-UNITDATA\n", msgb_tlli(msg));
347
Harald Welte9ba50052010-03-14 15:45:01 +0800348 /* Cell ID and LLC_PDU are the only mandatory IE */
Harald Welte25de8112010-05-13 21:26:28 +0200349 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200350 !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
351 LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
352 "missing mandatory IE\n", msgb_tlli(msg));
Harald Welte25de8112010-05-13 21:26:28 +0200353 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200354 }
Harald Welte30bc19a2010-05-02 11:19:37 +0200355
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200356 /* store pointer to LLC header and CELL ID in msgb->cb */
Holger Hans Peter Freytherb6eded82010-05-23 21:11:19 +0800357 msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
358 msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
Harald Welte9ba50052010-03-14 15:45:01 +0800359
Harald Welte25de8112010-05-13 21:26:28 +0200360 return gprs_llc_rcvmsg(msg, tp);
Harald Welte9ba50052010-03-14 15:45:01 +0800361}
362
Harald Welte25de8112010-05-13 21:26:28 +0200363static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200364 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800365{
Harald Welteec19c102010-05-02 09:50:42 +0200366 struct bssgp_normal_hdr *bgph =
367 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200368 struct gprs_ra_id raid;
369 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200370 int rc;
Harald Welte9ba50052010-03-14 15:45:01 +0800371
Harald Welte25de8112010-05-13 21:26:28 +0200372 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200373 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
374 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
375 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200376 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200377 }
Harald Welte9ba50052010-03-14 15:45:01 +0800378
Harald Weltea8aa4df2010-05-30 22:00:53 +0200379 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200380
Harald Welte17925322010-05-31 20:18:35 +0200381 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200382 ctx->bvci, tlli);
383
Harald Weltea8aa4df2010-05-30 22:00:53 +0200384 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
385
Harald Welte313cccf2010-06-09 11:22:47 +0200386 /* Inform GMM about the SUSPEND request */
387 rc = gprs_gmm_rx_suspend(&raid, tlli);
388 if (rc < 0)
Dieter Spaard2b13fc2010-12-12 12:45:08 +0100389 return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL);
Harald Welte313cccf2010-06-09 11:22:47 +0200390
Harald Weltea8aa4df2010-05-30 22:00:53 +0200391 bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
392
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800393 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800394}
395
Harald Welte25de8112010-05-13 21:26:28 +0200396static int bssgp_rx_resume(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 Welteec19c102010-05-02 09:50:42 +0200399 struct bssgp_normal_hdr *bgph =
400 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200401 struct gprs_ra_id raid;
402 uint32_t tlli;
Harald Welte313cccf2010-06-09 11:22:47 +0200403 uint8_t suspend_ref;
404 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) ||
407 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200408 !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
409 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
410 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200411 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200412 }
Harald Welte9ba50052010-03-14 15:45:01 +0800413
Harald Weltea8aa4df2010-05-30 22:00:53 +0200414 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Welte313cccf2010-06-09 11:22:47 +0200415 suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR);
Harald Weltee9686b62010-05-31 18:07:17 +0200416
417 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x RESUME\n", ctx->bvci, tlli);
418
Harald Weltea8aa4df2010-05-30 22:00:53 +0200419 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
420
Harald Welte313cccf2010-06-09 11:22:47 +0200421 /* Inform GMM about the RESUME request */
422 rc = gprs_gmm_rx_resume(&raid, tlli, suspend_ref);
423 if (rc < 0)
424 return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid,
425 NULL);
426
Harald Weltea8aa4df2010-05-30 22:00:53 +0200427 bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800428 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800429}
430
Harald Weltee9686b62010-05-31 18:07:17 +0200431
432static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
433 struct bssgp_bvc_ctx *ctx)
434{
Harald Welteb7363142010-07-23 21:59:29 +0200435 uint32_t tlli = 0;
Harald Weltee9686b62010-05-31 18:07:17 +0200436
437 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
438 !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
439 !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
440 !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
441 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
442 "missing mandatory IE\n", ctx->bvci);
443 }
444
Harald Welteb7363142010-07-23 21:59:29 +0200445 if (TLVP_PRESENT(tp, BSSGP_IE_TLLI))
446 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200447
Harald Welte0e430072010-06-29 18:34:26 +0200448 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x LLC DISCARDED\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200449 ctx->bvci, tlli);
450
451 rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
452
453 /* FIXME: send NM_LLC_DISCARDED to NM */
454 return 0;
455}
456
Harald Welte25de8112010-05-13 21:26:28 +0200457static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200458 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800459{
460
Harald Weltee9686b62010-05-31 18:07:17 +0200461 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
462 bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800463
464 if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
465 !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
466 !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
467 !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200468 !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
469 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
470 "missing mandatory IE\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800471 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200472 }
Harald Welte9ba50052010-03-14 15:45:01 +0800473
Harald Welte30bc19a2010-05-02 11:19:37 +0200474 /* FIXME: actually implement flow control */
475
Harald Welte9ba50052010-03-14 15:45:01 +0800476 /* Send FLOW_CONTROL_BVC_ACK */
Harald Welte24a655f2010-04-30 19:54:29 +0200477 return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
Harald Welte30bc19a2010-05-02 11:19:37 +0200478 msgb_bvci(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800479}
Harald Welte3fddf3c2010-05-01 16:48:27 +0200480
Harald Welte25de8112010-05-13 21:26:28 +0200481/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
482static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200483 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800484{
Harald Welteec19c102010-05-02 09:50:42 +0200485 struct bssgp_normal_hdr *bgph =
486 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Welte30bc19a2010-05-02 11:19:37 +0200487 uint8_t pdu_type = bgph->pdu_type;
Harald Welte9ba50052010-03-14 15:45:01 +0800488 int rc = 0;
489
Harald Welte58e65c92010-05-13 21:45:23 +0200490 /* If traffic is received on a BVC that is marked as blocked, the
491 * received PDU shall not be accepted and a STATUS PDU (Cause value:
492 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
493 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
494 uint16_t bvci = msgb_bvci(msg);
495 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
496 }
497
Harald Welte9ba50052010-03-14 15:45:01 +0800498 switch (pdu_type) {
499 case BSSGP_PDUT_UL_UNITDATA:
500 /* some LLC data from the MS */
Harald Welte25de8112010-05-13 21:26:28 +0200501 rc = bssgp_rx_ul_ud(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800502 break;
503 case BSSGP_PDUT_RA_CAPABILITY:
504 /* BSS requests RA capability or IMSI */
Harald Weltee9686b62010-05-31 18:07:17 +0200505 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
506 bctx->bvci);
Harald Welte6b7cf252010-05-13 19:41:31 +0200507 /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800508 /* FIXME: send RA_CAPA_UPDATE_ACK */
509 break;
510 case BSSGP_PDUT_RADIO_STATUS:
Harald Weltee9686b62010-05-31 18:07:17 +0200511 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800512 /* BSS informs us of some exception */
Harald Welte6b7cf252010-05-13 19:41:31 +0200513 /* FIXME: send GMM_RADIO_STATUS.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800514 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800515 case BSSGP_PDUT_FLOW_CONTROL_BVC:
516 /* BSS informs us of available bandwidth in Gb interface */
Harald Welte25de8112010-05-13 21:26:28 +0200517 rc = bssgp_rx_fc_bvc(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800518 break;
519 case BSSGP_PDUT_FLOW_CONTROL_MS:
520 /* BSS informs us of available bandwidth to one MS */
Harald Weltee9686b62010-05-31 18:07:17 +0200521 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
522 bctx->bvci);
Harald Welte30bc19a2010-05-02 11:19:37 +0200523 /* FIXME: actually implement flow control */
524 /* FIXME: Send FLOW_CONTROL_MS_ACK */
Harald Welte9ba50052010-03-14 15:45:01 +0800525 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800526 case BSSGP_PDUT_STATUS:
527 /* Some exception has occurred */
Harald Welte6b7cf252010-05-13 19:41:31 +0200528 /* FIXME: send NM_STATUS.ind to NM */
Harald Welte9ba50052010-03-14 15:45:01 +0800529 case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
530 case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
531 case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
532 case BSSGP_PDUT_MODIFY_BSS_PFC:
533 case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200534 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
535 "implemented\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200536 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800537 break;
538 /* those only exist in the SGSN -> BSS direction */
539 case BSSGP_PDUT_DL_UNITDATA:
540 case BSSGP_PDUT_PAGING_PS:
541 case BSSGP_PDUT_PAGING_CS:
542 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Harald Welte25de8112010-05-13 21:26:28 +0200543 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
544 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200545 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
546 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200547 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
548 rc = -EINVAL;
549 break;
550 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200551 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
552 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200553 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
554 break;
555 }
556
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800557 return rc;
Harald Welte25de8112010-05-13 21:26:28 +0200558}
559
560/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
561static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200562 struct bssgp_bvc_ctx *bctx)
Harald Welte25de8112010-05-13 21:26:28 +0200563{
564 struct bssgp_normal_hdr *bgph =
565 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
566 uint8_t pdu_type = bgph->pdu_type;
567 int rc = 0;
568 uint16_t ns_bvci = msgb_bvci(msg);
569 uint16_t bvci;
570
571 switch (bgph->pdu_type) {
572 case BSSGP_PDUT_SUSPEND:
573 /* MS wants to suspend */
574 rc = bssgp_rx_suspend(msg, tp, bctx);
575 break;
576 case BSSGP_PDUT_RESUME:
577 /* MS wants to resume */
578 rc = bssgp_rx_resume(msg, tp, bctx);
579 break;
580 case BSSGP_PDUT_FLUSH_LL_ACK:
581 /* BSS informs us it has performed LL FLUSH */
Harald Weltee9686b62010-05-31 18:07:17 +0200582 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx FLUSH LL ACK\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200583 /* FIXME: send NM_FLUSH_LL.res to NM */
584 break;
585 case BSSGP_PDUT_LLC_DISCARD:
586 /* BSS informs that some LLC PDU's have been discarded */
Harald Weltee9686b62010-05-31 18:07:17 +0200587 rc = bssgp_rx_llc_disc(msg, tp, bctx);
Harald Welte25de8112010-05-13 21:26:28 +0200588 break;
589 case BSSGP_PDUT_BVC_BLOCK:
590 /* BSS tells us that BVC shall be blocked */
Harald Welte25de8112010-05-13 21:26:28 +0200591 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200592 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
593 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
594 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200595 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200596 }
Harald Welte2677ea52010-05-31 17:16:36 +0200597 rc = bssgp_rx_bvc_block(msg, tp);
Harald Welte25de8112010-05-13 21:26:28 +0200598 break;
599 case BSSGP_PDUT_BVC_UNBLOCK:
600 /* BSS tells us that BVC shall be unblocked */
Harald Weltee9686b62010-05-31 18:07:17 +0200601 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
602 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
603 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200604 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200605 }
Harald Welte25de8112010-05-13 21:26:28 +0200606 rc = bssgp_rx_bvc_unblock(msg, tp);
607 break;
608 case BSSGP_PDUT_BVC_RESET:
609 /* BSS tells us that BVC init is required */
Harald Welte25de8112010-05-13 21:26:28 +0200610 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200611 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
612 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
613 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200614 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200615 }
Harald Welte25de8112010-05-13 21:26:28 +0200616 rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
617 break;
618 case BSSGP_PDUT_STATUS:
619 /* Some exception has occurred */
Harald Weltee9686b62010-05-31 18:07:17 +0200620 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200621 /* FIXME: send NM_STATUS.ind to NM */
622 break;
623 /* those only exist in the SGSN -> BSS direction */
624 case BSSGP_PDUT_PAGING_PS:
625 case BSSGP_PDUT_PAGING_CS:
Harald Welte9ba50052010-03-14 15:45:01 +0800626 case BSSGP_PDUT_SUSPEND_ACK:
627 case BSSGP_PDUT_SUSPEND_NACK:
628 case BSSGP_PDUT_RESUME_ACK:
629 case BSSGP_PDUT_RESUME_NACK:
Harald Welte6b7cf252010-05-13 19:41:31 +0200630 case BSSGP_PDUT_FLUSH_LL:
Harald Welte9ba50052010-03-14 15:45:01 +0800631 case BSSGP_PDUT_BVC_BLOCK_ACK:
632 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
633 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
Harald Weltee9686b62010-05-31 18:07:17 +0200634 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
635 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200636 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800637 rc = -EINVAL;
638 break;
639 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200640 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
641 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200642 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800643 break;
644 }
645
646 return rc;
647err_mand_ie:
648 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
649}
650
Harald Welte25de8112010-05-13 21:26:28 +0200651/* We expect msgb_bssgph() to point to the BSSGP header */
652int gprs_bssgp_rcvmsg(struct msgb *msg)
653{
654 struct bssgp_normal_hdr *bgph =
655 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
656 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
657 struct tlv_parsed tp;
Harald Welte8a521132010-05-17 22:59:29 +0200658 struct bssgp_bvc_ctx *bctx;
Harald Welte25de8112010-05-13 21:26:28 +0200659 uint8_t pdu_type = bgph->pdu_type;
660 uint16_t ns_bvci = msgb_bvci(msg);
661 int data_len;
662 int rc = 0;
663
664 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
665
666 /* UNITDATA BSSGP headers have TLLI in front */
667 if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
668 pdu_type != BSSGP_PDUT_DL_UNITDATA) {
669 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
670 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
671 } else {
672 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
673 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
674 }
675
676 /* look-up or create the BTS context for this BVC */
677 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
678 /* Only a RESET PDU can create a new BVC context */
679 if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
680 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
681 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
682 pdu_type);
683 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
684 }
685
Harald Welte16c8dbb2010-05-17 23:30:01 +0200686 if (bctx) {
Harald Welte4e5721d2010-05-17 23:41:43 +0200687 log_set_context(BSC_CTX_BVC, bctx);
Harald Welte16c8dbb2010-05-17 23:30:01 +0200688 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
689 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
690 msgb_bssgp_len(msg));
691 }
692
Harald Welte61c07842010-05-18 11:57:08 +0200693 if (ns_bvci == BVCI_SIGNALLING)
Harald Welte25de8112010-05-13 21:26:28 +0200694 rc = gprs_bssgp_rx_sign(msg, &tp, bctx);
Harald Welte61c07842010-05-18 11:57:08 +0200695 else if (ns_bvci == BVCI_PTM)
Harald Welte25de8112010-05-13 21:26:28 +0200696 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
697 else
698 rc = gprs_bssgp_rx_ptp(msg, &tp, bctx);
699
700 return rc;
701}
702
Harald Welte6752fa42010-05-02 09:23:16 +0200703/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
Harald Welte30bc19a2010-05-02 11:19:37 +0200704 * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
Harald Welte2f946832010-05-31 22:12:30 +0200705int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800706{
Harald Welte8a521132010-05-17 22:59:29 +0200707 struct bssgp_bvc_ctx *bctx;
Harald Welte9ba50052010-03-14 15:45:01 +0800708 struct bssgp_ud_hdr *budh;
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200709 uint8_t llc_pdu_tlv_hdr_len = 2;
710 uint8_t *llc_pdu_tlv, *qos_profile;
711 uint16_t pdu_lifetime = 1000; /* centi-seconds */
Harald Welte02f73252010-06-01 11:53:01 +0200712 uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 };
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200713 uint16_t msg_len = msg->len;
Harald Welte30bc19a2010-05-02 11:19:37 +0200714 uint16_t bvci = msgb_bvci(msg);
715 uint16_t nsei = msgb_nsei(msg);
Harald Welte2f946832010-05-31 22:12:30 +0200716 uint16_t drx_params;
Harald Welte9ba50052010-03-14 15:45:01 +0800717
Harald Welte30bc19a2010-05-02 11:19:37 +0200718 /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
Harald Welte61c07842010-05-18 11:57:08 +0200719 if (bvci <= BVCI_PTM ) {
Harald Welteb8a6a832010-05-11 05:54:22 +0200720 LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
Harald Welte30bc19a2010-05-02 11:19:37 +0200721 bvci);
722 return -EINVAL;
723 }
724
725 bctx = btsctx_by_bvci_nsei(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200726 if (!bctx) {
727 /* FIXME: don't simply create missing context, but reject message */
Harald Welte30bc19a2010-05-02 11:19:37 +0200728 bctx = btsctx_alloc(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200729 }
Harald Welte9ba50052010-03-14 15:45:01 +0800730
731 if (msg->len > TVLV_MAX_ONEBYTE)
732 llc_pdu_tlv_hdr_len += 1;
733
734 /* prepend the tag and length of the LLC-PDU TLV */
735 llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
736 llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
737 if (llc_pdu_tlv_hdr_len > 2) {
738 llc_pdu_tlv[1] = msg_len >> 8;
739 llc_pdu_tlv[2] = msg_len & 0xff;
740 } else {
Sylvain Munautb00d1ad2010-06-09 21:13:13 +0200741 llc_pdu_tlv[1] = msg_len & 0x7f;
Harald Welte9ba50052010-03-14 15:45:01 +0800742 llc_pdu_tlv[1] |= 0x80;
743 }
744
Harald Welte2f946832010-05-31 22:12:30 +0200745 /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
746
747 if (mmctx) {
748 /* Old TLLI to help BSS map from old->new */
749#if 0
750 if (mmctx->tlli_old)
751 msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old));
752#endif
753
754 /* IMSI */
755 if (strlen(mmctx->imsi)) {
756 uint8_t mi[10];
757 int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi);
758 if (imsi_len > 2)
759 msgb_tvlv_push(msg, BSSGP_IE_IMSI,
760 imsi_len-2, mi+2);
761 }
762
763 /* DRX parameters */
764 drx_params = htons(mmctx->drx_parms);
765 msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
766 (uint8_t *) &drx_params);
767
768 /* FIXME: Priority */
769
770 /* MS Radio Access Capability */
771 if (mmctx->ms_radio_access_capa.len)
772 msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
773 mmctx->ms_radio_access_capa.len,
774 mmctx->ms_radio_access_capa.buf);
775 }
Harald Welte9ba50052010-03-14 15:45:01 +0800776
777 /* prepend the pdu lifetime */
778 pdu_lifetime = htons(pdu_lifetime);
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200779 msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime);
Harald Welte9ba50052010-03-14 15:45:01 +0800780
781 /* prepend the QoS profile, TLLI and pdu type */
782 budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
783 memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default));
Harald Welte510c3922010-04-30 16:33:12 +0200784 budh->tlli = htonl(msgb_tlli(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800785 budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
786
Harald Welte16c8dbb2010-05-17 23:30:01 +0200787 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
788 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
789
Harald Welte30bc19a2010-05-02 11:19:37 +0200790 /* Identifiers down: BVCI, NSEI (in msgb->cb) */
Harald Welte24a655f2010-04-30 19:54:29 +0200791
792 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800793}
Harald Welte68b4f032010-06-09 16:22:28 +0200794
795/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */
796int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci,
797 struct bssgp_paging_info *pinfo)
798{
799 struct msgb *msg = bssgp_msgb_alloc();
800 struct bssgp_normal_hdr *bgph =
801 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
802 uint16_t drx_params = htons(pinfo->drx_params);
803 uint8_t mi[10];
804 int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi);
805 uint8_t ra[6];
806
807 if (imsi_len < 2)
808 return -EINVAL;
809
810 msgb_nsei(msg) = nsei;
811 msgb_bvci(msg) = ns_bvci;
812
813 if (pinfo->mode == BSSGP_PAGING_PS)
814 bgph->pdu_type = BSSGP_PDUT_PAGING_PS;
815 else
816 bgph->pdu_type = BSSGP_PDUT_PAGING_CS;
817 /* IMSI */
818 msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2);
819 /* DRX Parameters */
820 msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2,
821 (uint8_t *) &drx_params);
822 /* Scope */
823 switch (pinfo->scope) {
824 case BSSGP_PAGING_BSS_AREA:
825 {
826 uint8_t null = 0;
827 msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null);
828 }
829 break;
830 case BSSGP_PAGING_LOCATION_AREA:
831 gsm48_construct_ra(ra, &pinfo->raid);
832 msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra);
833 break;
834 case BSSGP_PAGING_ROUTEING_AREA:
835 gsm48_construct_ra(ra, &pinfo->raid);
836 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
837 break;
838 case BSSGP_PAGING_BVCI:
839 {
840 uint16_t bvci = htons(pinfo->bvci);
841 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci);
842 }
843 break;
844 }
845 /* QoS profile mandatory for PS */
846 if (pinfo->mode == BSSGP_PAGING_PS)
847 msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos);
848
849 /* Optional (P-)TMSI */
850 if (pinfo->ptmsi) {
851 uint32_t ptmsi = htonl(*pinfo->ptmsi);
852 msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi);
853 }
854
855 return gprs_ns_sendmsg(bssgp_nsi, msg);
856}