blob: 525bfab1171cfe8c0a6e140c15eafff10cd53f19 [file] [log] [blame]
Harald Welte9b455bf2010-03-14 15:45:01 +08001/* GPRS SGSN functionality */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
4 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +01008 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
Harald Welte9b455bf2010-03-14 15:45:01 +080010 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010015 * GNU Affero General Public License for more details.
Harald Welte9b455bf2010-03-14 15:45:01 +080016 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte9b455bf2010-03-14 15:45:01 +080019 *
20 */
21
Harald Welteeaa614c2010-05-02 11:26:34 +020022#include <stdint.h>
Harald Welte9b455bf2010-03-14 15:45:01 +080023
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010024#include <osmocom/core/linuxlist.h>
25#include <osmocom/core/talloc.h>
26#include <osmocom/core/timer.h>
27#include <osmocom/core/rate_ctr.h>
Harald Weltefdf453c2012-07-14 12:15:19 +020028#include <osmocom/core/backtrace.h>
Harald Welteea34a4e2012-06-16 14:59:56 +080029#include <osmocom/gprs/gprs_ns.h>
30#include <osmocom/gprs/gprs_bssgp.h>
31
Harald Welte9b455bf2010-03-14 15:45:01 +080032#include <openbsc/gsm_subscriber.h>
Harald Weltecb991632010-04-26 19:18:54 +020033#include <openbsc/debug.h>
Harald Welte9b455bf2010-03-14 15:45:01 +080034#include <openbsc/gprs_sgsn.h>
Harald Welteab1d5622010-05-18 19:58:38 +020035#include <openbsc/sgsn.h>
Harald Weltea9b473a2010-12-24 21:13:26 +010036#include <openbsc/gsm_04_08_gprs.h>
37#include <openbsc/gprs_gmm.h>
Jacob Erlbeck99985b52014-10-13 10:32:00 +020038#include "openbsc/gprs_llc.h"
Harald Welteab1d5622010-05-18 19:58:38 +020039
40extern struct sgsn_instance *sgsn;
Harald Welte9b455bf2010-03-14 15:45:01 +080041
Harald Welted193cb32010-05-17 22:58:03 +020042LLIST_HEAD(sgsn_mm_ctxts);
43LLIST_HEAD(sgsn_ggsn_ctxts);
44LLIST_HEAD(sgsn_apn_ctxts);
45LLIST_HEAD(sgsn_pdp_ctxts);
Harald Welte9b455bf2010-03-14 15:45:01 +080046
Harald Welte8acd88f2010-05-18 10:57:45 +020047static const struct rate_ctr_desc mmctx_ctr_description[] = {
48 { "sign.packets.in", "Signalling Messages ( In)" },
49 { "sign.packets.out", "Signalling Messages (Out)" },
50 { "udata.packets.in", "User Data Messages ( In)" },
51 { "udata.packets.out", "User Data Messages (Out)" },
52 { "udata.bytes.in", "User Data Bytes ( In)" },
53 { "udata.bytes.out", "User Data Bytes (Out)" },
54 { "pdp_ctx_act", "PDP Context Activations " },
55 { "suspend", "SUSPEND Count " },
56 { "paging.ps", "Paging Packet Switched " },
57 { "paging.cs", "Paging Circuit Switched " },
58 { "ra_update", "Routing Area Update " },
59};
60
61static const struct rate_ctr_group_desc mmctx_ctrg_desc = {
62 .group_name_prefix = "sgsn.mmctx",
63 .group_description = "SGSN MM Context Statistics",
64 .num_ctr = ARRAY_SIZE(mmctx_ctr_description),
65 .ctr_desc = mmctx_ctr_description,
66};
67
Harald Welteefbdee92010-06-10 00:20:12 +020068static const struct rate_ctr_desc pdpctx_ctr_description[] = {
69 { "udata.packets.in", "User Data Messages ( In)" },
70 { "udata.packets.out", "User Data Messages (Out)" },
71 { "udata.bytes.in", "User Data Bytes ( In)" },
72 { "udata.bytes.out", "User Data Bytes (Out)" },
73};
74
75static const struct rate_ctr_group_desc pdpctx_ctrg_desc = {
76 .group_name_prefix = "sgsn.pdpctx",
77 .group_description = "SGSN PDP Context Statistics",
78 .num_ctr = ARRAY_SIZE(pdpctx_ctr_description),
79 .ctr_desc = pdpctx_ctr_description,
80};
81
Harald Welte9b455bf2010-03-14 15:45:01 +080082static int ra_id_equals(const struct gprs_ra_id *id1,
83 const struct gprs_ra_id *id2)
84{
85 return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
86 id1->lac == id2->lac && id1->rac == id2->rac);
87}
88
Harald Weltef6bd3402010-12-23 23:34:43 +010089/* See 03.02 Chapter 2.6 */
90static inline uint32_t tlli_foreign(uint32_t tlli)
91{
92 return ((tlli | 0x80000000) & ~0x40000000);
93}
94
Harald Welte9b455bf2010-03-14 15:45:01 +080095/* look-up a SGSN MM context based on TLLI + RAI */
Harald Welteeaa614c2010-05-02 11:26:34 +020096struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
Harald Welte9b455bf2010-03-14 15:45:01 +080097 const struct gprs_ra_id *raid)
98{
99 struct sgsn_mm_ctx *ctx;
Harald Welteab1d5622010-05-18 19:58:38 +0200100 int tlli_type;
Harald Welte9b455bf2010-03-14 15:45:01 +0800101
102 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
103 if (tlli == ctx->tlli &&
104 ra_id_equals(raid, &ctx->ra))
105 return ctx;
106 }
Harald Welteab1d5622010-05-18 19:58:38 +0200107
108 tlli_type = gprs_tlli_type(tlli);
Harald Weltef6bd3402010-12-23 23:34:43 +0100109 switch (tlli_type) {
110 case TLLI_LOCAL:
Harald Welteab1d5622010-05-18 19:58:38 +0200111 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
Harald Weltec2e8cc42010-05-31 20:23:38 +0200112 if ((ctx->p_tmsi | 0xC0000000) == tlli ||
113 (ctx->p_tmsi_old && (ctx->p_tmsi_old | 0xC0000000) == tlli)) {
Harald Welteab1d5622010-05-18 19:58:38 +0200114 ctx->tlli = tlli;
115 return ctx;
116 }
117 }
Harald Weltef6bd3402010-12-23 23:34:43 +0100118 break;
119 case TLLI_FOREIGN:
120 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
121 if (tlli == tlli_foreign(ctx->tlli) &&
122 ra_id_equals(raid, &ctx->ra))
123 return ctx;
124 }
125 break;
126 default:
127 break;
Harald Welteab1d5622010-05-18 19:58:38 +0200128 }
129
Harald Welte9b455bf2010-03-14 15:45:01 +0800130 return NULL;
131}
132
Harald Welteeaa614c2010-05-02 11:26:34 +0200133struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
Harald Welte9b455bf2010-03-14 15:45:01 +0800134{
135 struct sgsn_mm_ctx *ctx;
136
137 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
Harald Weltec2e8cc42010-05-31 20:23:38 +0200138 if (p_tmsi == ctx->p_tmsi ||
139 (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi))
Harald Welte9b455bf2010-03-14 15:45:01 +0800140 return ctx;
141 }
142 return NULL;
143}
144
145struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi)
146{
147 struct sgsn_mm_ctx *ctx;
148
149 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
150 if (!strcmp(imsi, ctx->imsi))
151 return ctx;
152 }
153 return NULL;
154
155}
156
157/* Allocate a new SGSN MM context */
Harald Welteeaa614c2010-05-02 11:26:34 +0200158struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
Harald Welte9b455bf2010-03-14 15:45:01 +0800159 const struct gprs_ra_id *raid)
160{
Harald Welte2720e732010-05-17 00:44:57 +0200161 struct sgsn_mm_ctx *ctx;
Harald Welte9b455bf2010-03-14 15:45:01 +0800162
Harald Welte2720e732010-05-17 00:44:57 +0200163 ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
Harald Welte9b455bf2010-03-14 15:45:01 +0800164 if (!ctx)
165 return NULL;
166
167 memcpy(&ctx->ra, raid, sizeof(ctx->ra));
168 ctx->tlli = tlli;
169 ctx->mm_state = GMM_DEREGISTERED;
Jacob Erlbeckbd0cf112014-12-01 12:33:33 +0100170 ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
Harald Welte8acd88f2010-05-18 10:57:45 +0200171 ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
Harald Welte6ffbaab2010-05-18 12:44:45 +0200172 INIT_LLIST_HEAD(&ctx->pdp_list);
Harald Welte9b455bf2010-03-14 15:45:01 +0800173
174 llist_add(&ctx->list, &sgsn_mm_ctxts);
175
176 return ctx;
177}
Harald Welted193cb32010-05-17 22:58:03 +0200178
Harald Welte7b022ee2012-07-14 12:04:04 +0200179/* this is a hard _free_ function, it doesn't clean up the PDP contexts
180 * in libgtp! */
Harald Weltec728eea2010-12-24 23:07:18 +0100181void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
182{
183 struct sgsn_pdp_ctx *pdp, *pdp2;
184
Jacob Erlbeckae20b4b2014-10-20 16:05:55 +0200185 if (osmo_timer_pending(&mm->timer)) {
186 LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T);
187 osmo_timer_del(&mm->timer);
188 }
189
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100190 /* Detach from subscriber which is possibly freed then */
191 if (mm->subscr) {
192 struct gsm_subscriber *subscr = mm->subscr;
193 mm->subscr = NULL;
194 subscr->mm = NULL;
195 gprs_subscr_delete(subscr);
196 }
197
Harald Weltec728eea2010-12-24 23:07:18 +0100198 /* Unlink from global list of MM contexts */
199 llist_del(&mm->list);
200
201 /* Free all PDP contexts */
202 llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
203 sgsn_pdp_ctx_free(pdp);
204
205 rate_ctr_group_free(mm->ctrg);
206
207 talloc_free(mm);
208}
Harald Welte77289c22010-05-18 14:32:29 +0200209
Harald Welte96df6062010-06-03 06:37:26 +0200210/* look up PDP context by MM context and NSAPI */
Harald Welted193cb32010-05-17 22:58:03 +0200211struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
212 uint8_t nsapi)
213{
214 struct sgsn_pdp_ctx *pdp;
215
216 llist_for_each_entry(pdp, &mm->pdp_list, list) {
217 if (pdp->nsapi == nsapi)
218 return pdp;
219 }
220 return NULL;
221}
222
Harald Welte96df6062010-06-03 06:37:26 +0200223/* look up PDP context by MM context and transaction ID */
Harald Welte77289c22010-05-18 14:32:29 +0200224struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
225 uint8_t tid)
226{
227 struct sgsn_pdp_ctx *pdp;
228
229 llist_for_each_entry(pdp, &mm->pdp_list, list) {
230 if (pdp->ti == tid)
231 return pdp;
232 }
233 return NULL;
234}
235
Harald Welte7b022ee2012-07-14 12:04:04 +0200236/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
Harald Welted193cb32010-05-17 22:58:03 +0200237struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
238 uint8_t nsapi)
239{
240 struct sgsn_pdp_ctx *pdp;
241
242 pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
243 if (pdp)
244 return NULL;
245
246 pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
247 if (!pdp)
248 return NULL;
249
250 pdp->mm = mm;
251 pdp->nsapi = nsapi;
Harald Welteefbdee92010-06-10 00:20:12 +0200252 pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
Harald Welted193cb32010-05-17 22:58:03 +0200253 llist_add(&pdp->list, &mm->pdp_list);
254 llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
255
256 return pdp;
257}
258
Harald Weltefdf453c2012-07-14 12:15:19 +0200259#include <pdp.h>
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200260/*
261 * This function will not trigger any GSM DEACT PDP ACK messages, so you
262 * probably want to call sgsn_delete_pdp_ctx() instead if the connection
263 * isn't detached already.
264 */
265void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
266{
267 OSMO_ASSERT(pdp->mm != NULL);
268
269 /* There might still be pending callbacks in libgtp. So the parts of
270 * this object relevant to GTP need to remain intact in this case. */
271
272 LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
273
274 /* Force the deactivation of the SNDCP layer */
275 sndcp_sm_deactivate_ind(&pdp->mm->llme->lle[pdp->sapi], pdp->nsapi);
276
277 /* Detach from MM context */
278 llist_del(&pdp->list);
279 pdp->mm = NULL;
280
281 sgsn_delete_pdp_ctx(pdp);
282}
283
284/*
285 * Don't call this function directly unless you know what you are doing.
286 * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
287 * implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
288 */
Harald Welted193cb32010-05-17 22:58:03 +0200289void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
290{
Harald Welte376d5e52010-06-28 18:57:21 +0200291 rate_ctr_group_free(pdp->ctrg);
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200292 if (pdp->mm)
293 llist_del(&pdp->list);
Harald Welted193cb32010-05-17 22:58:03 +0200294 llist_del(&pdp->g_list);
Harald Weltefdf453c2012-07-14 12:15:19 +0200295
296 /* _if_ we still have a library handle, at least set it to NULL
297 * to avoid any dereferences of the now-deleted PDP context from
298 * sgsn_libgtp:cb_data_ind() */
299 if (pdp->lib) {
300 struct pdp_t *lib = pdp->lib;
Daniel Willmann46553142014-09-03 17:46:44 +0200301 LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still "
Harald Weltefdf453c2012-07-14 12:15:19 +0200302 "has a libgtp handle attached to it, this shouldn't "
303 "happen!\n");
304 osmo_generate_backtrace();
305 lib->priv = NULL;
306 }
307
Harald Welted193cb32010-05-17 22:58:03 +0200308 talloc_free(pdp);
309}
310
311/* GGSN contexts */
312
Harald Welte77289c22010-05-18 14:32:29 +0200313struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200314{
Harald Welte77289c22010-05-18 14:32:29 +0200315 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200316
Harald Welte77289c22010-05-18 14:32:29 +0200317 ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
Harald Welted193cb32010-05-17 22:58:03 +0200318 if (!ggc)
319 return NULL;
320
321 ggc->id = id;
322 ggc->gtp_version = 1;
Harald Weltea9b473a2010-12-24 21:13:26 +0100323 ggc->remote_restart_ctr = -1;
Harald Welteab1d5622010-05-18 19:58:38 +0200324 /* if we are called from config file parse, this gsn doesn't exist yet */
325 ggc->gsn = sgsn->gsn;
Harald Welte119c2ba2010-05-18 18:39:00 +0200326 llist_add(&ggc->list, &sgsn_ggsn_ctxts);
Harald Welted193cb32010-05-17 22:58:03 +0200327
328 return ggc;
329}
330
Harald Welte77289c22010-05-18 14:32:29 +0200331struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200332{
Harald Welte77289c22010-05-18 14:32:29 +0200333 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200334
335 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
336 if (id == ggc->id)
337 return ggc;
338 }
339 return NULL;
340}
341
Harald Weltea9b473a2010-12-24 21:13:26 +0100342struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
343{
344 struct sgsn_ggsn_ctx *ggc;
345
346 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
347 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
348 return ggc;
349 }
350 return NULL;
351}
352
353
Harald Welte77289c22010-05-18 14:32:29 +0200354struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200355{
Harald Welte77289c22010-05-18 14:32:29 +0200356 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200357
Harald Welte77289c22010-05-18 14:32:29 +0200358 ggc = sgsn_ggsn_ctx_by_id(id);
Harald Welted193cb32010-05-17 22:58:03 +0200359 if (!ggc)
Harald Welte77289c22010-05-18 14:32:29 +0200360 ggc = sgsn_ggsn_ctx_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200361 return ggc;
362}
363
364/* APN contexts */
365
366#if 0
367struct apn_ctx *apn_ctx_alloc(const char *ap_name)
368{
369 struct apn_ctx *actx;
370
371 actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx);
372 if (!actx)
373 return NULL;
374 actx->name = talloc_strdup(actx, ap_name);
375
376 return actx;
377}
378
379struct apn_ctx *apn_ctx_by_name(const char *name)
380{
381 struct apn_ctx *actx;
382
383 llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
384 if (!strcmp(name, actx->name))
385 return actx;
386 }
387 return NULL;
388}
389
390struct apn_ctx *apn_ctx_find_alloc(const char *name)
391{
392 struct apn_ctx *actx;
393
394 actx = apn_ctx_by_name(name);
395 if (!actx)
396 actx = apn_ctx_alloc(name);
397
398 return actx;
399}
400#endif
Harald Welte6463c072010-05-18 17:04:55 +0200401
402uint32_t sgsn_alloc_ptmsi(void)
403{
404 struct sgsn_mm_ctx *mm;
405 uint32_t ptmsi;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200406 int max_retries = 23;
Harald Welte6463c072010-05-18 17:04:55 +0200407
408restart:
Holger Hans Peter Freytherb773fbf2014-08-05 15:20:23 +0200409 ptmsi = rand() | 0xC0000000;
Harald Welte6463c072010-05-18 17:04:55 +0200410 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200411 if (mm->p_tmsi == ptmsi) {
412 if (!max_retries--)
413 goto failed;
Harald Welte6463c072010-05-18 17:04:55 +0200414 goto restart;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200415 }
Harald Welte6463c072010-05-18 17:04:55 +0200416 }
417
418 return ptmsi;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200419
420failed:
421 LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n");
422 return GSM_RESERVED_TMSI;
Harald Welte6463c072010-05-18 17:04:55 +0200423}
Harald Weltea9b473a2010-12-24 21:13:26 +0100424
425static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
426{
427 if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL)
428 gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
429 else {
430 /* FIXME: GPRS paging in case MS is SUSPENDED */
Daniel Willmann46553142014-09-03 17:46:44 +0200431 LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN "
Harald Weltea9b473a2010-12-24 21:13:26 +0100432 "recovery\n");
Harald Welte7b022ee2012-07-14 12:04:04 +0200433 /* FIXME: how to tell this to libgtp? */
Harald Weltea9b473a2010-12-24 21:13:26 +0100434 sgsn_pdp_ctx_free(pdp);
435 }
436}
437
438/* High-level function to be called in case a GGSN has disappeared or
Holger Hans Peter Freyther19e990d2014-10-27 10:24:37 +0100439 * otherwise lost state (recovery procedure) */
Harald Weltea9b473a2010-12-24 21:13:26 +0100440int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
441{
442 struct sgsn_mm_ctx *mm;
443 int num = 0;
444
445 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
446 struct sgsn_pdp_ctx *pdp;
447 llist_for_each_entry(pdp, &mm->pdp_list, list) {
448 if (pdp->ggsn == ggsn) {
449 drop_one_pdp(pdp);
450 num++;
451 }
452 }
453 }
454
455 return num;
456}
Jacob Erlbeck78ecaf02014-09-05 14:32:36 +0200457
458int sgsn_force_reattach_oldmsg(struct msgb *oldmsg)
459{
Jacob Erlbeckabdf02b2014-10-31 12:20:49 +0100460 return gsm0408_gprs_force_reattach_oldmsg(oldmsg);
Jacob Erlbeck78ecaf02014-09-05 14:32:36 +0200461}
462
Jacob Erlbeck33b6dad2014-11-12 10:12:11 +0100463void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx,
464 struct gsm_subscriber *subscr)
Jacob Erlbeck423f8bf2014-10-24 18:09:54 +0200465{
Jacob Erlbeckbe2c8d92014-11-12 10:18:09 +0100466 if (!mmctx && subscr && strlen(subscr->imsi) > 0) {
467 mmctx = sgsn_mm_ctx_by_imsi(subscr->imsi);
468 OSMO_ASSERT(!mmctx || !mmctx->subscr || mmctx->subscr == subscr);
469 }
470
471 if (!mmctx) {
472 LOGP(DMM, LOGL_INFO,
473 "Subscriber data update for unregistered MM context, IMSI %s\n",
474 subscr->imsi);
475 return;
476 }
477
478 LOGMMCTXP(LOGL_INFO, mmctx, "Subscriber data update");
479
480 if (!subscr->mm && !mmctx->subscr) {
481 mmctx->subscr = subscr_get(subscr);
482 mmctx->subscr->mm = mmctx;
483 }
Jacob Erlbeck423f8bf2014-10-24 18:09:54 +0200484
Jacob Erlbecka0b6efb2014-11-13 10:48:39 +0100485 sgsn_auth_update(mmctx);
Jacob Erlbeck423f8bf2014-10-24 18:09:54 +0200486}