blob: d7459161494c293c62e00256c12b6e31006b5e26 [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/* ***********************************************************
jjako52c24142002-12-16 13:33:51 +000036 * Functions related to PDP storage
37 *
38 * Lifecycle
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010039 * For a GGSN pdp context life begins with the reception of a
jjako52c24142002-12-16 13:33:51 +000040 * create pdp context request. It normally ends with the reception
41 * of a delete pdp context request, but will also end with the
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010042 * reception of an error indication message.
jjako52c24142002-12-16 13:33:51 +000043 * Provisions should probably be made for terminating pdp contexts
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010044 * based on either idle timeout, or by sending downlink probe
jjako52c24142002-12-16 13:33:51 +000045 * messages (ping?) to see if the MS is still responding.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010046 *
jjako52c24142002-12-16 13:33:51 +000047 * For an SGSN pdp context life begins with the application just
48 * before sending off a create pdp context request. It normally
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010049 * ends when a delete pdp context response message is received
jjako52c24142002-12-16 13:33:51 +000050 * from the GGSN, but should also end when with the reception of
51 * an error indication message.
52 *
53 *
54 * HASH Tables
55 *
56 * Downlink packets received in the GGSN are identified only by their
57 * network interface together with their destination IP address (Two
58 * network interfaces can use the same private IP address). Each IMSI
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010059 * (mobile station) can have several PDP contexts using the same IP
jjako52c24142002-12-16 13:33:51 +000060 * address. In this case the traffic flow template (TFT) is used to
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010061 * determine the correct PDP context for a particular IMSI. Also it
jjako52c24142002-12-16 13:33:51 +000062 * should be possible for each PDP context to use several IP adresses
63 * For fixed wireless access a mobile station might need a full class
64 * C network. Even in the case of several IP adresses the PDP context
65 * should be determined on the basis of the network IP address.
66 * Thus we need a hash table based on network interface + IP address.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010067 *
jjako52c24142002-12-16 13:33:51 +000068 * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
69 * is collectively called the tunnel identifier. There is also a 16 bit
70 * flow label that can be used for identification of uplink packets. This
71 * however is quite useless as it limits the number of contexts to 65536.
72 * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier
73 * (32 bit), or in some cases by the combination of IMSI and NSAPI.
74 * For GTP1 delete context requests there is a need to find the PDP
75 * contexts with the same IP address. This however can be done by using
76 * the IP hash table.
77 * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
78 * be used for directly addressing the PDP context.
79
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010080 * pdp_newpdp
jjako52c24142002-12-16 13:33:51 +000081 * Gives you a pdp context with no hash references In some way
82 * this should have a limited lifetime.
83 *
84 * pdp_freepdp
85 * Frees a context that was previously allocated with
86 * pdp_newpdp
87 *
88 *
89 * pdp_getpdpIP
90 * An incoming IP packet is uniquely identified by a pointer
91 * to a network connection (void *) and an IP address
92 * (struct in_addr)
93 *
94 * pdp_getpdpGTP
95 * An incoming GTP packet is uniquely identified by a the
96 * TID (imsi + nsapi (8 octets)) in or by the Flow Label
97 * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier
98 * (4 octets) in gtp1.
99 *
100 * This leads to an architecture where the receiving GSN
101 * chooses a Flow Label or a Tunnel Endpoint Identifier
102 * when the connection is setup.
103 * Thus no hash table is needed for GTP lookups.
104 *
105 *************************************************************/
106
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200107static struct gsn_t *g_gsn;
108
109int pdp_init(struct gsn_t *gsn)
Harald Weltebed35df2011-11-02 13:06:18 +0100110{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200111 if(!g_gsn) {
112 g_gsn = gsn;
113 } else {
114 LOGP(DLGTP, LOGL_FATAL, "This interface is depreacted and doesn't support multiple GGSN!");
115 return -1;
116 }
jjako52c24142002-12-16 13:33:51 +0000117
Harald Weltebed35df2011-11-02 13:06:18 +0100118 return 0;
jjako52c24142002-12-16 13:33:51 +0000119}
120
Harald Weltebed35df2011-11-02 13:06:18 +0100121int pdp_newpdp(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
122 struct pdp_t *pdp_old)
123{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200124 return gtp_pdp_newpdp(g_gsn, pdp, imsi, nsapi, pdp_old);
125}
126
127int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
128 struct pdp_t *pdp_old)
129{
130 struct pdp_t *pdpa = gsn->pdpa;
Harald Weltebed35df2011-11-02 13:06:18 +0100131 int n;
132 for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */
133 if (pdpa[n].inuse == 0) {
134 *pdp = &pdpa[n];
135 if (NULL != pdp_old)
136 memcpy(*pdp, pdp_old, sizeof(struct pdp_t));
137 else
138 memset(*pdp, 0, sizeof(struct pdp_t));
139 (*pdp)->inuse = 1;
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200140 (*pdp)->gsn = gsn;
Harald Weltebed35df2011-11-02 13:06:18 +0100141 (*pdp)->imsi = imsi;
142 (*pdp)->nsapi = nsapi;
143 (*pdp)->fllc = (uint16_t) n + 1;
144 (*pdp)->fllu = (uint16_t) n + 1;
145 (*pdp)->teid_own = (uint32_t) n + 1;
146 if (!(*pdp)->secondary)
147 (*pdp)->teic_own = (uint32_t) n + 1;
148 pdp_tidset(*pdp, pdp_gettid(imsi, nsapi));
149
150 /* Insert reference in primary context */
151 if (((*pdp)->teic_own > 0)
152 && ((*pdp)->teic_own <= PDP_MAX)) {
153 pdpa[(*pdp)->teic_own -
154 1].secondary_tei[(*pdp)->nsapi & 0x0f] =
155 (*pdp)->teid_own;
156 }
Harald Welte3c1cce22017-09-24 16:40:12 +0800157 /* Default: Generate G-PDU sequence numbers on Tx */
158 (*pdp)->tx_gpdu_seq = true;
Harald Weltebed35df2011-11-02 13:06:18 +0100159
160 return 0;
161 }
162 }
163 return EOF; /* No more available */
jjako52c24142002-12-16 13:33:51 +0000164}
165
Harald Weltebed35df2011-11-02 13:06:18 +0100166int pdp_freepdp(struct pdp_t *pdp)
167{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200168 struct pdp_t *pdpa = pdp->gsn->pdpa;
169
Harald Weltebed35df2011-11-02 13:06:18 +0100170 pdp_tiddel(pdp);
jjako2c381332003-10-21 19:09:53 +0000171
Harald Weltebed35df2011-11-02 13:06:18 +0100172 /* Remove any references in primary context */
173 if ((pdp->secondary) && (pdp->teic_own > 0)
174 && (pdp->teic_own <= PDP_MAX)) {
175 pdpa[pdp->teic_own - 1].secondary_tei[pdp->nsapi & 0x0f] = 0;
176 }
jjako2c381332003-10-21 19:09:53 +0000177
Harald Weltebed35df2011-11-02 13:06:18 +0100178 memset(pdp, 0, sizeof(struct pdp_t));
179 return 0;
jjako52c24142002-12-16 13:33:51 +0000180}
181
Harald Weltebed35df2011-11-02 13:06:18 +0100182int pdp_getpdp(struct pdp_t **pdp)
183{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200184 *pdp = &g_gsn->pdpa[0];
Harald Weltebed35df2011-11-02 13:06:18 +0100185 return 0;
jjako52c24142002-12-16 13:33:51 +0000186}
187
Harald Weltebed35df2011-11-02 13:06:18 +0100188int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
189{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200190 return gtp_pdp_getgtp0(g_gsn, pdp, fl);
191}
192
193
194int gtp_pdp_getgtp0(struct gsn_t *gsn, struct pdp_t **pdp, uint16_t fl)
195{
196 struct pdp_t *pdpa = gsn->pdpa;
197
Harald Weltebed35df2011-11-02 13:06:18 +0100198 if ((fl > PDP_MAX) || (fl < 1)) {
199 return EOF; /* Not found */
200 } else {
201 *pdp = &pdpa[fl - 1];
202 if ((*pdp)->inuse)
203 return 0;
204 else
205 return EOF;
206 /* Context exists. We do no further validity checking. */
207 }
jjako52c24142002-12-16 13:33:51 +0000208}
209
Harald Weltebed35df2011-11-02 13:06:18 +0100210int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
211{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200212 return gtp_pdp_getgtp1(g_gsn, pdp, tei);
213}
214
215int gtp_pdp_getgtp1(struct gsn_t *gsn, struct pdp_t **pdp, uint32_t tei)
216{
217 struct pdp_t *pdpa = gsn->pdpa;
218
Harald Weltebed35df2011-11-02 13:06:18 +0100219 if ((tei > PDP_MAX) || (tei < 1)) {
220 return EOF; /* Not found */
221 } else {
222 *pdp = &pdpa[tei - 1];
223 if ((*pdp)->inuse)
224 return 0;
225 else
226 return EOF;
227 /* Context exists. We do no further validity checking. */
228 }
jjako52c24142002-12-16 13:33:51 +0000229}
230
Harald Welte37d5b152017-08-12 23:58:29 +0200231/* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */
232int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
233{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200234 return gtp_pdp_getgtp1_peer_d(g_gsn, pdp, peer, teid_gn);
235}
236
237int gtp_pdp_getgtp1_peer_d(struct gsn_t *gsn, struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
238{
239 struct pdp_t *pdpa = gsn->pdpa;
Harald Welte37d5b152017-08-12 23:58:29 +0200240 unsigned int i;
241
242 /* this is O(n) but we don't have (nor want) another hash... */
243 for (i = 0; i < PDP_MAX; i++) {
244 struct pdp_t *candidate = &pdpa[i];
245 if (candidate->inuse && candidate->teid_gn == teid_gn &&
246 candidate->gsnru.l == sizeof(peer->sin_addr) &&
247 !memcmp(&peer->sin_addr, candidate->gsnru.v, sizeof(peer->sin_addr))) {
248 *pdp = &pdpa[i];
249 return 0;
250 }
251 }
252 return EOF;
253}
254
Harald Weltebed35df2011-11-02 13:06:18 +0100255int pdp_tidhash(uint64_t tid)
256{
257 return (lookup(&tid, sizeof(tid), 0) % PDP_MAX);
jjako52c24142002-12-16 13:33:51 +0000258}
259
Harald Weltebed35df2011-11-02 13:06:18 +0100260int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
261{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200262 struct pdp_t **hashtid = pdp->gsn->hashtid;
Harald Weltebed35df2011-11-02 13:06:18 +0100263 int hash = pdp_tidhash(tid);
264 struct pdp_t *pdp2;
265 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100266 DEBUGP(DLGTP, "Begin pdp_tidset tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100267 pdp->tidnext = NULL;
268 pdp->tid = tid;
269 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext)
270 pdp_prev = pdp2;
271 if (!pdp_prev)
272 hashtid[hash] = pdp;
273 else
274 pdp_prev->tidnext = pdp;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100275 DEBUGP(DLGTP, "End pdp_tidset\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100276 return 0;
jjako52c24142002-12-16 13:33:51 +0000277}
278
Harald Weltebed35df2011-11-02 13:06:18 +0100279int pdp_tiddel(struct pdp_t *pdp)
280{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200281 struct pdp_t **hashtid = pdp->gsn->hashtid;
Harald Weltebed35df2011-11-02 13:06:18 +0100282 int hash = pdp_tidhash(pdp->tid);
283 struct pdp_t *pdp2;
284 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100285 DEBUGP(DLGTP, "Begin pdp_tiddel tid = %"PRIx64"\n", pdp->tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100286 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
287 if (pdp2 == pdp) {
288 if (!pdp_prev)
289 hashtid[hash] = pdp2->tidnext;
290 else
291 pdp_prev->tidnext = pdp2->tidnext;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100292 DEBUGP(DLGTP, "End pdp_tiddel: PDP found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100293 return 0;
294 }
295 pdp_prev = pdp2;
296 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100297 DEBUGP(DLGTP, "End pdp_tiddel: PDP not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100298 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000299}
300
Harald Weltebed35df2011-11-02 13:06:18 +0100301int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
302{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200303 return gtp_pdp_tidget(g_gsn, pdp, tid);
304}
305
306int gtp_pdp_tidget(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t tid)
307{
308 struct pdp_t **hashtid = gsn->hashtid;
Harald Weltebed35df2011-11-02 13:06:18 +0100309 int hash = pdp_tidhash(tid);
310 struct pdp_t *pdp2;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100311 DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100312 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
313 if (pdp2->tid == tid) {
314 *pdp = pdp2;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100315 DEBUGP(DLGTP, "Begin pdp_tidget. Found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100316 return 0;
317 }
318 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100319 DEBUGP(DLGTP, "Begin pdp_tidget. Not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100320 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000321}
322
Harald Weltebed35df2011-11-02 13:06:18 +0100323int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
324{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200325 return gtp_pdp_getimsi(g_gsn, pdp, imsi, nsapi);
326}
327
328int gtp_pdp_getimsi(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
329{
330 return gtp_pdp_tidget(gsn, pdp,
Harald Weltebed35df2011-11-02 13:06:18 +0100331 (imsi & 0x0fffffffffffffffull) +
332 ((uint64_t) nsapi << 60));
jjako08d331d2003-10-13 20:33:30 +0000333}
334
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100335
jjako52c24142002-12-16 13:33:51 +0000336/* Various conversion functions */
337
Harald Weltebed35df2011-11-02 13:06:18 +0100338uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
339{
340 return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
jjako52c24142002-12-16 13:33:51 +0000341}
342
Pablo Neira Ayuso746b9442014-03-24 17:58:27 +0100343void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid)
344{
345 pdp->imsi = teid & 0x0fffffffffffffffull;
346 pdp->nsapi = (teid & 0xf000000000000000ull) >> 60;
347}
Pau Espin Pedrol9ee8d322019-05-30 14:11:22 +0200348
349/* Count amount of secondary PDP contexts linked to this primary PDP context
350 * (itself included). Must be called on a primary PDP context. */
351unsigned int pdp_count_secondary(struct pdp_t *pdp)
352{
353 unsigned int n;
354 unsigned int count = 0;
355 OSMO_ASSERT(!pdp->secondary);
356
357 for (n = 0; n < PDP_MAXNSAPI; n++)
358 if (pdp->secondary_tei[n])
359 count++;
360 return count;
361}