blob: 0d8fe50ef8ec7e7af61f921dc3c0122c42822bb2 [file] [log] [blame]
Harald Welte75bb8202010-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 Welte0e3e88e2011-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 Welte75bb8202010-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 Welte0e3e88e2011-01-01 15:25:50 +010015 * GNU Affero General Public License for more details.
Harald Welte75bb8202010-03-14 15:45:01 +080016 *
Harald Welte0e3e88e2011-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 Welte75bb8202010-03-14 15:45:01 +080019 *
20 */
21
Harald Welted85d9a92010-05-02 11:26:34 +020022#include <stdint.h>
Harald Welte75bb8202010-03-14 15:45:01 +080023
Pablo Neira Ayusodd5fff42011-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 Weltecfb6b282012-06-16 14:59:56 +080028#include <osmocom/gprs/gprs_ns.h>
29#include <osmocom/gprs/gprs_bssgp.h>
30
Harald Welte75bb8202010-03-14 15:45:01 +080031#include <openbsc/gsm_subscriber.h>
Harald Weltef67a5f92010-04-26 19:18:54 +020032#include <openbsc/debug.h>
Harald Welte75bb8202010-03-14 15:45:01 +080033#include <openbsc/gprs_sgsn.h>
Harald Welteebe8a6d2010-05-18 19:58:38 +020034#include <openbsc/sgsn.h>
Harald Welte94ecef32010-12-24 21:13:26 +010035#include <openbsc/gsm_04_08_gprs.h>
36#include <openbsc/gprs_gmm.h>
Harald Welteebe8a6d2010-05-18 19:58:38 +020037
38extern struct sgsn_instance *sgsn;
Harald Welte75bb8202010-03-14 15:45:01 +080039
Harald Weltec1f6bfe2010-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 Welte75bb8202010-03-14 15:45:01 +080044
Harald Welte8a035af2010-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 Welte0fe506b2010-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 Welte75bb8202010-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 Welte51f78ee2010-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 Welte75bb8202010-03-14 15:45:01 +080093/* look-up a SGSN MM context based on TLLI + RAI */
Harald Welted85d9a92010-05-02 11:26:34 +020094struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
Harald Welte75bb8202010-03-14 15:45:01 +080095 const struct gprs_ra_id *raid)
96{
97 struct sgsn_mm_ctx *ctx;
Harald Welteebe8a6d2010-05-18 19:58:38 +020098 int tlli_type;
Harald Welte75bb8202010-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 Welteebe8a6d2010-05-18 19:58:38 +0200105
106 tlli_type = gprs_tlli_type(tlli);
Harald Welte51f78ee2010-12-23 23:34:43 +0100107 switch (tlli_type) {
108 case TLLI_LOCAL:
Harald Welteebe8a6d2010-05-18 19:58:38 +0200109 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
Harald Welte7e01d8e2010-05-31 20:23:38 +0200110 if ((ctx->p_tmsi | 0xC0000000) == tlli ||
111 (ctx->p_tmsi_old && (ctx->p_tmsi_old | 0xC0000000) == tlli)) {
Harald Welteebe8a6d2010-05-18 19:58:38 +0200112 ctx->tlli = tlli;
113 return ctx;
114 }
115 }
Harald Welte51f78ee2010-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 Welteebe8a6d2010-05-18 19:58:38 +0200126 }
127
Harald Welte75bb8202010-03-14 15:45:01 +0800128 return NULL;
129}
130
Harald Welted85d9a92010-05-02 11:26:34 +0200131struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
Harald Welte75bb8202010-03-14 15:45:01 +0800132{
133 struct sgsn_mm_ctx *ctx;
134
135 llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
Harald Welte7e01d8e2010-05-31 20:23:38 +0200136 if (p_tmsi == ctx->p_tmsi ||
137 (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi))
Harald Welte75bb8202010-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 Welted85d9a92010-05-02 11:26:34 +0200156struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli,
Harald Welte75bb8202010-03-14 15:45:01 +0800157 const struct gprs_ra_id *raid)
158{
Harald Welte8f77f192010-05-17 00:44:57 +0200159 struct sgsn_mm_ctx *ctx;
Harald Welte75bb8202010-03-14 15:45:01 +0800160
Harald Welte8f77f192010-05-17 00:44:57 +0200161 ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
Harald Welte75bb8202010-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 Welte8a035af2010-05-18 10:57:45 +0200168 ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
Harald Welteded83ec2010-05-18 12:44:45 +0200169 INIT_LLIST_HEAD(&ctx->pdp_list);
Harald Welte75bb8202010-03-14 15:45:01 +0800170
171 llist_add(&ctx->list, &sgsn_mm_ctxts);
172
173 return ctx;
174}
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200175
Harald Welted8765b92012-07-14 12:04:04 +0200176/* this is a hard _free_ function, it doesn't clean up the PDP contexts
177 * in libgtp! */
Harald Weltec6e196f2010-12-24 23:07:18 +0100178void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
179{
180 struct sgsn_pdp_ctx *pdp, *pdp2;
181
182 /* Unlink from global list of MM contexts */
183 llist_del(&mm->list);
184
185 /* Free all PDP contexts */
186 llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
187 sgsn_pdp_ctx_free(pdp);
188
189 rate_ctr_group_free(mm->ctrg);
190
191 talloc_free(mm);
192}
Harald Welteeb471c92010-05-18 14:32:29 +0200193
Harald Welteff70e0e2010-06-03 06:37:26 +0200194/* look up PDP context by MM context and NSAPI */
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200195struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
196 uint8_t nsapi)
197{
198 struct sgsn_pdp_ctx *pdp;
199
200 llist_for_each_entry(pdp, &mm->pdp_list, list) {
201 if (pdp->nsapi == nsapi)
202 return pdp;
203 }
204 return NULL;
205}
206
Harald Welteff70e0e2010-06-03 06:37:26 +0200207/* look up PDP context by MM context and transaction ID */
Harald Welteeb471c92010-05-18 14:32:29 +0200208struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
209 uint8_t tid)
210{
211 struct sgsn_pdp_ctx *pdp;
212
213 llist_for_each_entry(pdp, &mm->pdp_list, list) {
214 if (pdp->ti == tid)
215 return pdp;
216 }
217 return NULL;
218}
219
Harald Welted8765b92012-07-14 12:04:04 +0200220/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200221struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
222 uint8_t nsapi)
223{
224 struct sgsn_pdp_ctx *pdp;
225
226 pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
227 if (pdp)
228 return NULL;
229
230 pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
231 if (!pdp)
232 return NULL;
233
234 pdp->mm = mm;
235 pdp->nsapi = nsapi;
Harald Welte0fe506b2010-06-10 00:20:12 +0200236 pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200237 llist_add(&pdp->list, &mm->pdp_list);
238 llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
239
240 return pdp;
241}
242
Harald Welted8765b92012-07-14 12:04:04 +0200243/* you probably want to call sgsn_delete_pdp_ctx() instead */
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200244void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
245{
Harald Welte17a40a62010-06-28 18:57:21 +0200246 rate_ctr_group_free(pdp->ctrg);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200247 llist_del(&pdp->list);
248 llist_del(&pdp->g_list);
249 talloc_free(pdp);
250}
251
252/* GGSN contexts */
253
Harald Welteeb471c92010-05-18 14:32:29 +0200254struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200255{
Harald Welteeb471c92010-05-18 14:32:29 +0200256 struct sgsn_ggsn_ctx *ggc;
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200257
Harald Welteeb471c92010-05-18 14:32:29 +0200258 ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200259 if (!ggc)
260 return NULL;
261
262 ggc->id = id;
263 ggc->gtp_version = 1;
Harald Welte94ecef32010-12-24 21:13:26 +0100264 ggc->remote_restart_ctr = -1;
Harald Welteebe8a6d2010-05-18 19:58:38 +0200265 /* if we are called from config file parse, this gsn doesn't exist yet */
266 ggc->gsn = sgsn->gsn;
Harald Weltee58eee52010-05-18 18:39:00 +0200267 llist_add(&ggc->list, &sgsn_ggsn_ctxts);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200268
269 return ggc;
270}
271
Harald Welteeb471c92010-05-18 14:32:29 +0200272struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200273{
Harald Welteeb471c92010-05-18 14:32:29 +0200274 struct sgsn_ggsn_ctx *ggc;
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200275
276 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
277 if (id == ggc->id)
278 return ggc;
279 }
280 return NULL;
281}
282
Harald Welte94ecef32010-12-24 21:13:26 +0100283struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
284{
285 struct sgsn_ggsn_ctx *ggc;
286
287 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
288 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
289 return ggc;
290 }
291 return NULL;
292}
293
294
Harald Welteeb471c92010-05-18 14:32:29 +0200295struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200296{
Harald Welteeb471c92010-05-18 14:32:29 +0200297 struct sgsn_ggsn_ctx *ggc;
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200298
Harald Welteeb471c92010-05-18 14:32:29 +0200299 ggc = sgsn_ggsn_ctx_by_id(id);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200300 if (!ggc)
Harald Welteeb471c92010-05-18 14:32:29 +0200301 ggc = sgsn_ggsn_ctx_alloc(id);
Harald Weltec1f6bfe2010-05-17 22:58:03 +0200302 return ggc;
303}
304
305/* APN contexts */
306
307#if 0
308struct apn_ctx *apn_ctx_alloc(const char *ap_name)
309{
310 struct apn_ctx *actx;
311
312 actx = talloc_zero(talloc_bsc_ctx, struct apn_ctx);
313 if (!actx)
314 return NULL;
315 actx->name = talloc_strdup(actx, ap_name);
316
317 return actx;
318}
319
320struct apn_ctx *apn_ctx_by_name(const char *name)
321{
322 struct apn_ctx *actx;
323
324 llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
325 if (!strcmp(name, actx->name))
326 return actx;
327 }
328 return NULL;
329}
330
331struct apn_ctx *apn_ctx_find_alloc(const char *name)
332{
333 struct apn_ctx *actx;
334
335 actx = apn_ctx_by_name(name);
336 if (!actx)
337 actx = apn_ctx_alloc(name);
338
339 return actx;
340}
341#endif
Harald Welte64df8ed2010-05-18 17:04:55 +0200342
343uint32_t sgsn_alloc_ptmsi(void)
344{
345 struct sgsn_mm_ctx *mm;
346 uint32_t ptmsi;
347
348restart:
349 ptmsi = rand();
350 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
351 if (mm->p_tmsi == ptmsi)
352 goto restart;
353 }
354
355 return ptmsi;
356}
Harald Welte94ecef32010-12-24 21:13:26 +0100357
358static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
359{
360 if (pdp->mm->mm_state == GMM_REGISTERED_NORMAL)
361 gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
362 else {
363 /* FIXME: GPRS paging in case MS is SUSPENDED */
364 LOGP(DGPRS, LOGL_NOTICE, "Hard-dropping PDP ctx due to GGSN "
365 "recovery\n");
Harald Welted8765b92012-07-14 12:04:04 +0200366 /* FIXME: how to tell this to libgtp? */
Harald Welte94ecef32010-12-24 21:13:26 +0100367 sgsn_pdp_ctx_free(pdp);
368 }
369}
370
371/* High-level function to be called in case a GGSN has disappeared or
372 * ottherwise lost state (recovery procedure) */
373int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
374{
375 struct sgsn_mm_ctx *mm;
376 int num = 0;
377
378 llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
379 struct sgsn_pdp_ctx *pdp;
380 llist_for_each_entry(pdp, &mm->pdp_list, list) {
381 if (pdp->ggsn == ggsn) {
382 drop_one_pdp(pdp);
383 num++;
384 }
385 }
386 }
387
388 return num;
389}