Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 1 | /* 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 Pedrol | 8ec269a | 2023-01-05 19:19:32 +0100 | [diff] [blame] | 32 | #include <osmocom/sgsn/gtp.h> |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 33 | #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 Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 38 | void 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 */ |
| 53 | static 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 Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 60 | struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(struct sgsn_instance *sgsn, uint32_t id) |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 61 | { |
| 62 | struct sgsn_ggsn_ctx *ggc; |
| 63 | |
Pau Espin Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 64 | ggc = talloc_zero(sgsn, struct sgsn_ggsn_ctx); |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 65 | 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 Pedrol | 44bde6b | 2023-01-05 17:23:26 +0100 | [diff] [blame] | 75 | llist_add(&ggc->list, &sgsn->ggsn_list); |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 76 | |
| 77 | return ggc; |
| 78 | } |
| 79 | |
| 80 | void 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 Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 87 | struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(struct sgsn_instance *sgsn, uint32_t id) |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 88 | { |
| 89 | struct sgsn_ggsn_ctx *ggc; |
| 90 | |
Pau Espin Pedrol | 44bde6b | 2023-01-05 17:23:26 +0100 | [diff] [blame] | 91 | llist_for_each_entry(ggc, &sgsn->ggsn_list, list) { |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 92 | if (id == ggc->id) |
| 93 | return ggc; |
| 94 | } |
| 95 | return NULL; |
| 96 | } |
| 97 | |
Pau Espin Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 98 | struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct sgsn_instance *sgsn, struct in_addr *addr) |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 99 | { |
| 100 | struct sgsn_ggsn_ctx *ggc; |
| 101 | |
Pau Espin Pedrol | 44bde6b | 2023-01-05 17:23:26 +0100 | [diff] [blame] | 102 | llist_for_each_entry(ggc, &sgsn->ggsn_list, list) { |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 103 | if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr))) |
| 104 | return ggc; |
| 105 | } |
| 106 | return NULL; |
| 107 | } |
| 108 | |
| 109 | |
Pau Espin Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 110 | struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(struct sgsn_instance *sgsn, uint32_t id) |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 111 | { |
| 112 | struct sgsn_ggsn_ctx *ggc; |
| 113 | |
Pau Espin Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 114 | ggc = sgsn_ggsn_ctx_by_id(sgsn, id); |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 115 | if (!ggc) |
Pau Espin Pedrol | 6956987 | 2023-01-05 19:39:01 +0100 | [diff] [blame] | 116 | ggc = sgsn_ggsn_ctx_alloc(sgsn, id); |
Pau Espin Pedrol | 5f4736a | 2023-01-04 21:30:28 +0100 | [diff] [blame] | 117 | return ggc; |
| 118 | } |
| 119 | |
| 120 | void 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. */ |
| 140 | int 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 | |
| 155 | int 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 | |
| 160 | void 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 | |
| 166 | void 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 | } |