diff options
author | jjako <jjako> | 2002-12-16 13:33:51 +0000 |
---|---|---|
committer | jjako <jjako> | 2002-12-16 13:33:51 +0000 |
commit | 52c2414f6cabefb0427475756e8ac4856180bc59 (patch) | |
tree | 5ecb31a74c392c36a7d7c802f18d37349973bf00 /gtp/pdp.c |
Initial revision
Diffstat (limited to 'gtp/pdp.c')
-rw-r--r-- | gtp/pdp.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/gtp/pdp.c b/gtp/pdp.c new file mode 100644 index 0000000..3e5951e --- /dev/null +++ b/gtp/pdp.c @@ -0,0 +1,320 @@ +/* + * OpenGGSN - Gateway GPRS Support Node + * Copyright (C) 2002 Mondru AB. + * + * The contents of this file may be used under the terms of the GNU + * General Public License Version 2, provided that the above copyright + * notice and this permission notice is included in all copies or + * substantial portions of the software. + * + * The initial developer of the original code is + * Jens Jakobsen <jj@openggsn.org> + * + * Contributor(s): + * + */ + +/* + * pdp.c: + * + */ + +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <string.h> +#include "pdp.h" +#include "lookupa.h" + +/* *********************************************************** + * Global variables TODO: most should be moved to gsn_t + *************************************************************/ + +struct pdp_t pdpa[PDP_MAX]; /* PDP storage */ +struct pdp_t* hashtid[PDP_MAX];/* Hash table for IMSI + NSAPI */ +struct pdp_t* haship[PDP_MAX]; /* Hash table for IP and network interface */ + +/* *********************************************************** + * Functions related to PDP storage + * + * Lifecycle + * For a GGSN pdp context life begins with the reception of a + * create pdp context request. It normally ends with the reception + * of a delete pdp context request, but will also end with the + * reception of an error indication message. + * Provisions should probably be made for terminating pdp contexts + * based on either idle timeout, or by sending downlink probe + * messages (ping?) to see if the MS is still responding. + * + * For an SGSN pdp context life begins with the application just + * before sending off a create pdp context request. It normally + * ends when a delete pdp context response message is received + * from the GGSN, but should also end when with the reception of + * an error indication message. + * + * + * HASH Tables + * + * Downlink packets received in the GGSN are identified only by their + * network interface together with their destination IP address (Two + * network interfaces can use the same private IP address). Each IMSI + * (mobile station) can have several PDP contexts using the same IP + * address. In this case the traffic flow template (TFT) is used to + * determine the correct PDP context for a particular IMSI. Also it + * should be possible for each PDP context to use several IP adresses + * For fixed wireless access a mobile station might need a full class + * C network. Even in the case of several IP adresses the PDP context + * should be determined on the basis of the network IP address. + * Thus we need a hash table based on network interface + IP address. + * + * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which + * is collectively called the tunnel identifier. There is also a 16 bit + * flow label that can be used for identification of uplink packets. This + * however is quite useless as it limits the number of contexts to 65536. + * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier + * (32 bit), or in some cases by the combination of IMSI and NSAPI. + * For GTP1 delete context requests there is a need to find the PDP + * contexts with the same IP address. This however can be done by using + * the IP hash table. + * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will + * be used for directly addressing the PDP context. + + * pdp_newpdp + * Gives you a pdp context with no hash references In some way + * this should have a limited lifetime. + * + * pdp_freepdp + * Frees a context that was previously allocated with + * pdp_newpdp + * + * + * pdp_getpdpIP + * An incoming IP packet is uniquely identified by a pointer + * to a network connection (void *) and an IP address + * (struct in_addr) + * + * pdp_getpdpGTP + * An incoming GTP packet is uniquely identified by a the + * TID (imsi + nsapi (8 octets)) in or by the Flow Label + * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier + * (4 octets) in gtp1. + * + * This leads to an architecture where the receiving GSN + * chooses a Flow Label or a Tunnel Endpoint Identifier + * when the connection is setup. + * Thus no hash table is needed for GTP lookups. + * + *************************************************************/ + +int pdp_init() { + memset(&pdpa, 0, sizeof(pdpa)); + memset(&hashtid, 0, sizeof(hashtid)); + memset(&haship, 0, sizeof(haship)); + + return 0; +} + +int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi, + struct pdp_t *pdp_old){ + int n; + for (n=0; n<PDP_MAX; n++) { /* TODO: Need to do better than linear search */ + if (pdpa[n].inuse == 0) { + *pdp = &pdpa[n]; + if (NULL != pdp_old) memcpy(*pdp, pdp_old, sizeof(struct pdp_t)); + else memset(*pdp, 0, sizeof(struct pdp_t)); + (*pdp)->inuse = 1; + (*pdp)->imsi = imsi; + (*pdp)->nsapi = nsapi; + (*pdp)->fllc = (uint16_t) n; + (*pdp)->fllu = (uint16_t) n; + (*pdp)->teic_own = (uint32_t) n; + (*pdp)->teic_own = (uint32_t) n; + pdp_tidset(*pdp, pdp_gettid(imsi, nsapi)); + return 0; + } + } + return EOF; /* No more available */ +} + +int pdp_freepdp(struct pdp_t *pdp){ + pdp_tiddel(pdp); + memset(pdp, 0, sizeof(struct pdp_t)); + /* Also need to clean up IP hash tables */ + return 0; +} + +int pdp_getpdp(struct pdp_t **pdp){ + *pdp = &pdpa[0]; + return 0; +} + +int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl){ + if (fl>=PDP_MAX) { + return EOF; /* Not found */ + } + else { + *pdp = &pdpa[fl]; + if ((*pdp)->inuse) return 0; + else return EOF; + /* Context exists. We do no further validity checking. */ + } +} + +int pdp_getgtp1(struct pdp_t **pdp, uint32_t teid){ + if (teid>=PDP_MAX) { + return -1; /* Not found */ + } + else { + *pdp = &pdpa[teid]; + return 0; /* We do no validity checking. */ + } +} + + +int pdp_tidhash(uint64_t tid) { + return (lookup(&tid, sizeof(tid), 0) % PDP_MAX); +} + +int pdp_tidset(struct pdp_t *pdp, uint64_t tid) { + int hash = pdp_tidhash(tid); + struct pdp_t *pdp2; + struct pdp_t *pdp_prev = NULL; + if (PDP_DEBUG) printf("Begin pdp_tidset tid = %llx\n", tid); + pdp->tid = tid; + for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) + pdp_prev = pdp2; + if (!pdp_prev) + hashtid[hash] = pdp; + else + pdp_prev->tidnext = pdp; + if (PDP_DEBUG) printf("End pdp_tidset\n"); + return 0; +} + +int pdp_tiddel(struct pdp_t *pdp) { + int hash = pdp_tidhash(pdp->tid); + struct pdp_t *pdp2; + struct pdp_t *pdp_prev = NULL; + if (PDP_DEBUG) printf("Begin pdp_tiddel tid = %llx\n", pdp->tid); + for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) { + if (pdp2 == pdp) { + if (!pdp_prev) + hashtid[hash] = pdp2->tidnext; + else + pdp_prev->tidnext = pdp2->tidnext; + if (PDP_DEBUG) printf("End pdp_tidset: PDP found\n"); + return 0; + } + pdp_prev = pdp2; + } + if (PDP_DEBUG) printf("End pdp_tidset: PDP not found\n"); + return EOF; /* End of linked list and not found */ +} + +int pdp_tidget(struct pdp_t **pdp, uint64_t tid) { + int hash = pdp_tidhash(tid); + struct pdp_t *pdp2; + if (PDP_DEBUG) printf("Begin pdp_tidget tid = %llx\n", tid); + for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) { + if (pdp2->tid == tid) { + *pdp = pdp2; + if (PDP_DEBUG) printf("Begin pdp_tidget. Found\n"); + return 0; + } + } + if (PDP_DEBUG) printf("Begin pdp_tidget. Not found\n"); + return EOF; /* End of linked list and not found */ +} + +int pdp_iphash(void* ipif, struct ul66_t *eua) { + /*printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);*/ + return (lookup(eua->v, eua->l, ipif) % PDP_MAX); +} + +int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) { + int hash; + struct pdp_t *pdp2; + struct pdp_t *pdp_prev = NULL; + + if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n", + (unsigned) ipif, eua->l, + eua->v[2], eua->v[3], + eua->v[4], eua->v[5]); + + pdp->ipif = ipif; + pdp->eua.l = eua->l; + memcpy(pdp->eua.v, eua->v, eua->l); + + hash = pdp_iphash(pdp->ipif, &pdp->eua); + + for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) + pdp_prev = pdp2; + if (!pdp_prev) + haship[hash] = pdp; + else + pdp_prev->ipnext = pdp; + if (PDP_DEBUG) printf("End pdp_ipset\n"); + return 0; +} + +int pdp_ipdel(struct pdp_t *pdp) { + int hash = pdp_iphash(pdp->ipif, &pdp->eua); + struct pdp_t *pdp2; + struct pdp_t *pdp_prev = NULL; + if (PDP_DEBUG) printf("Begin pdp_ipdel\n"); + for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) { + if (pdp2 == pdp) { + if (!pdp_prev) + haship[hash] = pdp2->ipnext; + else + pdp_prev->ipnext = pdp2->ipnext; + if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n"); + return 0; + } + pdp_prev = pdp2; + } + if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n"); + return EOF; /* End of linked list and not found */ +} + +int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) { + int hash = pdp_iphash(ipif, eua); + struct pdp_t *pdp2; + /*printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l, + eua->v[2],eua->v[3],eua->v[4],eua->v[5]);*/ + for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) { + if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) && + (memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) { + *pdp = pdp2; + /*printf("End pdp_ipget. Found\n");*/ + return 0; + } + } + if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n", + (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]); + return EOF; /* End of linked list and not found */ +} + +/* Various conversion functions */ + +int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua) { + eua->l=6; + eua->v[0]=0xf1; /* IETF */ + eua->v[1]=0x21; /* IPv4 */ + memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */ + return 0; +} + +uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi) { + return (imsi & 0x0fffffffffffffff) + ((uint64_t)nsapi << 60); +} + +int ulcpy(void* dst, void* src, size_t size) { + if (((struct ul255_t*)src)->l <= size) { + ((struct ul255_t*)dst)->l = ((struct ul255_t*)src)->l; + memcpy(((struct ul255_t*)dst)->v, ((struct ul255_t*)src)->v, + ((struct ul255_t*)dst)->l); + return 0; + } + else return EOF; +} |