diff options
author | Pau Espin Pedrol <pespin@sysmocom.de> | 2019-08-21 18:49:44 +0200 |
---|---|---|
committer | Pau Espin Pedrol <pespin@sysmocom.de> | 2019-08-28 11:34:11 +0200 |
commit | f5fbb419efc3f5210da1517a889ba828b6716a1a (patch) | |
tree | 749a30a7e033b627223117d240080cd8c3020166 /ggsn/ggsn.c | |
parent | 5d8b2265975f93616d7677bd41edb426839a2698 (diff) |
ggsn: Implement echo req/resp and recovery
This patch is quite big because implementing echo req/resp and recovery
requires having knowledge and managing differentiated state for each GSN
peer attached/connected to osmo-ggsn. This kind of information was not
available in osmo-ggsn nor in libgtp.
So osmo-ggsn is now able to track GSN peers connected to a
ggsn_ctx (associated gsn_t from libgtp) by means of "sgsn_peer" data
structure, and accessible from the ggsn through a list. The instances of
sgsn_peer are currently allocated and destroyed dynamically based on
discovered peer who have at least a pdp context attached to us (we are
not interested in peers without pdp contexts because we don't need to
send echo requests/responses and maintain state in that case).
A new private pointer (pdp_t->priv) data structure struct pdp_priv_t is
added to be able to relate a pdp_t to an sgsn as well as the already
existing pointer to an apn.
An "echo-interval <0-36000>" VTY command is added which allows
configuring time wait between echo requests being sent to each
sgsn_peer. Transmission of echo requests is disabled by default.
Finally, a new "show sgsn" VTY command is introduced, and its output is
also printed during "show ggsn".
Related: OS#4165
Change-Id: Id2c84165dc59dff495106758146a701ca488834f
Diffstat (limited to 'ggsn/ggsn.c')
-rw-r--r-- | ggsn/ggsn.c | 97 |
1 files changed, 87 insertions, 10 deletions
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 94f47e3..89b183f 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -61,6 +61,17 @@ static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what); static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len); +void ggsn_close_one_pdp(struct pdp_t *pdp) +{ + LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n"); + gtp_delete_context_req2(pdp->gsn, pdp, NULL, 1); + /* We have nothing more to do with pdp ctx, free it. Upon cb_delete_context + called during this call we'll clean up ggsn related stuff attached to this + pdp context. After this call, ippool member is cleared so + data is no longer valid and should not be accessed anymore. */ + gtp_freepdp_teardown(pdp->gsn, pdp); +} + static void pool_close_all_pdp(struct ippool_t *pool) { unsigned int i; @@ -77,13 +88,7 @@ static void pool_close_all_pdp(struct ippool_t *pool) pdp = member->peer; if (!pdp) continue; - LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n"); - gtp_delete_context_req2(pdp->gsn, pdp, NULL, 1); - /* We have nothing more to do with pdp ctx, free it. Upon cb_delete_context - called during this call we'll clean up ggsn related stuff attached to this - pdp context. After this call, ippool member is cleared so - data is no longer valid and should not be accessed anymore. */ - gtp_freepdp_teardown(pdp->gsn, pdp); + ggsn_close_one_pdp(pdp); } } @@ -341,7 +346,8 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st static int delete_context(struct pdp_t *pdp) { struct gsn_t *gsn = pdp->gsn; - struct apn_ctx *apn = pdp->priv; + struct pdp_priv_t *pdp_priv = pdp->priv; + struct apn_ctx *apn; struct ippoolm_t *member; int i; @@ -356,6 +362,15 @@ static int delete_context(struct pdp_t *pdp) LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n"); } + if (!pdp_priv) { + LOGPPDP(LOGL_NOTICE, pdp, "Deleting PDP context: without private structure!\n"); + return 0; + } + + /* Remove from SGSN */ + sgsn_peer_remove_pdp_priv(pdp_priv); + + apn = pdp_priv->apn; if (apn && apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) { if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) { LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n", @@ -363,6 +378,8 @@ static int delete_context(struct pdp_t *pdp) } } + talloc_free(pdp_priv); + return 0; } @@ -380,6 +397,36 @@ static bool apn_supports_ipv6(const struct apn_ctx *apn) return false; } +static struct sgsn_peer* ggsn_find_sgsn(struct ggsn_ctx *ggsn, struct in_addr *peer_addr) +{ + struct sgsn_peer *sgsn; + + llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) { + if (memcmp(&sgsn->addr, peer_addr, sizeof(*peer_addr)) == 0) + return sgsn; + } + return NULL; +} + +static struct sgsn_peer* ggsn_find_or_create_sgsn(struct ggsn_ctx *ggsn, struct pdp_t *pdp) +{ + struct sgsn_peer *sgsn; + struct in_addr ia; + + if (gsna2in_addr(&ia, &pdp->gsnrc)) { + LOGPPDP(LOGL_ERROR, pdp, "Failed parsing gsnrc (len=%u) to discover SGSN\n", + pdp->gsnrc.l); + return NULL; + } + + if ((sgsn = ggsn_find_sgsn(ggsn, &ia))) + return sgsn; + + sgsn = sgsn_peer_allocate(ggsn, &ia, pdp->version); + llist_add(&sgsn->entry, &ggsn->sgsn_list); + return sgsn; +} + int create_context_ind(struct pdp_t *pdp) { static char name_buf[256]; @@ -391,6 +438,8 @@ int create_context_ind(struct pdp_t *pdp) struct apn_ctx *apn = NULL; int rc, num_addr, i; char *apn_name; + struct sgsn_peer *sgsn; + struct pdp_priv_t *pdp_priv; apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l); LOGPPDP(LOGL_DEBUG, pdp, "Processing create PDP context request for APN '%s'\n", @@ -492,7 +541,14 @@ int create_context_ind(struct pdp_t *pdp) } pdp->ipif = apn->tun.tun; /* TODO */ - pdp->priv = apn; + + pdp_priv = talloc_zero(ggsn, struct pdp_priv_t); + pdp->priv = pdp_priv; + pdp_priv->lib = pdp; + /* Create sgsn and assign pdp to it */ + sgsn = ggsn_find_or_create_sgsn(ggsn, pdp); + sgsn_peer_add_pdp_priv(sgsn, pdp_priv); + pdp_priv->apn = apn; /* TODO: change trap to send 2 IPs */ if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */ @@ -707,6 +763,7 @@ static void ggsn_gtp_tmr_cb(void *data) /* libgtp callback for confirmations */ static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp) { + struct sgsn_peer *sgsn; int rc = 0; if (cause == EOF) @@ -725,12 +782,31 @@ static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp) Rx path. This code is nevertheless left here in order to ease future developent and avoid possible future memleaks once more scenarios where GGSN sends a DeleteCtxRequest are introduced. */ - if (pdp) + if (pdp) rc = pdp_freepdp(pdp); + break; + case GTP_ECHO_REQ: + sgsn = (struct sgsn_peer *)cbp; + sgsn_peer_echo_resp(sgsn, cause == EOF); + break; } return rc; } +static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery) +{ + struct ggsn_ctx *ggsn = (struct ggsn_ctx *)gsn->priv; + struct sgsn_peer *sgsn; + + sgsn = ggsn_find_sgsn(ggsn, &peer->sin_addr); + if (!sgsn) { + LOGPGGSN(LOGL_NOTICE, ggsn, "Received Recovery IE for unknown SGSN (no PDP contexts active)\n"); + return -EINVAL; + } + + return sgsn_peer_handle_recovery(sgsn, pdp, recovery); +} + /* Start a given GGSN */ int ggsn_start(struct ggsn_ctx *ggsn) { @@ -778,6 +854,7 @@ int ggsn_start(struct ggsn_ctx *ggsn) gtp_set_cb_delete_context(ggsn->gsn, delete_context); gtp_set_cb_create_context_ind(ggsn->gsn, create_context_ind); gtp_set_cb_conf(ggsn->gsn, cb_conf); + gtp_set_cb_recovery3(ggsn->gsn, cb_recovery3); LOGPGGSN(LOGL_NOTICE, ggsn, "Successfully started\n"); ggsn->started = true; |