blob: 2be2dd39896c1f5684e525d9965eff4522362f9a [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"
Pau Espin Pedrol623c5b32019-08-16 13:20:09 +020034#include "queue.h"
jjako52c24142002-12-16 13:33:51 +000035
36/* ***********************************************************
jjako52c24142002-12-16 13:33:51 +000037 * Functions related to PDP storage
38 *
39 * Lifecycle
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010040 * For a GGSN pdp context life begins with the reception of a
jjako52c24142002-12-16 13:33:51 +000041 * create pdp context request. It normally ends with the reception
42 * of a delete pdp context request, but will also end with the
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010043 * reception of an error indication message.
jjako52c24142002-12-16 13:33:51 +000044 * Provisions should probably be made for terminating pdp contexts
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010045 * based on either idle timeout, or by sending downlink probe
jjako52c24142002-12-16 13:33:51 +000046 * messages (ping?) to see if the MS is still responding.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010047 *
jjako52c24142002-12-16 13:33:51 +000048 * For an SGSN pdp context life begins with the application just
49 * before sending off a create pdp context request. It normally
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010050 * ends when a delete pdp context response message is received
jjako52c24142002-12-16 13:33:51 +000051 * from the GGSN, but should also end when with the reception of
52 * an error indication message.
53 *
54 *
55 * HASH Tables
56 *
57 * Downlink packets received in the GGSN are identified only by their
58 * network interface together with their destination IP address (Two
59 * network interfaces can use the same private IP address). Each IMSI
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010060 * (mobile station) can have several PDP contexts using the same IP
jjako52c24142002-12-16 13:33:51 +000061 * address. In this case the traffic flow template (TFT) is used to
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010062 * determine the correct PDP context for a particular IMSI. Also it
jjako52c24142002-12-16 13:33:51 +000063 * should be possible for each PDP context to use several IP adresses
64 * For fixed wireless access a mobile station might need a full class
65 * C network. Even in the case of several IP adresses the PDP context
66 * should be determined on the basis of the network IP address.
67 * Thus we need a hash table based on network interface + IP address.
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010068 *
jjako52c24142002-12-16 13:33:51 +000069 * Uplink packets are for GTP0 identified by their IMSI and NSAPI, which
70 * is collectively called the tunnel identifier. There is also a 16 bit
71 * flow label that can be used for identification of uplink packets. This
72 * however is quite useless as it limits the number of contexts to 65536.
73 * For GTP1 uplink packets are identified by a Tunnel Endpoint Identifier
74 * (32 bit), or in some cases by the combination of IMSI and NSAPI.
75 * For GTP1 delete context requests there is a need to find the PDP
76 * contexts with the same IP address. This however can be done by using
77 * the IP hash table.
78 * Thus we need a hash table based on TID (IMSI and NSAPI). The TEID will
79 * be used for directly addressing the PDP context.
80
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +010081 * pdp_newpdp
jjako52c24142002-12-16 13:33:51 +000082 * Gives you a pdp context with no hash references In some way
83 * this should have a limited lifetime.
84 *
85 * pdp_freepdp
86 * Frees a context that was previously allocated with
87 * pdp_newpdp
88 *
89 *
90 * pdp_getpdpIP
91 * An incoming IP packet is uniquely identified by a pointer
92 * to a network connection (void *) and an IP address
93 * (struct in_addr)
94 *
95 * pdp_getpdpGTP
96 * An incoming GTP packet is uniquely identified by a the
97 * TID (imsi + nsapi (8 octets)) in or by the Flow Label
98 * (2 octets) in gtp0 or by the Tunnel Endpoint Identifier
99 * (4 octets) in gtp1.
100 *
101 * This leads to an architecture where the receiving GSN
102 * chooses a Flow Label or a Tunnel Endpoint Identifier
103 * when the connection is setup.
104 * Thus no hash table is needed for GTP lookups.
105 *
106 *************************************************************/
107
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200108static struct gsn_t *g_gsn;
109
110int pdp_init(struct gsn_t *gsn)
Harald Weltebed35df2011-11-02 13:06:18 +0100111{
Pau Espin Pedrol3eb05d22019-08-28 11:17:08 +0200112 if (!g_gsn) {
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200113 g_gsn = gsn;
114 } else {
115 LOGP(DLGTP, LOGL_FATAL, "This interface is depreacted and doesn't support multiple GGSN!");
116 return -1;
117 }
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{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200125 return gtp_pdp_newpdp(g_gsn, pdp, imsi, nsapi, pdp_old);
126}
127
128int gtp_pdp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi,
129 struct pdp_t *pdp_old)
130{
131 struct pdp_t *pdpa = gsn->pdpa;
Harald Weltebed35df2011-11-02 13:06:18 +0100132 int n;
133 for (n = 0; n < PDP_MAX; n++) { /* TODO: Need to do better than linear search */
134 if (pdpa[n].inuse == 0) {
135 *pdp = &pdpa[n];
136 if (NULL != pdp_old)
137 memcpy(*pdp, pdp_old, sizeof(struct pdp_t));
138 else
139 memset(*pdp, 0, sizeof(struct pdp_t));
140 (*pdp)->inuse = 1;
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200141 (*pdp)->gsn = gsn;
Harald Weltebed35df2011-11-02 13:06:18 +0100142 (*pdp)->imsi = imsi;
143 (*pdp)->nsapi = nsapi;
144 (*pdp)->fllc = (uint16_t) n + 1;
145 (*pdp)->fllu = (uint16_t) n + 1;
146 (*pdp)->teid_own = (uint32_t) n + 1;
147 if (!(*pdp)->secondary)
148 (*pdp)->teic_own = (uint32_t) n + 1;
149 pdp_tidset(*pdp, pdp_gettid(imsi, nsapi));
150
151 /* Insert reference in primary context */
152 if (((*pdp)->teic_own > 0)
153 && ((*pdp)->teic_own <= PDP_MAX)) {
154 pdpa[(*pdp)->teic_own -
155 1].secondary_tei[(*pdp)->nsapi & 0x0f] =
156 (*pdp)->teid_own;
157 }
Harald Welte3c1cce22017-09-24 16:40:12 +0800158 /* Default: Generate G-PDU sequence numbers on Tx */
159 (*pdp)->tx_gpdu_seq = true;
Pau Espin Pedrol623c5b32019-08-16 13:20:09 +0200160 INIT_LLIST_HEAD(&(*pdp)->qmsg_list_req);
Harald Weltebed35df2011-11-02 13:06:18 +0100161 return 0;
162 }
163 }
164 return EOF; /* No more available */
jjako52c24142002-12-16 13:33:51 +0000165}
166
Harald Weltebed35df2011-11-02 13:06:18 +0100167int pdp_freepdp(struct pdp_t *pdp)
168{
Pau Espin Pedrol623c5b32019-08-16 13:20:09 +0200169 struct qmsg_t *qmsg, *qmsg2;
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200170 struct pdp_t *pdpa = pdp->gsn->pdpa;
Pau Espin Pedrol623c5b32019-08-16 13:20:09 +0200171 int rc;
172
173 /* Remove all enqueued messages belonging to this pdp from req tx transmit
174 queue. queue_freemsg will call llist_del(). */
175 llist_for_each_entry_safe(qmsg, qmsg2, &pdp->qmsg_list_req, entry) {
176 if ((rc = queue_freemsg(pdp->gsn->queue_req, qmsg)))
177 LOGP(DLGTP, LOGL_ERROR,
178 "Failed freeing qmsg from qmsg_list_req during pdp_freepdp()! %d\n", rc);
179 }
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200180
Harald Weltebed35df2011-11-02 13:06:18 +0100181 pdp_tiddel(pdp);
jjako2c381332003-10-21 19:09:53 +0000182
Harald Weltebed35df2011-11-02 13:06:18 +0100183 /* Remove any references in primary context */
184 if ((pdp->secondary) && (pdp->teic_own > 0)
185 && (pdp->teic_own <= PDP_MAX)) {
186 pdpa[pdp->teic_own - 1].secondary_tei[pdp->nsapi & 0x0f] = 0;
187 }
jjako2c381332003-10-21 19:09:53 +0000188
Harald Weltebed35df2011-11-02 13:06:18 +0100189 memset(pdp, 0, sizeof(struct pdp_t));
190 return 0;
jjako52c24142002-12-16 13:33:51 +0000191}
192
Harald Weltebed35df2011-11-02 13:06:18 +0100193int pdp_getpdp(struct pdp_t **pdp)
194{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200195 *pdp = &g_gsn->pdpa[0];
Harald Weltebed35df2011-11-02 13:06:18 +0100196 return 0;
jjako52c24142002-12-16 13:33:51 +0000197}
198
Harald Weltebed35df2011-11-02 13:06:18 +0100199int pdp_getgtp0(struct pdp_t **pdp, uint16_t fl)
200{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200201 return gtp_pdp_getgtp0(g_gsn, pdp, fl);
202}
203
204
205int gtp_pdp_getgtp0(struct gsn_t *gsn, struct pdp_t **pdp, uint16_t fl)
206{
207 struct pdp_t *pdpa = gsn->pdpa;
208
Harald Weltebed35df2011-11-02 13:06:18 +0100209 if ((fl > PDP_MAX) || (fl < 1)) {
210 return EOF; /* Not found */
211 } else {
212 *pdp = &pdpa[fl - 1];
213 if ((*pdp)->inuse)
214 return 0;
215 else
216 return EOF;
217 /* Context exists. We do no further validity checking. */
218 }
jjako52c24142002-12-16 13:33:51 +0000219}
220
Harald Weltebed35df2011-11-02 13:06:18 +0100221int pdp_getgtp1(struct pdp_t **pdp, uint32_t tei)
222{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200223 return gtp_pdp_getgtp1(g_gsn, pdp, tei);
224}
225
226int gtp_pdp_getgtp1(struct gsn_t *gsn, struct pdp_t **pdp, uint32_t tei)
227{
228 struct pdp_t *pdpa = gsn->pdpa;
229
Harald Weltebed35df2011-11-02 13:06:18 +0100230 if ((tei > PDP_MAX) || (tei < 1)) {
231 return EOF; /* Not found */
232 } else {
233 *pdp = &pdpa[tei - 1];
234 if ((*pdp)->inuse)
235 return 0;
236 else
237 return EOF;
238 /* Context exists. We do no further validity checking. */
239 }
jjako52c24142002-12-16 13:33:51 +0000240}
241
Harald Welte37d5b152017-08-12 23:58:29 +0200242/* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */
243int pdp_getgtp1_peer_d(struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
244{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200245 return gtp_pdp_getgtp1_peer_d(g_gsn, pdp, peer, teid_gn);
246}
247
248int gtp_pdp_getgtp1_peer_d(struct gsn_t *gsn, struct pdp_t **pdp, const struct sockaddr_in *peer, uint32_t teid_gn)
249{
250 struct pdp_t *pdpa = gsn->pdpa;
Harald Welte37d5b152017-08-12 23:58:29 +0200251 unsigned int i;
252
253 /* this is O(n) but we don't have (nor want) another hash... */
254 for (i = 0; i < PDP_MAX; i++) {
255 struct pdp_t *candidate = &pdpa[i];
256 if (candidate->inuse && candidate->teid_gn == teid_gn &&
257 candidate->gsnru.l == sizeof(peer->sin_addr) &&
258 !memcmp(&peer->sin_addr, candidate->gsnru.v, sizeof(peer->sin_addr))) {
259 *pdp = &pdpa[i];
260 return 0;
261 }
262 }
263 return EOF;
264}
265
Harald Weltebed35df2011-11-02 13:06:18 +0100266int pdp_tidhash(uint64_t tid)
267{
268 return (lookup(&tid, sizeof(tid), 0) % PDP_MAX);
jjako52c24142002-12-16 13:33:51 +0000269}
270
Harald Weltebed35df2011-11-02 13:06:18 +0100271int pdp_tidset(struct pdp_t *pdp, uint64_t tid)
272{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200273 struct pdp_t **hashtid = pdp->gsn->hashtid;
Harald Weltebed35df2011-11-02 13:06:18 +0100274 int hash = pdp_tidhash(tid);
275 struct pdp_t *pdp2;
276 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100277 DEBUGP(DLGTP, "Begin pdp_tidset tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100278 pdp->tidnext = NULL;
279 pdp->tid = tid;
280 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext)
281 pdp_prev = pdp2;
282 if (!pdp_prev)
283 hashtid[hash] = pdp;
284 else
285 pdp_prev->tidnext = pdp;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100286 DEBUGP(DLGTP, "End pdp_tidset\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100287 return 0;
jjako52c24142002-12-16 13:33:51 +0000288}
289
Harald Weltebed35df2011-11-02 13:06:18 +0100290int pdp_tiddel(struct pdp_t *pdp)
291{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200292 struct pdp_t **hashtid = pdp->gsn->hashtid;
Harald Weltebed35df2011-11-02 13:06:18 +0100293 int hash = pdp_tidhash(pdp->tid);
294 struct pdp_t *pdp2;
295 struct pdp_t *pdp_prev = NULL;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100296 DEBUGP(DLGTP, "Begin pdp_tiddel tid = %"PRIx64"\n", pdp->tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100297 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
298 if (pdp2 == pdp) {
299 if (!pdp_prev)
300 hashtid[hash] = pdp2->tidnext;
301 else
302 pdp_prev->tidnext = pdp2->tidnext;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100303 DEBUGP(DLGTP, "End pdp_tiddel: PDP found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100304 return 0;
305 }
306 pdp_prev = pdp2;
307 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100308 DEBUGP(DLGTP, "End pdp_tiddel: PDP not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100309 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000310}
311
Harald Weltebed35df2011-11-02 13:06:18 +0100312int pdp_tidget(struct pdp_t **pdp, uint64_t tid)
313{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200314 return gtp_pdp_tidget(g_gsn, pdp, tid);
315}
316
317int gtp_pdp_tidget(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t tid)
318{
319 struct pdp_t **hashtid = gsn->hashtid;
Harald Weltebed35df2011-11-02 13:06:18 +0100320 int hash = pdp_tidhash(tid);
321 struct pdp_t *pdp2;
Alexander Huemerdb852a12015-11-06 20:59:01 +0100322 DEBUGP(DLGTP, "Begin pdp_tidget tid = %"PRIx64"\n", tid);
Harald Weltebed35df2011-11-02 13:06:18 +0100323 for (pdp2 = hashtid[hash]; pdp2; pdp2 = pdp2->tidnext) {
324 if (pdp2->tid == tid) {
325 *pdp = pdp2;
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100326 DEBUGP(DLGTP, "Begin pdp_tidget. Found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100327 return 0;
328 }
329 }
Holger Hans Peter Freyther01b40d02014-12-04 15:01:53 +0100330 DEBUGP(DLGTP, "Begin pdp_tidget. Not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100331 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000332}
333
Harald Weltebed35df2011-11-02 13:06:18 +0100334int pdp_getimsi(struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
335{
Pau Espin Pedroleefa30d2019-05-31 15:42:49 +0200336 return gtp_pdp_getimsi(g_gsn, pdp, imsi, nsapi);
337}
338
339int gtp_pdp_getimsi(struct gsn_t *gsn, struct pdp_t **pdp, uint64_t imsi, uint8_t nsapi)
340{
Keith48318512020-10-10 15:19:59 +0200341 return gtp_pdp_tidget(gsn, pdp, pdp_gettid(imsi, nsapi));
jjako08d331d2003-10-13 20:33:30 +0000342}
343
Pau Espin Pedrol5f5fcff2018-01-25 18:09:02 +0100344
jjako52c24142002-12-16 13:33:51 +0000345/* Various conversion functions */
346
Harald Weltebed35df2011-11-02 13:06:18 +0100347uint64_t pdp_gettid(uint64_t imsi, uint8_t nsapi)
348{
349 return (imsi & 0x0fffffffffffffffull) + ((uint64_t) nsapi << 60);
jjako52c24142002-12-16 13:33:51 +0000350}
351
Pablo Neira Ayuso746b9442014-03-24 17:58:27 +0100352void pdp_set_imsi_nsapi(struct pdp_t *pdp, uint64_t teid)
353{
354 pdp->imsi = teid & 0x0fffffffffffffffull;
355 pdp->nsapi = (teid & 0xf000000000000000ull) >> 60;
356}
Pau Espin Pedrol9ee8d322019-05-30 14:11:22 +0200357
358/* Count amount of secondary PDP contexts linked to this primary PDP context
359 * (itself included). Must be called on a primary PDP context. */
Pau Espin Pedrol88ce94c2019-08-20 18:24:02 +0200360unsigned int pdp_count_secondary(const struct pdp_t *pdp)
Pau Espin Pedrol9ee8d322019-05-30 14:11:22 +0200361{
362 unsigned int n;
363 unsigned int count = 0;
364 OSMO_ASSERT(!pdp->secondary);
365
366 for (n = 0; n < PDP_MAXNSAPI; n++)
367 if (pdp->secondary_tei[n])
368 count++;
369 return count;
370}