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