blob: c1d2497db6b430bc63a718b168c6a406ff70d3e4 [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
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020026#include <openbsc/gsm_04_08_gprs.h>
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +020027#include <openbsc/gsm_data.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020028#include <openbsc/debug.h>
29
30#include <osmocom/gprs/protocol/gsm_08_18.h>
31#include <osmocom/core/rate_ctr.h>
32
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020033/* patch RA identifier in place */
34static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
35 int to_bss, const char *log_text)
36{
37 struct gbproxy_patch_state *state = &peer->patch_state;
38 int old_mcc;
39 int old_mnc;
40 struct gprs_ra_id raid;
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +020041 enum gbproxy_peer_ctr counter =
42 to_bss ?
43 GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
44 GBPROX_PEER_CTR_RAID_PATCHED_BSS;
45
46 if (!state->local_mcc || !state->local_mnc)
47 return;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020048
49 gsm48_parse_ra(&raid, raid_enc);
50
51 old_mcc = raid.mcc;
52 old_mnc = raid.mnc;
53
54 if (!to_bss) {
55 /* BSS -> SGSN */
56 if (state->local_mcc)
57 raid.mcc = peer->cfg->core_mcc;
58
59 if (state->local_mnc)
60 raid.mnc = peer->cfg->core_mnc;
61 } else {
62 /* SGSN -> BSS */
63 if (state->local_mcc)
64 raid.mcc = state->local_mcc;
65
66 if (state->local_mnc)
67 raid.mnc = state->local_mnc;
68 }
69
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +020070 LOGP(DGPRS, LOGL_DEBUG,
71 "Patching %s to %s: "
72 "%d-%d-%d-%d -> %d-%d-%d-%d\n",
73 log_text,
74 to_bss ? "BSS" : "SGSN",
75 old_mcc, old_mnc, raid.lac, raid.rac,
76 raid.mcc, raid.mnc, raid.lac, raid.rac);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020077
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +020078 gsm48_construct_ra(raid_enc, &raid);
79 rate_ctr_inc(&peer->ctrg->ctr[counter]);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020080}
81
82static void gbproxy_patch_apn_ie(struct msgb *msg,
83 uint8_t *apn_ie, size_t apn_ie_len,
84 struct gbproxy_peer *peer,
85 size_t *new_apn_ie_len, const char *log_text)
86{
87 struct apn_ie_hdr {
88 uint8_t iei;
89 uint8_t apn_len;
90 uint8_t apn[0];
91 } *hdr = (void *)apn_ie;
92
93 size_t apn_len = hdr->apn_len;
94 uint8_t *apn = hdr->apn;
95
96 OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
97 OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
98
99 if (peer->cfg->core_apn_size == 0) {
100 char str1[110];
101 /* Remove the IE */
102 LOGP(DGPRS, LOGL_DEBUG,
103 "Patching %s to SGSN: Removing APN '%s'\n",
104 log_text,
105 gprs_apn_to_str(str1, apn, apn_len));
106
107 *new_apn_ie_len = 0;
108 gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
109 } else {
110 /* Resize the IE */
111 char str1[110];
112 char str2[110];
113
114 OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
115
116 LOGP(DGPRS, LOGL_DEBUG,
117 "Patching %s to SGSN: "
118 "Replacing APN '%s' -> '%s'\n",
119 log_text,
120 gprs_apn_to_str(str1, apn, apn_len),
121 gprs_apn_to_str(str2, peer->cfg->core_apn,
122 peer->cfg->core_apn_size));
123
124 *new_apn_ie_len = peer->cfg->core_apn_size + 2;
125 gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
126 memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
127 hdr->apn_len = peer->cfg->core_apn_size;
128 }
129
130 rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
131}
132
133static int gbproxy_patch_tlli(uint8_t *tlli_enc,
134 struct gbproxy_peer *peer,
135 uint32_t new_tlli,
136 int to_bss, const char *log_text)
137{
138 uint32_t tlli_be;
139 uint32_t tlli;
140 enum gbproxy_peer_ctr counter =
141 to_bss ?
142 GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
143 GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
144
145 memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
146 tlli = ntohl(tlli_be);
147
148 if (tlli == new_tlli)
149 return 0;
150
151 LOGP(DGPRS, LOGL_DEBUG,
152 "Patching %ss: "
153 "Replacing %08x -> %08x\n",
154 log_text, tlli, new_tlli);
155
156 tlli_be = htonl(new_tlli);
157 memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
158
159 rate_ctr_inc(&peer->ctrg->ctr[counter]);
160
161 return 1;
162}
163
164static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
165 struct gbproxy_peer *peer,
166 uint32_t new_ptmsi,
167 int to_bss, const char *log_text)
168{
169 uint32_t ptmsi_be;
170 uint32_t ptmsi;
171 enum gbproxy_peer_ctr counter =
172 to_bss ?
173 GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
174 GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
Jacob Erlbeck49389172014-10-02 16:14:47 +0200175 memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200176 ptmsi = ntohl(ptmsi_be);
177
178 if (ptmsi == new_ptmsi)
179 return 0;
180
181 LOGP(DGPRS, LOGL_DEBUG,
182 "Patching %ss: "
183 "Replacing %08x -> %08x\n",
184 log_text, ptmsi, new_ptmsi);
185
186 ptmsi_be = htonl(new_ptmsi);
Jacob Erlbeck49389172014-10-02 16:14:47 +0200187 memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200188
189 rate_ctr_inc(&peer->ctrg->ctr[counter]);
190
191 return 1;
192}
193
194int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
195 struct gbproxy_peer *peer,
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200196 struct gbproxy_link_info *link_info, int *len_change,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200197 struct gprs_gb_parse_context *parse_ctx)
198{
199 struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
200 int have_patched = 0;
201 int fcs;
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200202 struct gbproxy_config *cfg = peer->cfg;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200203
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200204 if (parse_ctx->ptmsi_enc && link_info &&
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +0200205 !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200206 uint32_t ptmsi;
207 if (parse_ctx->to_bss)
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200208 ptmsi = link_info->tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200209 else
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200210 ptmsi = link_info->sgsn_tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200211
212 if (ptmsi != GSM_RESERVED_TMSI) {
213 if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
214 ptmsi, parse_ctx->to_bss, "P-TMSI"))
215 have_patched = 1;
216 } else {
217 /* TODO: invalidate old RAI if present (see below) */
218 }
219 }
220
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200221 if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200222 uint32_t ptmsi;
223 if (parse_ctx->to_bss)
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200224 ptmsi = link_info->tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200225 else
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200226 ptmsi = link_info->sgsn_tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200227
228 OSMO_ASSERT(ptmsi);
229 if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
230 ptmsi, parse_ctx->to_bss, "new P-TMSI"))
231 have_patched = 1;
232 }
233
234 if (parse_ctx->raid_enc) {
235 gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
236 parse_ctx->llc_msg_name);
237 have_patched = 1;
238 }
239
Jacob Erlbeck948c07f2014-09-11 15:22:18 +0200240 if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200241 /* TODO: Patch to invalid if P-TMSI unknown. */
242 gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
243 parse_ctx->llc_msg_name);
244 have_patched = 1;
245 }
246
247 if (parse_ctx->apn_ie &&
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200248 cfg->core_apn &&
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200249 !parse_ctx->to_bss &&
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200250 gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
251 cfg->core_apn) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200252 size_t new_len;
253 gbproxy_patch_apn_ie(msg,
254 parse_ctx->apn_ie, parse_ctx->apn_ie_len,
255 peer, &new_len, parse_ctx->llc_msg_name);
256 *len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
257
258 have_patched = 1;
259 }
260
261 if (have_patched) {
262 llc_len += *len_change;
263 ghp->crc_length += *len_change;
264
265 /* Fix FCS */
266 fcs = gprs_llc_fcs(llc, ghp->crc_length);
267 LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
268 ghp->fcs, fcs);
269
270 llc[llc_len - 3] = fcs & 0xff;
271 llc[llc_len - 2] = (fcs >> 8) & 0xff;
272 llc[llc_len - 1] = (fcs >> 16) & 0xff;
273 }
274
275 return have_patched;
276}
277
278/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
279void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
280 struct gbproxy_peer *peer,
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200281 struct gbproxy_link_info *link_info, int *len_change,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200282 struct gprs_gb_parse_context *parse_ctx)
283{
284 const char *err_info = NULL;
285 int err_ctr = -1;
286
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200287 if (parse_ctx->bssgp_raid_enc)
288 gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer,
289 parse_ctx->to_bss, "BSSGP");
290
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200291 if (parse_ctx->need_decryption &&
Jacob Erlbeck1abfdc22014-09-04 11:42:08 +0200292 (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200293 /* Patching LLC messages has been requested
294 * explicitly, but the message (including the
295 * type) is encrypted, so we possibly fail to
296 * patch the LLC part of the message. */
297 err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
298 err_info = "GMM message is encrypted";
299 goto patch_error;
300 }
301
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200302 if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
Jacob Erlbeck37fda772014-09-05 10:22:27 +0200303 /* Happens with unknown (not cached) TLLI coming from
304 * the SGSN */
305 /* TODO: What shall be done with the message in this case? */
306 err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
307 err_info = "TLLI sent by the SGSN is unknown";
308 goto patch_error;
309 }
310
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200311 if (!link_info)
Jacob Erlbeckecbd56c2014-08-26 10:01:57 +0200312 return;
313
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +0200314 if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200315 uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200316 link_info, parse_ctx->to_bss);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200317
318 if (tlli) {
319 gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
320 parse_ctx->to_bss, "TLLI");
321 parse_ctx->tlli = tlli;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200322 } else {
323 /* Internal error */
324 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
325 err_info = "Replacement TLLI is 0";
326 goto patch_error;
327 }
328 }
329
Jacob Erlbeckc37ef6c2014-09-30 13:49:43 +0200330 if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
331 uint32_t ptmsi;
332 if (parse_ctx->to_bss)
333 ptmsi = link_info->tlli.ptmsi;
334 else
335 ptmsi = link_info->sgsn_tlli.ptmsi;
336
337 if (ptmsi != GSM_RESERVED_TMSI)
338 gbproxy_patch_ptmsi(
339 parse_ctx->bssgp_ptmsi_enc, peer,
340 ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
341 }
342
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200343 if (parse_ctx->llc) {
344 uint8_t *llc = parse_ctx->llc;
345 size_t llc_len = parse_ctx->llc_len;
346 int llc_len_change = 0;
347
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200348 gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200349 &llc_len_change, parse_ctx);
350 /* Note that the APN might have been resized here, but no
351 * pointer int the parse_ctx will refer to an adress after the
352 * APN. So it's possible to patch first and do the TLLI
353 * handling afterwards. */
354
355 if (llc_len_change) {
356 llc_len += llc_len_change;
357
358 /* Fix LLC IE len */
359 /* TODO: This is a kludge, but the a pointer to the
360 * start of the IE is not available here */
361 if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
362 /* most probably a one byte length */
363 if (llc_len > 127) {
364 err_info = "Cannot increase size";
365 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
366 goto patch_error;
367 }
368 llc[-1] = llc_len | 0x80;
369 } else {
370 llc[-2] = (llc_len >> 8) & 0x7f;
371 llc[-1] = llc_len & 0xff;
372 }
373 *len_change += llc_len_change;
374 }
375 /* Note that the tp struct might contain invalid pointers here
376 * if the LLC field has changed its size */
377 parse_ctx->llc_len = llc_len;
378 }
379 return;
380
381patch_error:
382 OSMO_ASSERT(err_ctr >= 0);
383 rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
384 LOGP(DGPRS, LOGL_ERROR,
385 "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
386 msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
387 err_info);
388}
389
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200390void gbproxy_clear_patch_filter(struct gbproxy_match *match)
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200391{
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200392 if (match->enable) {
393 regfree(&match->re_comp);
394 match->enable = 0;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200395 }
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200396 talloc_free(match->re_str);
397 match->re_str = NULL;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200398}
399
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200400int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200401 const char **err_msg)
402{
403 static char err_buf[300];
404 int rc;
405
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200406 gbproxy_clear_patch_filter(match);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200407
408 if (!filter)
409 return 0;
410
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200411 rc = regcomp(&match->re_comp, filter,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200412 REG_EXTENDED | REG_NOSUB | REG_ICASE);
413
414 if (rc == 0) {
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200415 match->enable = 1;
416 match->re_str = talloc_strdup(tall_bsc_ctx, filter);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200417 return 0;
418 }
419
420 if (err_msg) {
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200421 regerror(rc, &match->re_comp,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200422 err_buf, sizeof(err_buf));
423 *err_msg = err_buf;
424 }
425
426 return -1;
427}
428
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200429int gbproxy_check_imsi(struct gbproxy_match *match,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200430 const uint8_t *imsi, size_t imsi_len)
431{
432 char mi_buf[200];
433 int rc;
434
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200435 if (!match->enable)
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200436 return 1;
437
438 rc = gprs_is_mi_imsi(imsi, imsi_len);
439 if (rc > 0)
440 rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
441 if (rc <= 0) {
442 LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
443 osmo_hexdump(imsi, imsi_len));
444 return -1;
445 }
446
447 LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
448
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200449 rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200450 if (rc == REG_NOMATCH) {
451 LOGP(DGPRS, LOGL_INFO,
452 "IMSI '%s' doesn't match pattern '%s'\n",
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200453 mi_buf, match->re_str);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200454 return 0;
455 }
456
457 return 1;
458}
459