/* GGSN context (peer) */ /* (C) 2009 by Harald Welte * (C) 2023 by sysmocom - s.m.f.c. GmbH * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include extern void *tall_sgsn_ctx; void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc) { bool pending = osmo_timer_pending(&ggc->echo_timer); /* Only enable if allowed by policy and at least 1 pdp ctx exists against ggsn */ if (!llist_empty(&ggc->pdp_list) && ggc->echo_interval) { if (!pending) osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); } else { if (pending) osmo_timer_del(&ggc->echo_timer); } } /* GGSN contexts */ static void echo_timer_cb(void *data) { struct sgsn_ggsn_ctx *ggc = (struct sgsn_ggsn_ctx *) data; sgsn_ggsn_echo_req(ggc); osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id) { struct sgsn_ggsn_ctx *ggc; ggc = talloc_zero(tall_sgsn_ctx, struct sgsn_ggsn_ctx); if (!ggc) return NULL; ggc->id = id; ggc->gtp_version = 1; ggc->remote_restart_ctr = -1; /* if we are called from config file parse, this gsn doesn't exist yet */ ggc->gsn = sgsn->gsn; INIT_LLIST_HEAD(&ggc->pdp_list); osmo_timer_setup(&ggc->echo_timer, echo_timer_cb, ggc); llist_add(&ggc->list, &sgsn->ggsn_list); return ggc; } void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc) { OSMO_ASSERT(llist_empty(&ggc->pdp_list)); llist_del(&ggc->list); talloc_free(ggc); } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id) { struct sgsn_ggsn_ctx *ggc; llist_for_each_entry(ggc, &sgsn->ggsn_list, list) { if (id == ggc->id) return ggc; } return NULL; } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr) { struct sgsn_ggsn_ctx *ggc; llist_for_each_entry(ggc, &sgsn->ggsn_list, list) { if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr))) return ggc; } return NULL; } struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id) { struct sgsn_ggsn_ctx *ggc; ggc = sgsn_ggsn_ctx_by_id(id); if (!ggc) ggc = sgsn_ggsn_ctx_alloc(id); return ggc; } void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx) { /* the MM context can be deleted while the GGSN is not reachable or * if has been crashed. */ if (pctx->mm && pctx->mm->gmm_fsm->state == ST_GMM_REGISTERED_NORMAL) { gsm48_tx_gsm_deact_pdp_req(pctx, GSM_CAUSE_NET_FAIL, true); sgsn_ggsn_ctx_remove_pdp(pctx->ggsn, pctx); } else { /* FIXME: GPRS paging in case MS is SUSPENDED */ LOGPDPCTXP(LOGL_NOTICE, pctx, "Hard-dropping PDP ctx due to GGSN " "recovery\n"); /* FIXME: how to tell this to libgtp? */ sgsn_pdp_ctx_free(pctx); } } /* High-level function to be called in case a GGSN has disappeared or * otherwise lost state (recovery procedure). It will detach all related pdp ctx * from a ggsn and communicate deact to MS. Optionally (!NULL), one pdp ctx can * be kept alive to allow handling later message which contained the Recovery IE. */ int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except) { int num = 0; struct sgsn_pdp_ctx *pdp, *pdp2; llist_for_each_entry_safe(pdp, pdp2, &ggsn->pdp_list, ggsn_list) { if (pdp == except) continue; sgsn_ggsn_ctx_drop_pdp(pdp); num++; } return num; } int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn) { return sgsn_ggsn_ctx_drop_all_pdp_except(ggsn, NULL); } void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) { llist_add(&pdp->ggsn_list, &ggc->pdp_list); sgsn_ggsn_ctx_check_echo_timer(ggc); } void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) { llist_del(&pdp->ggsn_list); sgsn_ggsn_ctx_check_echo_timer(ggc); if (pdp->destroy_ggsn) sgsn_ggsn_ctx_free(pdp->ggsn); pdp->ggsn = NULL; /* Drop references to libgtp since the conn is down */ if (pdp->lib) pdp_freepdp(pdp->lib); pdp->lib = NULL; }