diff --git a/include/osmocom/sgsn/Makefile.am b/include/osmocom/sgsn/Makefile.am index 64ba16f1e..dbbc0c651 100644 --- a/include/osmocom/sgsn/Makefile.am +++ b/include/osmocom/sgsn/Makefile.am @@ -22,6 +22,7 @@ noinst_HEADERS = \ gprs_subscriber.h \ gprs_utils.h \ gtphub.h \ + gtp_ggsn.h \ gtp_mme.h \ sgsn.h \ sgsn_rim.h \ diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h index dcda5c37a..47983e22e 100644 --- a/include/osmocom/sgsn/gprs_sgsn.h +++ b/include/osmocom/sgsn/gprs_sgsn.h @@ -20,6 +20,7 @@ struct gprs_llc_lle; struct ctrl_handle; struct gprs_subscr; +struct sgsn_ggsn_ctx; enum gsm48_gsm_cause; @@ -366,35 +367,6 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp); void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp); - -struct sgsn_ggsn_ctx { - struct llist_head list; - uint32_t id; - unsigned int gtp_version; - struct in_addr remote_addr; - int remote_restart_ctr; - struct gsn_t *gsn; - struct llist_head pdp_list; /* list of associated pdp ctx (struct sgsn_pdp_ctx*) */ - struct osmo_timer_list echo_timer; - unsigned int echo_interval; -}; -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id); -void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc); -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id); -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr); -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id); -void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx); -int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except); -int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn); -void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp); -void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp); -void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc); - -#define LOGGGSN(ggc, level, fmt, args...) { \ - char _buf[INET_ADDRSTRLEN]; \ - LOGP(DGTP, level, "GGSN(%" PRIu32 ":%s): " fmt, (ggc)->id, inet_ntop(AF_INET, &(ggc)->remote_addr, _buf, sizeof(_buf)), ## args); \ - } while (0) - struct apn_ctx { struct llist_head list; struct sgsn_ggsn_ctx *ggsn; diff --git a/include/osmocom/sgsn/gtp_ggsn.h b/include/osmocom/sgsn/gtp_ggsn.h new file mode 100644 index 000000000..224fa3559 --- /dev/null +++ b/include/osmocom/sgsn/gtp_ggsn.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +struct gsn_t; +struct sgsn_pdp_ctx; + +struct sgsn_ggsn_ctx { + struct llist_head list; + uint32_t id; + unsigned int gtp_version; + struct in_addr remote_addr; + int remote_restart_ctr; + struct gsn_t *gsn; + struct llist_head pdp_list; /* list of associated pdp ctx (struct sgsn_pdp_ctx*) */ + struct osmo_timer_list echo_timer; + unsigned int echo_interval; +}; +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id); +void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc); +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id); +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr); +struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id); +void sgsn_ggsn_ctx_drop_pdp(struct sgsn_pdp_ctx *pctx); +int sgsn_ggsn_ctx_drop_all_pdp_except(struct sgsn_ggsn_ctx *ggsn, struct sgsn_pdp_ctx *except); +int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn); +void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp); +void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp); +void sgsn_ggsn_ctx_check_echo_timer(struct sgsn_ggsn_ctx *ggc); + +#define LOGGGSN(ggc, level, fmt, args...) { \ + char _buf[INET_ADDRSTRLEN]; \ + LOGP(DGTP, level, "GGSN(%" PRIu32 ":%s): " fmt, (ggc)->id, inet_ntop(AF_INET, &(ggc)->remote_addr, _buf, sizeof(_buf)), ## args); \ + } while (0) diff --git a/src/sgsn/Makefile.am b/src/sgsn/Makefile.am index f97d2ba8c..638eaccd6 100644 --- a/src/sgsn/Makefile.am +++ b/src/sgsn/Makefile.am @@ -54,6 +54,7 @@ osmo_sgsn_SOURCES = \ gprs_sndcp_pcomp.c \ gprs_sndcp_vty.c \ gprs_sndcp_xid.c \ + gtp_ggsn.c \ gtp_mme.c \ sgsn_main.c \ sgsn_vty.c \ diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c index ab3a968ae..72d744d09 100644 --- a/src/sgsn/gprs_ranap.c +++ b/src/sgsn/gprs_ranap.c @@ -37,6 +37,7 @@ #include #include #include +#include /* Send RAB activation requests for all PDP contexts */ void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) diff --git a/src/sgsn/gprs_sgsn.c b/src/sgsn/gprs_sgsn.c index 304ecc516..8cfee69b2 100644 --- a/src/sgsn/gprs_sgsn.c +++ b/src/sgsn/gprs_sgsn.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -535,88 +536,6 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) talloc_free(pdp); } -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_ctxts); - - 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_ctxts, 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_ctxts, 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; -} - /* APN contexts */ static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) @@ -764,64 +683,6 @@ failed: return GSM_RESERVED_TMSI; } -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; -} - void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { OSMO_ASSERT(mmctx != NULL); diff --git a/src/sgsn/gprs_sm.c b/src/sgsn/gprs_sm.c index 6c09c4f97..182528d04 100644 --- a/src/sgsn/gprs_sm.c +++ b/src/sgsn/gprs_sm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/sgsn/gtp_ggsn.c b/src/sgsn/gtp_ggsn.c new file mode 100644 index 000000000..0a670e151 --- /dev/null +++ b/src/sgsn/gtp_ggsn.c @@ -0,0 +1,178 @@ +/* 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_ctxts); + + 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_ctxts, 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_ctxts, 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; +} diff --git a/src/sgsn/sgsn_cdr.c b/src/sgsn/sgsn_cdr.c index a50b4dfdd..24d75241e 100644 --- a/src/sgsn/sgsn_cdr.c +++ b/src/sgsn/sgsn_cdr.c @@ -27,6 +27,7 @@ #include #include +#include #include #include diff --git a/src/sgsn/sgsn_libgtp.c b/src/sgsn/sgsn_libgtp.c index a2d0c7a63..58e912d45 100644 --- a/src/sgsn/sgsn_libgtp.c +++ b/src/sgsn/sgsn_libgtp.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c index 7a94f79c1..18951f8e2 100644 --- a/src/sgsn/sgsn_vty.c +++ b/src/sgsn/sgsn_vty.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/sgsn/Makefile.am b/tests/sgsn/Makefile.am index 6d8fb7627..e6cb71b10 100644 --- a/tests/sgsn/Makefile.am +++ b/tests/sgsn/Makefile.am @@ -56,6 +56,7 @@ sgsn_test_LDADD = \ $(top_builddir)/src/sgsn/gprs_gmm_fsm.o \ $(top_builddir)/src/sgsn/gprs_mm_state_gb_fsm.o \ $(top_builddir)/src/sgsn/gprs_sgsn.o \ + $(top_builddir)/src/sgsn/gtp_ggsn.o \ $(top_builddir)/src/sgsn/gtp_mme.o \ $(top_builddir)/src/sgsn/sgsn_vty.o \ $(top_builddir)/src/sgsn/sgsn_libgtp.o \ diff --git a/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c index 91a3dcf56..44218de05 100644 --- a/tests/sgsn/sgsn_test.c +++ b/tests/sgsn/sgsn_test.c @@ -38,6 +38,7 @@ #include #include #include +#include #include