blob: 7ed8c3f19da1874a4f5c42dd49ca252fd48a9b7f [file] [log] [blame]
Holger Hans Peter Freyther8ac3a722014-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 Welte35ade5e2016-04-20 17:11:43 +020027#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
Jacob Erlbeckcf11e932014-08-19 12:21:01 +020028#include <osmocom/gsm/protocol/gsm_04_08.h>
Jacob Erlbeckd6466c92016-01-04 18:43:34 +010029#include <osmocom/gsm/gsm48.h>
Jacob Erlbeckcf11e932014-08-19 12:21:01 +020030
Holger Hans Peter Freyther8ac3a722014-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 Erlbeck75575612014-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 Freyther8ac3a722014-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 Erlbeck75575612014-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 Freyther8ac3a722014-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 Erlbeck75575612014-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 Freyther8ac3a722014-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 Hofmeyrf16657a2015-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 Freyther2d10ad12014-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 Freyther8ac3a722014-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 Freyther2d10ad12014-08-04 14:22:13 +0200145int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str)
Holger Hans Peter Freyther8ac3a722014-08-04 11:52:52 +0200146{
Holger Hans Peter Freyther2d10ad12014-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 Freyther8ac3a722014-08-04 11:52:52 +0200157 apn_enc += 1;
158
159 while (str[0]) {
Holger Hans Peter Freyther2d10ad12014-08-04 14:22:13 +0200160 if (len >= max_len)
161 return -1;
162
Holger Hans Peter Freyther8ac3a722014-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 Freyther8ac3a722014-08-04 11:52:52 +0200172 }
173
174 *last_len_field = (apn_enc - last_len_field) - 1;
175
176 return len;
177}
178
Jacob Erlbeckc9a7ba32015-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 Erlbeckcf11e932014-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 Erlbeck17608c62014-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 Erlbeckdce45012014-12-11 11:01:46 +0100268
269/* TODO: Move shift functions to libosmocore */
270
Harald Welte8f4b2b82016-04-25 15:14:01 +0200271/*! Advance the data pointer, subtract length and assign value pointer
272 * \param data pointer to the pointer to data
273 * \param data_len pointer to size_t containing \arg data length
274 * \param[in] len the length that we expect the fixed IE to hav
275 * \param[out] value pointer to pointer of value part of IE
276 * \returns length of IE value; negative in case of error
277 */
278int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,
279 size_t len, uint8_t **value)
Jacob Erlbeckdce45012014-12-11 11:01:46 +0100280{
281 if (len > *data_len)
282 goto fail;
283
284 if (value)
285 *value = *data;
286
287 *data += len;
288 *data_len -= len;
289
290 return len;
291
292fail:
293 *data += *data_len;
294 *data_len = 0;
295 return -1;
296}
297
Harald Welte8f4b2b82016-04-25 15:14:01 +0200298/*! Match tag, check length and assign value pointer
299 * \param data pointer to the pointer to data
300 * \param data_len pointer to size_t containing \arg data length
301 * \param[in] tag the tag (IEI) that we expect at \arg data
302 * \param[in] len the length that we expect the fixed IE to have
303 * \param[out] value pointer to pointer of value part of IE
304 * \returns length of IE value; negative in case of error
305 */
306int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,
307 uint8_t tag, size_t len,
308 uint8_t **value)
Jacob Erlbeckdce45012014-12-11 11:01:46 +0100309{
310 size_t ie_len;
311
312 if (*data_len == 0)
313 goto fail;
314
315 if ((*data)[0] != tag)
316 return 0;
317
318 if (len > *data_len - 1)
319 goto fail;
320
321 if (value)
322 *value = *data + 1;
323
324 ie_len = len + 1;
325 *data += ie_len;
326 *data_len -= ie_len;
327
328 return ie_len;
329
330fail:
331 *data += *data_len;
332 *data_len = 0;
333 return -1;
334}
335
Harald Welte8f4b2b82016-04-25 15:14:01 +0200336/*! Verify TLV header and advance data / subtract length
337 * \param data pointer to the pointer to data
338 * \param data_len pointer to size_t containing \arg data length
339 * \param[in] expected_tag the tag (IEI) that we expect at \arg data
340 * \param[out] value pointer to pointer of value part of IE
341 * \param[out] value_len pointer to length of \arg value
342 * \returns length of IE value; negative in case of error
343 */
344int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,
345 uint8_t expected_tag, uint8_t **value,
346 size_t *value_len)
Jacob Erlbeck96b81da2014-12-11 12:05:29 +0100347{
348 int rc;
349 uint8_t tag;
350 uint8_t *old_data = *data;
351 size_t old_data_len = *data_len;
352
Harald Welte8f4b2b82016-04-25 15:14:01 +0200353 rc = osmo_shift_tlv(data, data_len, &tag, value, value_len);
Jacob Erlbeck96b81da2014-12-11 12:05:29 +0100354
355 if (rc > 0 && tag != expected_tag) {
356 *data = old_data;
357 *data_len = old_data_len;
358 return 0;
359 }
360
361 return rc;
362}
363
Harald Welte8f4b2b82016-04-25 15:14:01 +0200364/*! Extract TLV and advance data pointer + subtract length
365 * \param data pointer to the pointer to data
366 * \param data_len pointer to size_t containing \arg data lengt
367 * \param[out] tag extract the tag (IEI) at start of \arg data
368 * \param[out] value extracted pointer to value part of TLV
369 * \param[out] value_len extracted length of \arg value
370 * \returns number of bytes subtracted
371 */
372int osmo_shift_tlv(uint8_t **data, size_t *data_len,
Jacob Erlbeck96b81da2014-12-11 12:05:29 +0100373 uint8_t *tag, uint8_t **value, size_t *value_len)
Jacob Erlbeckdce45012014-12-11 11:01:46 +0100374{
375 size_t len;
376 size_t ie_len;
377
378 if (*data_len < 2)
379 goto fail;
380
Jacob Erlbeckdce45012014-12-11 11:01:46 +0100381 len = (*data)[1];
382 if (len > *data_len - 2)
383 goto fail;
384
Jacob Erlbeck96b81da2014-12-11 12:05:29 +0100385 if (tag)
386 *tag = (*data)[0];
Jacob Erlbeckdce45012014-12-11 11:01:46 +0100387 if (value)
388 *value = *data + 2;
389 if (value_len)
390 *value_len = len;
391
392 ie_len = len + 2;
393
394 *data += ie_len;
395 *data_len -= ie_len;
396
397 return ie_len;
398
399fail:
400 *data += *data_len;
401 *data_len = 0;
402 return -1;
403}
404
Harald Welte8f4b2b82016-04-25 15:14:01 +0200405/*! Extract LV and advance data pointer + subtract length
406 * \param data pointer to the pointer to data
407 * \param data_len pointer to size_t containing \arg data lengt
408 * \param[out] value extracted pointer to value part of TLV
409 * \param[out] value_len extracted length of \arg value
410 * \returns number of bytes subtracted
411 */
412int osmo_shift_lv(uint8_t **data, size_t *data_len,
413 uint8_t **value, size_t *value_len)
Jacob Erlbeckdce45012014-12-11 11:01:46 +0100414{
415 size_t len;
416 size_t ie_len;
417
418 if (*data_len < 1)
419 goto fail;
420
421 len = (*data)[0];
422 if (len > *data_len - 1)
423 goto fail;
424
425 if (value)
426 *value = *data + 1;
427 if (value_len)
428 *value_len = len;
429
430 ie_len = len + 1;
431 *data += ie_len;
432 *data_len -= ie_len;
433
434 return ie_len;
435
436fail:
437 *data += *data_len;
438 *data_len = 0;
439 return -1;
440}
441
Jacob Erlbeckd6466c92016-01-04 18:43:34 +0100442int gprs_ra_id_equals(const struct gprs_ra_id *id1,
443 const struct gprs_ra_id *id2)
444{
445 return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
446 id1->lac == id2->lac && id1->rac == id2->rac);
447}