blob: f0d6adf13321b992d853a89ecc6f07c52b707214 [file] [log] [blame]
jjako52c24142002-12-16 13:33:51 +00001/*
2 * OpenGGSN - Gateway GPRS Support Node
jjako0fe0df02004-09-17 11:30:40 +00003 * Copyright (C) 2002, 2003, 2004 Mondru AB.
jjako52c24142002-12-16 13:33:51 +00004 *
5 * The contents of this file may be used under the terms of the GNU
6 * General Public License Version 2, provided that the above copyright
7 * notice and this permission notice is included in all copies or
8 * substantial portions of the software.
9 *
jjako52c24142002-12-16 13:33:51 +000010 */
11
12/*
13 * pdp.c:
14 *
15 */
16
jjako0fe0df02004-09-17 11:30:40 +000017#include <../config.h>
18
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +010019#include <osmocom/core/logging.h>
20
jjako0fe0df02004-09-17 11:30:40 +000021#ifdef HAVE_STDINT_H
22#include <stdint.h>
23#endif
24
jjako52c24142002-12-16 13:33:51 +000025#include <stdio.h>
26#include <sys/types.h>
27#include <netinet/in.h>
28#include <string.h>
Alexander Huemerdb852a12015-11-06 20:59:01 +010029#include <inttypes.h>
jjako52c24142002-12-16 13:33:51 +000030#include "pdp.h"
31#include "lookupa.h"
32
33/* ***********************************************************
34 * Global variables TODO: most should be moved to gsn_t
35 *************************************************************/
36
Holger Hans Peter Freytherd7566b82012-11-06 14:17:45 +010037static struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
38static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
jjakoa7cd2492003-04-11 09:40:12 +000039/* struct pdp_t* haship[PDP_MAX]; Hash table for IP and network interface */
jjako52c24142002-12-16 13:33:51 +000040
41/* ***********************************************************
42 * Functions related to PDP storage
43 *
44 * Lifecycle
45 * For a GGSN pdp context life begins with the reception of a
46 * create pdp context request. It normally ends with the reception
47 * of a delete pdp context request, but will also end with the
48 * reception of an error indication message.
49 * Provisions should probably be made for terminating pdp contexts
50 * based on either idle timeout, or by sending downlink probe
51 * messages (ping?) to see if the MS is still responding.
52 *
53 * For an SGSN pdp context life begins with the application just
54 * before sending off a create pdp context request. It normally
55 * ends when a delete pdp context response message is received
56 * from the GGSN, but should also end when with the reception of
57 * an error indication message.
58 *
59 *
60 * HASH Tables
61 *
62 * Downlink packets received in the GGSN are identified only by their
63 * network interface together with their destination IP address (Two
64 * network interfaces can use the same private IP address). Each IMSI
65 * (mobile station) can have several PDP contexts using the same IP
66 * address. In this case the traffic flow template (TFT) is used to
67 * determine the correct PDP context for a particular IMSI. Also it
68 * should be possible for each PDP context to use several IP adresses
69 * For fixed wireless access a mobile station might need a full class
70 * C network. Even in the case of several IP adresses the PDP context
71 * should be determined on the basis of the network IP address.
72 * Thus we need a hash table based on network interface + IP address.
73 *
74 * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
75 * is collectively called the tunnel identifier. There is also a 16 bit
76 * flow label that can be used for identification of uplink packets. This
77 * however is quite useless as it limits the number of contexts to 65536.
78 * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier
79 * (32 bit), or in some cases by the combination of IMSI and NSAPI.
80 * For GTP1 delete context requests there is a need to find the PDP
81 * contexts with the same IP address. This however can be done by using
82 * the IP hash table.
83 * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
84 * be used for directly addressing the PDP context.
85
86 * pdp_newpdp
87 * Gives you a pdp context with no hash references In some way
88 * this should have a limited lifetime.
89 *
90 * pdp_freepdp
91 * Frees a context that was previously allocated with
92 * pdp_newpdp
93 *
94 *
95 * pdp_getpdpIP
96 * An incoming IP packet is uniquely identified by a pointer
97 * to a network connection (void *) and an IP address
98 * (struct in_addr)
99 *
100 * pdp_getpdpGTP
101 * An incoming GTP packet is uniquely identified by a the
102 * TID (imsi + nsapi (8 octets)) in or by the Flow Label
103 * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier
104 * (4 octets) in gtp1.
105 *
106 * This leads to an architecture where the receiving GSN
107 * chooses a Flow Label or a Tunnel Endpoint Identifier
108 * when the connection is setup.
109 * Thus no hash table is needed for GTP lookups.
110 *
111 *************************************************************/
112
Harald Weltebed35df2011-11-02 13:06:18 +0100113int pdp_init()
114{
115 memset(&pdpa, 0, sizeof(pdpa));
116 memset(&hashtid, 0, sizeof(hashtid));
117 /* memset(&haship, 0, sizeof(haship)); */
jjako52c24142002-12-16 13:33:51 +0000118
Harald Weltebed35df2011-11-02 13:06:18 +0100119 return 0;
jjako52c24142002-12-16 13:33:51 +0000120}
121
Harald Weltebed35df2011-11-02 13:06:18 +0100122int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
123 struct pdp_t *pdp_old)
124{
125 int n;
126 for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */
127 if (pdpa[n].inuse == 0) {
128 *pdp = &pdpa[n];
129 if (NULL != pdp_old)
130 memcpy(*pdp, pdp_old, sizeof(struct pdp_t));
131 else
132 memset(*pdp, 0, sizeof(struct pdp_t));
133 (*pdp)->inuse = 1;
134 (*pdp)->imsi = imsi;
135 (*pdp)->nsapi = nsapi;
136 (*pdp)->fllc = (uint16_t) n + 1;
137 (*pdp)->fllu = (uint16_t) n + 1;
138 (*pdp)->teid_own = (uint32_t) n + 1;
139 if (!(*pdp)->secondary)
140 (*pdp)->teic_own = (uint32_t) n + 1;
141 pdp_tidset(*pdp, pdp_gettid(imsi, nsapi));
142
143 /* Insert reference in primary context */
144 if (((*pdp)->teic_own > 0)
145 && ((*pdp)->teic_own <= PDP_MAX)) {
146 pdpa[(*pdp)->teic_own -
147 1].secondary_tei[(*pdp)->nsapi & 0x0f] =
148 (*pdp)->teid_own;
149 }
150
151 return 0;
152 }
153 }
154 return EOF; /* No more available */
jjako52c24142002-12-16 13:33:51 +0000155}
156
Harald Weltebed35df2011-11-02 13:06:18 +0100157int pdp_freepdp(struct pdp_t *pdp)
158{
159 pdp_tiddel(pdp);
jjako2c381332003-10-21 19:09:53 +0000160
Harald Weltebed35df2011-11-02 13:06:18 +0100161 /* Remove any references in primary context */
162 if ((pdp->secondary) && (pdp->teic_own > 0)
163 && (pdp->teic_own <= PDP_MAX)) {
164 pdpa[pdp->teic_own - 1].secondary_tei[pdp->nsapi & 0x0f] = 0;
165 }
jjako2c381332003-10-21 19:09:53 +0000166
Harald Weltebed35df2011-11-02 13:06:18 +0100167 memset(pdp, 0, sizeof(struct pdp_t));
168 return 0;
jjako52c24142002-12-16 13:33:51 +0000169}
170
Harald Weltebed35df2011-11-02 13:06:18 +0100171int pdp_getpdp(struct pdp_t **pdp)
172{
173 *pdp = &pdpa[0];
174 return 0;
jjako52c24142002-12-16 13:33:51 +0000175}
176
Harald Weltebed35df2011-11-02 13:06:18 +0100177int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
178{
179 if ((fl > PDP_MAX) || (fl < 1)) {
180 return EOF; /* Not found */
181 } else {
182 *pdp = &pdpa[fl - 1];
183 if ((*pdp)->inuse)
184 return 0;
185 else
186 return EOF;
187 /* Context exists. We do no further validity checking. */
188 }
jjako52c24142002-12-16 13:33:51 +0000189}
190
Harald Weltebed35df2011-11-02 13:06:18 +0100191int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
192{
193 if ((tei > PDP_MAX) || (tei < 1)) {
194 return EOF; /* Not found */
195 } else {
196 *pdp = &pdpa[tei - 1];
197 if ((*pdp)->inuse)
198 return 0;
199 else
200 return EOF;
201 /* Context exists. We do no further validity checking. */
202 }
jjako52c24142002-12-16 13:33:51 +0000203}
204
Harald Weltebed35df2011-11-02 13:06:18 +0100205int pdp_tidhash(uint64_t tid)
206{
207 return (lookup(&tid, sizeof(tid), 0) % PDP_MAX);
jjako52c24142002-12-16 13:33:51 +0000208}
209
Harald Weltebed35df2011-11-02 13:06:18 +0100210int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
211{
212 int hash = pdp_tidhash(tid);
213 struct pdp_t *pdp2;
214 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100215 DEBUGP(DLGTP, "Begin pdp_tidset tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100216 pdp->tidnext = NULL;
217 pdp->tid = tid;
218 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext)
219 pdp_prev = pdp2;
220 if (!pdp_prev)
221 hashtid[hash] = pdp;
222 else
223 pdp_prev->tidnext = pdp;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100224 DEBUGP(DLGTP, "End pdp_tidset\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100225 return 0;
jjako52c24142002-12-16 13:33:51 +0000226}
227
Harald Weltebed35df2011-11-02 13:06:18 +0100228int pdp_tiddel(struct pdp_t *pdp)
229{
230 int hash = pdp_tidhash(pdp->tid);
231 struct pdp_t *pdp2;
232 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100233 DEBUGP(DLGTP, "Begin pdp_tiddel tid = %"PRIx64"\n", pdp->tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100234 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
235 if (pdp2 == pdp) {
236 if (!pdp_prev)
237 hashtid[hash] = pdp2->tidnext;
238 else
239 pdp_prev->tidnext = pdp2->tidnext;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100240 DEBUGP(DLGTP, "End pdp_tiddel: PDP found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100241 return 0;
242 }
243 pdp_prev = pdp2;
244 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100245 DEBUGP(DLGTP, "End pdp_tiddel: PDP not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100246 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000247}
248
Harald Weltebed35df2011-11-02 13:06:18 +0100249int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
250{
251 int hash = pdp_tidhash(tid);
252 struct pdp_t *pdp2;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100253 DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100254 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
255 if (pdp2->tid == tid) {
256 *pdp = pdp2;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100257 DEBUGP(DLGTP, "Begin pdp_tidget. Found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100258 return 0;
259 }
260 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100261 DEBUGP(DLGTP, "Begin pdp_tidget. Not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100262 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000263}
264
Harald Weltebed35df2011-11-02 13:06:18 +0100265int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
266{
267 return pdp_tidget(pdp,
268 (imsi & 0x0fffffffffffffffull) +
269 ((uint64_t) nsapi << 60));
jjako08d331d2003-10-13 20:33:30 +0000270}
271
jjakoa7cd2492003-04-11 09:40:12 +0000272/*
jjako52c24142002-12-16 13:33:51 +0000273int pdp_iphash(void* ipif, struct ul66_t *eua) {
jjakoa7cd2492003-04-11 09:40:12 +0000274 /#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
jjako52c24142002-12-16 13:33:51 +0000275 return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
276}
277
278int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
279 int hash;
280 struct pdp_t *pdp2;
281 struct pdp_t *pdp_prev = NULL;
282
283 if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
284 (unsigned) ipif, eua->l,
285 eua->v[2], eua->v[3],
286 eua->v[4], eua->v[5]);
287
jjakob88f6162003-01-05 18:09:07 +0000288 pdp->ipnext = NULL;
jjako52c24142002-12-16 13:33:51 +0000289 pdp->ipif = ipif;
290 pdp->eua.l = eua->l;
291 memcpy(pdp->eua.v, eua->v, eua->l);
292
293 hash = pdp_iphash(pdp->ipif, &pdp->eua);
294
295 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
296 pdp_prev = pdp2;
297 if (!pdp_prev)
298 haship[hash] = pdp;
299 else
300 pdp_prev->ipnext = pdp;
301 if (PDP_DEBUG) printf("End pdp_ipset\n");
302 return 0;
303}
304
305int pdp_ipdel(struct pdp_t *pdp) {
306 int hash = pdp_iphash(pdp->ipif, &pdp->eua);
307 struct pdp_t *pdp2;
308 struct pdp_t *pdp_prev = NULL;
309 if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
310 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
311 if (pdp2 == pdp) {
312 if (!pdp_prev)
313 haship[hash] = pdp2->ipnext;
314 else
315 pdp_prev->ipnext = pdp2->ipnext;
316 if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
317 return 0;
318 }
319 pdp_prev = pdp2;
320 }
321 if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
jjakoa7cd2492003-04-11 09:40:12 +0000322 return EOF; /# End of linked list and not found #/
jjako52c24142002-12-16 13:33:51 +0000323}
324
325int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
326 int hash = pdp_iphash(ipif, eua);
327 struct pdp_t *pdp2;
jjakoa7cd2492003-04-11 09:40:12 +0000328 /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
329 eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
jjako52c24142002-12-16 13:33:51 +0000330 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
331 if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
332 (memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
333 *pdp = pdp2;
jjakoa7cd2492003-04-11 09:40:12 +0000334 /#printf("End pdp_ipget. Found\n");#/
jjako52c24142002-12-16 13:33:51 +0000335 return 0;
336 }
337 }
338 if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
339 (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
jjakoa7cd2492003-04-11 09:40:12 +0000340 return EOF; /# End of linked list and not found #/
jjako52c24142002-12-16 13:33:51 +0000341}
jjakoa7cd2492003-04-11 09:40:12 +0000342*/
jjako52c24142002-12-16 13:33:51 +0000343/* Various conversion functions */
344
Harald Weltebed35df2011-11-02 13:06:18 +0100345int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
346{
347 eua->l = 6;
348 eua->v[0] = 0xf1; /* IETF */
349 eua->v[1] = 0x21; /* IPv4 */
350 memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */
351 return 0;
jjako52c24142002-12-16 13:33:51 +0000352}
353
Harald Weltebed35df2011-11-02 13:06:18 +0100354int pdp_euaton(struct ul66_t *eua, struct in_addr *dst)
355{
356 if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] != 0x21)) {
357 return EOF;
358 }
359 memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
360 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000361}
362
Harald Weltebed35df2011-11-02 13:06:18 +0100363uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
364{
365 return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
jjako52c24142002-12-16 13:33:51 +0000366}
367
Pablo Neira Ayuso746b9442014-03-24 17:58:27 +0100368void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid)
369{
370 pdp->imsi = teid & 0x0fffffffffffffffull;
371 pdp->nsapi = (teid & 0xf000000000000000ull) >> 60;
372}
373
Harald Weltebed35df2011-11-02 13:06:18 +0100374int ulcpy(void *dst, void *src, size_t size)
375{
376 if (((struct ul255_t *)src)->l <= size) {
377 ((struct ul255_t *)dst)->l = ((struct ul255_t *)src)->l;
378 memcpy(((struct ul255_t *)dst)->v, ((struct ul255_t *)src)->v,
379 ((struct ul255_t *)dst)->l);
380 return 0;
381 } else
382 return EOF;
jjako52c24142002-12-16 13:33:51 +0000383}