blob: 0a670e1514a05f6413a31851fb66e11cdf07b7e9 [file] [log] [blame]
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +01001/* GGSN context (peer) */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23#include <stdint.h>
24
25#include <osmocom/core/linuxlist.h>
26#include <osmocom/core/talloc.h>
27#include <osmocom/core/timer.h>
28#include <osmocom/core/rate_ctr.h>
29#include <osmocom/core/stats.h>
30
31#include <osmocom/sgsn/gtp_ggsn.h>
32#include <osmocom/sgsn/sgsn.h>
33#include <osmocom/sgsn/debug.h>
34#include <osmocom/sgsn/gprs_gmm_fsm.h>
35#include <osmocom/sgsn/gprs_sm.h>
36
37extern void *tall_sgsn_ctx;
38
39void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc)
40{
41 bool pending = osmo_timer_pending(&ggc->echo_timer);
42
43 /* Only enable if allowed by policy and at least 1 pdp ctx exists against ggsn */
44 if (!llist_empty(&ggc->pdp_list) && ggc->echo_interval) {
45 if (!pending)
46 osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0);
47 } else {
48 if (pending)
49 osmo_timer_del(&ggc->echo_timer);
50 }
51}
52
53/* GGSN contexts */
54static void echo_timer_cb(void *data)
55{
56 struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data;
57 sgsn_ggsn_echo_req(ggc);
58 osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0);
59}
60
61struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
62{
63 struct sgsn_ggsn_ctx *ggc;
64
65 ggc = talloc_zero(tall_sgsn_ctx, struct sgsn_ggsn_ctx);
66 if (!ggc)
67 return NULL;
68
69 ggc->id = id;
70 ggc->gtp_version = 1;
71 ggc->remote_restart_ctr = -1;
72 /* if we are called from config file parse, this gsn doesn't exist yet */
73 ggc->gsn = sgsn->gsn;
74 INIT_LLIST_HEAD(&ggc->pdp_list);
75 osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc);
76 llist_add(&ggc->list, &sgsn_ggsn_ctxts);
77
78 return ggc;
79}
80
81void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc)
82{
83 OSMO_ASSERT(llist_empty(&ggc->pdp_list));
84 llist_del(&ggc->list);
85 talloc_free(ggc);
86}
87
88struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
89{
90 struct sgsn_ggsn_ctx *ggc;
91
92 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
93 if (id == ggc->id)
94 return ggc;
95 }
96 return NULL;
97}
98
99struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
100{
101 struct sgsn_ggsn_ctx *ggc;
102
103 llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
104 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
105 return ggc;
106 }
107 return NULL;
108}
109
110
111struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
112{
113 struct sgsn_ggsn_ctx *ggc;
114
115 ggc = sgsn_ggsn_ctx_by_id(id);
116 if (!ggc)
117 ggc = sgsn_ggsn_ctx_alloc(id);
118 return ggc;
119}
120
121void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx)
122{
123 /* the MM context can be deleted while the GGSN is not reachable or
124 * if has been crashed. */
125 if (pctx->mm && pctx->mm->gmm_fsm->state == ST_GMM_REGISTERED_NORMAL) {
126 gsm48_tx_gsm_deact_pdp_req(pctx, GSM_CAUSE_NET_FAIL, true);
127 sgsn_ggsn_ctx_remove_pdp(pctx->ggsn, pctx);
128 } else {
129 /* FIXME: GPRS paging in case MS is SUSPENDED */
130 LOGPDPCTXP(LOGL_NOTICE, pctx, "Hard-dropping PDP ctx due to GGSN "
131 "recovery\n");
132 /* FIXME: how to tell this to libgtp? */
133 sgsn_pdp_ctx_free(pctx);
134 }
135}
136
137/* High-level function to be called in case a GGSN has disappeared or
138 * otherwise lost state (recovery procedure). It will detach all related pdp ctx
139 * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can
140 * be kept alive to allow handling later message which contained the Recovery IE. */
141int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except)
142{
143 int num = 0;
144
145 struct sgsn_pdp_ctx *pdp, *pdp2;
146 llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) {
147 if (pdp == except)
148 continue;
149 sgsn_ggsn_ctx_drop_pdp(pdp);
150 num++;
151 }
152
153 return num;
154}
155
156int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn)
157{
158 return sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, NULL);
159}
160
161void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp)
162{
163 llist_add(&pdp->ggsn_list, &ggc->pdp_list);
164 sgsn_ggsn_ctx_check_echo_timer(ggc);
165}
166
167void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp)
168{
169 llist_del(&pdp->ggsn_list);
170 sgsn_ggsn_ctx_check_echo_timer(ggc);
171 if (pdp->destroy_ggsn)
172 sgsn_ggsn_ctx_free(pdp->ggsn);
173 pdp->ggsn = NULL;
174 /* Drop references to libgtp since the conn is down */
175 if (pdp->lib)
176 pdp_freepdp(pdp->lib);
177 pdp->lib = NULL;
178}