blob: 59d0b2f2b88ec59ad49b77b6502f686b558c6281 [file] [log] [blame]
Jacob Erlbeck9114bee2014-08-19 12:21:01 +02001/* Gb-proxy message patching */
2
3/* (C) 2014 by On-Waves
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <openbsc/gb_proxy.h>
22
23#include <openbsc/gprs_utils.h>
24#include <openbsc/gprs_gb_parse.h>
25
26#include <openbsc/gsm_data_shared.h>
27#include <openbsc/gsm_04_08_gprs.h>
28#include <openbsc/debug.h>
29
30#include <osmocom/gprs/protocol/gsm_08_18.h>
31#include <osmocom/core/rate_ctr.h>
32
33/* check whether patching is enabled at this level */
34static int patching_is_enabled(struct gbproxy_peer *peer,
35 enum gbproxy_patch_mode need_at_least)
36{
37 enum gbproxy_patch_mode patch_mode = peer->cfg->patch_mode;
38 if (patch_mode == GBPROX_PATCH_DEFAULT)
39 patch_mode = GBPROX_PATCH_LLC;
40
41 return need_at_least <= patch_mode;
42}
43
44/* check whether patching is enabled at this level */
45static int patching_is_required(struct gbproxy_peer *peer,
46 enum gbproxy_patch_mode need_at_least)
47{
48 return need_at_least <= peer->cfg->patch_mode;
49}
50
51static int allow_message_patching(struct gbproxy_peer *peer, int msg_type)
52{
53 if (msg_type >= GSM48_MT_GSM_ACT_PDP_REQ) {
54 return patching_is_enabled(peer, GBPROX_PATCH_LLC_GSM);
55 } else if (msg_type > GSM48_MT_GMM_ATTACH_REJ) {
56 return patching_is_enabled(peer, GBPROX_PATCH_LLC);
57 } else if (msg_type > GSM48_MT_GMM_ATTACH_REQ) {
58 return patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH);
59 } else {
60 return patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ);
61 }
62}
63
64/* patch RA identifier in place */
65static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
66 int to_bss, const char *log_text)
67{
68 struct gbproxy_patch_state *state = &peer->patch_state;
69 int old_mcc;
70 int old_mnc;
71 struct gprs_ra_id raid;
72
73 gsm48_parse_ra(&raid, raid_enc);
74
75 old_mcc = raid.mcc;
76 old_mnc = raid.mnc;
77
78 if (!to_bss) {
79 /* BSS -> SGSN */
80 if (state->local_mcc)
81 raid.mcc = peer->cfg->core_mcc;
82
83 if (state->local_mnc)
84 raid.mnc = peer->cfg->core_mnc;
85 } else {
86 /* SGSN -> BSS */
87 if (state->local_mcc)
88 raid.mcc = state->local_mcc;
89
90 if (state->local_mnc)
91 raid.mnc = state->local_mnc;
92 }
93
94 if (state->local_mcc || state->local_mnc) {
95 enum gbproxy_peer_ctr counter =
96 to_bss ?
97 GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
98 GBPROX_PEER_CTR_RAID_PATCHED_BSS;
99
100 LOGP(DGPRS, LOGL_DEBUG,
101 "Patching %s to %s: "
102 "%d-%d-%d-%d -> %d-%d-%d-%d\n",
103 log_text,
104 to_bss ? "BSS" : "SGSN",
105 old_mcc, old_mnc, raid.lac, raid.rac,
106 raid.mcc, raid.mnc, raid.lac, raid.rac);
107
108 gsm48_construct_ra(raid_enc, &raid);
109 rate_ctr_inc(&peer->ctrg->ctr[counter]);
110 }
111}
112
113static void gbproxy_patch_apn_ie(struct msgb *msg,
114 uint8_t *apn_ie, size_t apn_ie_len,
115 struct gbproxy_peer *peer,
116 size_t *new_apn_ie_len, const char *log_text)
117{
118 struct apn_ie_hdr {
119 uint8_t iei;
120 uint8_t apn_len;
121 uint8_t apn[0];
122 } *hdr = (void *)apn_ie;
123
124 size_t apn_len = hdr->apn_len;
125 uint8_t *apn = hdr->apn;
126
127 OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
128 OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
129
130 if (peer->cfg->core_apn_size == 0) {
131 char str1[110];
132 /* Remove the IE */
133 LOGP(DGPRS, LOGL_DEBUG,
134 "Patching %s to SGSN: Removing APN '%s'\n",
135 log_text,
136 gprs_apn_to_str(str1, apn, apn_len));
137
138 *new_apn_ie_len = 0;
139 gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
140 } else {
141 /* Resize the IE */
142 char str1[110];
143 char str2[110];
144
145 OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
146
147 LOGP(DGPRS, LOGL_DEBUG,
148 "Patching %s to SGSN: "
149 "Replacing APN '%s' -> '%s'\n",
150 log_text,
151 gprs_apn_to_str(str1, apn, apn_len),
152 gprs_apn_to_str(str2, peer->cfg->core_apn,
153 peer->cfg->core_apn_size));
154
155 *new_apn_ie_len = peer->cfg->core_apn_size + 2;
156 gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
157 memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
158 hdr->apn_len = peer->cfg->core_apn_size;
159 }
160
161 rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
162}
163
164static int gbproxy_patch_tlli(uint8_t *tlli_enc,
165 struct gbproxy_peer *peer,
166 uint32_t new_tlli,
167 int to_bss, const char *log_text)
168{
169 uint32_t tlli_be;
170 uint32_t tlli;
171 enum gbproxy_peer_ctr counter =
172 to_bss ?
173 GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
174 GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
175
176 memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
177 tlli = ntohl(tlli_be);
178
179 if (tlli == new_tlli)
180 return 0;
181
182 LOGP(DGPRS, LOGL_DEBUG,
183 "Patching %ss: "
184 "Replacing %08x -> %08x\n",
185 log_text, tlli, new_tlli);
186
187 tlli_be = htonl(new_tlli);
188 memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
189
190 rate_ctr_inc(&peer->ctrg->ctr[counter]);
191
192 return 1;
193}
194
195static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
196 struct gbproxy_peer *peer,
197 uint32_t new_ptmsi,
198 int to_bss, const char *log_text)
199{
200 uint32_t ptmsi_be;
201 uint32_t ptmsi;
202 enum gbproxy_peer_ctr counter =
203 to_bss ?
204 GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
205 GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
206 memcpy(&ptmsi_be, ptmsi_enc + 1, sizeof(ptmsi_be));
207 ptmsi = ntohl(ptmsi_be);
208
209 if (ptmsi == new_ptmsi)
210 return 0;
211
212 LOGP(DGPRS, LOGL_DEBUG,
213 "Patching %ss: "
214 "Replacing %08x -> %08x\n",
215 log_text, ptmsi, new_ptmsi);
216
217 ptmsi_be = htonl(new_ptmsi);
218 memcpy(ptmsi_enc + 1, &ptmsi_be, sizeof(ptmsi_be));
219
220 rate_ctr_inc(&peer->ctrg->ctr[counter]);
221
222 return 1;
223}
224
225int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
226 struct gbproxy_peer *peer,
227 struct gbproxy_tlli_info *tlli_info, int *len_change,
228 struct gprs_gb_parse_context *parse_ctx)
229{
230 struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
231 int have_patched = 0;
232 int fcs;
233
234 if (parse_ctx->g48_hdr && !allow_message_patching(peer, parse_ctx->g48_hdr->msg_type))
235 return have_patched;
236
237 if (parse_ctx->ptmsi_enc && tlli_info) {
238 uint32_t ptmsi;
239 if (parse_ctx->to_bss)
240 ptmsi = tlli_info->tlli.ptmsi;
241 else
242 ptmsi = tlli_info->sgsn_tlli.ptmsi;
243
244 if (ptmsi != GSM_RESERVED_TMSI) {
245 if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
246 ptmsi, parse_ctx->to_bss, "P-TMSI"))
247 have_patched = 1;
248 } else {
249 /* TODO: invalidate old RAI if present (see below) */
250 }
251 }
252
253 if (parse_ctx->new_ptmsi_enc && tlli_info) {
254 uint32_t ptmsi;
255 if (parse_ctx->to_bss)
256 ptmsi = tlli_info->tlli.ptmsi;
257 else
258 ptmsi = tlli_info->sgsn_tlli.ptmsi;
259
260 OSMO_ASSERT(ptmsi);
261 if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
262 ptmsi, parse_ctx->to_bss, "new P-TMSI"))
263 have_patched = 1;
264 }
265
266 if (parse_ctx->raid_enc) {
267 gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
268 parse_ctx->llc_msg_name);
269 have_patched = 1;
270 }
271
272 if (parse_ctx->old_raid_enc && parse_ctx->old_raid_matches) {
273 /* TODO: Patch to invalid if P-TMSI unknown. */
274 gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
275 parse_ctx->llc_msg_name);
276 have_patched = 1;
277 }
278
279 if (parse_ctx->apn_ie &&
280 peer->cfg->core_apn &&
281 !parse_ctx->to_bss &&
282 gbproxy_check_tlli(peer, parse_ctx->tlli)) {
283 size_t new_len;
284 gbproxy_patch_apn_ie(msg,
285 parse_ctx->apn_ie, parse_ctx->apn_ie_len,
286 peer, &new_len, parse_ctx->llc_msg_name);
287 *len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
288
289 have_patched = 1;
290 }
291
292 if (have_patched) {
293 llc_len += *len_change;
294 ghp->crc_length += *len_change;
295
296 /* Fix FCS */
297 fcs = gprs_llc_fcs(llc, ghp->crc_length);
298 LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
299 ghp->fcs, fcs);
300
301 llc[llc_len - 3] = fcs & 0xff;
302 llc[llc_len - 2] = (fcs >> 8) & 0xff;
303 llc[llc_len - 1] = (fcs >> 16) & 0xff;
304 }
305
306 return have_patched;
307}
308
309/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
310void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
311 struct gbproxy_peer *peer,
312 struct gbproxy_tlli_info *tlli_info, int *len_change,
313 struct gprs_gb_parse_context *parse_ctx)
314{
315 const char *err_info = NULL;
316 int err_ctr = -1;
317
318 if (!patching_is_enabled(peer, GBPROX_PATCH_BSSGP))
319 return;
320
321 if (parse_ctx->bssgp_raid_enc)
322 gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer,
323 parse_ctx->to_bss, "BSSGP");
324
325 if (!patching_is_enabled(peer, GBPROX_PATCH_LLC_ATTACH_REQ))
326 return;
327
328 if (parse_ctx->need_decryption &&
329 patching_is_required(peer, GBPROX_PATCH_LLC_ATTACH)) {
330 /* Patching LLC messages has been requested
331 * explicitly, but the message (including the
332 * type) is encrypted, so we possibly fail to
333 * patch the LLC part of the message. */
334 err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
335 err_info = "GMM message is encrypted";
336 goto patch_error;
337 }
338
339 if (parse_ctx->tlli_enc && tlli_info) {
340 uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
341 tlli_info, parse_ctx->to_bss);
342
343 if (tlli) {
344 gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
345 parse_ctx->to_bss, "TLLI");
346 parse_ctx->tlli = tlli;
347 } else if (parse_ctx->to_bss) {
348 /* Happens with unknown (not cached) TLLI coming from
349 * the SGSN */
350 /* TODO: What shall be done with the message in this case? */
351 err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
352 err_info = "TLLI sent by the SGSN is unknown";
353 goto patch_error;
354 } else {
355 /* Internal error */
356 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
357 err_info = "Replacement TLLI is 0";
358 goto patch_error;
359 }
360 }
361
362 if (parse_ctx->llc) {
363 uint8_t *llc = parse_ctx->llc;
364 size_t llc_len = parse_ctx->llc_len;
365 int llc_len_change = 0;
366
367 gbproxy_patch_llc(msg, llc, llc_len, peer, tlli_info,
368 &llc_len_change, parse_ctx);
369 /* Note that the APN might have been resized here, but no
370 * pointer int the parse_ctx will refer to an adress after the
371 * APN. So it's possible to patch first and do the TLLI
372 * handling afterwards. */
373
374 if (llc_len_change) {
375 llc_len += llc_len_change;
376
377 /* Fix LLC IE len */
378 /* TODO: This is a kludge, but the a pointer to the
379 * start of the IE is not available here */
380 if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
381 /* most probably a one byte length */
382 if (llc_len > 127) {
383 err_info = "Cannot increase size";
384 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
385 goto patch_error;
386 }
387 llc[-1] = llc_len | 0x80;
388 } else {
389 llc[-2] = (llc_len >> 8) & 0x7f;
390 llc[-1] = llc_len & 0xff;
391 }
392 *len_change += llc_len_change;
393 }
394 /* Note that the tp struct might contain invalid pointers here
395 * if the LLC field has changed its size */
396 parse_ctx->llc_len = llc_len;
397 }
398 return;
399
400patch_error:
401 OSMO_ASSERT(err_ctr >= 0);
402 rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
403 LOGP(DGPRS, LOGL_ERROR,
404 "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
405 msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
406 err_info);
407}
408
409void gbproxy_clear_patch_filter(struct gbproxy_config *cfg)
410{
411 if (cfg->check_imsi) {
412 regfree(&cfg->imsi_re_comp);
413 cfg->check_imsi = 0;
414 }
415}
416
417int gbproxy_set_patch_filter(struct gbproxy_config *cfg, const char *filter,
418 const char **err_msg)
419{
420 static char err_buf[300];
421 int rc;
422
423 gbproxy_clear_patch_filter(cfg);
424
425 if (!filter)
426 return 0;
427
428 rc = regcomp(&cfg->imsi_re_comp, filter,
429 REG_EXTENDED | REG_NOSUB | REG_ICASE);
430
431 if (rc == 0) {
432 cfg->check_imsi = 1;
433 return 0;
434 }
435
436 if (err_msg) {
437 regerror(rc, &cfg->imsi_re_comp,
438 err_buf, sizeof(err_buf));
439 *err_msg = err_buf;
440 }
441
442 return -1;
443}
444
445int gbproxy_check_imsi(struct gbproxy_peer *peer,
446 const uint8_t *imsi, size_t imsi_len)
447{
448 char mi_buf[200];
449 int rc;
450
451 if (!peer->cfg->check_imsi)
452 return 1;
453
454 rc = gprs_is_mi_imsi(imsi, imsi_len);
455 if (rc > 0)
456 rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
457 if (rc <= 0) {
458 LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
459 osmo_hexdump(imsi, imsi_len));
460 return -1;
461 }
462
463 LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
464
465 rc = regexec(&peer->cfg->imsi_re_comp, mi_buf, 0, NULL, 0);
466 if (rc == REG_NOMATCH) {
467 LOGP(DGPRS, LOGL_INFO,
468 "IMSI '%s' doesn't match pattern '%s'\n",
469 mi_buf, peer->cfg->match_re);
470 return 0;
471 }
472
473 return 1;
474}
475