From fa1201004f85b6e44d746d6044d5a43d04634fa4 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Mon, 9 Jul 2018 20:37:47 +0200 Subject: osmo-sgsn: ping GGSN periodically and check for restart counter Before this commit, echo req/rsp logic was implemented in libgtp but never used in osmo-sgsn. This commit adds a timer which periodically sends a GTP ECHO Request to every GGSN if there's at least one pdpd context associated with it. This way by checking the restart counter in the ECHO Reply it can be known if the GGSN was restarted. In this case, logic already present in osmo-sgsn will terminate all pdp contexts associated with that GGSN. Change-Id: I9d714726785407859f26bbef052cd0efc28e8dae --- include/osmocom/sgsn/gprs_sgsn.h | 4 ++++ include/osmocom/sgsn/sgsn.h | 1 + src/gprs/gprs_sgsn.c | 25 +++++++++++++++++++++++-- src/gprs/sgsn_libgtp.c | 5 +++++ src/gprs/sgsn_vty.c | 25 +++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h index 33b12dc25..5f3088961 100644 --- a/include/osmocom/sgsn/gprs_sgsn.h +++ b/include/osmocom/sgsn/gprs_sgsn.h @@ -353,6 +353,8 @@ struct sgsn_ggsn_ctx { 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; + int echo_interval; }; struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id); void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc); @@ -360,6 +362,8 @@ 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); 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); struct apn_ctx { struct llist_head list; diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h index 829a8fc6e..dae9d12aa 100644 --- a/include/osmocom/sgsn/sgsn.h +++ b/include/osmocom/sgsn/sgsn.h @@ -171,6 +171,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, struct tlv_parsed *tp); int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx); void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen); +void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc); /* gprs_sndcp.c */ diff --git a/src/gprs/gprs_sgsn.c b/src/gprs/gprs_sgsn.c index a58e504c5..55ce09616 100644 --- a/src/gprs/gprs_sgsn.c +++ b/src/gprs/gprs_sgsn.c @@ -411,7 +411,7 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, return NULL; } llist_add(&pdp->list, &mm->pdp_list); - llist_add(&pdp->ggsn_list, &ggsn->pdp_list); + sgsn_ggsn_ctx_add_pdp(pdp->ggsn, pdp); llist_add(&pdp->g_list, &sgsn_pdp_ctxts); return pdp; @@ -466,7 +466,7 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) if (pdp->mm) llist_del(&pdp->list); if (pdp->ggsn) - llist_del(&pdp->ggsn_list); + sgsn_ggsn_ctx_remove_pdp(pdp->ggsn, pdp); llist_del(&pdp->g_list); /* _if_ we still have a library handle, at least set it to NULL @@ -487,6 +487,12 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) } /* 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) { @@ -499,9 +505,11 @@ struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id) ggc->id = id; ggc->gtp_version = 1; ggc->remote_restart_ctr = -1; + ggc->echo_interval = -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; @@ -722,6 +730,19 @@ int sgsn_ggsn_ctx_drop_all_pdp(struct sgsn_ggsn_ctx *ggsn) return num; } +void sgsn_ggsn_ctx_add_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) +{ + if (llist_empty(&ggc->pdp_list) && ggc->echo_interval > 0) + osmo_timer_schedule(&ggc->echo_timer, ggc->echo_interval, 0); + llist_add(&pdp->ggsn_list, &ggc->pdp_list); +} +void sgsn_ggsn_ctx_remove_pdp(struct sgsn_ggsn_ctx *ggc, struct sgsn_pdp_ctx *pdp) +{ + llist_del(&pdp->ggsn_list); + if (llist_empty(&ggc->pdp_list) && osmo_timer_pending(&ggc->echo_timer)) + osmo_timer_del(&ggc->echo_timer); +} + void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) { OSMO_ASSERT(mmctx != NULL); diff --git a/src/gprs/sgsn_libgtp.c b/src/gprs/sgsn_libgtp.c index 2da8c4a74..88cea5d4a 100644 --- a/src/gprs/sgsn_libgtp.c +++ b/src/gprs/sgsn_libgtp.c @@ -460,6 +460,11 @@ void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen) gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); } +void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc) +{ + gtp_echo_req(ggc->gsn, ggc->gtp_version, NULL, &ggc->remote_addr); +} + #ifdef BUILD_IU /* Callback for RAB assignment response */ int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) diff --git a/src/gprs/sgsn_vty.c b/src/gprs/sgsn_vty.c index 07d4293db..9f9332c32 100644 --- a/src/gprs/sgsn_vty.c +++ b/src/gprs/sgsn_vty.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -181,6 +182,9 @@ static int config_write_sgsn(struct vty *vty) inet_ntoa(gctx->remote_addr), VTY_NEWLINE); vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id, gctx->gtp_version, VTY_NEWLINE); + if (gctx->echo_interval != -1) + vty_out(vty, " ggsn %u echo-interval %"PRId32"%s", + gctx->id, gctx->echo_interval, VTY_NEWLINE); } if (sgsn->cfg.dynamic_lookup) @@ -356,6 +360,26 @@ DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd, return CMD_SUCCESS; } +/* Seee 3GPP TS 29.060 section 7.2.1 */ +DEFUN(cfg_ggsn_echo_interval, cfg_ggsn_echo_interval_cmd, + "ggsn <0-255> echo-interval <1-36000>", + GGSN_STR "GGSN Number\n" + "Send an echo request to this static GGSN every interval.\n" + "Interval between echo requests in seconds.\n") +{ + uint32_t id = atoi(argv[0]); + struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); + + ggc->echo_interval = atoi(argv[1]); + + if (ggc->echo_interval < 60) + vty_out(vty, "%% 3GPP TS 29.060 section states inteval should " \ + "not be lower than 60 seconds, use this value for " \ + "testing purposes only!%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + DEFUN(cfg_ggsn_dynamic_lookup, cfg_ggsn_dynamic_lookup_cmd, "ggsn dynamic", GGSN_STR @@ -1277,6 +1301,7 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd); //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd); install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd); + install_element(SGSN_NODE, &cfg_ggsn_echo_interval_cmd); install_element(SGSN_NODE, &cfg_imsi_acl_cmd); install_element(SGSN_NODE, &cfg_auth_policy_cmd); install_element(SGSN_NODE, &cfg_encrypt_cmd); -- cgit v1.2.3