blob: 75f8ae5f057ac2c6ba13e4097a17fd5db896b977 [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;
Harald Welte8acd88f2010-05-18 10:57:45 +0200170 ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
Harald Welte6ffbaab2010-05-18 12:44:45 +0200171 INIT_LLIST_HEAD(&ctx->pdp_list);
Harald Welte9b455bf2010-03-14 15:45:01 +0800172
173 llist_add(&ctx->list, &sgsn_mm_ctxts);
174
175 return ctx;
176}
Harald Welted193cb32010-05-17 22:58:03 +0200177
Harald Welte7b022ee2012-07-14 12:04:04 +0200178/* this is a hard _free_ function, it doesn't clean up the PDP contexts
179 * in libgtp! */
Harald Weltec728eea2010-12-24 23:07:18 +0100180void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
181{
182 struct sgsn_pdp_ctx *pdp, *pdp2;
183
Jacob Erlbeckae20b4b2014-10-20 16:05:55 +0200184 if (osmo_timer_pending(&mm->timer)) {
185 LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T);
186 osmo_timer_del(&mm->timer);
187 }
188
Harald Weltec728eea2010-12-24 23:07:18 +0100189 /* Unlink from global list of MM contexts */
190 llist_del(&mm->list);
191
192 /* Free all PDP contexts */
193 llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
194 sgsn_pdp_ctx_free(pdp);
195
196 rate_ctr_group_free(mm->ctrg);
197
198 talloc_free(mm);
199}
Harald Welte77289c22010-05-18 14:32:29 +0200200
Harald Welte96df6062010-06-03 06:37:26 +0200201/* look up PDP context by MM context and NSAPI */
Harald Welted193cb32010-05-17 22:58:03 +0200202struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
203 uint8_t nsapi)
204{
205 struct sgsn_pdp_ctx *pdp;
206
207 llist_for_each_entry(pdp, &mm->pdp_list, list) {
208 if (pdp->nsapi == nsapi)
209 return pdp;
210 }
211 return NULL;
212}
213
Harald Welte96df6062010-06-03 06:37:26 +0200214/* look up PDP context by MM context and transaction ID */
Harald Welte77289c22010-05-18 14:32:29 +0200215struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
216 uint8_t tid)
217{
218 struct sgsn_pdp_ctx *pdp;
219
220 llist_for_each_entry(pdp, &mm->pdp_list, list) {
221 if (pdp->ti == tid)
222 return pdp;
223 }
224 return NULL;
225}
226
Harald Welte7b022ee2012-07-14 12:04:04 +0200227/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
Harald Welted193cb32010-05-17 22:58:03 +0200228struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
229 uint8_t nsapi)
230{
231 struct sgsn_pdp_ctx *pdp;
232
233 pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
234 if (pdp)
235 return NULL;
236
237 pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
238 if (!pdp)
239 return NULL;
240
241 pdp->mm = mm;
242 pdp->nsapi = nsapi;
Harald Welteefbdee92010-06-10 00:20:12 +0200243 pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
Harald Welted193cb32010-05-17 22:58:03 +0200244 llist_add(&pdp->list, &mm->pdp_list);
245 llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
246
247 return pdp;
248}
249
Harald Weltefdf453c2012-07-14 12:15:19 +0200250#include <pdp.h>
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200251/*
252 * This function will not trigger any GSM DEACT PDP ACK messages, so you
253 * probably want to call sgsn_delete_pdp_ctx() instead if the connection
254 * isn't detached already.
255 */
256void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
257{
258 OSMO_ASSERT(pdp->mm != NULL);
259
260 /* There might still be pending callbacks in libgtp. So the parts of
261 * this object relevant to GTP need to remain intact in this case. */
262
263 LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
264
265 /* Force the deactivation of the SNDCP layer */
266 sndcp_sm_deactivate_ind(&pdp->mm->llme->lle[pdp->sapi], pdp->nsapi);
267
268 /* Detach from MM context */
269 llist_del(&pdp->list);
270 pdp->mm = NULL;
271
272 sgsn_delete_pdp_ctx(pdp);
273}
274
275/*
276 * Don't call this function directly unless you know what you are doing.
277 * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
278 * implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
279 */
Harald Welted193cb32010-05-17 22:58:03 +0200280void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
281{
Harald Welte376d5e52010-06-28 18:57:21 +0200282 rate_ctr_group_free(pdp->ctrg);
Jacob Erlbeck99985b52014-10-13 10:32:00 +0200283 if (pdp->mm)
284 llist_del(&pdp->list);
Harald Welted193cb32010-05-17 22:58:03 +0200285 llist_del(&pdp->g_list);
Harald Weltefdf453c2012-07-14 12:15:19 +0200286
287 /* _if_ we still have a library handle, at least set it to NULL
288 * to avoid any dereferences of the now-deleted PDP context from
289 * sgsn_libgtp:cb_data_ind() */
290 if (pdp->lib) {
291 struct pdp_t *lib = pdp->lib;
Daniel Willmann46553142014-09-03 17:46:44 +0200292 LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still "
Harald Weltefdf453c2012-07-14 12:15:19 +0200293 "has a libgtp handle attached to it, this shouldn't "
294 "happen!\n");
295 osmo_generate_backtrace();
296 lib->priv = NULL;
297 }
298
Harald Welted193cb32010-05-17 22:58:03 +0200299 talloc_free(pdp);
300}
301
302/* GGSN contexts */
303
Harald Welte77289c22010-05-18 14:32:29 +0200304struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200305{
Harald Welte77289c22010-05-18 14:32:29 +0200306 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200307
Harald Welte77289c22010-05-18 14:32:29 +0200308 ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
Harald Welted193cb32010-05-17 22:58:03 +0200309 if (!ggc)
310 return NULL;
311
312 ggc->id = id;
313 ggc->gtp_version = 1;
Harald Weltea9b473a2010-12-24 21:13:26 +0100314 ggc->remote_restart_ctr = -1;
Harald Welteab1d5622010-05-18 19:58:38 +0200315 /* if we are called from config file parse, this gsn doesn't exist yet */
316 ggc->gsn = sgsn->gsn;
Harald Welte119c2ba2010-05-18 18:39:00 +0200317 llist_add(&ggc->list, &sgsn_ggsn_ctxts);
Harald Welted193cb32010-05-17 22:58:03 +0200318
319 return ggc;
320}
321
Harald Welte77289c22010-05-18 14:32:29 +0200322struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200323{
Harald Welte77289c22010-05-18 14:32:29 +0200324 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200325
326 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
327 if (id == ggc->id)
328 return ggc;
329 }
330 return NULL;
331}
332
Harald Weltea9b473a2010-12-24 21:13:26 +0100333struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
334{
335 struct sgsn_ggsn_ctx *ggc;
336
337 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
338 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
339 return ggc;
340 }
341 return NULL;
342}
343
344
Harald Welte77289c22010-05-18 14:32:29 +0200345struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200346{
Harald Welte77289c22010-05-18 14:32:29 +0200347 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200348
Harald Welte77289c22010-05-18 14:32:29 +0200349 ggc = sgsn_ggsn_ctx_by_id(id);
Harald Welted193cb32010-05-17 22:58:03 +0200350 if (!ggc)
Harald Welte77289c22010-05-18 14:32:29 +0200351 ggc = sgsn_ggsn_ctx_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200352 return ggc;
353}
354
355/* APN contexts */
356
357#if 0
358struct apn_ctx *apn_ctx_alloc(const char *ap_name)
359{
360 struct apn_ctx *actx;
361
362 actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx);
363 if (!actx)
364 return NULL;
365 actx->name = talloc_strdup(actx, ap_name);
366
367 return actx;
368}
369
370struct apn_ctx *apn_ctx_by_name(const char *name)
371{
372 struct apn_ctx *actx;
373
374 llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
375 if (!strcmp(name, actx->name))
376 return actx;
377 }
378 return NULL;
379}
380
381struct apn_ctx *apn_ctx_find_alloc(const char *name)
382{
383 struct apn_ctx *actx;
384
385 actx = apn_ctx_by_name(name);
386 if (!actx)
387 actx = apn_ctx_alloc(name);
388
389 return actx;
390}
391#endif
Harald Welte6463c072010-05-18 17:04:55 +0200392
393uint32_t sgsn_alloc_ptmsi(void)
394{
395 struct sgsn_mm_ctx *mm;
396 uint32_t ptmsi;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200397 int max_retries = 23;
Harald Welte6463c072010-05-18 17:04:55 +0200398
399restart:
Holger Hans Peter Freytherb773fbf2014-08-05 15:20:23 +0200400 ptmsi = rand() | 0xC0000000;
Harald Welte6463c072010-05-18 17:04:55 +0200401 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200402 if (mm->p_tmsi == ptmsi) {
403 if (!max_retries--)
404 goto failed;
Harald Welte6463c072010-05-18 17:04:55 +0200405 goto restart;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200406 }
Harald Welte6463c072010-05-18 17:04:55 +0200407 }
408
409 return ptmsi;
Jacob Erlbeck08fbeb82014-09-19 09:28:42 +0200410
411failed:
412 LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n");
413 return GSM_RESERVED_TMSI;
Harald Welte6463c072010-05-18 17:04:55 +0200414}
Harald Weltea9b473a2010-12-24 21:13:26 +0100415
416static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
417{
418 if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL)
419 gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
420 else {
421 /* FIXME: GPRS paging in case MS is SUSPENDED */
Daniel Willmann46553142014-09-03 17:46:44 +0200422 LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN "
Harald Weltea9b473a2010-12-24 21:13:26 +0100423 "recovery\n");
Harald Welte7b022ee2012-07-14 12:04:04 +0200424 /* FIXME: how to tell this to libgtp? */
Harald Weltea9b473a2010-12-24 21:13:26 +0100425 sgsn_pdp_ctx_free(pdp);
426 }
427}
428
429/* High-level function to be called in case a GGSN has disappeared or
Holger Hans Peter Freyther19e990d2014-10-27 10:24:37 +0100430 * otherwise lost state (recovery procedure) */
Harald Weltea9b473a2010-12-24 21:13:26 +0100431int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
432{
433 struct sgsn_mm_ctx *mm;
434 int num = 0;
435
436 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
437 struct sgsn_pdp_ctx *pdp;
438 llist_for_each_entry(pdp, &mm->pdp_list, list) {
439 if (pdp->ggsn == ggsn) {
440 drop_one_pdp(pdp);
441 num++;
442 }
443 }
444 }
445
446 return num;
447}