blob: a630ee9de84bfac5f2e400150bfe20f085e4f05c [file] [log] [blame]
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +01001/*
Harald Welte632e8432017-09-05 18:12:14 +02002 * OsmoGGSN - Gateway GPRS Support Node
jjako0fe0df02004-09-17 11:30:40 +00003 * Copyright (C) 2002, 2003, 2004 Mondru AB.
Harald Welte632e8432017-09-05 18:12:14 +02004 * Copyright (C) 2017 Harald Welte <laforge@gnumonks.org>
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +01005 *
jjako52c24142002-12-16 13:33:51 +00006 * The contents of this file may be used under the terms of the GNU
7 * General Public License Version 2, provided that the above copyright
8 * notice and this permission notice is included in all copies or
9 * substantial portions of the software.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010010 *
jjako52c24142002-12-16 13:33:51 +000011 */
12
13/*
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010014 * pdp.c:
jjako52c24142002-12-16 13:33:51 +000015 *
16 */
17
jjako0fe0df02004-09-17 11:30:40 +000018#include <../config.h>
19
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +010020#include <osmocom/core/logging.h>
21
jjako0fe0df02004-09-17 11:30:40 +000022#ifdef HAVE_STDINT_H
23#include <stdint.h>
24#endif
25
jjako52c24142002-12-16 13:33:51 +000026#include <stdio.h>
27#include <sys/types.h>
28#include <netinet/in.h>
29#include <string.h>
Alexander Huemerdb852a12015-11-06 20:59:01 +010030#include <inttypes.h>
jjako52c24142002-12-16 13:33:51 +000031#include "pdp.h"
Harald Welte37d5b152017-08-12 23:58:29 +020032#include "gtp.h"
jjako52c24142002-12-16 13:33:51 +000033#include "lookupa.h"
34
35/* ***********************************************************
36 * Global variables TODO: most should be moved to gsn_t
37 *************************************************************/
38
Holger Hans Peter Freytherd7566b82012-11-06 14:17:45 +010039static struct pdp_t pdpa[PDP_MAX]; /* PDP storage */
40static struct pdp_t *hashtid[PDP_MAX]; /* Hash table for IMSI + NSAPI */
jjakoa7cd2492003-04-11 09:40:12 +000041/* struct pdp_t* haship[PDP_MAX]; Hash table for IP and network interface */
jjako52c24142002-12-16 13:33:51 +000042
43/* ***********************************************************
44 * Functions related to PDP storage
45 *
46 * Lifecycle
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010047 * For a GGSN pdp context life begins with the reception of a
jjako52c24142002-12-16 13:33:51 +000048 * create pdp context request. It normally ends with the reception
49 * of a delete pdp context request, but will also end with the
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010050 * reception of an error indication message.
jjako52c24142002-12-16 13:33:51 +000051 * Provisions should probably be made for terminating pdp contexts
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010052 * based on either idle timeout, or by sending downlink probe
jjako52c24142002-12-16 13:33:51 +000053 * messages (ping?) to see if the MS is still responding.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010054 *
jjako52c24142002-12-16 13:33:51 +000055 * For an SGSN pdp context life begins with the application just
56 * before sending off a create pdp context request. It normally
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010057 * ends when a delete pdp context response message is received
jjako52c24142002-12-16 13:33:51 +000058 * from the GGSN, but should also end when with the reception of
59 * an error indication message.
60 *
61 *
62 * HASH Tables
63 *
64 * Downlink packets received in the GGSN are identified only by their
65 * network interface together with their destination IP address (Two
66 * network interfaces can use the same private IP address). Each IMSI
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010067 * (mobile station) can have several PDP contexts using the same IP
jjako52c24142002-12-16 13:33:51 +000068 * address. In this case the traffic flow template (TFT) is used to
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010069 * determine the correct PDP context for a particular IMSI. Also it
jjako52c24142002-12-16 13:33:51 +000070 * should be possible for each PDP context to use several IP adresses
71 * For fixed wireless access a mobile station might need a full class
72 * C network. Even in the case of several IP adresses the PDP context
73 * should be determined on the basis of the network IP address.
74 * Thus we need a hash table based on network interface + IP address.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010075 *
jjako52c24142002-12-16 13:33:51 +000076 * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
77 * is collectively called the tunnel identifier. There is also a 16 bit
78 * flow label that can be used for identification of uplink packets. This
79 * however is quite useless as it limits the number of contexts to 65536.
80 * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier
81 * (32 bit), or in some cases by the combination of IMSI and NSAPI.
82 * For GTP1 delete context requests there is a need to find the PDP
83 * contexts with the same IP address. This however can be done by using
84 * the IP hash table.
85 * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
86 * be used for directly addressing the PDP context.
87
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010088 * pdp_newpdp
jjako52c24142002-12-16 13:33:51 +000089 * Gives you a pdp context with no hash references In some way
90 * this should have a limited lifetime.
91 *
92 * pdp_freepdp
93 * Frees a context that was previously allocated with
94 * pdp_newpdp
95 *
96 *
97 * pdp_getpdpIP
98 * An incoming IP packet is uniquely identified by a pointer
99 * to a network connection (void *) and an IP address
100 * (struct in_addr)
101 *
102 * pdp_getpdpGTP
103 * An incoming GTP packet is uniquely identified by a the
104 * TID (imsi + nsapi (8 octets)) in or by the Flow Label
105 * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier
106 * (4 octets) in gtp1.
107 *
108 * This leads to an architecture where the receiving GSN
109 * chooses a Flow Label or a Tunnel Endpoint Identifier
110 * when the connection is setup.
111 * Thus no hash table is needed for GTP lookups.
112 *
113 *************************************************************/
114
Harald Weltebed35df2011-11-02 13:06:18 +0100115int pdp_init()
116{
117 memset(&pdpa, 0, sizeof(pdpa));
118 memset(&hashtid, 0, sizeof(hashtid));
119 /* memset(&haship, 0, sizeof(haship)); */
jjako52c24142002-12-16 13:33:51 +0000120
Harald Weltebed35df2011-11-02 13:06:18 +0100121 return 0;
jjako52c24142002-12-16 13:33:51 +0000122}
123
Harald Weltebed35df2011-11-02 13:06:18 +0100124int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
125 struct pdp_t *pdp_old)
126{
127 int n;
128 for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */
129 if (pdpa[n].inuse == 0) {
130 *pdp = &pdpa[n];
131 if (NULL != pdp_old)
132 memcpy(*pdp, pdp_old, sizeof(struct pdp_t));
133 else
134 memset(*pdp, 0, sizeof(struct pdp_t));
135 (*pdp)->inuse = 1;
136 (*pdp)->imsi = imsi;
137 (*pdp)->nsapi = nsapi;
138 (*pdp)->fllc = (uint16_t) n + 1;
139 (*pdp)->fllu = (uint16_t) n + 1;
140 (*pdp)->teid_own = (uint32_t) n + 1;
141 if (!(*pdp)->secondary)
142 (*pdp)->teic_own = (uint32_t) n + 1;
143 pdp_tidset(*pdp, pdp_gettid(imsi, nsapi));
144
145 /* Insert reference in primary context */
146 if (((*pdp)->teic_own > 0)
147 && ((*pdp)->teic_own <= PDP_MAX)) {
148 pdpa[(*pdp)->teic_own -
149 1].secondary_tei[(*pdp)->nsapi & 0x0f] =
150 (*pdp)->teid_own;
151 }
Harald Welte3c1cce22017-09-24 16:40:12 +0800152 /* Default: Generate G-PDU sequence numbers on Tx */
153 (*pdp)->tx_gpdu_seq = true;
Harald Weltebed35df2011-11-02 13:06:18 +0100154
155 return 0;
156 }
157 }
158 return EOF; /* No more available */
jjako52c24142002-12-16 13:33:51 +0000159}
160
Harald Weltebed35df2011-11-02 13:06:18 +0100161int pdp_freepdp(struct pdp_t *pdp)
162{
163 pdp_tiddel(pdp);
jjako2c381332003-10-21 19:09:53 +0000164
Harald Weltebed35df2011-11-02 13:06:18 +0100165 /* Remove any references in primary context */
166 if ((pdp->secondary) && (pdp->teic_own > 0)
167 && (pdp->teic_own <= PDP_MAX)) {
168 pdpa[pdp->teic_own - 1].secondary_tei[pdp->nsapi & 0x0f] = 0;
169 }
jjako2c381332003-10-21 19:09:53 +0000170
Harald Weltebed35df2011-11-02 13:06:18 +0100171 memset(pdp, 0, sizeof(struct pdp_t));
172 return 0;
jjako52c24142002-12-16 13:33:51 +0000173}
174
Harald Weltebed35df2011-11-02 13:06:18 +0100175int pdp_getpdp(struct pdp_t **pdp)
176{
177 *pdp = &pdpa[0];
178 return 0;
jjako52c24142002-12-16 13:33:51 +0000179}
180
Harald Weltebed35df2011-11-02 13:06:18 +0100181int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
182{
183 if ((fl > PDP_MAX) || (fl < 1)) {
184 return EOF; /* Not found */
185 } else {
186 *pdp = &pdpa[fl - 1];
187 if ((*pdp)->inuse)
188 return 0;
189 else
190 return EOF;
191 /* Context exists. We do no further validity checking. */
192 }
jjako52c24142002-12-16 13:33:51 +0000193}
194
Harald Weltebed35df2011-11-02 13:06:18 +0100195int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
196{
197 if ((tei > PDP_MAX) || (tei < 1)) {
198 return EOF; /* Not found */
199 } else {
200 *pdp = &pdpa[tei - 1];
201 if ((*pdp)->inuse)
202 return 0;
203 else
204 return EOF;
205 /* Context exists. We do no further validity checking. */
206 }
jjako52c24142002-12-16 13:33:51 +0000207}
208
Harald Welte37d5b152017-08-12 23:58:29 +0200209/* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */
210int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
211{
212 unsigned int i;
213
214 /* this is O(n) but we don't have (nor want) another hash... */
215 for (i = 0; i < PDP_MAX; i++) {
216 struct pdp_t *candidate = &pdpa[i];
217 if (candidate->inuse && candidate->teid_gn == teid_gn &&
218 candidate->gsnru.l == sizeof(peer->sin_addr) &&
219 !memcmp(&peer->sin_addr, candidate->gsnru.v, sizeof(peer->sin_addr))) {
220 *pdp = &pdpa[i];
221 return 0;
222 }
223 }
224 return EOF;
225}
226
Harald Weltebed35df2011-11-02 13:06:18 +0100227int pdp_tidhash(uint64_t tid)
228{
229 return (lookup(&tid, sizeof(tid), 0) % PDP_MAX);
jjako52c24142002-12-16 13:33:51 +0000230}
231
Harald Weltebed35df2011-11-02 13:06:18 +0100232int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
233{
234 int hash = pdp_tidhash(tid);
235 struct pdp_t *pdp2;
236 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100237 DEBUGP(DLGTP, "Begin pdp_tidset tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100238 pdp->tidnext = NULL;
239 pdp->tid = tid;
240 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext)
241 pdp_prev = pdp2;
242 if (!pdp_prev)
243 hashtid[hash] = pdp;
244 else
245 pdp_prev->tidnext = pdp;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100246 DEBUGP(DLGTP, "End pdp_tidset\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100247 return 0;
jjako52c24142002-12-16 13:33:51 +0000248}
249
Harald Weltebed35df2011-11-02 13:06:18 +0100250int pdp_tiddel(struct pdp_t *pdp)
251{
252 int hash = pdp_tidhash(pdp->tid);
253 struct pdp_t *pdp2;
254 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100255 DEBUGP(DLGTP, "Begin pdp_tiddel tid = %"PRIx64"\n", pdp->tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100256 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
257 if (pdp2 == pdp) {
258 if (!pdp_prev)
259 hashtid[hash] = pdp2->tidnext;
260 else
261 pdp_prev->tidnext = pdp2->tidnext;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100262 DEBUGP(DLGTP, "End pdp_tiddel: PDP found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100263 return 0;
264 }
265 pdp_prev = pdp2;
266 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100267 DEBUGP(DLGTP, "End pdp_tiddel: PDP not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100268 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000269}
270
Harald Weltebed35df2011-11-02 13:06:18 +0100271int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
272{
273 int hash = pdp_tidhash(tid);
274 struct pdp_t *pdp2;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100275 DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100276 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
277 if (pdp2->tid == tid) {
278 *pdp = pdp2;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100279 DEBUGP(DLGTP, "Begin pdp_tidget. Found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100280 return 0;
281 }
282 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100283 DEBUGP(DLGTP, "Begin pdp_tidget. Not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100284 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000285}
286
Harald Weltebed35df2011-11-02 13:06:18 +0100287int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
288{
289 return pdp_tidget(pdp,
290 (imsi & 0x0fffffffffffffffull) +
291 ((uint64_t) nsapi << 60));
jjako08d331d2003-10-13 20:33:30 +0000292}
293
jjakoa7cd2492003-04-11 09:40:12 +0000294/*
jjako52c24142002-12-16 13:33:51 +0000295int pdp_iphash(void* ipif, struct ul66_t *eua) {
jjakoa7cd2492003-04-11 09:40:12 +0000296 /#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
jjako52c24142002-12-16 13:33:51 +0000297 return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
298}
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100299
jjako52c24142002-12-16 13:33:51 +0000300int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
301 int hash;
302 struct pdp_t *pdp2;
303 struct pdp_t *pdp_prev = NULL;
304
305 if (PDP_DEBUG) printf("Begin pdp_ipset %d %d %2x%2x%2x%2x\n",
306 (unsigned) ipif, eua->l,
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100307 eua->v[2], eua->v[3],
jjako52c24142002-12-16 13:33:51 +0000308 eua->v[4], eua->v[5]);
309
jjakob88f6162003-01-05 18:09:07 +0000310 pdp->ipnext = NULL;
jjako52c24142002-12-16 13:33:51 +0000311 pdp->ipif = ipif;
312 pdp->eua.l = eua->l;
313 memcpy(pdp->eua.v, eua->v, eua->l);
314
315 hash = pdp_iphash(pdp->ipif, &pdp->eua);
316
317 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
318 pdp_prev = pdp2;
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100319 if (!pdp_prev)
jjako52c24142002-12-16 13:33:51 +0000320 haship[hash] = pdp;
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100321 else
jjako52c24142002-12-16 13:33:51 +0000322 pdp_prev->ipnext = pdp;
323 if (PDP_DEBUG) printf("End pdp_ipset\n");
324 return 0;
325}
326
327int pdp_ipdel(struct pdp_t *pdp) {
328 int hash = pdp_iphash(pdp->ipif, &pdp->eua);
329 struct pdp_t *pdp2;
330 struct pdp_t *pdp_prev = NULL;
331 if (PDP_DEBUG) printf("Begin pdp_ipdel\n");
332 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
333 if (pdp2 == pdp) {
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100334 if (!pdp_prev)
jjako52c24142002-12-16 13:33:51 +0000335 haship[hash] = pdp2->ipnext;
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100336 else
jjako52c24142002-12-16 13:33:51 +0000337 pdp_prev->ipnext = pdp2->ipnext;
338 if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
339 return 0;
340 }
341 pdp_prev = pdp2;
342 }
343 if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
jjakoa7cd2492003-04-11 09:40:12 +0000344 return EOF; /# End of linked list and not found #/
jjako52c24142002-12-16 13:33:51 +0000345}
346
347int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
348 int hash = pdp_iphash(ipif, eua);
349 struct pdp_t *pdp2;
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100350 /#printf("Begin pdp_ipget %d %d %2x%2x%2x%2x\n", (unsigned)ipif, eua->l,
jjakoa7cd2492003-04-11 09:40:12 +0000351 eua->v[2],eua->v[3],eua->v[4],eua->v[5]);#/
jjako52c24142002-12-16 13:33:51 +0000352 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext) {
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100353 if ((pdp2->ipif == ipif) && (pdp2->eua.l == eua->l) &&
jjako52c24142002-12-16 13:33:51 +0000354 (memcmp(&pdp2->eua.v, &eua->v, eua->l) == 0)) {
355 *pdp = pdp2;
jjakoa7cd2492003-04-11 09:40:12 +0000356 /#printf("End pdp_ipget. Found\n");#/
jjako52c24142002-12-16 13:33:51 +0000357 return 0;
358 }
359 }
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100360 if (PDP_DEBUG) printf("End pdp_ipget Notfound %d %d %2x%2x%2x%2x\n",
jjako52c24142002-12-16 13:33:51 +0000361 (unsigned)ipif, eua->l, eua->v[2],eua->v[3],eua->v[4],eua->v[5]);
jjakoa7cd2492003-04-11 09:40:12 +0000362 return EOF; /# End of linked list and not found #/
jjako52c24142002-12-16 13:33:51 +0000363}
jjakoa7cd2492003-04-11 09:40:12 +0000364*/
jjako52c24142002-12-16 13:33:51 +0000365/* Various conversion functions */
366
Harald Weltebed35df2011-11-02 13:06:18 +0100367uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
368{
369 return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
jjako52c24142002-12-16 13:33:51 +0000370}
371
Pablo Neira Ayuso746b9442014-03-24 17:58:27 +0100372void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid)
373{
374 pdp->imsi = teid & 0x0fffffffffffffffull;
375 pdp->nsapi = (teid & 0xf000000000000000ull) >> 60;
376}