blob: 59157221fc858bc0a3284a4b8afef1af1a11e7bd [file] [log] [blame]
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +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
Alexander Couzens951e1332020-09-22 13:21:46 +020021#include <osmocom/gprs/gprs_msgb.h>
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020022#include <osmocom/sgsn/gb_proxy.h>
23
24#include <osmocom/sgsn/gprs_utils.h>
25#include <osmocom/sgsn/gprs_gb_parse.h>
26
27#include <osmocom/sgsn/debug.h>
28
29#include <osmocom/gprs/protocol/gsm_08_18.h>
30#include <osmocom/core/rate_ctr.h>
31#include <osmocom/gsm/apn.h>
32
33extern void *tall_sgsn_ctx;
34
35/* patch RA identifier in place */
36static void gbproxy_patch_raid(struct gsm48_ra_id *raid_enc, struct gbproxy_peer *peer,
37 int to_bss, const char *log_text)
38{
Daniel Willmann5e595ca2020-12-02 13:54:21 +010039 OSMO_ASSERT(peer);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020040 struct gbproxy_patch_state *state = &peer->patch_state;
41 struct osmo_plmn_id old_plmn;
42 struct gprs_ra_id raid;
43 enum gbproxy_peer_ctr counter =
44 to_bss ?
45 GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
46 GBPROX_PEER_CTR_RAID_PATCHED_BSS;
47
Daniel Willmanne50550e2020-11-26 18:19:21 +010048 OSMO_ASSERT(peer->nse);
49 struct gbproxy_config *cfg = peer->nse->cfg;
50 OSMO_ASSERT(cfg);
51
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020052 if (!state->local_plmn.mcc || !state->local_plmn.mnc)
53 return;
54
55 gsm48_parse_ra(&raid, (uint8_t *)raid_enc);
56
57 old_plmn = (struct osmo_plmn_id){
58 .mcc = raid.mcc,
59 .mnc = raid.mnc,
60 .mnc_3_digits = raid.mnc_3_digits,
61 };
62
63 if (!to_bss) {
64 /* BSS -> SGSN */
65 if (state->local_plmn.mcc)
Daniel Willmanne50550e2020-11-26 18:19:21 +010066 raid.mcc = cfg->core_plmn.mcc;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020067
68 if (state->local_plmn.mnc) {
Daniel Willmanne50550e2020-11-26 18:19:21 +010069 raid.mnc = cfg->core_plmn.mnc;
70 raid.mnc_3_digits = cfg->core_plmn.mnc_3_digits;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020071 }
72 } else {
73 /* SGSN -> BSS */
74 if (state->local_plmn.mcc)
75 raid.mcc = state->local_plmn.mcc;
76
77 if (state->local_plmn.mnc) {
78 raid.mnc = state->local_plmn.mnc;
79 raid.mnc_3_digits = state->local_plmn.mnc_3_digits;
80 }
81 }
82
Daniel Willmann5e595ca2020-12-02 13:54:21 +010083 LOGPBVC(peer, LOGL_DEBUG,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +020084 "Patching %s to %s: "
85 "%s-%d-%d -> %s\n",
86 log_text,
87 to_bss ? "BSS" : "SGSN",
88 osmo_plmn_name(&old_plmn), raid.lac, raid.rac,
89 osmo_rai_name(&raid));
90
91 gsm48_encode_ra(raid_enc, &raid);
92 rate_ctr_inc(&peer->ctrg->ctr[counter]);
93}
94
95static void gbproxy_patch_apn_ie(struct msgb *msg,
96 uint8_t *apn_ie, size_t apn_ie_len,
97 struct gbproxy_peer *peer,
98 size_t *new_apn_ie_len, const char *log_text)
99{
100 struct apn_ie_hdr {
101 uint8_t iei;
102 uint8_t apn_len;
103 uint8_t apn[0];
104 } *hdr = (void *)apn_ie;
105
106 size_t apn_len = hdr->apn_len;
107 uint8_t *apn = hdr->apn;
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100108 OSMO_ASSERT(peer);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100109 OSMO_ASSERT(peer->nse);
110 struct gbproxy_config *cfg = peer->nse->cfg;
111 OSMO_ASSERT(cfg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200112
113 OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
114 OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
115
Daniel Willmanne50550e2020-11-26 18:19:21 +0100116 if (cfg->core_apn_size == 0) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200117 char str1[110];
118 /* Remove the IE */
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100119 LOGPBVC(peer, LOGL_DEBUG,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200120 "Patching %s to SGSN: Removing APN '%s'\n",
121 log_text,
122 osmo_apn_to_str(str1, apn, apn_len));
123
124 *new_apn_ie_len = 0;
125 msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
126 } else {
127 /* Resize the IE */
128 char str1[110];
129 char str2[110];
130
Daniel Willmanne50550e2020-11-26 18:19:21 +0100131 OSMO_ASSERT(cfg->core_apn_size <= 100);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200132
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100133 LOGPBVC(peer, LOGL_DEBUG,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200134 "Patching %s to SGSN: "
135 "Replacing APN '%s' -> '%s'\n",
136 log_text,
137 osmo_apn_to_str(str1, apn, apn_len),
Daniel Willmanne50550e2020-11-26 18:19:21 +0100138 osmo_apn_to_str(str2, cfg->core_apn,
139 cfg->core_apn_size));
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200140
Daniel Willmanne50550e2020-11-26 18:19:21 +0100141 *new_apn_ie_len = cfg->core_apn_size + 2;
142 msgb_resize_area(msg, apn, apn_len, cfg->core_apn_size);
143 memcpy(apn, cfg->core_apn, cfg->core_apn_size);
144 hdr->apn_len = cfg->core_apn_size;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200145 }
146
147 rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
148}
149
150static int gbproxy_patch_tlli(uint8_t *tlli_enc,
151 struct gbproxy_peer *peer,
152 uint32_t new_tlli,
153 int to_bss, const char *log_text)
154{
155 uint32_t tlli_be;
156 uint32_t tlli;
157 enum gbproxy_peer_ctr counter =
158 to_bss ?
159 GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
160 GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100161 OSMO_ASSERT(peer);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200162
163 memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
164 tlli = ntohl(tlli_be);
165
166 if (tlli == new_tlli)
167 return 0;
168
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100169 LOGPBVC(peer, LOGL_DEBUG,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200170 "Patching %ss: "
171 "Replacing %08x -> %08x\n",
172 log_text, tlli, new_tlli);
173
174 tlli_be = htonl(new_tlli);
175 memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
176
177 rate_ctr_inc(&peer->ctrg->ctr[counter]);
178
179 return 1;
180}
181
182static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
183 struct gbproxy_peer *peer,
184 uint32_t new_ptmsi,
185 int to_bss, const char *log_text)
186{
187 uint32_t ptmsi_be;
188 uint32_t ptmsi;
189 enum gbproxy_peer_ctr counter =
190 to_bss ?
191 GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
192 GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100193 OSMO_ASSERT(peer);
194
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200195 memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
196 ptmsi = ntohl(ptmsi_be);
197
198 if (ptmsi == new_ptmsi)
199 return 0;
200
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100201 LOGPBVC(peer, LOGL_DEBUG,
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200202 "Patching %ss: "
203 "Replacing %08x -> %08x\n",
204 log_text, ptmsi, new_ptmsi);
205
206 ptmsi_be = htonl(new_ptmsi);
207 memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
208
209 rate_ctr_inc(&peer->ctrg->ctr[counter]);
210
211 return 1;
212}
213
214int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
215 struct gbproxy_peer *peer,
216 struct gbproxy_link_info *link_info, int *len_change,
217 struct gprs_gb_parse_context *parse_ctx)
218{
219 struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
220 int have_patched = 0;
221 int fcs;
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100222 OSMO_ASSERT(peer);
Daniel Willmanne50550e2020-11-26 18:19:21 +0100223 OSMO_ASSERT(peer->nse);
224 struct gbproxy_config *cfg = peer->nse->cfg;
225 OSMO_ASSERT(cfg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200226
227 if (parse_ctx->ptmsi_enc && link_info &&
Daniel Willmanne50550e2020-11-26 18:19:21 +0100228 !parse_ctx->old_raid_is_foreign && cfg->patch_ptmsi) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200229 uint32_t ptmsi;
230 if (parse_ctx->to_bss)
231 ptmsi = link_info->tlli.ptmsi;
232 else
233 ptmsi = link_info->sgsn_tlli.ptmsi;
234
235 if (ptmsi != GSM_RESERVED_TMSI) {
236 if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
237 ptmsi, parse_ctx->to_bss, "P-TMSI"))
238 have_patched = 1;
239 } else {
240 /* TODO: invalidate old RAI if present (see below) */
241 }
242 }
243
244 if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
245 uint32_t ptmsi;
246 if (parse_ctx->to_bss)
247 ptmsi = link_info->tlli.ptmsi;
248 else
249 ptmsi = link_info->sgsn_tlli.ptmsi;
250
251 OSMO_ASSERT(ptmsi);
252 if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
253 ptmsi, parse_ctx->to_bss, "new P-TMSI"))
254 have_patched = 1;
255 }
256
257 if (parse_ctx->raid_enc) {
258 gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, peer, parse_ctx->to_bss,
259 parse_ctx->llc_msg_name);
260 have_patched = 1;
261 }
262
263 if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
264 /* TODO: Patch to invalid if P-TMSI unknown. */
265 gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
266 parse_ctx->llc_msg_name);
267 have_patched = 1;
268 }
269
270 if (parse_ctx->apn_ie &&
271 cfg->core_apn &&
272 !parse_ctx->to_bss &&
273 gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
274 cfg->core_apn) {
275 size_t new_len;
276 gbproxy_patch_apn_ie(msg,
277 parse_ctx->apn_ie, parse_ctx->apn_ie_len,
278 peer, &new_len, parse_ctx->llc_msg_name);
279 *len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
280
281 have_patched = 1;
282 }
283
284 if (have_patched) {
285 llc_len += *len_change;
286 ghp->crc_length += *len_change;
287
288 /* Fix FCS */
289 fcs = gprs_llc_fcs(llc, ghp->crc_length);
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100290 LOGPBVC_CAT(peer, DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200291 ghp->fcs, fcs);
292
293 llc[llc_len - 3] = fcs & 0xff;
294 llc[llc_len - 2] = (fcs >> 8) & 0xff;
295 llc[llc_len - 1] = (fcs >> 16) & 0xff;
296 }
297
298 return have_patched;
299}
300
301/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */
302void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
303 struct gbproxy_peer *peer,
304 struct gbproxy_link_info *link_info, int *len_change,
305 struct gprs_gb_parse_context *parse_ctx)
306{
307 const char *err_info = NULL;
308 int err_ctr = -1;
Daniel Willmanne50550e2020-11-26 18:19:21 +0100309 OSMO_ASSERT(peer->nse);
310 struct gbproxy_config *cfg = peer->nse->cfg;
311 OSMO_ASSERT(cfg);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200312
313 if (parse_ctx->bssgp_raid_enc)
314 gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer,
315 parse_ctx->to_bss, "BSSGP");
316
317 if (parse_ctx->need_decryption &&
Daniel Willmanne50550e2020-11-26 18:19:21 +0100318 (cfg->patch_ptmsi || cfg->core_apn)) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200319 /* Patching LLC messages has been requested
320 * explicitly, but the message (including the
321 * type) is encrypted, so we possibly fail to
322 * patch the LLC part of the message. */
323 err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
324 err_info = "GMM message is encrypted";
325 goto patch_error;
326 }
327
328 if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
329 /* Happens with unknown (not cached) TLLI coming from
330 * the SGSN */
331 /* TODO: What shall be done with the message in this case? */
332 err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
333 err_info = "TLLI sent by the SGSN is unknown";
334 goto patch_error;
335 }
336
337 if (!link_info)
338 return;
339
Daniel Willmanne50550e2020-11-26 18:19:21 +0100340 if (parse_ctx->tlli_enc && cfg->patch_ptmsi) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200341 uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
342 link_info, parse_ctx->to_bss);
343
344 if (tlli) {
345 gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
346 parse_ctx->to_bss, "TLLI");
347 parse_ctx->tlli = tlli;
348 } else {
349 /* Internal error */
350 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
351 err_info = "Replacement TLLI is 0";
352 goto patch_error;
353 }
354 }
355
Daniel Willmanne50550e2020-11-26 18:19:21 +0100356 if (parse_ctx->bssgp_ptmsi_enc && cfg->patch_ptmsi) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200357 uint32_t ptmsi;
358 if (parse_ctx->to_bss)
359 ptmsi = link_info->tlli.ptmsi;
360 else
361 ptmsi = link_info->sgsn_tlli.ptmsi;
362
363 if (ptmsi != GSM_RESERVED_TMSI)
364 gbproxy_patch_ptmsi(
365 parse_ctx->bssgp_ptmsi_enc, peer,
366 ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
367 }
368
369 if (parse_ctx->llc) {
370 uint8_t *llc = parse_ctx->llc;
371 size_t llc_len = parse_ctx->llc_len;
372 int llc_len_change = 0;
373
374 gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
375 &llc_len_change, parse_ctx);
376 /* Note that the APN might have been resized here, but no
377 * pointer int the parse_ctx will refer to an adress after the
378 * APN. So it's possible to patch first and do the TLLI
379 * handling afterwards. */
380
381 if (llc_len_change) {
382 llc_len += llc_len_change;
383
384 /* Fix LLC IE len */
385 /* TODO: This is a kludge, but the a pointer to the
386 * start of the IE is not available here */
387 if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
388 /* most probably a one byte length */
389 if (llc_len > 127) {
390 err_info = "Cannot increase size";
391 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
392 goto patch_error;
393 }
394 llc[-1] = llc_len | 0x80;
395 } else {
396 llc[-2] = (llc_len >> 8) & 0x7f;
397 llc[-1] = llc_len & 0xff;
398 }
399 *len_change += llc_len_change;
400 }
401 /* Note that the tp struct might contain invalid pointers here
402 * if the LLC field has changed its size */
403 parse_ctx->llc_len = llc_len;
404 }
405 return;
406
407patch_error:
408 OSMO_ASSERT(err_ctr >= 0);
409 rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
Daniel Willmann5e595ca2020-12-02 13:54:21 +0100410 LOGPBVC(peer, LOGL_ERROR,
411 "NSE(%05u/%s) failed to patch BSSGP message as requested: %s.\n",
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200412 msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
413 err_info);
414}
415
416void gbproxy_clear_patch_filter(struct gbproxy_match *match)
417{
418 if (match->enable) {
419 regfree(&match->re_comp);
420 match->enable = false;
421 }
422 talloc_free(match->re_str);
423 match->re_str = NULL;
424}
425
426int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
427 const char **err_msg)
428{
429 static char err_buf[300];
430 int rc;
431
432 gbproxy_clear_patch_filter(match);
433
434 if (!filter)
435 return 0;
436
437 rc = regcomp(&match->re_comp, filter,
438 REG_EXTENDED | REG_NOSUB | REG_ICASE);
439
440 if (rc == 0) {
441 match->enable = true;
442 match->re_str = talloc_strdup(tall_sgsn_ctx, filter);
443 return 0;
444 }
445
446 if (err_msg) {
447 regerror(rc, &match->re_comp,
448 err_buf, sizeof(err_buf));
449 *err_msg = err_buf;
450 }
451
452 return -1;
453}
454
455int gbproxy_check_imsi(struct gbproxy_match *match,
456 const uint8_t *imsi, size_t imsi_len)
457{
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200458 int rc;
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200459 struct osmo_mobile_identity mi;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200460
461 if (!match->enable)
462 return 1;
463
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200464 rc = osmo_mobile_identity_decode(&mi, imsi, imsi_len, false);
465 if (rc || mi.type != GSM_MI_TYPE_IMSI) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200466 LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
467 osmo_hexdump(imsi, imsi_len));
468 return -1;
469 }
470
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200471 LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi.imsi, rc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200472
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200473 rc = regexec(&match->re_comp, mi.imsi, 0, NULL, 0);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200474 if (rc == REG_NOMATCH) {
475 LOGP(DGPRS, LOGL_INFO,
476 "IMSI '%s' doesn't match pattern '%s'\n",
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200477 mi.imsi, match->re_str);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200478 return 0;
479 }
480
481 return 1;
482}