blob: 58ade4d21561361e48b7f24f8c9d4fcc08a1952c [file] [log] [blame]
Harald Welte9ba50052010-03-14 15:45:01 +08001/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */
2
Harald Welte6752fa42010-05-02 09:23:16 +02003/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
Harald Welte9ba50052010-03-14 15:45:01 +08004 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
Harald Welte4e5721d2010-05-17 23:41:43 +020021 * TODO:
22 * o properly count incoming BVC-RESET packets in counter group
23 * o set log context as early as possible for outgoing packets
Harald Welte9ba50052010-03-14 15:45:01 +080024 */
25
26#include <errno.h>
Harald Welte8f9a3ee2010-05-02 11:26:34 +020027#include <stdint.h>
Harald Welte9ba50052010-03-14 15:45:01 +080028
29#include <netinet/in.h>
30
31#include <osmocore/msgb.h>
32#include <osmocore/tlv.h>
Harald Welte6752fa42010-05-02 09:23:16 +020033#include <osmocore/talloc.h>
Harald Welte25de8112010-05-13 21:26:28 +020034#include <osmocore/rate_ctr.h>
Harald Welte6752fa42010-05-02 09:23:16 +020035
Harald Welte9ba50052010-03-14 15:45:01 +080036#include <openbsc/debug.h>
37#include <openbsc/gsm_data.h>
38#include <openbsc/gsm_04_08_gprs.h>
39#include <openbsc/gprs_bssgp.h>
40#include <openbsc/gprs_llc.h>
41#include <openbsc/gprs_ns.h>
Harald Welte2f946832010-05-31 22:12:30 +020042#include <openbsc/gprs_sgsn.h>
Harald 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,
158 uint8_t *cause)
159{
160 struct msgb *msg = bssgp_msgb_alloc();
161 struct bssgp_normal_hdr *bgph =
162 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
163 uint32_t _tlli;
164
165 msgb_nsei(msg) = nsei;
166 msgb_bvci(msg) = 0; /* Signalling */
167 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
168
169 _tlli = htonl(tlli);
170 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
171 if (cause)
172 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
173
174 return gprs_ns_sendmsg(bssgp_nsi, msg);
175}
176
177/* 10.3.10 RESUME-ACK PDU */
178int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli,
179 const struct gprs_ra_id *ra_id)
180{
181 struct msgb *msg = bssgp_msgb_alloc();
182 struct bssgp_normal_hdr *bgph =
183 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
184 uint32_t _tlli;
185 uint8_t ra[6];
186
187 msgb_nsei(msg) = nsei;
188 msgb_bvci(msg) = 0; /* Signalling */
189 bgph->pdu_type = BSSGP_PDUT_RESUME_ACK;
190
191 _tlli = htonl(tlli);
192 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
193 gsm48_construct_ra(ra, ra_id);
194 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
195
196 return gprs_ns_sendmsg(bssgp_nsi, msg);
197}
198
199/* 10.3.11 RESUME-NACK PDU */
200int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli,
201 const struct gprs_ra_id *ra_id, uint8_t *cause)
202{
203 struct msgb *msg = bssgp_msgb_alloc();
204 struct bssgp_normal_hdr *bgph =
205 (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
206 uint32_t _tlli;
207 uint8_t ra[6];
208
209 msgb_nsei(msg) = nsei;
210 msgb_bvci(msg) = 0; /* Signalling */
211 bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK;
212
213 _tlli = htonl(tlli);
214 msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli);
215 gsm48_construct_ra(ra, ra_id);
216 msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra);
217 if (cause)
218 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause);
219
220 return gprs_ns_sendmsg(bssgp_nsi, msg);
221}
222
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200223uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf)
Harald Welte6752fa42010-05-02 09:23:16 +0200224{
225 /* 6 octets RAC */
226 gsm48_parse_ra(raid, buf);
227 /* 2 octets CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200228 return ntohs(*(uint16_t *) (buf+6));
Harald Welte6752fa42010-05-02 09:23:16 +0200229}
230
Harald Welte3fddf3c2010-05-01 16:48:27 +0200231/* Chapter 8.4 BVC-Reset Procedure */
232static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp,
233 uint16_t ns_bvci)
234{
Harald Welte8a521132010-05-17 22:59:29 +0200235 struct bssgp_bvc_ctx *bctx;
Harald Welte6752fa42010-05-02 09:23:16 +0200236 uint16_t nsei = msgb_nsei(msg);
237 uint16_t bvci;
Harald Welte3fddf3c2010-05-01 16:48:27 +0200238 int rc;
239
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200240 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Weltee9686b62010-05-31 18:07:17 +0200241 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci,
Harald Welte3fddf3c2010-05-01 16:48:27 +0200242 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
243
Harald Welte6752fa42010-05-02 09:23:16 +0200244 /* look-up or create the BTS context for this BVC */
245 bctx = btsctx_by_bvci_nsei(bvci, nsei);
246 if (!bctx)
247 bctx = btsctx_alloc(bvci, nsei);
248
Harald Welte25de8112010-05-13 21:26:28 +0200249 /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */
250 bctx->state &= ~BVC_S_BLOCKED;
251
Harald Welte3fddf3c2010-05-01 16:48:27 +0200252 /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS
253 * informs us about its RAC + Cell ID, so we can create a mapping */
Harald Welte6752fa42010-05-02 09:23:16 +0200254 if (bvci != 0 && bvci != 1) {
255 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) {
Harald Weltee9686b62010-05-31 18:07:17 +0200256 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u RESET "
Harald Welte6752fa42010-05-02 09:23:16 +0200257 "missing mandatory IE\n", bvci);
258 return -EINVAL;
259 }
260 /* actually extract RAC / CID */
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200261 bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id,
262 TLVP_VAL(tp, BSSGP_IE_CELL_ID));
Harald Welteb8a6a832010-05-11 05:54:22 +0200263 LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n",
Harald Welte6752fa42010-05-02 09:23:16 +0200264 bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac,
265 bctx->ra_id.rac, bctx->cell_id, bvci);
266 }
Harald Welte3fddf3c2010-05-01 16:48:27 +0200267
Harald Welte6752fa42010-05-02 09:23:16 +0200268 /* Acknowledge the RESET to the BTS */
Harald Welte3fddf3c2010-05-01 16:48:27 +0200269 rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK,
Harald Welte6752fa42010-05-02 09:23:16 +0200270 nsei, bvci, ns_bvci);
Harald Welte3fddf3c2010-05-01 16:48:27 +0200271 return 0;
272}
273
Harald Welte25de8112010-05-13 21:26:28 +0200274static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp)
275{
276 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200277 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200278
279 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200280 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200281 /* 8.3.2: Signalling BVC shall never be blocked */
282 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
283 "received block for signalling BVC!?!\n",
284 msgb_nsei(msg), msgb_bvci(msg));
285 return 0;
286 }
Harald Welte25de8112010-05-13 21:26:28 +0200287
Harald Weltee9686b62010-05-31 18:07:17 +0200288 LOGP(DBSSGP, LOGL_INFO, "BSSGP BVCI=%u BVC-BLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200289
290 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
291 if (!ptp_ctx)
292 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
293
294 ptp_ctx->state |= BVC_S_BLOCKED;
295 rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]);
296
297 /* FIXME: Send NM_BVC_BLOCK.ind to NM */
298
299 /* We always acknowledge the BLOCKing */
300 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg),
301 bvci, msgb_bvci(msg));
302};
303
304static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp)
305{
306 uint16_t bvci;
Harald Welte8a521132010-05-17 22:59:29 +0200307 struct bssgp_bvc_ctx *ptp_ctx;
Harald Welte25de8112010-05-13 21:26:28 +0200308
309 bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI));
Harald Welte61c07842010-05-18 11:57:08 +0200310 if (bvci == BVCI_SIGNALLING) {
Harald Welte58e65c92010-05-13 21:45:23 +0200311 /* 8.3.2: Signalling BVC shall never be blocked */
312 LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u "
313 "received unblock for signalling BVC!?!\n",
314 msgb_nsei(msg), msgb_bvci(msg));
315 return 0;
316 }
Harald Welte25de8112010-05-13 21:26:28 +0200317
Harald Weltee9686b62010-05-31 18:07:17 +0200318 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200319
320 ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg));
321 if (!ptp_ctx)
322 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg);
323
324 ptp_ctx->state &= ~BVC_S_BLOCKED;
325
326 /* FIXME: Send NM_BVC_UNBLOCK.ind to NM */
327
328 /* We always acknowledge the unBLOCKing */
329 return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg),
330 bvci, msgb_bvci(msg));
331};
332
Harald Welte9ba50052010-03-14 15:45:01 +0800333/* Uplink unit-data */
Harald Welte25de8112010-05-13 21:26:28 +0200334static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200335 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800336{
Harald Welteec19c102010-05-02 09:50:42 +0200337 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800338
Harald Welte6752fa42010-05-02 09:23:16 +0200339 /* extract TLLI and parse TLV IEs */
Harald Welte510c3922010-04-30 16:33:12 +0200340 msgb_tlli(msg) = ntohl(budh->tlli);
Harald Welte9ba50052010-03-14 15:45:01 +0800341
Harald Weltee9686b62010-05-31 18:07:17 +0200342 DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x UPLINK-UNITDATA\n", msgb_tlli(msg));
343
Harald Welte9ba50052010-03-14 15:45:01 +0800344 /* Cell ID and LLC_PDU are the only mandatory IE */
Harald Welte25de8112010-05-13 21:26:28 +0200345 if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200346 !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
347 LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD "
348 "missing mandatory IE\n", msgb_tlli(msg));
Harald Welte25de8112010-05-13 21:26:28 +0200349 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200350 }
Harald Welte30bc19a2010-05-02 11:19:37 +0200351
Harald Weltea2ca4ed2010-05-02 11:54:55 +0200352 /* store pointer to LLC header and CELL ID in msgb->cb */
Holger Hans Peter Freytherb6eded82010-05-23 21:11:19 +0800353 msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
354 msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID);
Harald Welte9ba50052010-03-14 15:45:01 +0800355
Harald Welte25de8112010-05-13 21:26:28 +0200356 return gprs_llc_rcvmsg(msg, tp);
Harald Welte9ba50052010-03-14 15:45:01 +0800357}
358
Harald Welte25de8112010-05-13 21:26:28 +0200359static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200360 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800361{
Harald Welteec19c102010-05-02 09:50:42 +0200362 struct bssgp_normal_hdr *bgph =
363 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200364 struct gprs_ra_id raid;
365 uint32_t tlli;
Harald Welte9ba50052010-03-14 15:45:01 +0800366
Harald Welte25de8112010-05-13 21:26:28 +0200367 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200368 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
369 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND "
370 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200371 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200372 }
Harald Welte9ba50052010-03-14 15:45:01 +0800373
Harald Weltea8aa4df2010-05-30 22:00:53 +0200374 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200375
Harald Welte17925322010-05-31 20:18:35 +0200376 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n",
Harald Weltee9686b62010-05-31 18:07:17 +0200377 ctx->bvci, tlli);
378
Harald Weltea8aa4df2010-05-30 22:00:53 +0200379 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
380
Harald Welte30bc19a2010-05-02 11:19:37 +0200381 /* FIXME: pass the SUSPEND request to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800382 /* SEND SUSPEND_ACK or SUSPEND_NACK */
Harald Weltea8aa4df2010-05-30 22:00:53 +0200383 bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0);
384
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800385 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800386}
387
Harald Welte25de8112010-05-13 21:26:28 +0200388static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200389 struct bssgp_bvc_ctx *ctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800390{
Harald Welteec19c102010-05-02 09:50:42 +0200391 struct bssgp_normal_hdr *bgph =
392 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Weltea8aa4df2010-05-30 22:00:53 +0200393 struct gprs_ra_id raid;
394 uint32_t tlli;
Harald Welte9ba50052010-03-14 15:45:01 +0800395
Harald Welte25de8112010-05-13 21:26:28 +0200396 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
397 !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200398 !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) {
399 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME "
400 "missing mandatory IE\n", ctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200401 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200402 }
Harald Welte9ba50052010-03-14 15:45:01 +0800403
Harald Weltea8aa4df2010-05-30 22:00:53 +0200404 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
Harald Weltee9686b62010-05-31 18:07:17 +0200405
406 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x RESUME\n", ctx->bvci, tlli);
407
Harald Weltea8aa4df2010-05-30 22:00:53 +0200408 gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA));
409
Harald Welte30bc19a2010-05-02 11:19:37 +0200410 /* FIXME: pass the RESUME request to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800411 /* SEND RESUME_ACK or RESUME_NACK */
Harald Weltea8aa4df2010-05-30 22:00:53 +0200412 bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid);
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800413 return 0;
Harald Welte9ba50052010-03-14 15:45:01 +0800414}
415
Harald Weltee9686b62010-05-31 18:07:17 +0200416
417static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp,
418 struct bssgp_bvc_ctx *ctx)
419{
420 uint32_t tlli;
421
422 if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) ||
423 !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) ||
424 !TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
425 !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) {
426 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED "
427 "missing mandatory IE\n", ctx->bvci);
428 }
429
430 tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI));
431
432 DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%u LLC DISCARDED\n",
433 ctx->bvci, tlli);
434
435 rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]);
436
437 /* FIXME: send NM_LLC_DISCARDED to NM */
438 return 0;
439}
440
Harald Welte25de8112010-05-13 21:26:28 +0200441static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200442 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800443{
444
Harald Weltee9686b62010-05-31 18:07:17 +0200445 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n",
446 bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800447
448 if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) ||
449 !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) ||
450 !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) ||
451 !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200452 !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) {
453 LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC "
454 "missing mandatory IE\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800455 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
Harald Weltee9686b62010-05-31 18:07:17 +0200456 }
Harald Welte9ba50052010-03-14 15:45:01 +0800457
Harald Welte30bc19a2010-05-02 11:19:37 +0200458 /* FIXME: actually implement flow control */
459
Harald Welte9ba50052010-03-14 15:45:01 +0800460 /* Send FLOW_CONTROL_BVC_ACK */
Harald Welte24a655f2010-04-30 19:54:29 +0200461 return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG),
Harald Welte30bc19a2010-05-02 11:19:37 +0200462 msgb_bvci(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800463}
Harald Welte3fddf3c2010-05-01 16:48:27 +0200464
Harald Welte25de8112010-05-13 21:26:28 +0200465/* Receive a BSSGP PDU from a BSS on a PTP BVCI */
466static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200467 struct bssgp_bvc_ctx *bctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800468{
Harald Welteec19c102010-05-02 09:50:42 +0200469 struct bssgp_normal_hdr *bgph =
470 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
Harald Welte30bc19a2010-05-02 11:19:37 +0200471 uint8_t pdu_type = bgph->pdu_type;
Harald Welte9ba50052010-03-14 15:45:01 +0800472 int rc = 0;
473
Harald Welte58e65c92010-05-13 21:45:23 +0200474 /* If traffic is received on a BVC that is marked as blocked, the
475 * received PDU shall not be accepted and a STATUS PDU (Cause value:
476 * BVC Blocked) shall be sent to the peer entity on the signalling BVC */
477 if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) {
478 uint16_t bvci = msgb_bvci(msg);
479 return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg);
480 }
481
Harald Welte9ba50052010-03-14 15:45:01 +0800482 switch (pdu_type) {
483 case BSSGP_PDUT_UL_UNITDATA:
484 /* some LLC data from the MS */
Harald Welte25de8112010-05-13 21:26:28 +0200485 rc = bssgp_rx_ul_ud(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800486 break;
487 case BSSGP_PDUT_RA_CAPABILITY:
488 /* BSS requests RA capability or IMSI */
Harald Weltee9686b62010-05-31 18:07:17 +0200489 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n",
490 bctx->bvci);
Harald Welte6b7cf252010-05-13 19:41:31 +0200491 /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800492 /* FIXME: send RA_CAPA_UPDATE_ACK */
493 break;
494 case BSSGP_PDUT_RADIO_STATUS:
Harald Weltee9686b62010-05-31 18:07:17 +0200495 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci);
Harald Welte9ba50052010-03-14 15:45:01 +0800496 /* BSS informs us of some exception */
Harald Welte6b7cf252010-05-13 19:41:31 +0200497 /* FIXME: send GMM_RADIO_STATUS.ind to GMM */
Harald Welte9ba50052010-03-14 15:45:01 +0800498 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800499 case BSSGP_PDUT_FLOW_CONTROL_BVC:
500 /* BSS informs us of available bandwidth in Gb interface */
Harald Welte25de8112010-05-13 21:26:28 +0200501 rc = bssgp_rx_fc_bvc(msg, tp, bctx);
Harald Welte9ba50052010-03-14 15:45:01 +0800502 break;
503 case BSSGP_PDUT_FLOW_CONTROL_MS:
504 /* BSS informs us of available bandwidth to one MS */
Harald Weltee9686b62010-05-31 18:07:17 +0200505 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n",
506 bctx->bvci);
Harald Welte30bc19a2010-05-02 11:19:37 +0200507 /* FIXME: actually implement flow control */
508 /* FIXME: Send FLOW_CONTROL_MS_ACK */
Harald Welte9ba50052010-03-14 15:45:01 +0800509 break;
Harald Welte9ba50052010-03-14 15:45:01 +0800510 case BSSGP_PDUT_STATUS:
511 /* Some exception has occurred */
Harald Welte6b7cf252010-05-13 19:41:31 +0200512 /* FIXME: send NM_STATUS.ind to NM */
Harald Welte9ba50052010-03-14 15:45:01 +0800513 case BSSGP_PDUT_DOWNLOAD_BSS_PFC:
514 case BSSGP_PDUT_CREATE_BSS_PFC_ACK:
515 case BSSGP_PDUT_CREATE_BSS_PFC_NACK:
516 case BSSGP_PDUT_MODIFY_BSS_PFC:
517 case BSSGP_PDUT_DELETE_BSS_PFC_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200518 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] "
519 "implemented\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200520 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800521 break;
522 /* those only exist in the SGSN -> BSS direction */
523 case BSSGP_PDUT_DL_UNITDATA:
524 case BSSGP_PDUT_PAGING_PS:
525 case BSSGP_PDUT_PAGING_CS:
526 case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
Harald Welte25de8112010-05-13 21:26:28 +0200527 case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
528 case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
Harald Weltee9686b62010-05-31 18:07:17 +0200529 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists "
530 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200531 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
532 rc = -EINVAL;
533 break;
534 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200535 DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n",
536 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200537 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
538 break;
539 }
540
Holger Hans Peter Freytherd30cefa2010-05-23 21:12:15 +0800541 return rc;
Harald Welte25de8112010-05-13 21:26:28 +0200542}
543
544/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */
545static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp,
Harald Welte8a521132010-05-17 22:59:29 +0200546 struct bssgp_bvc_ctx *bctx)
Harald Welte25de8112010-05-13 21:26:28 +0200547{
548 struct bssgp_normal_hdr *bgph =
549 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
550 uint8_t pdu_type = bgph->pdu_type;
551 int rc = 0;
552 uint16_t ns_bvci = msgb_bvci(msg);
553 uint16_t bvci;
554
555 switch (bgph->pdu_type) {
556 case BSSGP_PDUT_SUSPEND:
557 /* MS wants to suspend */
558 rc = bssgp_rx_suspend(msg, tp, bctx);
559 break;
560 case BSSGP_PDUT_RESUME:
561 /* MS wants to resume */
562 rc = bssgp_rx_resume(msg, tp, bctx);
563 break;
564 case BSSGP_PDUT_FLUSH_LL_ACK:
565 /* BSS informs us it has performed LL FLUSH */
Harald Weltee9686b62010-05-31 18:07:17 +0200566 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx FLUSH LL ACK\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200567 /* FIXME: send NM_FLUSH_LL.res to NM */
568 break;
569 case BSSGP_PDUT_LLC_DISCARD:
570 /* BSS informs that some LLC PDU's have been discarded */
Harald Weltee9686b62010-05-31 18:07:17 +0200571 rc = bssgp_rx_llc_disc(msg, tp, bctx);
Harald Welte25de8112010-05-13 21:26:28 +0200572 break;
573 case BSSGP_PDUT_BVC_BLOCK:
574 /* BSS tells us that BVC shall be blocked */
Harald Welte25de8112010-05-13 21:26:28 +0200575 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200576 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
577 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK "
578 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200579 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200580 }
Harald Welte2677ea52010-05-31 17:16:36 +0200581 rc = bssgp_rx_bvc_block(msg, tp);
Harald Welte25de8112010-05-13 21:26:28 +0200582 break;
583 case BSSGP_PDUT_BVC_UNBLOCK:
584 /* BSS tells us that BVC shall be unblocked */
Harald Weltee9686b62010-05-31 18:07:17 +0200585 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
586 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK "
587 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200588 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200589 }
Harald Welte25de8112010-05-13 21:26:28 +0200590 rc = bssgp_rx_bvc_unblock(msg, tp);
591 break;
592 case BSSGP_PDUT_BVC_RESET:
593 /* BSS tells us that BVC init is required */
Harald Welte25de8112010-05-13 21:26:28 +0200594 if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) ||
Harald Weltee9686b62010-05-31 18:07:17 +0200595 !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) {
596 LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET "
597 "missing mandatory IE\n");
Harald Welte25de8112010-05-13 21:26:28 +0200598 goto err_mand_ie;
Harald Weltee9686b62010-05-31 18:07:17 +0200599 }
Harald Welte25de8112010-05-13 21:26:28 +0200600 rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci);
601 break;
602 case BSSGP_PDUT_STATUS:
603 /* Some exception has occurred */
Harald Weltee9686b62010-05-31 18:07:17 +0200604 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci);
Harald Welte25de8112010-05-13 21:26:28 +0200605 /* FIXME: send NM_STATUS.ind to NM */
606 break;
607 /* those only exist in the SGSN -> BSS direction */
608 case BSSGP_PDUT_PAGING_PS:
609 case BSSGP_PDUT_PAGING_CS:
Harald Welte9ba50052010-03-14 15:45:01 +0800610 case BSSGP_PDUT_SUSPEND_ACK:
611 case BSSGP_PDUT_SUSPEND_NACK:
612 case BSSGP_PDUT_RESUME_ACK:
613 case BSSGP_PDUT_RESUME_NACK:
Harald Welte6b7cf252010-05-13 19:41:31 +0200614 case BSSGP_PDUT_FLUSH_LL:
Harald Welte9ba50052010-03-14 15:45:01 +0800615 case BSSGP_PDUT_BVC_BLOCK_ACK:
616 case BSSGP_PDUT_BVC_UNBLOCK_ACK:
617 case BSSGP_PDUT_SGSN_INVOKE_TRACE:
Harald Weltee9686b62010-05-31 18:07:17 +0200618 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists "
619 "in DL\n", bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200620 bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800621 rc = -EINVAL;
622 break;
623 default:
Harald Weltee9686b62010-05-31 18:07:17 +0200624 DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n",
625 bctx->bvci, pdu_type);
Harald Welte25de8112010-05-13 21:26:28 +0200626 rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800627 break;
628 }
629
630 return rc;
631err_mand_ie:
632 return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
633}
634
Harald Welte25de8112010-05-13 21:26:28 +0200635/* We expect msgb_bssgph() to point to the BSSGP header */
636int gprs_bssgp_rcvmsg(struct msgb *msg)
637{
638 struct bssgp_normal_hdr *bgph =
639 (struct bssgp_normal_hdr *) msgb_bssgph(msg);
640 struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg);
641 struct tlv_parsed tp;
Harald Welte8a521132010-05-17 22:59:29 +0200642 struct bssgp_bvc_ctx *bctx;
Harald Welte25de8112010-05-13 21:26:28 +0200643 uint8_t pdu_type = bgph->pdu_type;
644 uint16_t ns_bvci = msgb_bvci(msg);
645 int data_len;
646 int rc = 0;
647
648 /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
649
650 /* UNITDATA BSSGP headers have TLLI in front */
651 if (pdu_type != BSSGP_PDUT_UL_UNITDATA &&
652 pdu_type != BSSGP_PDUT_DL_UNITDATA) {
653 data_len = msgb_bssgp_len(msg) - sizeof(*bgph);
654 rc = bssgp_tlv_parse(&tp, bgph->data, data_len);
655 } else {
656 data_len = msgb_bssgp_len(msg) - sizeof(*budh);
657 rc = bssgp_tlv_parse(&tp, budh->data, data_len);
658 }
659
660 /* look-up or create the BTS context for this BVC */
661 bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
662 /* Only a RESET PDU can create a new BVC context */
663 if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) {
664 LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
665 "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci,
666 pdu_type);
667 return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg);
668 }
669
Harald Welte16c8dbb2010-05-17 23:30:01 +0200670 if (bctx) {
Harald Welte4e5721d2010-05-17 23:41:43 +0200671 log_set_context(BSC_CTX_BVC, bctx);
Harald Welte16c8dbb2010-05-17 23:30:01 +0200672 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]);
673 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN],
674 msgb_bssgp_len(msg));
675 }
676
Harald Welte61c07842010-05-18 11:57:08 +0200677 if (ns_bvci == BVCI_SIGNALLING)
Harald Welte25de8112010-05-13 21:26:28 +0200678 rc = gprs_bssgp_rx_sign(msg, &tp, bctx);
Harald Welte61c07842010-05-18 11:57:08 +0200679 else if (ns_bvci == BVCI_PTM)
Harald Welte25de8112010-05-13 21:26:28 +0200680 rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
681 else
682 rc = gprs_bssgp_rx_ptp(msg, &tp, bctx);
683
684 return rc;
685}
686
Harald Welte6752fa42010-05-02 09:23:16 +0200687/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU
Harald Welte30bc19a2010-05-02 11:19:37 +0200688 * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */
Harald Welte2f946832010-05-31 22:12:30 +0200689int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx)
Harald Welte9ba50052010-03-14 15:45:01 +0800690{
Harald Welte8a521132010-05-17 22:59:29 +0200691 struct bssgp_bvc_ctx *bctx;
Harald Welte9ba50052010-03-14 15:45:01 +0800692 struct bssgp_ud_hdr *budh;
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200693 uint8_t llc_pdu_tlv_hdr_len = 2;
694 uint8_t *llc_pdu_tlv, *qos_profile;
695 uint16_t pdu_lifetime = 1000; /* centi-seconds */
696 uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x21 };
697 uint16_t msg_len = msg->len;
Harald Welte30bc19a2010-05-02 11:19:37 +0200698 uint16_t bvci = msgb_bvci(msg);
699 uint16_t nsei = msgb_nsei(msg);
Harald Welte2f946832010-05-31 22:12:30 +0200700 uint16_t drx_params;
Harald Welte9ba50052010-03-14 15:45:01 +0800701
Harald Welte30bc19a2010-05-02 11:19:37 +0200702 /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */
Harald Welte61c07842010-05-18 11:57:08 +0200703 if (bvci <= BVCI_PTM ) {
Harald Welteb8a6a832010-05-11 05:54:22 +0200704 LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n",
Harald Welte30bc19a2010-05-02 11:19:37 +0200705 bvci);
706 return -EINVAL;
707 }
708
709 bctx = btsctx_by_bvci_nsei(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200710 if (!bctx) {
711 /* FIXME: don't simply create missing context, but reject message */
Harald Welte30bc19a2010-05-02 11:19:37 +0200712 bctx = btsctx_alloc(bvci, nsei);
Harald Welte25de8112010-05-13 21:26:28 +0200713 }
Harald Welte9ba50052010-03-14 15:45:01 +0800714
715 if (msg->len > TVLV_MAX_ONEBYTE)
716 llc_pdu_tlv_hdr_len += 1;
717
718 /* prepend the tag and length of the LLC-PDU TLV */
719 llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len);
720 llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU;
721 if (llc_pdu_tlv_hdr_len > 2) {
722 llc_pdu_tlv[1] = msg_len >> 8;
723 llc_pdu_tlv[2] = msg_len & 0xff;
724 } else {
725 llc_pdu_tlv[1] = msg_len & 0x3f;
726 llc_pdu_tlv[1] |= 0x80;
727 }
728
Harald Welte2f946832010-05-31 22:12:30 +0200729 /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */
730
731 if (mmctx) {
732 /* Old TLLI to help BSS map from old->new */
733#if 0
734 if (mmctx->tlli_old)
735 msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old));
736#endif
737
738 /* IMSI */
739 if (strlen(mmctx->imsi)) {
740 uint8_t mi[10];
741 int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi);
742 if (imsi_len > 2)
743 msgb_tvlv_push(msg, BSSGP_IE_IMSI,
744 imsi_len-2, mi+2);
745 }
746
747 /* DRX parameters */
748 drx_params = htons(mmctx->drx_parms);
749 msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2,
750 (uint8_t *) &drx_params);
751
752 /* FIXME: Priority */
753
754 /* MS Radio Access Capability */
755 if (mmctx->ms_radio_access_capa.len)
756 msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP,
757 mmctx->ms_radio_access_capa.len,
758 mmctx->ms_radio_access_capa.buf);
759 }
Harald Welte9ba50052010-03-14 15:45:01 +0800760
761 /* prepend the pdu lifetime */
762 pdu_lifetime = htons(pdu_lifetime);
Harald Welte8f9a3ee2010-05-02 11:26:34 +0200763 msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime);
Harald Welte9ba50052010-03-14 15:45:01 +0800764
765 /* prepend the QoS profile, TLLI and pdu type */
766 budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh));
767 memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default));
Harald Welte510c3922010-04-30 16:33:12 +0200768 budh->tlli = htonl(msgb_tlli(msg));
Harald Welte9ba50052010-03-14 15:45:01 +0800769 budh->pdu_type = BSSGP_PDUT_DL_UNITDATA;
770
Harald Welte16c8dbb2010-05-17 23:30:01 +0200771 rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]);
772 rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len);
773
Harald Welte30bc19a2010-05-02 11:19:37 +0200774 /* Identifiers down: BVCI, NSEI (in msgb->cb) */
Harald Welte24a655f2010-04-30 19:54:29 +0200775
776 return gprs_ns_sendmsg(bssgp_nsi, msg);
Harald Welte9ba50052010-03-14 15:45:01 +0800777}