blob: caedf89659ac9c68dbdd3fdcabd32aa207ac2698 [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>
27#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;
40
41 gsm48_parse_ra(&raid, raid_enc);
42
43 old_mcc = raid.mcc;
44 old_mnc = raid.mnc;
45
46 if (!to_bss) {
47 /* BSS -> SGSN */
48 if (state->local_mcc)
49 raid.mcc = peer->cfg->core_mcc;
50
51 if (state->local_mnc)
52 raid.mnc = peer->cfg->core_mnc;
53 } else {
54 /* SGSN -> BSS */
55 if (state->local_mcc)
56 raid.mcc = state->local_mcc;
57
58 if (state->local_mnc)
59 raid.mnc = state->local_mnc;
60 }
61
62 if (state->local_mcc || state->local_mnc) {
63 enum gbproxy_peer_ctr counter =
64 to_bss ?
65 GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
66 GBPROX_PEER_CTR_RAID_PATCHED_BSS;
67
68 LOGP(DGPRS, LOGL_DEBUG,
69 "Patching %s to %s: "
70 "%d-%d-%d-%d -> %d-%d-%d-%d\n",
71 log_text,
72 to_bss ? "BSS" : "SGSN",
73 old_mcc, old_mnc, raid.lac, raid.rac,
74 raid.mcc, raid.mnc, raid.lac, raid.rac);
75
76 gsm48_construct_ra(raid_enc, &raid);
77 rate_ctr_inc(&peer->ctrg->ctr[counter]);
78 }
79}
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;
174 memcpy(&ptmsi_be, ptmsi_enc + 1, sizeof(ptmsi_be));
175 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);
186 memcpy(ptmsi_enc + 1, &ptmsi_be, sizeof(ptmsi_be));
187
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,
195 struct gbproxy_tlli_info *tlli_info, int *len_change,
196 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;
201
Jacob Erlbeck948c07f2014-09-11 15:22:18 +0200202 if (parse_ctx->ptmsi_enc && tlli_info &&
203 !parse_ctx->old_raid_is_foreign) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200204 uint32_t ptmsi;
205 if (parse_ctx->to_bss)
206 ptmsi = tlli_info->tlli.ptmsi;
207 else
208 ptmsi = tlli_info->sgsn_tlli.ptmsi;
209
210 if (ptmsi != GSM_RESERVED_TMSI) {
211 if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
212 ptmsi, parse_ctx->to_bss, "P-TMSI"))
213 have_patched = 1;
214 } else {
215 /* TODO: invalidate old RAI if present (see below) */
216 }
217 }
218
219 if (parse_ctx->new_ptmsi_enc && tlli_info) {
220 uint32_t ptmsi;
221 if (parse_ctx->to_bss)
222 ptmsi = tlli_info->tlli.ptmsi;
223 else
224 ptmsi = tlli_info->sgsn_tlli.ptmsi;
225
226 OSMO_ASSERT(ptmsi);
227 if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
228 ptmsi, parse_ctx->to_bss, "new P-TMSI"))
229 have_patched = 1;
230 }
231
232 if (parse_ctx->raid_enc) {
233 gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
234 parse_ctx->llc_msg_name);
235 have_patched = 1;
236 }
237
Jacob Erlbeck948c07f2014-09-11 15:22:18 +0200238 if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200239 /* TODO: Patch to invalid if P-TMSI unknown. */
240 gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
241 parse_ctx->llc_msg_name);
242 have_patched = 1;
243 }
244
245 if (parse_ctx->apn_ie &&
246 peer->cfg->core_apn &&
247 !parse_ctx->to_bss &&
Jacob Erlbeck18a37872014-09-08 09:59:16 +0200248 gbproxy_check_tlli(peer, tlli_info)) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200249 size_t new_len;
250 gbproxy_patch_apn_ie(msg,
251 parse_ctx->apn_ie, parse_ctx->apn_ie_len,
252 peer, &new_len, parse_ctx->llc_msg_name);
253 *len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
254
255 have_patched = 1;
256 }
257
258 if (have_patched) {
259 llc_len += *len_change;
260 ghp->crc_length += *len_change;
261
262 /* Fix FCS */
263 fcs = gprs_llc_fcs(llc, ghp->crc_length);
264 LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
265 ghp->fcs, fcs);
266
267 llc[llc_len - 3] = fcs & 0xff;
268 llc[llc_len - 2] = (fcs >> 8) & 0xff;
269 llc[llc_len - 1] = (fcs >> 16) & 0xff;
270 }
271
272 return have_patched;
273}
274
275/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
276void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
277 struct gbproxy_peer *peer,
278 struct gbproxy_tlli_info *tlli_info, int *len_change,
279 struct gprs_gb_parse_context *parse_ctx)
280{
281 const char *err_info = NULL;
282 int err_ctr = -1;
283
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200284 if (parse_ctx->bssgp_raid_enc)
285 gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer,
286 parse_ctx->to_bss, "BSSGP");
287
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200288 if (parse_ctx->need_decryption &&
Jacob Erlbeck1abfdc22014-09-04 11:42:08 +0200289 (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200290 /* Patching LLC messages has been requested
291 * explicitly, but the message (including the
292 * type) is encrypted, so we possibly fail to
293 * patch the LLC part of the message. */
294 err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
295 err_info = "GMM message is encrypted";
296 goto patch_error;
297 }
298
Jacob Erlbeck37fda772014-09-05 10:22:27 +0200299 if (!tlli_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
300 /* Happens with unknown (not cached) TLLI coming from
301 * the SGSN */
302 /* TODO: What shall be done with the message in this case? */
303 err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
304 err_info = "TLLI sent by the SGSN is unknown";
305 goto patch_error;
306 }
307
Jacob Erlbeckecbd56c2014-08-26 10:01:57 +0200308 if (!tlli_info)
309 return;
310
311 if (parse_ctx->tlli_enc) {
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200312 uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
313 tlli_info, parse_ctx->to_bss);
314
315 if (tlli) {
316 gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
317 parse_ctx->to_bss, "TLLI");
318 parse_ctx->tlli = tlli;
Jacob Erlbeck9114bee2014-08-19 12:21:01 +0200319 } else {
320 /* Internal error */
321 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
322 err_info = "Replacement TLLI is 0";
323 goto patch_error;
324 }
325 }
326
327 if (parse_ctx->llc) {
328 uint8_t *llc = parse_ctx->llc;
329 size_t llc_len = parse_ctx->llc_len;
330 int llc_len_change = 0;
331
332 gbproxy_patch_llc(msg, llc, llc_len, peer, tlli_info,
333 &llc_len_change, parse_ctx);
334 /* Note that the APN might have been resized here, but no
335 * pointer int the parse_ctx will refer to an adress after the
336 * APN. So it's possible to patch first and do the TLLI
337 * handling afterwards. */
338
339 if (llc_len_change) {
340 llc_len += llc_len_change;
341
342 /* Fix LLC IE len */
343 /* TODO: This is a kludge, but the a pointer to the
344 * start of the IE is not available here */
345 if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
346 /* most probably a one byte length */
347 if (llc_len > 127) {
348 err_info = "Cannot increase size";
349 err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
350 goto patch_error;
351 }
352 llc[-1] = llc_len | 0x80;
353 } else {
354 llc[-2] = (llc_len >> 8) & 0x7f;
355 llc[-1] = llc_len & 0xff;
356 }
357 *len_change += llc_len_change;
358 }
359 /* Note that the tp struct might contain invalid pointers here
360 * if the LLC field has changed its size */
361 parse_ctx->llc_len = llc_len;
362 }
363 return;
364
365patch_error:
366 OSMO_ASSERT(err_ctr >= 0);
367 rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
368 LOGP(DGPRS, LOGL_ERROR,
369 "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
370 msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
371 err_info);
372}
373
374void gbproxy_clear_patch_filter(struct gbproxy_config *cfg)
375{
376 if (cfg->check_imsi) {
377 regfree(&cfg->imsi_re_comp);
378 cfg->check_imsi = 0;
379 }
380}
381
382int gbproxy_set_patch_filter(struct gbproxy_config *cfg, const char *filter,
383 const char **err_msg)
384{
385 static char err_buf[300];
386 int rc;
387
388 gbproxy_clear_patch_filter(cfg);
389
390 if (!filter)
391 return 0;
392
393 rc = regcomp(&cfg->imsi_re_comp, filter,
394 REG_EXTENDED | REG_NOSUB | REG_ICASE);
395
396 if (rc == 0) {
397 cfg->check_imsi = 1;
398 return 0;
399 }
400
401 if (err_msg) {
402 regerror(rc, &cfg->imsi_re_comp,
403 err_buf, sizeof(err_buf));
404 *err_msg = err_buf;
405 }
406
407 return -1;
408}
409
410int gbproxy_check_imsi(struct gbproxy_peer *peer,
411 const uint8_t *imsi, size_t imsi_len)
412{
413 char mi_buf[200];
414 int rc;
415
416 if (!peer->cfg->check_imsi)
417 return 1;
418
419 rc = gprs_is_mi_imsi(imsi, imsi_len);
420 if (rc > 0)
421 rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
422 if (rc <= 0) {
423 LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
424 osmo_hexdump(imsi, imsi_len));
425 return -1;
426 }
427
428 LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
429
430 rc = regexec(&peer->cfg->imsi_re_comp, mi_buf, 0, NULL, 0);
431 if (rc == REG_NOMATCH) {
432 LOGP(DGPRS, LOGL_INFO,
433 "IMSI '%s' doesn't match pattern '%s'\n",
434 mi_buf, peer->cfg->match_re);
435 return 0;
436 }
437
438 return 1;
439}
440