blob: 39bfa84bc8835c2b8b4ccb76839ff04e6e3d7404 [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
Harald Weltec728eea2010-12-24 23:07:18 +0100190 /* Unlink from global list of MM contexts */
191 llist_del(&mm->list);
192
193 /* Free all PDP contexts */
194 llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
195 sgsn_pdp_ctx_free(pdp);
196
197 rate_ctr_group_free(mm->ctrg);
198
199 talloc_free(mm);
200}
Harald Welte77289c22010-05-18 14:32:29 +0200201
Harald Welte96df6062010-06-03 06:37:26 +0200202/* look up PDP context by MM context and NSAPI */
Harald Welted193cb32010-05-17 22:58:03 +0200203struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
204 uint8_t nsapi)
205{
206 struct sgsn_pdp_ctx *pdp;
207
208 llist_for_each_entry(pdp, &mm->pdp_list, list) {
209 if (pdp->nsapi == nsapi)
210 return pdp;
211 }
212 return NULL;
213}
214
Harald Welte96df6062010-06-03 06:37:26 +0200215/* look up PDP context by MM context and transaction ID */
Harald Welte77289c22010-05-18 14:32:29 +0200216struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
217 uint8_t tid)
218{
219 struct sgsn_pdp_ctx *pdp;
220
221 llist_for_each_entry(pdp, &mm->pdp_list, list) {
222 if (pdp->ti == tid)
223 return pdp;
224 }
225 return NULL;
226}
227
Harald Welte7b022ee2012-07-14 12:04:04 +0200228/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
Harald Welted193cb32010-05-17 22:58:03 +0200229struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
230 uint8_t nsapi)
231{
232 struct sgsn_pdp_ctx *pdp;
233
234 pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
235 if (pdp)
236 return NULL;
237
238 pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
239 if (!pdp)
240 return NULL;
241
242 pdp->mm = mm;
243 pdp->nsapi = nsapi;
Harald Welteefbdee92010-06-10 00:20:12 +0200244 pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
Harald Welted193cb32010-05-17 22:58:03 +0200245 llist_add(&pdp->list, &mm->pdp_list);
246 llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
247
248 return pdp;
249}
250
Harald Weltefdf453c2012-07-14 12:15:19 +0200251#include <pdp.h>
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200252/*
253 * This function will not trigger any GSM DEACT PDP ACK messages, so you
254 * probably want to call sgsn_delete_pdp_ctx() instead if the connection
255 * isn't detached already.
256 */
257void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
258{
259 OSMO_ASSERT(pdp->mm != NULL);
260
261 /* There might still be pending callbacks in libgtp. So the parts of
262 * this object relevant to GTP need to remain intact in this case. */
263
264 LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
265
266 /* Force the deactivation of the SNDCP layer */
267 sndcp_sm_deactivate_ind(&pdp->mm->llme->lle[pdp->sapi], pdp->nsapi);
268
269 /* Detach from MM context */
270 llist_del(&pdp->list);
271 pdp->mm = NULL;
272
273 sgsn_delete_pdp_ctx(pdp);
274}
275
276/*
277 * Don't call this function directly unless you know what you are doing.
278 * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
279 * implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
280 */
Harald Welted193cb32010-05-17 22:58:03 +0200281void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
282{
Harald Welte376d5e52010-06-28 18:57:21 +0200283 rate_ctr_group_free(pdp->ctrg);
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200284 if (pdp->mm)
285 llist_del(&pdp->list);
Harald Welted193cb32010-05-17 22:58:03 +0200286 llist_del(&pdp->g_list);
Harald Weltefdf453c2012-07-14 12:15:19 +0200287
288 /* _if_ we still have a library handle, at least set it to NULL
289 * to avoid any dereferences of the now-deleted PDP context from
290 * sgsn_libgtp:cb_data_ind() */
291 if (pdp->lib) {
292 struct pdp_t *lib = pdp->lib;
Daniel Willmann46553142014-09-03 17:46:44 +0200293 LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still "
Harald Weltefdf453c2012-07-14 12:15:19 +0200294 "has a libgtp handle attached to it, this shouldn't "
295 "happen!\n");
296 osmo_generate_backtrace();
297 lib->priv = NULL;
298 }
299
Harald Welted193cb32010-05-17 22:58:03 +0200300 talloc_free(pdp);
301}
302
303/* GGSN contexts */
304
Harald Welte77289c22010-05-18 14:32:29 +0200305struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200306{
Harald Welte77289c22010-05-18 14:32:29 +0200307 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200308
Harald Welte77289c22010-05-18 14:32:29 +0200309 ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
Harald Welted193cb32010-05-17 22:58:03 +0200310 if (!ggc)
311 return NULL;
312
313 ggc->id = id;
314 ggc->gtp_version = 1;
Harald Weltea9b473a2010-12-24 21:13:26 +0100315 ggc->remote_restart_ctr = -1;
Harald Welteab1d5622010-05-18 19:58:38 +0200316 /* if we are called from config file parse, this gsn doesn't exist yet */
317 ggc->gsn = sgsn->gsn;
Harald Welte119c2ba2010-05-18 18:39:00 +0200318 llist_add(&ggc->list, &sgsn_ggsn_ctxts);
Harald Welted193cb32010-05-17 22:58:03 +0200319
320 return ggc;
321}
322
Harald Welte77289c22010-05-18 14:32:29 +0200323struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200324{
Harald Welte77289c22010-05-18 14:32:29 +0200325 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200326
327 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
328 if (id == ggc->id)
329 return ggc;
330 }
331 return NULL;
332}
333
Harald Weltea9b473a2010-12-24 21:13:26 +0100334struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
335{
336 struct sgsn_ggsn_ctx *ggc;
337
338 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
339 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
340 return ggc;
341 }
342 return NULL;
343}
344
345
Harald Welte77289c22010-05-18 14:32:29 +0200346struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200347{
Harald Welte77289c22010-05-18 14:32:29 +0200348 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200349
Harald Welte77289c22010-05-18 14:32:29 +0200350 ggc = sgsn_ggsn_ctx_by_id(id);
Harald Welted193cb32010-05-17 22:58:03 +0200351 if (!ggc)
Harald Welte77289c22010-05-18 14:32:29 +0200352 ggc = sgsn_ggsn_ctx_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200353 return ggc;
354}
355
356/* APN contexts */
357
358#if 0
359struct apn_ctx *apn_ctx_alloc(const char *ap_name)
360{
361 struct apn_ctx *actx;
362
363 actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx);
364 if (!actx)
365 return NULL;
366 actx->name = talloc_strdup(actx, ap_name);
367
368 return actx;
369}
370
371struct apn_ctx *apn_ctx_by_name(const char *name)
372{
373 struct apn_ctx *actx;
374
375 llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
376 if (!strcmp(name, actx->name))
377 return actx;
378 }
379 return NULL;
380}
381
382struct apn_ctx *apn_ctx_find_alloc(const char *name)
383{
384 struct apn_ctx *actx;
385
386 actx = apn_ctx_by_name(name);
387 if (!actx)
388 actx = apn_ctx_alloc(name);
389
390 return actx;
391}
392#endif
Harald Welte6463c072010-05-18 17:04:55 +0200393
394uint32_t sgsn_alloc_ptmsi(void)
395{
396 struct sgsn_mm_ctx *mm;
397 uint32_t ptmsi;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200398 int max_retries = 23;
Harald Welte6463c072010-05-18 17:04:55 +0200399
400restart:
Holger Hans Peter Freytherb773fbf2014-08-05 15:20:23 +0200401 ptmsi = rand() | 0xC0000000;
Harald Welte6463c072010-05-18 17:04:55 +0200402 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200403 if (mm->p_tmsi == ptmsi) {
404 if (!max_retries--)
405 goto failed;
Harald Welte6463c072010-05-18 17:04:55 +0200406 goto restart;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200407 }
Harald Welte6463c072010-05-18 17:04:55 +0200408 }
409
410 return ptmsi;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200411
412failed:
413 LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n");
414 return GSM_RESERVED_TMSI;
Harald Welte6463c072010-05-18 17:04:55 +0200415}
Harald Weltea9b473a2010-12-24 21:13:26 +0100416
417static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
418{
419 if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL)
420 gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
421 else {
422 /* FIXME: GPRS paging in case MS is SUSPENDED */
Daniel Willmann46553142014-09-03 17:46:44 +0200423 LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN "
Harald Weltea9b473a2010-12-24 21:13:26 +0100424 "recovery\n");
Harald Welte7b022ee2012-07-14 12:04:04 +0200425 /* FIXME: how to tell this to libgtp? */
Harald Weltea9b473a2010-12-24 21:13:26 +0100426 sgsn_pdp_ctx_free(pdp);
427 }
428}
429
430/* High-level function to be called in case a GGSN has disappeared or
Holger Hans Peter Freyther19e990d2014-10-27 10:24:37 +0100431 * otherwise lost state (recovery procedure) */
Harald Weltea9b473a2010-12-24 21:13:26 +0100432int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
433{
434 struct sgsn_mm_ctx *mm;
435 int num = 0;
436
437 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
438 struct sgsn_pdp_ctx *pdp;
439 llist_for_each_entry(pdp, &mm->pdp_list, list) {
440 if (pdp->ggsn == ggsn) {
441 drop_one_pdp(pdp);
442 num++;
443 }
444 }
445 }
446
447 return num;
448}
Jacob Erlbeck78ecaf02014-09-05 14:32:36 +0200449
450int sgsn_force_reattach_oldmsg(struct msgb *oldmsg)
451{
Jacob Erlbeckabdf02b2014-10-31 12:20:49 +0100452 return gsm0408_gprs_force_reattach_oldmsg(oldmsg);
Jacob Erlbeck78ecaf02014-09-05 14:32:36 +0200453}
454
Jacob Erlbeckf951a012014-11-07 14:17:44 +0100455void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx)
Jacob Erlbeck423f8bf2014-10-24 18:09:54 +0200456{
457 OSMO_ASSERT(mmctx);
458
Jacob Erlbecka0b6efb2014-11-13 10:48:39 +0100459 sgsn_auth_update(mmctx);
Jacob Erlbeck423f8bf2014-10-24 18:09:54 +0200460}