blob: d8da846619d94e54c782798aeca3c8241a03c525 [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 Welteea34a4e2012-06-16 14:59:56 +080028#include <osmocom/gprs/gprs_ns.h>
29#include <osmocom/gprs/gprs_bssgp.h>
30
Harald Welte9b455bf2010-03-14 15:45:01 +080031#include <openbsc/gsm_subscriber.h>
Harald Weltecb991632010-04-26 19:18:54 +020032#include <openbsc/debug.h>
Harald Welte9b455bf2010-03-14 15:45:01 +080033#include <openbsc/gprs_sgsn.h>
Harald Welteab1d5622010-05-18 19:58:38 +020034#include <openbsc/sgsn.h>
Harald Weltea9b473a2010-12-24 21:13:26 +010035#include <openbsc/gsm_04_08_gprs.h>
36#include <openbsc/gprs_gmm.h>
Harald Welteab1d5622010-05-18 19:58:38 +020037
38extern struct sgsn_instance *sgsn;
Harald Welte9b455bf2010-03-14 15:45:01 +080039
Harald Welted193cb32010-05-17 22:58:03 +020040LLIST_HEAD(sgsn_mm_ctxts);
41LLIST_HEAD(sgsn_ggsn_ctxts);
42LLIST_HEAD(sgsn_apn_ctxts);
43LLIST_HEAD(sgsn_pdp_ctxts);
Harald Welte9b455bf2010-03-14 15:45:01 +080044
Harald Welte8acd88f2010-05-18 10:57:45 +020045static const struct rate_ctr_desc mmctx_ctr_description[] = {
46 { "sign.packets.in", "Signalling Messages ( In)" },
47 { "sign.packets.out", "Signalling Messages (Out)" },
48 { "udata.packets.in", "User Data Messages ( In)" },
49 { "udata.packets.out", "User Data Messages (Out)" },
50 { "udata.bytes.in", "User Data Bytes ( In)" },
51 { "udata.bytes.out", "User Data Bytes (Out)" },
52 { "pdp_ctx_act", "PDP Context Activations " },
53 { "suspend", "SUSPEND Count " },
54 { "paging.ps", "Paging Packet Switched " },
55 { "paging.cs", "Paging Circuit Switched " },
56 { "ra_update", "Routing Area Update " },
57};
58
59static const struct rate_ctr_group_desc mmctx_ctrg_desc = {
60 .group_name_prefix = "sgsn.mmctx",
61 .group_description = "SGSN MM Context Statistics",
62 .num_ctr = ARRAY_SIZE(mmctx_ctr_description),
63 .ctr_desc = mmctx_ctr_description,
64};
65
Harald Welteefbdee92010-06-10 00:20:12 +020066static const struct rate_ctr_desc pdpctx_ctr_description[] = {
67 { "udata.packets.in", "User Data Messages ( In)" },
68 { "udata.packets.out", "User Data Messages (Out)" },
69 { "udata.bytes.in", "User Data Bytes ( In)" },
70 { "udata.bytes.out", "User Data Bytes (Out)" },
71};
72
73static const struct rate_ctr_group_desc pdpctx_ctrg_desc = {
74 .group_name_prefix = "sgsn.pdpctx",
75 .group_description = "SGSN PDP Context Statistics",
76 .num_ctr = ARRAY_SIZE(pdpctx_ctr_description),
77 .ctr_desc = pdpctx_ctr_description,
78};
79
Harald Welte9b455bf2010-03-14 15:45:01 +080080static int ra_id_equals(const struct gprs_ra_id *id1,
81 const struct gprs_ra_id *id2)
82{
83 return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
84 id1->lac == id2->lac && id1->rac == id2->rac);
85}
86
Harald Weltef6bd3402010-12-23 23:34:43 +010087/* See 03.02 Chapter 2.6 */
88static inline uint32_t tlli_foreign(uint32_t tlli)
89{
90 return ((tlli | 0x80000000) & ~0x40000000);
91}
92
Harald Welte9b455bf2010-03-14 15:45:01 +080093/* look-up a SGSN MM context based on TLLI + RAI */
Harald Welteeaa614c2010-05-02 11:26:34 +020094struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
Harald Welte9b455bf2010-03-14 15:45:01 +080095 const struct gprs_ra_id *raid)
96{
97 struct sgsn_mm_ctx *ctx;
Harald Welteab1d5622010-05-18 19:58:38 +020098 int tlli_type;
Harald Welte9b455bf2010-03-14 15:45:01 +080099
100 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
101 if (tlli == ctx->tlli &&
102 ra_id_equals(raid, &ctx->ra))
103 return ctx;
104 }
Harald Welteab1d5622010-05-18 19:58:38 +0200105
106 tlli_type = gprs_tlli_type(tlli);
Harald Weltef6bd3402010-12-23 23:34:43 +0100107 switch (tlli_type) {
108 case TLLI_LOCAL:
Harald Welteab1d5622010-05-18 19:58:38 +0200109 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
Harald Weltec2e8cc42010-05-31 20:23:38 +0200110 if ((ctx->p_tmsi | 0xC0000000) == tlli ||
111 (ctx->p_tmsi_old && (ctx->p_tmsi_old | 0xC0000000) == tlli)) {
Harald Welteab1d5622010-05-18 19:58:38 +0200112 ctx->tlli = tlli;
113 return ctx;
114 }
115 }
Harald Weltef6bd3402010-12-23 23:34:43 +0100116 break;
117 case TLLI_FOREIGN:
118 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
119 if (tlli == tlli_foreign(ctx->tlli) &&
120 ra_id_equals(raid, &ctx->ra))
121 return ctx;
122 }
123 break;
124 default:
125 break;
Harald Welteab1d5622010-05-18 19:58:38 +0200126 }
127
Harald Welte9b455bf2010-03-14 15:45:01 +0800128 return NULL;
129}
130
Harald Welteeaa614c2010-05-02 11:26:34 +0200131struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
Harald Welte9b455bf2010-03-14 15:45:01 +0800132{
133 struct sgsn_mm_ctx *ctx;
134
135 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
Harald Weltec2e8cc42010-05-31 20:23:38 +0200136 if (p_tmsi == ctx->p_tmsi ||
137 (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi))
Harald Welte9b455bf2010-03-14 15:45:01 +0800138 return ctx;
139 }
140 return NULL;
141}
142
143struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi)
144{
145 struct sgsn_mm_ctx *ctx;
146
147 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
148 if (!strcmp(imsi, ctx->imsi))
149 return ctx;
150 }
151 return NULL;
152
153}
154
155/* Allocate a new SGSN MM context */
Harald Welteeaa614c2010-05-02 11:26:34 +0200156struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
Harald Welte9b455bf2010-03-14 15:45:01 +0800157 const struct gprs_ra_id *raid)
158{
Harald Welte2720e732010-05-17 00:44:57 +0200159 struct sgsn_mm_ctx *ctx;
Harald Welte9b455bf2010-03-14 15:45:01 +0800160
Harald Welte2720e732010-05-17 00:44:57 +0200161 ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
Harald Welte9b455bf2010-03-14 15:45:01 +0800162 if (!ctx)
163 return NULL;
164
165 memcpy(&ctx->ra, raid, sizeof(ctx->ra));
166 ctx->tlli = tlli;
167 ctx->mm_state = GMM_DEREGISTERED;
Harald Welte8acd88f2010-05-18 10:57:45 +0200168 ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
Harald Welte6ffbaab2010-05-18 12:44:45 +0200169 INIT_LLIST_HEAD(&ctx->pdp_list);
Harald Welte9b455bf2010-03-14 15:45:01 +0800170
171 llist_add(&ctx->list, &sgsn_mm_ctxts);
172
173 return ctx;
174}
Harald Welted193cb32010-05-17 22:58:03 +0200175
Harald Weltec728eea2010-12-24 23:07:18 +0100176void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
177{
178 struct sgsn_pdp_ctx *pdp, *pdp2;
179
180 /* Unlink from global list of MM contexts */
181 llist_del(&mm->list);
182
183 /* Free all PDP contexts */
184 llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
185 sgsn_pdp_ctx_free(pdp);
186
187 rate_ctr_group_free(mm->ctrg);
188
189 talloc_free(mm);
190}
Harald Welte77289c22010-05-18 14:32:29 +0200191
Harald Welte96df6062010-06-03 06:37:26 +0200192/* look up PDP context by MM context and NSAPI */
Harald Welted193cb32010-05-17 22:58:03 +0200193struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
194 uint8_t nsapi)
195{
196 struct sgsn_pdp_ctx *pdp;
197
198 llist_for_each_entry(pdp, &mm->pdp_list, list) {
199 if (pdp->nsapi == nsapi)
200 return pdp;
201 }
202 return NULL;
203}
204
Harald Welte96df6062010-06-03 06:37:26 +0200205/* look up PDP context by MM context and transaction ID */
Harald Welte77289c22010-05-18 14:32:29 +0200206struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
207 uint8_t tid)
208{
209 struct sgsn_pdp_ctx *pdp;
210
211 llist_for_each_entry(pdp, &mm->pdp_list, list) {
212 if (pdp->ti == tid)
213 return pdp;
214 }
215 return NULL;
216}
217
Harald Welted193cb32010-05-17 22:58:03 +0200218struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
219 uint8_t nsapi)
220{
221 struct sgsn_pdp_ctx *pdp;
222
223 pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
224 if (pdp)
225 return NULL;
226
227 pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
228 if (!pdp)
229 return NULL;
230
231 pdp->mm = mm;
232 pdp->nsapi = nsapi;
Harald Welteefbdee92010-06-10 00:20:12 +0200233 pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
Harald Welted193cb32010-05-17 22:58:03 +0200234 llist_add(&pdp->list, &mm->pdp_list);
235 llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
236
237 return pdp;
238}
239
240void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
241{
Harald Welte376d5e52010-06-28 18:57:21 +0200242 rate_ctr_group_free(pdp->ctrg);
Harald Welted193cb32010-05-17 22:58:03 +0200243 llist_del(&pdp->list);
244 llist_del(&pdp->g_list);
245 talloc_free(pdp);
246}
247
248/* GGSN contexts */
249
Harald Welte77289c22010-05-18 14:32:29 +0200250struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200251{
Harald Welte77289c22010-05-18 14:32:29 +0200252 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200253
Harald Welte77289c22010-05-18 14:32:29 +0200254 ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
Harald Welted193cb32010-05-17 22:58:03 +0200255 if (!ggc)
256 return NULL;
257
258 ggc->id = id;
259 ggc->gtp_version = 1;
Harald Weltea9b473a2010-12-24 21:13:26 +0100260 ggc->remote_restart_ctr = -1;
Harald Welteab1d5622010-05-18 19:58:38 +0200261 /* if we are called from config file parse, this gsn doesn't exist yet */
262 ggc->gsn = sgsn->gsn;
Harald Welte119c2ba2010-05-18 18:39:00 +0200263 llist_add(&ggc->list, &sgsn_ggsn_ctxts);
Harald Welted193cb32010-05-17 22:58:03 +0200264
265 return ggc;
266}
267
Harald Welte77289c22010-05-18 14:32:29 +0200268struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200269{
Harald Welte77289c22010-05-18 14:32:29 +0200270 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200271
272 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
273 if (id == ggc->id)
274 return ggc;
275 }
276 return NULL;
277}
278
Harald Weltea9b473a2010-12-24 21:13:26 +0100279struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
280{
281 struct sgsn_ggsn_ctx *ggc;
282
283 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
284 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
285 return ggc;
286 }
287 return NULL;
288}
289
290
Harald Welte77289c22010-05-18 14:32:29 +0200291struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
Harald Welted193cb32010-05-17 22:58:03 +0200292{
Harald Welte77289c22010-05-18 14:32:29 +0200293 struct sgsn_ggsn_ctx *ggc;
Harald Welted193cb32010-05-17 22:58:03 +0200294
Harald Welte77289c22010-05-18 14:32:29 +0200295 ggc = sgsn_ggsn_ctx_by_id(id);
Harald Welted193cb32010-05-17 22:58:03 +0200296 if (!ggc)
Harald Welte77289c22010-05-18 14:32:29 +0200297 ggc = sgsn_ggsn_ctx_alloc(id);
Harald Welted193cb32010-05-17 22:58:03 +0200298 return ggc;
299}
300
301/* APN contexts */
302
303#if 0
304struct apn_ctx *apn_ctx_alloc(const char *ap_name)
305{
306 struct apn_ctx *actx;
307
308 actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx);
309 if (!actx)
310 return NULL;
311 actx->name = talloc_strdup(actx, ap_name);
312
313 return actx;
314}
315
316struct apn_ctx *apn_ctx_by_name(const char *name)
317{
318 struct apn_ctx *actx;
319
320 llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
321 if (!strcmp(name, actx->name))
322 return actx;
323 }
324 return NULL;
325}
326
327struct apn_ctx *apn_ctx_find_alloc(const char *name)
328{
329 struct apn_ctx *actx;
330
331 actx = apn_ctx_by_name(name);
332 if (!actx)
333 actx = apn_ctx_alloc(name);
334
335 return actx;
336}
337#endif
Harald Welte6463c072010-05-18 17:04:55 +0200338
339uint32_t sgsn_alloc_ptmsi(void)
340{
341 struct sgsn_mm_ctx *mm;
342 uint32_t ptmsi;
343
344restart:
345 ptmsi = rand();
346 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
347 if (mm->p_tmsi == ptmsi)
348 goto restart;
349 }
350
351 return ptmsi;
352}
Harald Weltea9b473a2010-12-24 21:13:26 +0100353
354static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
355{
356 if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL)
357 gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
358 else {
359 /* FIXME: GPRS paging in case MS is SUSPENDED */
360 LOGP(DGPRS, LOGL_NOTICE, "Hard-dropping PDP ctx due to GGSN "
361 "recovery\n");
362 sgsn_pdp_ctx_free(pdp);
363 }
364}
365
366/* High-level function to be called in case a GGSN has disappeared or
367 * ottherwise lost state (recovery procedure) */
368int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
369{
370 struct sgsn_mm_ctx *mm;
371 int num = 0;
372
373 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
374 struct sgsn_pdp_ctx *pdp;
375 llist_for_each_entry(pdp, &mm->pdp_list, list) {
376 if (pdp->ggsn == ggsn) {
377 drop_one_pdp(pdp);
378 num++;
379 }
380 }
381 }
382
383 return num;
384}