blob: 9c70d3f75c798a96fc885ba638033dcff594f8a0 [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{
39 struct gbproxy_patch_state *state = &peer->patch_state;
40 struct osmo_plmn_id old_plmn;
41 struct gprs_ra_id raid;
42 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_plmn.mcc || !state->local_plmn.mnc)
48 return;
49
50 gsm48_parse_ra(&raid, (uint8_t *)raid_enc);
51
52 old_plmn = (struct osmo_plmn_id){
53 .mcc = raid.mcc,
54 .mnc = raid.mnc,
55 .mnc_3_digits = raid.mnc_3_digits,
56 };
57
58 if (!to_bss) {
59 /* BSS -> SGSN */
60 if (state->local_plmn.mcc)
61 raid.mcc = peer->cfg->core_plmn.mcc;
62
63 if (state->local_plmn.mnc) {
64 raid.mnc = peer->cfg->core_plmn.mnc;
65 raid.mnc_3_digits = peer->cfg->core_plmn.mnc_3_digits;
66 }
67 } else {
68 /* SGSN -> BSS */
69 if (state->local_plmn.mcc)
70 raid.mcc = state->local_plmn.mcc;
71
72 if (state->local_plmn.mnc) {
73 raid.mnc = state->local_plmn.mnc;
74 raid.mnc_3_digits = state->local_plmn.mnc_3_digits;
75 }
76 }
77
78 LOGP(DGPRS, LOGL_DEBUG,
79 "Patching %s to %s: "
80 "%s-%d-%d -> %s\n",
81 log_text,
82 to_bss ? "BSS" : "SGSN",
83 osmo_plmn_name(&old_plmn), raid.lac, raid.rac,
84 osmo_rai_name(&raid));
85
86 gsm48_encode_ra(raid_enc, &raid);
87 rate_ctr_inc(&peer->ctrg->ctr[counter]);
88}
89
90static void gbproxy_patch_apn_ie(struct msgb *msg,
91 uint8_t *apn_ie, size_t apn_ie_len,
92 struct gbproxy_peer *peer,
93 size_t *new_apn_ie_len, const char *log_text)
94{
95 struct apn_ie_hdr {
96 uint8_t iei;
97 uint8_t apn_len;
98 uint8_t apn[0];
99 } *hdr = (void *)apn_ie;
100
101 size_t apn_len = hdr->apn_len;
102 uint8_t *apn = hdr->apn;
103
104 OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
105 OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
106
107 if (peer->cfg->core_apn_size == 0) {
108 char str1[110];
109 /* Remove the IE */
110 LOGP(DGPRS, LOGL_DEBUG,
111 "Patching %s to SGSN: Removing APN '%s'\n",
112 log_text,
113 osmo_apn_to_str(str1, apn, apn_len));
114
115 *new_apn_ie_len = 0;
116 msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
117 } else {
118 /* Resize the IE */
119 char str1[110];
120 char str2[110];
121
122 OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
123
124 LOGP(DGPRS, LOGL_DEBUG,
125 "Patching %s to SGSN: "
126 "Replacing APN '%s' -> '%s'\n",
127 log_text,
128 osmo_apn_to_str(str1, apn, apn_len),
129 osmo_apn_to_str(str2, peer->cfg->core_apn,
130 peer->cfg->core_apn_size));
131
132 *new_apn_ie_len = peer->cfg->core_apn_size + 2;
133 msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
134 memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
135 hdr->apn_len = peer->cfg->core_apn_size;
136 }
137
138 rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
139}
140
141static int gbproxy_patch_tlli(uint8_t *tlli_enc,
142 struct gbproxy_peer *peer,
143 uint32_t new_tlli,
144 int to_bss, const char *log_text)
145{
146 uint32_t tlli_be;
147 uint32_t tlli;
148 enum gbproxy_peer_ctr counter =
149 to_bss ?
150 GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
151 GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
152
153 memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
154 tlli = ntohl(tlli_be);
155
156 if (tlli == new_tlli)
157 return 0;
158
159 LOGP(DGPRS, LOGL_DEBUG,
160 "Patching %ss: "
161 "Replacing %08x -> %08x\n",
162 log_text, tlli, new_tlli);
163
164 tlli_be = htonl(new_tlli);
165 memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
166
167 rate_ctr_inc(&peer->ctrg->ctr[counter]);
168
169 return 1;
170}
171
172static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
173 struct gbproxy_peer *peer,
174 uint32_t new_ptmsi,
175 int to_bss, const char *log_text)
176{
177 uint32_t ptmsi_be;
178 uint32_t ptmsi;
179 enum gbproxy_peer_ctr counter =
180 to_bss ?
181 GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
182 GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
183 memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
184 ptmsi = ntohl(ptmsi_be);
185
186 if (ptmsi == new_ptmsi)
187 return 0;
188
189 LOGP(DGPRS, LOGL_DEBUG,
190 "Patching %ss: "
191 "Replacing %08x -> %08x\n",
192 log_text, ptmsi, new_ptmsi);
193
194 ptmsi_be = htonl(new_ptmsi);
195 memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
196
197 rate_ctr_inc(&peer->ctrg->ctr[counter]);
198
199 return 1;
200}
201
202int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
203 struct gbproxy_peer *peer,
204 struct gbproxy_link_info *link_info, int *len_change,
205 struct gprs_gb_parse_context *parse_ctx)
206{
207 struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
208 int have_patched = 0;
209 int fcs;
210 struct gbproxy_config *cfg = peer->cfg;
211
212 if (parse_ctx->ptmsi_enc && link_info &&
213 !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
214 uint32_t ptmsi;
215 if (parse_ctx->to_bss)
216 ptmsi = link_info->tlli.ptmsi;
217 else
218 ptmsi = link_info->sgsn_tlli.ptmsi;
219
220 if (ptmsi != GSM_RESERVED_TMSI) {
221 if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
222 ptmsi, parse_ctx->to_bss, "P-TMSI"))
223 have_patched = 1;
224 } else {
225 /* TODO: invalidate old RAI if present (see below) */
226 }
227 }
228
229 if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
230 uint32_t ptmsi;
231 if (parse_ctx->to_bss)
232 ptmsi = link_info->tlli.ptmsi;
233 else
234 ptmsi = link_info->sgsn_tlli.ptmsi;
235
236 OSMO_ASSERT(ptmsi);
237 if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
238 ptmsi, parse_ctx->to_bss, "new P-TMSI"))
239 have_patched = 1;
240 }
241
242 if (parse_ctx->raid_enc) {
243 gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->raid_enc, peer, parse_ctx->to_bss,
244 parse_ctx->llc_msg_name);
245 have_patched = 1;
246 }
247
248 if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
249 /* TODO: Patch to invalid if P-TMSI unknown. */
250 gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
251 parse_ctx->llc_msg_name);
252 have_patched = 1;
253 }
254
255 if (parse_ctx->apn_ie &&
256 cfg->core_apn &&
257 !parse_ctx->to_bss &&
258 gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
259 cfg->core_apn) {
260 size_t new_len;
261 gbproxy_patch_apn_ie(msg,
262 parse_ctx->apn_ie, parse_ctx->apn_ie_len,
263 peer, &new_len, parse_ctx->llc_msg_name);
264 *len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
265
266 have_patched = 1;
267 }
268
269 if (have_patched) {
270 llc_len += *len_change;
271 ghp->crc_length += *len_change;
272
273 /* Fix FCS */
274 fcs = gprs_llc_fcs(llc, ghp->crc_length);
275 LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
276 ghp->fcs, fcs);
277
278 llc[llc_len - 3] = fcs & 0xff;
279 llc[llc_len - 2] = (fcs >> 8) & 0xff;
280 llc[llc_len - 1] = (fcs >> 16) & 0xff;
281 }
282
283 return have_patched;
284}
285
286/* patch BSSGP message to use core_plmn.mcc/mnc on the SGSN side */
287void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
288 struct gbproxy_peer *peer,
289 struct gbproxy_link_info *link_info, int *len_change,
290 struct gprs_gb_parse_context *parse_ctx)
291{
292 const char *err_info = NULL;
293 int err_ctr = -1;
294
295 if (parse_ctx->bssgp_raid_enc)
296 gbproxy_patch_raid((struct gsm48_ra_id *)parse_ctx->bssgp_raid_enc, peer,
297 parse_ctx->to_bss, "BSSGP");
298
299 if (parse_ctx->need_decryption &&
300 (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
301 /* Patching LLC messages has been requested
302 * explicitly, but the message (including the
303 * type) is encrypted, so we possibly fail to
304 * patch the LLC part of the message. */
305 err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
306 err_info = "GMM message is encrypted";
307 goto patch_error;
308 }
309
310 if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
311 /* Happens with unknown (not cached) TLLI coming from
312 * the SGSN */
313 /* TODO: What shall be done with the message in this case? */
314 err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
315 err_info = "TLLI sent by the SGSN is unknown";
316 goto patch_error;
317 }
318
319 if (!link_info)
320 return;
321
322 if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
323 uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
324 link_info, parse_ctx->to_bss);
325
326 if (tlli) {
327 gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
328 parse_ctx->to_bss, "TLLI");
329 parse_ctx->tlli = tlli;
330 } else {
331 /* Internal error */
332 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
333 err_info = "Replacement TLLI is 0";
334 goto patch_error;
335 }
336 }
337
338 if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
339 uint32_t ptmsi;
340 if (parse_ctx->to_bss)
341 ptmsi = link_info->tlli.ptmsi;
342 else
343 ptmsi = link_info->sgsn_tlli.ptmsi;
344
345 if (ptmsi != GSM_RESERVED_TMSI)
346 gbproxy_patch_ptmsi(
347 parse_ctx->bssgp_ptmsi_enc, peer,
348 ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
349 }
350
351 if (parse_ctx->llc) {
352 uint8_t *llc = parse_ctx->llc;
353 size_t llc_len = parse_ctx->llc_len;
354 int llc_len_change = 0;
355
356 gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
357 &llc_len_change, parse_ctx);
358 /* Note that the APN might have been resized here, but no
359 * pointer int the parse_ctx will refer to an adress after the
360 * APN. So it's possible to patch first and do the TLLI
361 * handling afterwards. */
362
363 if (llc_len_change) {
364 llc_len += llc_len_change;
365
366 /* Fix LLC IE len */
367 /* TODO: This is a kludge, but the a pointer to the
368 * start of the IE is not available here */
369 if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
370 /* most probably a one byte length */
371 if (llc_len > 127) {
372 err_info = "Cannot increase size";
373 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
374 goto patch_error;
375 }
376 llc[-1] = llc_len | 0x80;
377 } else {
378 llc[-2] = (llc_len >> 8) & 0x7f;
379 llc[-1] = llc_len & 0xff;
380 }
381 *len_change += llc_len_change;
382 }
383 /* Note that the tp struct might contain invalid pointers here
384 * if the LLC field has changed its size */
385 parse_ctx->llc_len = llc_len;
386 }
387 return;
388
389patch_error:
390 OSMO_ASSERT(err_ctr >= 0);
391 rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
392 LOGP(DGPRS, LOGL_ERROR,
393 "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
394 msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
395 err_info);
396}
397
398void gbproxy_clear_patch_filter(struct gbproxy_match *match)
399{
400 if (match->enable) {
401 regfree(&match->re_comp);
402 match->enable = false;
403 }
404 talloc_free(match->re_str);
405 match->re_str = NULL;
406}
407
408int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
409 const char **err_msg)
410{
411 static char err_buf[300];
412 int rc;
413
414 gbproxy_clear_patch_filter(match);
415
416 if (!filter)
417 return 0;
418
419 rc = regcomp(&match->re_comp, filter,
420 REG_EXTENDED | REG_NOSUB | REG_ICASE);
421
422 if (rc == 0) {
423 match->enable = true;
424 match->re_str = talloc_strdup(tall_sgsn_ctx, filter);
425 return 0;
426 }
427
428 if (err_msg) {
429 regerror(rc, &match->re_comp,
430 err_buf, sizeof(err_buf));
431 *err_msg = err_buf;
432 }
433
434 return -1;
435}
436
437int gbproxy_check_imsi(struct gbproxy_match *match,
438 const uint8_t *imsi, size_t imsi_len)
439{
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200440 int rc;
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200441 struct osmo_mobile_identity mi;
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200442
443 if (!match->enable)
444 return 1;
445
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200446 rc = osmo_mobile_identity_decode(&mi, imsi, imsi_len, false);
447 if (rc || mi.type != GSM_MI_TYPE_IMSI) {
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200448 LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
449 osmo_hexdump(imsi, imsi_len));
450 return -1;
451 }
452
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200453 LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi.imsi, rc);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200454
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200455 rc = regexec(&match->re_comp, mi.imsi, 0, NULL, 0);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200456 if (rc == REG_NOMATCH) {
457 LOGP(DGPRS, LOGL_INFO,
458 "IMSI '%s' doesn't match pattern '%s'\n",
Neels Hofmeyr7facc862020-05-29 16:53:23 +0200459 mi.imsi, match->re_str);
Pau Espin Pedrol1ddefb12019-08-30 19:48:34 +0200460 return 0;
461 }
462
463 return 1;
464}