diff options
Diffstat (limited to 'gtp')
-rw-r--r-- | gtp/gtp.c | 39 | ||||
-rw-r--r-- | gtp/pdp.c | 19 | ||||
-rw-r--r-- | gtp/pdp.h | 1 |
3 files changed, 53 insertions, 6 deletions
@@ -2581,17 +2581,44 @@ int gtp_error_ind_resp(struct gsn_t *gsn, int version, int gtp_error_ind_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, void *pack, unsigned len) { + union gtpie_member *ie[GTPIE_SIZE]; struct pdp_t *pdp; /* Find the context in question */ - if (pdp_tidget(&pdp, be64toh(((union gtp_packet *)pack)->gtp0.h.tid))) { - gsn->err_unknownpdp++; - GTP_LOGPKG(LOGL_ERROR, peer, pack, len, - "Unknown PDP context\n"); - return EOF; + if (version == 0) { + if (pdp_tidget(&pdp, be64toh(((union gtp_packet *)pack)->gtp0.h.tid))) { + gsn->err_unknownpdp++; + GTP_LOGPKG(LOGL_ERROR, peer, pack, len, + "Unknown PDP context\n"); + return EOF; + } + } else if (version == 1) { + /* we have to look-up based on the *peer* TEID */ + int hlen = get_hlen(pack); + uint32_t teid_gn; + + /* 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"); + return EOF; + } + + if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &teid_gn)) { + gsn->missing++; + GTP_LOGPKG(LOGL_ERROR, peer, pack, len, + "Missing mandatory information field\n"); + return EOF; + } + + if (pdp_getgtp1_peer_d(&pdp, peer, teid_gn)) { + gsn->err_unknownpdp++; + GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Unknown PDP context\n"); + return EOF; + } } - gsn->err_unknownpdp++; /* TODO: Change counter */ GTP_LOGPKG(LOGL_ERROR, peer, pack, len, "Received Error Indication\n"); @@ -28,6 +28,7 @@ #include <string.h> #include <inttypes.h> #include "pdp.h" +#include "gtp.h" #include "lookupa.h" /* *********************************************************** @@ -202,6 +203,24 @@ int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei) } } +/* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */ +int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn) +{ + unsigned int i; + + /* this is O(n) but we don't have (nor want) another hash... */ + for (i = 0; i < PDP_MAX; i++) { + struct pdp_t *candidate = &pdpa[i]; + if (candidate->inuse && candidate->teid_gn == teid_gn && + candidate->gsnru.l == sizeof(peer->sin_addr) && + !memcmp(&peer->sin_addr, candidate->gsnru.v, sizeof(peer->sin_addr))) { + *pdp = &pdpa[i]; + return 0; + } + } + return EOF; +} + int pdp_tidhash(uint64_t tid) { return (lookup(&tid, sizeof(tid), 0) % PDP_MAX); @@ -232,6 +232,7 @@ int pdp_getpdp(struct pdp_t **pdp); int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl); int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei); +int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn); int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi); |