diff options
author | Daniel Willmann <dwillmann@sysmocom.de> | 2018-08-31 18:03:16 +0200 |
---|---|---|
committer | Daniel Willmann <dwillmann@sysmocom.de> | 2018-08-31 18:03:16 +0200 |
commit | 8e97fccc346d2bf981968f00a003d93baec9a4c7 (patch) | |
tree | c264a4dbd9fbc9d7e15b21c3dd0627be295b352f | |
parent | 835496d7dd68988dc87257b28c83230be85435d8 (diff) | |
parent | ee44b82b967929eaf8867d967a22428972b58d0a (diff) |
Merge remote-tracking branch 'origin/master' into daniel/onwaves
-rwxr-xr-x | debian/rules | 4 | ||||
-rw-r--r-- | ggsn/ggsn.c | 101 | ||||
-rw-r--r-- | gtp/gtp.c | 182 | ||||
-rw-r--r-- | gtp/gtp.h | 12 |
4 files changed, 192 insertions, 107 deletions
diff --git a/debian/rules b/debian/rules index bcf1931..5db7886 100755 --- a/debian/rules +++ b/debian/rules @@ -17,7 +17,3 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all override_dh_strip: dh_strip -posmo-ggsn --dbg-package=osmo-ggsn-dbg dh_strip -plibgtp3 --dbg-package=libgtp-dbg - -override_dh_autoreconf: - echo $(VERSION) > .tarball-version - dh_autoreconf diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 14bf04f..39695b9 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -413,16 +413,19 @@ struct ipcp_hdr { } __attribute__ ((packed)); /* determine if IPCP contains given option */ -static struct ipcp_option_hdr *ipcp_contains_option(struct ipcp_hdr *ipcp, enum ipcp_options opt) +static uint8_t *ipcp_contains_option(uint8_t *ipcp, size_t ipcp_len, enum ipcp_options opt, size_t opt_minlen) { - uint8_t *cur = ipcp->options; + uint8_t *cur_opt = ipcp + sizeof(struct ipcp_hdr); /* iterate over Options and check if protocol contained */ - while (cur + 2 <= ((uint8_t *)ipcp) + ntohs(ipcp->len)) { - struct ipcp_option_hdr *cur_opt = (struct ipcp_option_hdr *) cur; - if (cur_opt->type == opt) + while (cur_opt + 2 <= ipcp + ipcp_len) { + uint8_t type = cur_opt[0]; + uint8_t len = cur_opt[1]; /* length value includes 2 bytes type/length */ + if (len < 2) + return NULL; + if (type == opt && len >= 2 + opt_minlen) return cur_opt; - cur += cur_opt->len; + cur_opt += len; } return NULL; } @@ -460,15 +463,15 @@ enum pco_protocols { }; /* determine if PCO contains given protocol */ -static uint8_t *pco_contains_proto(struct ul255_t *pco, uint16_t prot) +static uint8_t *pco_contains_proto(struct ul255_t *pco, size_t offset, uint16_t prot, size_t prot_minlen) { - uint8_t *cur = pco->v + 1; + uint8_t *cur = pco->v + 1 + offset; /* iterate over PCO and check if protocol contained */ while (cur + 3 <= pco->v + pco->l) { uint16_t cur_prot = osmo_load16be(cur); uint8_t cur_len = cur[2]; - if (cur_prot == prot) + if (cur_prot == prot && cur_len >= prot_minlen) return cur; cur += cur_len + 3; } @@ -500,46 +503,59 @@ static struct ippoolm_t *pdp_get_peer_ipv(struct pdp_t *pdp, bool is_ipv6) { } /* construct an IPCP PCO response from request*/ -static int build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb *msg) +static void build_ipcp_pco(struct apn_ctx *apn, struct pdp_t *pdp, struct msgb *msg) { const struct in46_addr *dns1 = &apn->v4.cfg.dns[0]; const struct in46_addr *dns2 = &apn->v4.cfg.dns[1]; - struct ipcp_hdr *ipcp; + uint8_t *ipcp; + uint16_t ipcp_len; uint8_t *len1, *len2, *pco_ipcp; - uint8_t *start = msg->tail; unsigned int len_appended; + ptrdiff_t consumed; + size_t remain, offset = 0; + + /* pco_contains_proto() returns a potentially unaligned pointer into pco_req->v (see OS#3194) */ + pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr)); + while (pco_ipcp) { + uint8_t *start = msg->tail; + + ipcp = (pco_ipcp + 3); /* 2=type + 1=len */ + consumed = (ipcp - &pdp->pco_req.v[0]); + remain = sizeof(pdp->pco_req.v) - consumed; + ipcp_len = osmo_load16be(ipcp + 2); /* 1=code + 1=id */ + if (remain < 0 || remain < ipcp_len) + return; + + /* Three byte T16L header */ + msgb_put_u16(msg, 0x8021); /* IPCP */ + len1 = msgb_put(msg, 1); /* Length of contents: delay */ + + msgb_put_u8(msg, 0x02); /* ACK */ + msgb_put_u8(msg, ipcp[1]); /* ID: Needs to match request */ + msgb_put_u8(msg, 0x00); /* Length MSB */ + len2 = msgb_put(msg, 1); /* Length LSB: delay */ + + if (dns1->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_PRIMARY_DNS, 4)) { + msgb_put_u8(msg, 0x81); /* DNS1 Tag */ + msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */ + msgb_put_u32(msg, ntohl(dns1->v4.s_addr)); + } - if (!(pco_ipcp = pco_contains_proto(&pdp->pco_req, PCO_P_IPCP))) - return 0; - ipcp = (struct ipcp_hdr*) (pco_ipcp + 3); /* 2=type + 1=len */ - - /* Three byte T16L header */ - msgb_put_u16(msg, 0x8021); /* IPCP */ - len1 = msgb_put(msg, 1); /* Length of contents: delay */ - - msgb_put_u8(msg, 0x02); /* ACK */ - msgb_put_u8(msg, ipcp->id); /* ID: Needs to match request */ - msgb_put_u8(msg, 0x00); /* Length MSB */ - len2 = msgb_put(msg, 1); /* Length LSB: delay */ + if (dns2->len == 4 && ipcp_contains_option(ipcp, ipcp_len, IPCP_OPT_SECONDARY_DNS, 4)) { + msgb_put_u8(msg, 0x83); /* DNS2 Tag */ + msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */ + msgb_put_u32(msg, ntohl(dns2->v4.s_addr)); + } - if (dns1->len == 4 && ipcp_contains_option(ipcp, IPCP_OPT_PRIMARY_DNS)) { - msgb_put_u8(msg, 0x81); /* DNS1 Tag */ - msgb_put_u8(msg, 2 + dns1->len);/* DNS1 Length, incl. TL */ - msgb_put_u32(msg, ntohl(dns1->v4.s_addr)); - } + /* patch in length values */ + len_appended = msg->tail - start; + *len1 = len_appended - 3; + *len2 = len_appended - 3; - if (dns2->len == 4 && ipcp_contains_option(ipcp, IPCP_OPT_SECONDARY_DNS)) { - msgb_put_u8(msg, 0x83); /* DNS2 Tag */ - msgb_put_u8(msg, 2 + dns2->len);/* DNS2 Length, incl. TL */ - msgb_put_u32(msg, ntohl(dns2->v4.s_addr)); + offset += 3 + ipcp_len; + pco_ipcp = pco_contains_proto(&pdp->pco_req, offset, PCO_P_IPCP, sizeof(struct ipcp_hdr)); } - /* patch in length values */ - len_appended = msg->tail - start; - *len1 = len_appended - 3; - *len2 = len_appended - 3; - - return 0; } /* process one PCO request from a MS/UE, putting together the proper responses */ @@ -555,7 +571,7 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp) if (peer_v4) build_ipcp_pco(apn, pdp, msg); - if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv6_ADDR)) { + if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv6_ADDR, 0)) { for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) { struct in46_addr *i46a = &apn->v6.cfg.dns[i]; if (i46a->len != 16) @@ -564,7 +580,7 @@ static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp) } } - if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv4_ADDR)) { + if (pco_contains_proto(&pdp->pco_req, 0, PCO_P_DNS_IPv4_ADDR, 0)) { for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) { struct in46_addr *i46a = &apn->v4.cfg.dns[i]; if (i46a->len != 4) @@ -1111,7 +1127,8 @@ int main(int argc, char **argv) if (rc < 0) exit(1); - g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL); + g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), + OSMO_CTRL_PORT_GGSN, NULL); if (!g_ctrlh) { LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n"); exit(1); @@ -190,6 +190,15 @@ int gtp_set_cb_conf(struct gsn_t *gsn, return 0; } +static void emit_cb_recovery(struct gsn_t *gsn, struct sockaddr_in * peer, + struct pdp_t * pdp, uint8_t recovery) +{ + if (gsn->cb_recovery) + gsn->cb_recovery(peer, recovery); + if (gsn->cb_recovery2) + gsn->cb_recovery2(peer, pdp, recovery); +} + int gtp_set_cb_recovery(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer, uint8_t recovery)) { @@ -197,6 +206,20 @@ int gtp_set_cb_recovery(struct gsn_t *gsn, return 0; } +/* cb_recovery() + * pdp may be NULL if Recovery IE was received from a message independent + * of any PDP ctx (such as Echo Response), or because pdp ctx is unknown to the + * local setup. In case pdp is known, caller may want to keep that pdp alive to + * handle subsequent msg cb as this specific pdp ctx is still valid according to + * specs. + */ +int gtp_set_cb_recovery2(struct gsn_t *gsn, + int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery)) +{ + gsn->cb_recovery2 = cb_recovery2; + return 0; +} + int gtp_set_cb_data_ind(struct gsn_t *gsn, int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len)) @@ -977,8 +1000,7 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, if (gsn->cb_conf) gsn->cb_conf(type, recovery, NULL, cbp); - if (gsn->cb_recovery) - gsn->cb_recovery(peer, recovery); + emit_cb_recovery(gsn, peer, NULL, recovery); return 0; } @@ -1310,6 +1332,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version, struct pdp_t pdp_buf; union gtpie_member *ie[GTPIE_SIZE]; uint8_t recovery; + bool recovery_recvd = false; + int rc; uint16_t seq = get_seq(pack); int hlen = get_hlen(pack); @@ -1410,8 +1434,8 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version, /* Recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) - gsn->cb_recovery(peer, recovery); + /* we use recovery futher down after announcing new pdp ctx to user */ + recovery_recvd = true; } /* Selection mode (conditional) */ @@ -1612,6 +1636,9 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version, /* Switch to using the old pdp context */ pdp = pdp_old; + if (recovery_recvd) + emit_cb_recovery(gsn, peer, pdp, recovery); + /* Confirm to peer that things were "successful" */ return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_ACC_REQ); @@ -1633,13 +1660,16 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version, /* Callback function to validata login */ if (gsn->cb_create_context_ind != 0) - return gsn->cb_create_context_ind(pdp); + rc = gsn->cb_create_context_ind(pdp); else { GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "No create_context_ind callback defined\n"); - return gtp_create_pdp_resp(gsn, version, pdp, + rc = gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_NOT_SUPPORTED); } + if (recovery_recvd) + emit_cb_recovery(gsn, peer, pdp, recovery); + return rc; } /* Handle Create PDP Context Response */ @@ -1696,8 +1726,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version, /* Extract recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) - gsn->cb_recovery(peer, recovery); + emit_cb_recovery(gsn, peer, pdp, recovery); } /* Extract protocol configuration options (optional) */ @@ -2106,8 +2135,7 @@ static int gtp_update_pdp_ind(struct gsn_t *gsn, uint8_t version, /* Recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) - gsn->cb_recovery(peer, recovery); + emit_cb_recovery(gsn, peer, pdp, recovery); } if (version == 0) { @@ -2267,8 +2295,7 @@ static int gtp_update_pdp_conf(struct gsn_t *gsn, uint8_t version, /* Extract recovery (optional) */ if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) - gsn->cb_recovery(peer, recovery); + emit_cb_recovery(gsn, peer, pdp, recovery); } /* Check all conditional information elements */ @@ -2337,24 +2364,13 @@ err_out: return EOF; } -/* API: Send Delete PDP Context Request */ +/* API: Deprecated. Send Delete PDP Context Request And free pdp ctx. */ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, int teardown) { - union gtp_packet packet; - unsigned int length = - get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet); - struct in_addr addr; struct pdp_t *linked_pdp; struct pdp_t *secondary_pdp; int n; - int count = 0; - - if (gsna2in_addr(&addr, &pdp->gsnrc)) { - gsn->err_address++; - LOGP(DLGTP, LOGL_ERROR, "GSN address (len=%u) conversion failed\n", pdp->gsnrc.l); - return EOF; - } if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { LOGP(DLGTP, LOGL_ERROR, @@ -2362,26 +2378,8 @@ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, return EOF; } - if (!teardown) { - for (n = 0; n < PDP_MAXNSAPI; n++) - if (linked_pdp->secondary_tei[n]) - count++; - if (count <= 1) { - LOGP(DLGTP, LOGL_ERROR, - "Must use teardown for last context: %d\n", count); - return EOF; - } - } - - if (pdp->version == 1) { - if (teardown) - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_TEARDOWN, - 0xff); - - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); - } - - gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp); + if (gtp_delete_context_req2(gsn, pdp, cbp, teardown) == EOF) + return EOF; if (teardown) { /* Remove all contexts */ for (n = 0; n < PDP_MAXNSAPI; n++) { @@ -2417,6 +2415,54 @@ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, return 0; } +/* API: Send Delete PDP Context Request. PDP CTX shall be free'd by user at cb_conf(GTP_DELETE_PDP_RSP) */ +int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, + int teardown) +{ + union gtp_packet packet; + unsigned int length = + get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet); + struct in_addr addr; + struct pdp_t *linked_pdp; + int n; + int count = 0; + + if (gsna2in_addr(&addr, &pdp->gsnrc)) { + gsn->err_address++; + LOGP(DLGTP, LOGL_ERROR, "GSN address (len=%u) conversion failed\n", pdp->gsnrc.l); + return EOF; + } + + if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { + LOGP(DLGTP, LOGL_ERROR, + "Unknown linked PDP context: %u\n", pdp->teic_own); + return EOF; + } + + if (!teardown) { + for (n = 0; n < PDP_MAXNSAPI; n++) + if (linked_pdp->secondary_tei[n]) + count++; + if (count <= 1) { + LOGP(DLGTP, LOGL_ERROR, + "Must use teardown for last context: %d\n", count); + return EOF; + } + } + + if (pdp->version == 1) { + if (teardown) + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_TEARDOWN, + 0xff); + + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); + } + + gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp); + + return 0; +} + /* Send Delete PDP Context Response */ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, @@ -2553,6 +2599,9 @@ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version, if (linked_pdp->secondary_tei[n]) count++; if (count <= 1) { + GTP_LOGPKG(LOGL_NOTICE, peer, pack, len, + "Ignoring CTX DEL without teardown and count=%d\n", + count); return 0; /* 29.060 7.3.5 Ignore message */ } } @@ -2570,19 +2619,32 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version, uint8_t cause; void *cbp = NULL; uint8_t type = 0; + struct pdp_t *pdp = NULL; int hlen = get_hlen(pack); /* Remove packet from queue */ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; + /* Find the context in question. It may not be available if gtp_delete_context_req + * was used and as a result the PDP ctx was already freed */ + if (pdp_getgtp1(&pdp, get_tei(pack))) { + gsn->err_unknownpdp++; + GTP_LOGPKG(LOGL_NOTICE, peer, pack, len, + "Unknown PDP context: %u (expected if gtp_delete_context_req is used)\n", + get_tei(pack)); + if (gsn->cb_conf) + gsn->cb_conf(type, EOF, NULL, cbp); + return EOF; + } + /* Decode information elements */ if (gtpie_decaps(ie, version, pack + hlen, len - hlen)) { gsn->invalid++; GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Invalid message format\n"); if (gsn->cb_conf) - gsn->cb_conf(type, EOF, NULL, cbp); + gsn->cb_conf(type, EOF, pdp, cbp); return EOF; } @@ -2592,7 +2654,7 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version, GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Missing mandatory information field\n"); if (gsn->cb_conf) - gsn->cb_conf(type, EOF, NULL, cbp); + gsn->cb_conf(type, EOF, pdp, cbp); return EOF; } @@ -2602,13 +2664,13 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version, GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unexpected cause value received: %d\n", cause); if (gsn->cb_conf) - gsn->cb_conf(type, cause, NULL, cbp); + gsn->cb_conf(type, cause, pdp, cbp); return EOF; } /* Callback function to notify application */ if (gsn->cb_conf) - gsn->cb_conf(type, cause, NULL, cbp); + gsn->cb_conf(type, cause, pdp, cbp); return 0; } @@ -2830,23 +2892,23 @@ int gtp_decaps0(struct gsn_t *gsn) if ((gsn->mode == GTP_MODE_GGSN) && ((pheader->type == GTP_CREATE_PDP_RSP) || - (pheader->type == GTP_UPDATE_PDP_RSP) || - (pheader->type == GTP_DELETE_PDP_RSP))) { + (pheader->type == GTP_UPDATE_PDP_RSP))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, - "Unexpected GTPv0 Signalling Message\n"); + "Unexpected GTPv0 Signalling Message '%s'\n", + get_value_string(gtp_type_names, pheader->type)); continue; /* Silently discard 29.60: 11.1.4 */ } if ((gsn->mode == GTP_MODE_SGSN) && ((pheader->type == GTP_CREATE_PDP_REQ) || - (pheader->type == GTP_UPDATE_PDP_REQ) || - (pheader->type == GTP_DELETE_PDP_REQ))) { + (pheader->type == GTP_UPDATE_PDP_REQ))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, - "Unexpected GTPv0 Signalling Message\n"); + "Unexpected GTPv0 Signalling Message '%s'\n", + get_value_string(gtp_type_names, pheader->type)); continue; /* Silently discard 29.60: 11.1.4 */ } @@ -3007,23 +3069,23 @@ int gtp_decaps1c(struct gsn_t *gsn) if ((gsn->mode == GTP_MODE_GGSN) && ((pheader->type == GTP_CREATE_PDP_RSP) || - (pheader->type == GTP_UPDATE_PDP_RSP) || - (pheader->type == GTP_DELETE_PDP_RSP))) { + (pheader->type == GTP_UPDATE_PDP_RSP))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, - "Unexpected GTPv1 Signalling Message\n"); + "Unexpected GTPv1 Signalling Message '%s'\n", + get_value_string(gtp_type_names, pheader->type)); continue; /* Silently discard 29.60: 11.1.4 */ } if ((gsn->mode == GTP_MODE_SGSN) && ((pheader->type == GTP_CREATE_PDP_REQ) || - (pheader->type == GTP_UPDATE_PDP_REQ) || - (pheader->type == GTP_DELETE_PDP_REQ))) { + (pheader->type == GTP_UPDATE_PDP_REQ))) { gsn->unexpect++; GTP_LOGPKG(LOGL_ERROR, &peer, buffer, status, - "Unexpected GTPv1 Signalling Message\n"); + "Unexpected GTPv1 Signalling Message '%s'\n", + get_value_string(gtp_type_names, pheader->type)); continue; /* Silently discard 29.60: 11.1.4 */ } @@ -13,6 +13,7 @@ #define _GTP_H #include <osmocom/core/utils.h> +#include <osmocom/core/defs.h> #define GTP_MODE_GGSN 1 #define GTP_MODE_SGSN 2 @@ -270,6 +271,7 @@ struct gsn_t { int (*cb_conf) (int type, int cause, struct pdp_t * pdp, void *cbp); int (*cb_data_ind) (struct pdp_t * pdp, void *pack, unsigned len); int (*cb_recovery) (struct sockaddr_in * peer, uint8_t recovery); + int (*cb_recovery2) (struct sockaddr_in * peer, struct pdp_t * pdp, uint8_t recovery); /* Counters */ @@ -323,7 +325,10 @@ extern int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, struct in_addr *inetaddr); extern int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, - void *cbp, int teardown); + void *cbp, int teardown) + OSMO_DEPRECATED("Use gtp_delete_context_req2() instead, to avoid freeing pdp ctx before reply"); +extern int gtp_delete_context_req2(struct gsn_t *gsn, struct pdp_t *pdp, + void *cbp, int teardown); extern int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len); @@ -357,6 +362,11 @@ extern int gtp_set_cb_conf(struct gsn_t *gsn, int gtp_set_cb_recovery(struct gsn_t *gsn, int (*cb) (struct sockaddr_in * peer, + uint8_t recovery)) + OSMO_DEPRECATED("Use gtp_set_cb_recovery2() instead, to obtain pdp ctx originating the recovery"); +int gtp_set_cb_recovery2(struct gsn_t *gsn, + int (*cb) (struct sockaddr_in * peer, + struct pdp_t * pdp, uint8_t recovery)); /* Internal functions (not part of the API */ |