blob: b43fb25cd29c36c863fbbd2c9f433b02b2c25444 [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>
Pau Espin Pedrol8ec269a2023-01-05 19:19:32 +010032#include <osmocom/sgsn/gtp.h>
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010033#include <osmocom/sgsn/sgsn.h>
34#include <osmocom/sgsn/debug.h>
35#include <osmocom/sgsn/gprs_gmm_fsm.h>
36#include <osmocom/sgsn/gprs_sm.h>
37
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010038void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc)
39{
40 bool pending = osmo_timer_pending(&ggc->echo_timer);
41
42 /* Only enable if allowed by policy and at least 1 pdp ctx exists against ggsn */
43 if (!llist_empty(&ggc->pdp_list) && ggc->echo_interval) {
44 if (!pending)
45 osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0);
46 } else {
47 if (pending)
48 osmo_timer_del(&ggc->echo_timer);
49 }
50}
51
52/* GGSN contexts */
53static void echo_timer_cb(void *data)
54{
55 struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data;
56 sgsn_ggsn_echo_req(ggc);
57 osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0);
58}
59
Pau Espin Pedrol69569872023-01-05 19:39:01 +010060struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(struct sgsn_instance *sgsn, uint32_t id)
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010061{
62 struct sgsn_ggsn_ctx *ggc;
63
Pau Espin Pedrol69569872023-01-05 19:39:01 +010064 ggc = talloc_zero(sgsn, struct sgsn_ggsn_ctx);
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010065 if (!ggc)
66 return NULL;
67
68 ggc->id = id;
69 ggc->gtp_version = 1;
70 ggc->remote_restart_ctr = -1;
71 /* if we are called from config file parse, this gsn doesn't exist yet */
72 ggc->gsn = sgsn->gsn;
73 INIT_LLIST_HEAD(&ggc->pdp_list);
74 osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc);
Pau Espin Pedrol44bde6b2023-01-05 17:23:26 +010075 llist_add(&ggc->list, &sgsn->ggsn_list);
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010076
77 return ggc;
78}
79
80void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc)
81{
82 OSMO_ASSERT(llist_empty(&ggc->pdp_list));
83 llist_del(&ggc->list);
84 talloc_free(ggc);
85}
86
Pau Espin Pedrol69569872023-01-05 19:39:01 +010087struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(struct sgsn_instance *sgsn, uint32_t id)
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010088{
89 struct sgsn_ggsn_ctx *ggc;
90
Pau Espin Pedrol44bde6b2023-01-05 17:23:26 +010091 llist_for_each_entry(ggc, &sgsn->ggsn_list, list) {
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010092 if (id == ggc->id)
93 return ggc;
94 }
95 return NULL;
96}
97
Pau Espin Pedrol69569872023-01-05 19:39:01 +010098struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct sgsn_instance *sgsn, struct in_addr *addr)
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +010099{
100 struct sgsn_ggsn_ctx *ggc;
101
Pau Espin Pedrol44bde6b2023-01-05 17:23:26 +0100102 llist_for_each_entry(ggc, &sgsn->ggsn_list, list) {
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +0100103 if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
104 return ggc;
105 }
106 return NULL;
107}
108
109
Pau Espin Pedrol69569872023-01-05 19:39:01 +0100110struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(struct sgsn_instance *sgsn, uint32_t id)
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +0100111{
112 struct sgsn_ggsn_ctx *ggc;
113
Pau Espin Pedrol69569872023-01-05 19:39:01 +0100114 ggc = sgsn_ggsn_ctx_by_id(sgsn, id);
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +0100115 if (!ggc)
Pau Espin Pedrol69569872023-01-05 19:39:01 +0100116 ggc = sgsn_ggsn_ctx_alloc(sgsn, id);
Pau Espin Pedrol5f4736a2023-01-04 21:30:28 +0100117 return ggc;
118}
119
120void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx)
121{
122 /* the MM context can be deleted while the GGSN is not reachable or
123 * if has been crashed. */
124 if (pctx->mm && pctx->mm->gmm_fsm->state == ST_GMM_REGISTERED_NORMAL) {
125 gsm48_tx_gsm_deact_pdp_req(pctx, GSM_CAUSE_NET_FAIL, true);
126 sgsn_ggsn_ctx_remove_pdp(pctx->ggsn, pctx);
127 } else {
128 /* FIXME: GPRS paging in case MS is SUSPENDED */
129 LOGPDPCTXP(LOGL_NOTICE, pctx, "Hard-dropping PDP ctx due to GGSN "
130 "recovery\n");
131 /* FIXME: how to tell this to libgtp? */
132 sgsn_pdp_ctx_free(pctx);
133 }
134}
135
136/* High-level function to be called in case a GGSN has disappeared or
137 * otherwise lost state (recovery procedure). It will detach all related pdp ctx
138 * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can
139 * be kept alive to allow handling later message which contained the Recovery IE. */
140int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except)
141{
142 int num = 0;
143
144 struct sgsn_pdp_ctx *pdp, *pdp2;
145 llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) {
146 if (pdp == except)
147 continue;
148 sgsn_ggsn_ctx_drop_pdp(pdp);
149 num++;
150 }
151
152 return num;
153}
154
155int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn)
156{
157 return sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, NULL);
158}
159
160void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp)
161{
162 llist_add(&pdp->ggsn_list, &ggc->pdp_list);
163 sgsn_ggsn_ctx_check_echo_timer(ggc);
164}
165
166void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp)
167{
168 llist_del(&pdp->ggsn_list);
169 sgsn_ggsn_ctx_check_echo_timer(ggc);
170 if (pdp->destroy_ggsn)
171 sgsn_ggsn_ctx_free(pdp->ggsn);
172 pdp->ggsn = NULL;
173 /* Drop references to libgtp since the conn is down */
174 if (pdp->lib)
175 pdp_freepdp(pdp->lib);
176 pdp->lib = NULL;
177}