blob: 48627550fecfc809d85edc48be48c4dbdbd4ccb4 [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
Neels Hofmeyr396f2e62017-09-04 15:13:25 +020021#include <osmocom/sgsn/gb_proxy.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020022
Neels Hofmeyr396f2e62017-09-04 15:13:25 +020023#include <osmocom/sgsn/gprs_utils.h>
24#include <osmocom/sgsn/gprs_gb_parse.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020025
Neels Hofmeyr396f2e62017-09-04 15:13:25 +020026#include <osmocom/sgsn/debug.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020027
28#include <osmocom/gprs/protocol/gsm_08_18.h>
29#include <osmocom/core/rate_ctr.h>
Harald Welte7e82b742017-08-12 13:43:54 +020030#include <osmocom/gsm/apn.h>
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020031
Neels Hofmeyree6cfdc2017-07-13 02:03:50 +020032extern void *tall_bsc_ctx;
33
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020034/* patch RA identifier in place */
35static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
36 int to_bss, const char *log_text)
37{
38 struct gbproxy_patch_state *state = &peer->patch_state;
39 int old_mcc;
40 int old_mnc;
41 struct gprs_ra_id raid;
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +020042 enum gbproxy_peer_ctr counter =
43 to_bss ?
44 GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
45 GBPROX_PEER_CTR_RAID_PATCHED_BSS;
46
47 if (!state->local_mcc || !state->local_mnc)
48 return;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020049
50 gsm48_parse_ra(&raid, raid_enc);
51
52 old_mcc = raid.mcc;
53 old_mnc = raid.mnc;
54
55 if (!to_bss) {
56 /* BSS -> SGSN */
57 if (state->local_mcc)
58 raid.mcc = peer->cfg->core_mcc;
59
60 if (state->local_mnc)
61 raid.mnc = peer->cfg->core_mnc;
62 } else {
63 /* SGSN -> BSS */
64 if (state->local_mcc)
65 raid.mcc = state->local_mcc;
66
67 if (state->local_mnc)
68 raid.mnc = state->local_mnc;
69 }
70
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +020071 LOGP(DGPRS, LOGL_DEBUG,
72 "Patching %s to %s: "
73 "%d-%d-%d-%d -> %d-%d-%d-%d\n",
74 log_text,
75 to_bss ? "BSS" : "SGSN",
76 old_mcc, old_mnc, raid.lac, raid.rac,
77 raid.mcc, raid.mnc, raid.lac, raid.rac);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020078
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +020079 gsm48_construct_ra(raid_enc, &raid);
80 rate_ctr_inc(&peer->ctrg->ctr[counter]);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +020081}
82
83static void gbproxy_patch_apn_ie(struct msgb *msg,
84 uint8_t *apn_ie, size_t apn_ie_len,
85 struct gbproxy_peer *peer,
86 size_t *new_apn_ie_len, const char *log_text)
87{
88 struct apn_ie_hdr {
89 uint8_t iei;
90 uint8_t apn_len;
91 uint8_t apn[0];
92 } *hdr = (void *)apn_ie;
93
94 size_t apn_len = hdr->apn_len;
95 uint8_t *apn = hdr->apn;
96
97 OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
98 OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
99
100 if (peer->cfg->core_apn_size == 0) {
101 char str1[110];
102 /* Remove the IE */
103 LOGP(DGPRS, LOGL_DEBUG,
104 "Patching %s to SGSN: Removing APN '%s'\n",
105 log_text,
Harald Welte7e82b742017-08-12 13:43:54 +0200106 osmo_apn_to_str(str1, apn, apn_len));
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200107
108 *new_apn_ie_len = 0;
109 gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
110 } else {
111 /* Resize the IE */
112 char str1[110];
113 char str2[110];
114
115 OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
116
117 LOGP(DGPRS, LOGL_DEBUG,
118 "Patching %s to SGSN: "
119 "Replacing APN '%s' -> '%s'\n",
120 log_text,
Harald Welte7e82b742017-08-12 13:43:54 +0200121 osmo_apn_to_str(str1, apn, apn_len),
122 osmo_apn_to_str(str2, peer->cfg->core_apn,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200123 peer->cfg->core_apn_size));
124
125 *new_apn_ie_len = peer->cfg->core_apn_size + 2;
126 gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
127 memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
128 hdr->apn_len = peer->cfg->core_apn_size;
129 }
130
131 rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
132}
133
134static int gbproxy_patch_tlli(uint8_t *tlli_enc,
135 struct gbproxy_peer *peer,
136 uint32_t new_tlli,
137 int to_bss, const char *log_text)
138{
139 uint32_t tlli_be;
140 uint32_t tlli;
141 enum gbproxy_peer_ctr counter =
142 to_bss ?
143 GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
144 GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
145
146 memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
147 tlli = ntohl(tlli_be);
148
149 if (tlli == new_tlli)
150 return 0;
151
152 LOGP(DGPRS, LOGL_DEBUG,
153 "Patching %ss: "
154 "Replacing %08x -> %08x\n",
155 log_text, tlli, new_tlli);
156
157 tlli_be = htonl(new_tlli);
158 memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
159
160 rate_ctr_inc(&peer->ctrg->ctr[counter]);
161
162 return 1;
163}
164
165static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
166 struct gbproxy_peer *peer,
167 uint32_t new_ptmsi,
168 int to_bss, const char *log_text)
169{
170 uint32_t ptmsi_be;
171 uint32_t ptmsi;
172 enum gbproxy_peer_ctr counter =
173 to_bss ?
174 GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
175 GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
Jacob Erlbeck49389172014-10-02 16:14:47 +0200176 memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200177 ptmsi = ntohl(ptmsi_be);
178
179 if (ptmsi == new_ptmsi)
180 return 0;
181
182 LOGP(DGPRS, LOGL_DEBUG,
183 "Patching %ss: "
184 "Replacing %08x -> %08x\n",
185 log_text, ptmsi, new_ptmsi);
186
187 ptmsi_be = htonl(new_ptmsi);
Jacob Erlbeck49389172014-10-02 16:14:47 +0200188 memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200189
190 rate_ctr_inc(&peer->ctrg->ctr[counter]);
191
192 return 1;
193}
194
195int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
196 struct gbproxy_peer *peer,
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200197 struct gbproxy_link_info *link_info, int *len_change,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200198 struct gprs_gb_parse_context *parse_ctx)
199{
200 struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
201 int have_patched = 0;
202 int fcs;
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200203 struct gbproxy_config *cfg = peer->cfg;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200204
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200205 if (parse_ctx->ptmsi_enc && link_info &&
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +0200206 !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200207 uint32_t ptmsi;
208 if (parse_ctx->to_bss)
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200209 ptmsi = link_info->tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200210 else
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200211 ptmsi = link_info->sgsn_tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200212
213 if (ptmsi != GSM_RESERVED_TMSI) {
214 if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
215 ptmsi, parse_ctx->to_bss, "P-TMSI"))
216 have_patched = 1;
217 } else {
218 /* TODO: invalidate old RAI if present (see below) */
219 }
220 }
221
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200222 if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200223 uint32_t ptmsi;
224 if (parse_ctx->to_bss)
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200225 ptmsi = link_info->tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200226 else
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200227 ptmsi = link_info->sgsn_tlli.ptmsi;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200228
229 OSMO_ASSERT(ptmsi);
230 if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
231 ptmsi, parse_ctx->to_bss, "new P-TMSI"))
232 have_patched = 1;
233 }
234
235 if (parse_ctx->raid_enc) {
236 gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
237 parse_ctx->llc_msg_name);
238 have_patched = 1;
239 }
240
Jacob Erlbeck948c07f2014-09-11 15:22:18 +0200241 if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200242 /* TODO: Patch to invalid if P-TMSI unknown. */
243 gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
244 parse_ctx->llc_msg_name);
245 have_patched = 1;
246 }
247
248 if (parse_ctx->apn_ie &&
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200249 cfg->core_apn &&
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200250 !parse_ctx->to_bss &&
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200251 gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
252 cfg->core_apn) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200253 size_t new_len;
254 gbproxy_patch_apn_ie(msg,
255 parse_ctx->apn_ie, parse_ctx->apn_ie_len,
256 peer, &new_len, parse_ctx->llc_msg_name);
257 *len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
258
259 have_patched = 1;
260 }
261
262 if (have_patched) {
263 llc_len += *len_change;
264 ghp->crc_length += *len_change;
265
266 /* Fix FCS */
267 fcs = gprs_llc_fcs(llc, ghp->crc_length);
268 LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
269 ghp->fcs, fcs);
270
271 llc[llc_len - 3] = fcs & 0xff;
272 llc[llc_len - 2] = (fcs >> 8) & 0xff;
273 llc[llc_len - 1] = (fcs >> 16) & 0xff;
274 }
275
276 return have_patched;
277}
278
279/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
280void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
281 struct gbproxy_peer *peer,
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200282 struct gbproxy_link_info *link_info, int *len_change,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200283 struct gprs_gb_parse_context *parse_ctx)
284{
285 const char *err_info = NULL;
286 int err_ctr = -1;
287
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200288 if (parse_ctx->bssgp_raid_enc)
289 gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer,
290 parse_ctx->to_bss, "BSSGP");
291
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200292 if (parse_ctx->need_decryption &&
Jacob Erlbeck1abfdc22014-09-04 11:42:08 +0200293 (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200294 /* Patching LLC messages has been requested
295 * explicitly, but the message (including the
296 * type) is encrypted, so we possibly fail to
297 * patch the LLC part of the message. */
298 err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
299 err_info = "GMM message is encrypted";
300 goto patch_error;
301 }
302
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200303 if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
Jacob Erlbeck37fda772014-09-05 10:22:27 +0200304 /* Happens with unknown (not cached) TLLI coming from
305 * the SGSN */
306 /* TODO: What shall be done with the message in this case? */
307 err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
308 err_info = "TLLI sent by the SGSN is unknown";
309 goto patch_error;
310 }
311
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200312 if (!link_info)
Jacob Erlbeckecbd56c2014-08-26 10:01:57 +0200313 return;
314
Jacob Erlbeckcba4c0c2014-09-15 14:38:37 +0200315 if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200316 uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200317 link_info, parse_ctx->to_bss);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200318
319 if (tlli) {
320 gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
321 parse_ctx->to_bss, "TLLI");
322 parse_ctx->tlli = tlli;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200323 } else {
324 /* Internal error */
325 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
326 err_info = "Replacement TLLI is 0";
327 goto patch_error;
328 }
329 }
330
Jacob Erlbeckc37ef6c2014-09-30 13:49:43 +0200331 if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
332 uint32_t ptmsi;
333 if (parse_ctx->to_bss)
334 ptmsi = link_info->tlli.ptmsi;
335 else
336 ptmsi = link_info->sgsn_tlli.ptmsi;
337
338 if (ptmsi != GSM_RESERVED_TMSI)
339 gbproxy_patch_ptmsi(
340 parse_ctx->bssgp_ptmsi_enc, peer,
341 ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
342 }
343
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200344 if (parse_ctx->llc) {
345 uint8_t *llc = parse_ctx->llc;
346 size_t llc_len = parse_ctx->llc_len;
347 int llc_len_change = 0;
348
Jacob Erlbeck91d2f8a2014-09-19 15:07:27 +0200349 gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200350 &llc_len_change, parse_ctx);
351 /* Note that the APN might have been resized here, but no
352 * pointer int the parse_ctx will refer to an adress after the
353 * APN. So it's possible to patch first and do the TLLI
354 * handling afterwards. */
355
356 if (llc_len_change) {
357 llc_len += llc_len_change;
358
359 /* Fix LLC IE len */
360 /* TODO: This is a kludge, but the a pointer to the
361 * start of the IE is not available here */
362 if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
363 /* most probably a one byte length */
364 if (llc_len > 127) {
365 err_info = "Cannot increase size";
366 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
367 goto patch_error;
368 }
369 llc[-1] = llc_len | 0x80;
370 } else {
371 llc[-2] = (llc_len >> 8) & 0x7f;
372 llc[-1] = llc_len & 0xff;
373 }
374 *len_change += llc_len_change;
375 }
376 /* Note that the tp struct might contain invalid pointers here
377 * if the LLC field has changed its size */
378 parse_ctx->llc_len = llc_len;
379 }
380 return;
381
382patch_error:
383 OSMO_ASSERT(err_ctr >= 0);
384 rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
385 LOGP(DGPRS, LOGL_ERROR,
386 "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
387 msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
388 err_info);
389}
390
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200391void gbproxy_clear_patch_filter(struct gbproxy_match *match)
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200392{
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200393 if (match->enable) {
394 regfree(&match->re_comp);
395 match->enable = 0;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200396 }
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200397 talloc_free(match->re_str);
398 match->re_str = NULL;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200399}
400
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200401int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200402 const char **err_msg)
403{
404 static char err_buf[300];
405 int rc;
406
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200407 gbproxy_clear_patch_filter(match);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200408
409 if (!filter)
410 return 0;
411
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200412 rc = regcomp(&match->re_comp, filter,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200413 REG_EXTENDED | REG_NOSUB | REG_ICASE);
414
415 if (rc == 0) {
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200416 match->enable = 1;
417 match->re_str = talloc_strdup(tall_bsc_ctx, filter);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200418 return 0;
419 }
420
421 if (err_msg) {
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200422 regerror(rc, &match->re_comp,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200423 err_buf, sizeof(err_buf));
424 *err_msg = err_buf;
425 }
426
427 return -1;
428}
429
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200430int gbproxy_check_imsi(struct gbproxy_match *match,
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200431 const uint8_t *imsi, size_t imsi_len)
432{
433 char mi_buf[200];
434 int rc;
435
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200436 if (!match->enable)
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200437 return 1;
438
439 rc = gprs_is_mi_imsi(imsi, imsi_len);
440 if (rc > 0)
441 rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
442 if (rc <= 0) {
443 LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
444 osmo_hexdump(imsi, imsi_len));
445 return -1;
446 }
447
448 LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
449
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200450 rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200451 if (rc == REG_NOMATCH) {
452 LOGP(DGPRS, LOGL_INFO,
453 "IMSI '%s' doesn't match pattern '%s'\n",
Jacob Erlbeck9a83d7a2014-09-25 11:17:31 +0200454 mi_buf, match->re_str);
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200455 return 0;
456 }
457
458 return 1;
459}
460