blob: 64bfd743dcb098ea276c628d389a50631979662f [file] [log] [blame]
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +02001/* GPRS utility functions */
2
3/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2010-2014 by On-Waves
5 * (C) 2013 by Holger Hans Peter Freyther
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22#include <openbsc/gprs_utils.h>
23
24#include <osmocom/core/msgb.h>
25#include <osmocom/gprs/gprs_ns.h>
26
Harald Welte53373bc2016-04-20 17:11:43 +020027#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020028#include <osmocom/gsm/protocol/gsm_04_08.h>
Jacob Erlbecke7bcdc32016-01-04 18:43:34 +010029#include <osmocom/gsm/gsm48.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020030
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +020031#include <string.h>
32
33/* FIXME: this needs to go to libosmocore/msgb.c */
34struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name)
35{
36 struct libgb_msgb_cb *old_cb, *new_cb;
37 struct msgb *new_msg;
38
39 new_msg = msgb_alloc(msg->data_len, name);
40 if (!new_msg)
41 return NULL;
42
43 /* copy data */
44 memcpy(new_msg->_data, msg->_data, new_msg->data_len);
45
46 /* copy header */
47 new_msg->len = msg->len;
48 new_msg->data += msg->data - msg->_data;
49 new_msg->head += msg->head - msg->_data;
50 new_msg->tail += msg->tail - msg->_data;
51
Jacob Erlbeck7e31f842014-09-22 18:50:08 +020052 if (msg->l1h)
53 new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
54 if (msg->l2h)
55 new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data);
56 if (msg->l3h)
57 new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data);
58 if (msg->l4h)
59 new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +020060
61 /* copy GB specific data */
62 old_cb = LIBGB_MSGB_CB(msg);
63 new_cb = LIBGB_MSGB_CB(new_msg);
64
Jacob Erlbeck7e31f842014-09-22 18:50:08 +020065 if (old_cb->bssgph)
66 new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data);
67 if (old_cb->llch)
68 new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data);
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +020069
70 /* bssgp_cell_id is a pointer into the old msgb, so we need to make
71 * it a pointer into the new msgb */
Jacob Erlbeck7e31f842014-09-22 18:50:08 +020072 if (old_cb->bssgp_cell_id)
73 new_cb->bssgp_cell_id = new_msg->_data +
74 (old_cb->bssgp_cell_id - msg->_data);
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +020075 new_cb->nsei = old_cb->nsei;
76 new_cb->bvci = old_cb->bvci;
77 new_cb->tlli = old_cb->tlli;
78
79 return new_msg;
80}
81
82/* TODO: Move this to libosmocore/msgb.c */
83int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
84 size_t old_size, size_t new_size)
85{
86 int rc;
87 uint8_t *rest = area + old_size;
88 int rest_len = msg->len - old_size - (area - msg->data);
89 int delta_size = (int)new_size - (int)old_size;
90
91 if (delta_size == 0)
92 return 0;
93
94 if (delta_size > 0) {
95 rc = msgb_trim(msg, msg->len + delta_size);
96 if (rc < 0)
97 return rc;
98 }
99
100 memmove(area + new_size, area + old_size, rest_len);
101
102 if (msg->l1h >= rest)
103 msg->l1h += delta_size;
104 if (msg->l2h >= rest)
105 msg->l2h += delta_size;
106 if (msg->l3h >= rest)
107 msg->l3h += delta_size;
108 if (msg->l4h >= rest)
109 msg->l4h += delta_size;
110
111 if (delta_size < 0)
112 msgb_trim(msg, msg->len + delta_size);
113
114 return 0;
115}
116
117/* TODO: Move these conversion functions to a utils file. */
Neels Hofmeyr30f7bcb2015-11-08 20:34:47 +0100118/* TODO: consolidate with gprs_apn2str(). */
119/** memmove apn_enc to out_str, replacing the length octets in apn_enc with '.'
120 * (omitting the first one) and terminating with a '\0'.
Holger Hans Peter Freytherce1b22e2014-08-04 14:22:13 +0200121 * out_str needs to have rest_chars amount of bytes or 1 whatever is bigger.
122 */
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +0200123char * gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars)
124{
125 char *str = out_str;
126
127 while (rest_chars > 0 && apn_enc[0]) {
128 size_t label_size = apn_enc[0];
129 if (label_size + 1 > rest_chars)
130 return NULL;
131
132 memmove(str, apn_enc + 1, label_size);
133 str += label_size;
134 rest_chars -= label_size + 1;
135 apn_enc += label_size + 1;
136
137 if (rest_chars)
138 *(str++) = '.';
139 }
140 str[0] = '\0';
141
142 return out_str;
143}
144
Holger Hans Peter Freytherce1b22e2014-08-04 14:22:13 +0200145int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str)
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +0200146{
Holger Hans Peter Freytherce1b22e2014-08-04 14:22:13 +0200147 uint8_t *last_len_field;
148 int len;
149
150 /* Can we even write the length field to the output? */
151 if (max_len == 0)
152 return -1;
153
154 /* Remember where we need to put the length once we know it */
155 last_len_field = apn_enc;
156 len = 1;
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +0200157 apn_enc += 1;
158
159 while (str[0]) {
Holger Hans Peter Freytherce1b22e2014-08-04 14:22:13 +0200160 if (len >= max_len)
161 return -1;
162
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +0200163 if (str[0] == '.') {
164 *last_len_field = (apn_enc - last_len_field) - 1;
165 last_len_field = apn_enc;
166 } else {
167 *apn_enc = str[0];
168 }
169 apn_enc += 1;
170 str += 1;
171 len += 1;
Holger Hans Peter Freyther7127b022014-08-04 11:52:52 +0200172 }
173
174 *last_len_field = (apn_enc - last_len_field) - 1;
175
176 return len;
177}
178
Jacob Erlbeck79af67d2015-01-19 08:27:34 +0100179/* GSM 04.08, 10.5.7.3 GPRS Timer */
180int gprs_tmr_to_secs(uint8_t tmr)
181{
182 switch (tmr & GPRS_TMR_UNIT_MASK) {
183 case GPRS_TMR_2SECONDS:
184 return 2 * (tmr & GPRS_TMR_FACT_MASK);
185 default:
186 case GPRS_TMR_MINUTE:
187 return 60 * (tmr & GPRS_TMR_FACT_MASK);
188 case GPRS_TMR_6MINUTE:
189 return 360 * (tmr & GPRS_TMR_FACT_MASK);
190 case GPRS_TMR_DEACTIVATED:
191 return -1;
192 }
193}
194
195/* This functions returns a tmr value such that
196 * - f is monotonic
197 * - f(s) <= s
198 * - f(s) == s if a tmr exists with s = gprs_tmr_to_secs(tmr)
199 * - the best possible resolution is used
200 * where
201 * f(s) = gprs_tmr_to_secs(gprs_secs_to_tmr_floor(s))
202 */
203uint8_t gprs_secs_to_tmr_floor(int secs)
204{
205 if (secs < 0)
206 return GPRS_TMR_DEACTIVATED;
207 if (secs < 2 * 32)
208 return GPRS_TMR_2SECONDS | (secs / 2);
209 if (secs < 60 * 2)
210 /* Ensure monotonicity */
211 return GPRS_TMR_2SECONDS | GPRS_TMR_FACT_MASK;
212 if (secs < 60 * 32)
213 return GPRS_TMR_MINUTE | (secs / 60);
214 if (secs < 360 * 6)
215 /* Ensure monotonicity */
216 return GPRS_TMR_MINUTE | GPRS_TMR_FACT_MASK;
217 if (secs < 360 * 32)
218 return GPRS_TMR_6MINUTE | (secs / 360);
219
220 return GPRS_TMR_6MINUTE | GPRS_TMR_FACT_MASK;
221}
222
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200223/* GSM 04.08, 10.5.1.4 */
224int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len)
225{
226 if (value_len != GSM48_TMSI_LEN)
227 return 0;
228
229 if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI)
230 return 0;
231
232 return 1;
233}
234
235/* GSM 04.08, 10.5.1.4 */
236int gprs_is_mi_imsi(const uint8_t *value, size_t value_len)
237{
238 if (value_len == 0)
239 return 0;
240
241 if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI)
242 return 0;
243
244 return 1;
245}
246
247int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi)
248{
249 uint32_t tmsi_be;
250
251 if (!gprs_is_mi_tmsi(value, value_len))
252 return 0;
253
254 memcpy(&tmsi_be, value + 1, sizeof(tmsi_be));
255
256 *tmsi = ntohl(tmsi_be);
257 return 1;
258}
259
Jacob Erlbeck49389172014-10-02 16:14:47 +0200260void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi)
261{
262 uint32_t tmsi_be;
263
264 memcpy(&tmsi_be, value, sizeof(tmsi_be));
265
266 *tmsi = ntohl(tmsi_be);
267}
Jacob Erlbeckdcfd4562014-12-11 11:01:46 +0100268
269/* TODO: Move shift functions to libosmocore */
270
271int gprs_shift_v_fixed(uint8_t **data, size_t *data_len,
272 size_t len, uint8_t **value)
273{
274 if (len > *data_len)
275 goto fail;
276
277 if (value)
278 *value = *data;
279
280 *data += len;
281 *data_len -= len;
282
283 return len;
284
285fail:
286 *data += *data_len;
287 *data_len = 0;
288 return -1;
289}
290
291int gprs_match_tv_fixed(uint8_t **data, size_t *data_len,
292 uint8_t tag, size_t len,
293 uint8_t **value)
294{
295 size_t ie_len;
296
297 if (*data_len == 0)
298 goto fail;
299
300 if ((*data)[0] != tag)
301 return 0;
302
303 if (len > *data_len - 1)
304 goto fail;
305
306 if (value)
307 *value = *data + 1;
308
309 ie_len = len + 1;
310 *data += ie_len;
311 *data_len -= ie_len;
312
313 return ie_len;
314
315fail:
316 *data += *data_len;
317 *data_len = 0;
318 return -1;
319}
320
321int gprs_match_tlv(uint8_t **data, size_t *data_len,
Jacob Erlbeck697a5342014-12-11 12:05:29 +0100322 uint8_t expected_tag, uint8_t **value, size_t *value_len)
323{
324 int rc;
325 uint8_t tag;
326 uint8_t *old_data = *data;
327 size_t old_data_len = *data_len;
328
329 rc = gprs_shift_tlv(data, data_len, &tag, value, value_len);
330
331 if (rc > 0 && tag != expected_tag) {
332 *data = old_data;
333 *data_len = old_data_len;
334 return 0;
335 }
336
337 return rc;
338}
339
340int gprs_shift_tlv(uint8_t **data, size_t *data_len,
341 uint8_t *tag, uint8_t **value, size_t *value_len)
Jacob Erlbeckdcfd4562014-12-11 11:01:46 +0100342{
343 size_t len;
344 size_t ie_len;
345
346 if (*data_len < 2)
347 goto fail;
348
Jacob Erlbeckdcfd4562014-12-11 11:01:46 +0100349 len = (*data)[1];
350 if (len > *data_len - 2)
351 goto fail;
352
Jacob Erlbeck697a5342014-12-11 12:05:29 +0100353 if (tag)
354 *tag = (*data)[0];
Jacob Erlbeckdcfd4562014-12-11 11:01:46 +0100355 if (value)
356 *value = *data + 2;
357 if (value_len)
358 *value_len = len;
359
360 ie_len = len + 2;
361
362 *data += ie_len;
363 *data_len -= ie_len;
364
365 return ie_len;
366
367fail:
368 *data += *data_len;
369 *data_len = 0;
370 return -1;
371}
372
373int gprs_shift_lv(uint8_t **data, size_t *data_len,
374 uint8_t **value, size_t *value_len)
375{
376 size_t len;
377 size_t ie_len;
378
379 if (*data_len < 1)
380 goto fail;
381
382 len = (*data)[0];
383 if (len > *data_len - 1)
384 goto fail;
385
386 if (value)
387 *value = *data + 1;
388 if (value_len)
389 *value_len = len;
390
391 ie_len = len + 1;
392 *data += ie_len;
393 *data_len -= ie_len;
394
395 return ie_len;
396
397fail:
398 *data += *data_len;
399 *data_len = 0;
400 return -1;
401}
402
Jacob Erlbecke7bcdc32016-01-04 18:43:34 +0100403int gprs_ra_id_equals(const struct gprs_ra_id *id1,
404 const struct gprs_ra_id *id2)
405{
406 return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
407 id1->lac == id2->lac && id1->rac == id2->rac);
408}