blob: c576a4e6e1039fc61d145f0b546db73243521b15 [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"
Harald Welte37d5b152017-08-12 23:58:29 +020031#include "gtp.h"
jjako52c24142002-12-16 13:33:51 +000032#include "lookupa.h"
33
34/* ***********************************************************
35 * Global variables TODO: most should be moved to gsn_t
36 *************************************************************/
37
Holger Hans Peter Freytherd7566b82012-11-06 14:17:45 +010038static struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
39static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
jjakoa7cd2492003-04-11 09:40:12 +000040/* struct pdp_t* haship[PDP_MAX]; Hash table for IP and network interface */
jjako52c24142002-12-16 13:33:51 +000041
42/* ***********************************************************
43 * Functions related to PDP storage
44 *
45 * Lifecycle
46 * For a GGSN pdp context life begins with the reception of a
47 * create pdp context request. It normally ends with the reception
48 * of a delete pdp context request, but will also end with the
49 * reception of an error indication message.
50 * Provisions should probably be made for terminating pdp contexts
51 * based on either idle timeout, or by sending downlink probe
52 * messages (ping?) to see if the MS is still responding.
53 *
54 * For an SGSN pdp context life begins with the application just
55 * before sending off a create pdp context request. It normally
56 * ends when a delete pdp context response message is received
57 * from the GGSN, but should also end when with the reception of
58 * an error indication message.
59 *
60 *
61 * HASH Tables
62 *
63 * Downlink packets received in the GGSN are identified only by their
64 * network interface together with their destination IP address (Two
65 * network interfaces can use the same private IP address). Each IMSI
66 * (mobile station) can have several PDP contexts using the same IP
67 * address. In this case the traffic flow template (TFT) is used to
68 * determine the correct PDP context for a particular IMSI. Also it
69 * should be possible for each PDP context to use several IP adresses
70 * For fixed wireless access a mobile station might need a full class
71 * C network. Even in the case of several IP adresses the PDP context
72 * should be determined on the basis of the network IP address.
73 * Thus we need a hash table based on network interface + IP address.
74 *
75 * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
76 * is collectively called the tunnel identifier. There is also a 16 bit
77 * flow label that can be used for identification of uplink packets. This
78 * however is quite useless as it limits the number of contexts to 65536.
79 * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier
80 * (32 bit), or in some cases by the combination of IMSI and NSAPI.
81 * For GTP1 delete context requests there is a need to find the PDP
82 * contexts with the same IP address. This however can be done by using
83 * the IP hash table.
84 * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
85 * be used for directly addressing the PDP context.
86
87 * pdp_newpdp
88 * Gives you a pdp context with no hash references In some way
89 * this should have a limited lifetime.
90 *
91 * pdp_freepdp
92 * Frees a context that was previously allocated with
93 * pdp_newpdp
94 *
95 *
96 * pdp_getpdpIP
97 * An incoming IP packet is uniquely identified by a pointer
98 * to a network connection (void *) and an IP address
99 * (struct in_addr)
100 *
101 * pdp_getpdpGTP
102 * An incoming GTP packet is uniquely identified by a the
103 * TID (imsi + nsapi (8 octets)) in or by the Flow Label
104 * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier
105 * (4 octets) in gtp1.
106 *
107 * This leads to an architecture where the receiving GSN
108 * chooses a Flow Label or a Tunnel Endpoint Identifier
109 * when the connection is setup.
110 * Thus no hash table is needed for GTP lookups.
111 *
112 *************************************************************/
113
Harald Weltebed35df2011-11-02 13:06:18 +0100114int pdp_init()
115{
116 memset(&pdpa, 0, sizeof(pdpa));
117 memset(&hashtid, 0, sizeof(hashtid));
118 /* memset(&haship, 0, sizeof(haship)); */
jjako52c24142002-12-16 13:33:51 +0000119
Harald Weltebed35df2011-11-02 13:06:18 +0100120 return 0;
jjako52c24142002-12-16 13:33:51 +0000121}
122
Harald Weltebed35df2011-11-02 13:06:18 +0100123int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
124 struct pdp_t *pdp_old)
125{
126 int n;
127 for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */
128 if (pdpa[n].inuse == 0) {
129 *pdp = &pdpa[n];
130 if (NULL != pdp_old)
131 memcpy(*pdp, pdp_old, sizeof(struct pdp_t));
132 else
133 memset(*pdp, 0, sizeof(struct pdp_t));
134 (*pdp)->inuse = 1;
135 (*pdp)->imsi = imsi;
136 (*pdp)->nsapi = nsapi;
137 (*pdp)->fllc = (uint16_t) n + 1;
138 (*pdp)->fllu = (uint16_t) n + 1;
139 (*pdp)->teid_own = (uint32_t) n + 1;
140 if (!(*pdp)->secondary)
141 (*pdp)->teic_own = (uint32_t) n + 1;
142 pdp_tidset(*pdp, pdp_gettid(imsi, nsapi));
143
144 /* Insert reference in primary context */
145 if (((*pdp)->teic_own > 0)
146 && ((*pdp)->teic_own <= PDP_MAX)) {
147 pdpa[(*pdp)->teic_own -
148 1].secondary_tei[(*pdp)->nsapi & 0x0f] =
149 (*pdp)->teid_own;
150 }
151
152 return 0;
153 }
154 }
155 return EOF; /* No more available */
jjako52c24142002-12-16 13:33:51 +0000156}
157
Harald Weltebed35df2011-11-02 13:06:18 +0100158int pdp_freepdp(struct pdp_t *pdp)
159{
160 pdp_tiddel(pdp);
jjako2c381332003-10-21 19:09:53 +0000161
Harald Weltebed35df2011-11-02 13:06:18 +0100162 /* Remove any references in primary context */
163 if ((pdp->secondary) && (pdp->teic_own > 0)
164 && (pdp->teic_own <= PDP_MAX)) {
165 pdpa[pdp->teic_own - 1].secondary_tei[pdp->nsapi & 0x0f] = 0;
166 }
jjako2c381332003-10-21 19:09:53 +0000167
Harald Weltebed35df2011-11-02 13:06:18 +0100168 memset(pdp, 0, sizeof(struct pdp_t));
169 return 0;
jjako52c24142002-12-16 13:33:51 +0000170}
171
Harald Weltebed35df2011-11-02 13:06:18 +0100172int pdp_getpdp(struct pdp_t **pdp)
173{
174 *pdp = &pdpa[0];
175 return 0;
jjako52c24142002-12-16 13:33:51 +0000176}
177
Harald Weltebed35df2011-11-02 13:06:18 +0100178int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
179{
180 if ((fl > PDP_MAX) || (fl < 1)) {
181 return EOF; /* Not found */
182 } else {
183 *pdp = &pdpa[fl - 1];
184 if ((*pdp)->inuse)
185 return 0;
186 else
187 return EOF;
188 /* Context exists. We do no further validity checking. */
189 }
jjako52c24142002-12-16 13:33:51 +0000190}
191
Harald Weltebed35df2011-11-02 13:06:18 +0100192int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
193{
194 if ((tei > PDP_MAX) || (tei < 1)) {
195 return EOF; /* Not found */
196 } else {
197 *pdp = &pdpa[tei - 1];
198 if ((*pdp)->inuse)
199 return 0;
200 else
201 return EOF;
202 /* Context exists. We do no further validity checking. */
203 }
jjako52c24142002-12-16 13:33:51 +0000204}
205
Harald Welte37d5b152017-08-12 23:58:29 +0200206/* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */
207int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
208{
209 unsigned int i;
210
211 /* this is O(n) but we don't have (nor want) another hash... */
212 for (i = 0; i < PDP_MAX; i++) {
213 struct pdp_t *candidate = &pdpa[i];
214 if (candidate->inuse && candidate->teid_gn == teid_gn &&
215 candidate->gsnru.l == sizeof(peer->sin_addr) &&
216 !memcmp(&peer->sin_addr, candidate->gsnru.v, sizeof(peer->sin_addr))) {
217 *pdp = &pdpa[i];
218 return 0;
219 }
220 }
221 return EOF;
222}
223
Harald Weltebed35df2011-11-02 13:06:18 +0100224int pdp_tidhash(uint64_t tid)
225{
226 return (lookup(&tid, sizeof(tid), 0) % PDP_MAX);
jjako52c24142002-12-16 13:33:51 +0000227}
228
Harald Weltebed35df2011-11-02 13:06:18 +0100229int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
230{
231 int hash = pdp_tidhash(tid);
232 struct pdp_t *pdp2;
233 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100234 DEBUGP(DLGTP, "Begin pdp_tidset tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100235 pdp->tidnext = NULL;
236 pdp->tid = tid;
237 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext)
238 pdp_prev = pdp2;
239 if (!pdp_prev)
240 hashtid[hash] = pdp;
241 else
242 pdp_prev->tidnext = pdp;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100243 DEBUGP(DLGTP, "End pdp_tidset\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100244 return 0;
jjako52c24142002-12-16 13:33:51 +0000245}
246
Harald Weltebed35df2011-11-02 13:06:18 +0100247int pdp_tiddel(struct pdp_t *pdp)
248{
249 int hash = pdp_tidhash(pdp->tid);
250 struct pdp_t *pdp2;
251 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100252 DEBUGP(DLGTP, "Begin pdp_tiddel tid = %"PRIx64"\n", pdp->tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100253 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
254 if (pdp2 == pdp) {
255 if (!pdp_prev)
256 hashtid[hash] = pdp2->tidnext;
257 else
258 pdp_prev->tidnext = pdp2->tidnext;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100259 DEBUGP(DLGTP, "End pdp_tiddel: PDP found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100260 return 0;
261 }
262 pdp_prev = pdp2;
263 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100264 DEBUGP(DLGTP, "End pdp_tiddel: PDP not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100265 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000266}
267
Harald Weltebed35df2011-11-02 13:06:18 +0100268int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
269{
270 int hash = pdp_tidhash(tid);
271 struct pdp_t *pdp2;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100272 DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100273 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
274 if (pdp2->tid == tid) {
275 *pdp = pdp2;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100276 DEBUGP(DLGTP, "Begin pdp_tidget. Found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100277 return 0;
278 }
279 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100280 DEBUGP(DLGTP, "Begin pdp_tidget. Not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100281 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000282}
283
Harald Weltebed35df2011-11-02 13:06:18 +0100284int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
285{
286 return pdp_tidget(pdp,
287 (imsi & 0x0fffffffffffffffull) +
288 ((uint64_t) nsapi << 60));
jjako08d331d2003-10-13 20:33:30 +0000289}
290
jjakoa7cd2492003-04-11 09:40:12 +0000291/*
jjako52c24142002-12-16 13:33:51 +0000292int pdp_iphash(void* ipif, struct ul66_t *eua) {
jjakoa7cd2492003-04-11 09:40:12 +0000293 /#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
jjako52c24142002-12-16 13:33:51 +0000294 return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
295}
296
297int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
298 int hash;
299 struct pdp_t *pdp2;
300 struct pdp_t *pdp_prev = NULL;
301
302 if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
303 (unsigned) ipif, eua->l,
304 eua->v[2], eua->v[3],
305 eua->v[4], eua->v[5]);
306
jjakob88f6162003-01-05 18:09:07 +0000307 pdp->ipnext = NULL;
jjako52c24142002-12-16 13:33:51 +0000308 pdp->ipif = ipif;
309 pdp->eua.l = eua->l;
310 memcpy(pdp->eua.v, eua->v, eua->l);
311
312 hash = pdp_iphash(pdp->ipif, &pdp->eua);
313
314 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
315 pdp_prev = pdp2;
316 if (!pdp_prev)
317 haship[hash] = pdp;
318 else
319 pdp_prev->ipnext = pdp;
320 if (PDP_DEBUG) printf("End pdp_ipset\n");
321 return 0;
322}
323
324int pdp_ipdel(struct pdp_t *pdp) {
325 int hash = pdp_iphash(pdp->ipif, &pdp->eua);
326 struct pdp_t *pdp2;
327 struct pdp_t *pdp_prev = NULL;
328 if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
329 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
330 if (pdp2 == pdp) {
331 if (!pdp_prev)
332 haship[hash] = pdp2->ipnext;
333 else
334 pdp_prev->ipnext = pdp2->ipnext;
335 if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
336 return 0;
337 }
338 pdp_prev = pdp2;
339 }
340 if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
jjakoa7cd2492003-04-11 09:40:12 +0000341 return EOF; /# End of linked list and not found #/
jjako52c24142002-12-16 13:33:51 +0000342}
343
344int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
345 int hash = pdp_iphash(ipif, eua);
346 struct pdp_t *pdp2;
jjakoa7cd2492003-04-11 09:40:12 +0000347 /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
348 eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
jjako52c24142002-12-16 13:33:51 +0000349 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
350 if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
351 (memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
352 *pdp = pdp2;
jjakoa7cd2492003-04-11 09:40:12 +0000353 /#printf("End pdp_ipget. Found\n");#/
jjako52c24142002-12-16 13:33:51 +0000354 return 0;
355 }
356 }
357 if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
358 (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
jjakoa7cd2492003-04-11 09:40:12 +0000359 return EOF; /# End of linked list and not found #/
jjako52c24142002-12-16 13:33:51 +0000360}
jjakoa7cd2492003-04-11 09:40:12 +0000361*/
jjako52c24142002-12-16 13:33:51 +0000362/* Various conversion functions */
363
Harald Weltebed35df2011-11-02 13:06:18 +0100364int pdp_ntoeua(struct in_addr *src, struct ul66_t *eua)
365{
366 eua->l = 6;
367 eua->v[0] = 0xf1; /* IETF */
368 eua->v[1] = 0x21; /* IPv4 */
369 memcpy(&eua->v[2], src, 4); /* Copy a 4 byte address */
370 return 0;
jjako52c24142002-12-16 13:33:51 +0000371}
372
Harald Weltebed35df2011-11-02 13:06:18 +0100373int pdp_euaton(struct ul66_t *eua, struct in_addr *dst)
374{
375 if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] != 0x21)) {
376 return EOF;
377 }
378 memcpy(dst, &eua->v[2], 4); /* Copy a 4 byte address */
379 return 0;
jjakoa7cd2492003-04-11 09:40:12 +0000380}
381
Harald Weltebed35df2011-11-02 13:06:18 +0100382uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
383{
384 return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
jjako52c24142002-12-16 13:33:51 +0000385}
386
Pablo Neira Ayuso746b9442014-03-24 17:58:27 +0100387void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid)
388{
389 pdp->imsi = teid & 0x0fffffffffffffffull;
390 pdp->nsapi = (teid & 0xf000000000000000ull) >> 60;
391}