2 * OpenGGSN - Gateway GPRS Support Node
3 * Copyright (C) 2002, 2003, 2004 Mondru AB.
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.
17 #include <../config.h>
19 #include <osmocom/core/logging.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
34 /* ***********************************************************
35 * Global variables TODO: most should be moved to gsn_t
36 *************************************************************/
38 static struct pdp_t pdpa
[PDP_MAX
]; /* PDP storage */
39 static struct pdp_t
*hashtid
[PDP_MAX
]; /* Hash table for IMSI + NSAPI */
40 /* struct pdp_t* haship[PDP_MAX]; Hash table for IP and network interface */
42 /* ***********************************************************
43 * Functions related to PDP storage
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.
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.
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.
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
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.
88 * Gives you a pdp context with no hash references In some way
89 * this should have a limited lifetime.
92 * Frees a context that was previously allocated with
97 * An incoming IP packet is uniquely identified by a pointer
98 * to a network connection (void *) and an IP address
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.
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.
112 *************************************************************/
116 memset(&pdpa
, 0, sizeof(pdpa
));
117 memset(&hashtid
, 0, sizeof(hashtid
));
118 /* memset(&haship, 0, sizeof(haship)); */
123 int pdp_newpdp(struct pdp_t
**pdp
, uint64_t imsi
, uint8_t nsapi
,
124 struct pdp_t
*pdp_old
)
127 for (n
= 0; n
< PDP_MAX
; n
++) { /* TODO: Need to do better than linear search */
128 if (pdpa
[n
].inuse
== 0) {
131 memcpy(*pdp
, pdp_old
, sizeof(struct pdp_t
));
133 memset(*pdp
, 0, sizeof(struct pdp_t
));
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
));
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] =
155 return EOF
; /* No more available */
158 int pdp_freepdp(struct pdp_t
*pdp
)
162 /* 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;
168 memset(pdp
, 0, sizeof(struct pdp_t
));
172 int pdp_getpdp(struct pdp_t
**pdp
)
178 int pdp_getgtp0(struct pdp_t
**pdp
, uint16_t fl
)
180 if ((fl
> PDP_MAX
) || (fl
< 1)) {
181 return EOF
; /* Not found */
183 *pdp
= &pdpa
[fl
- 1];
188 /* Context exists. We do no further validity checking. */
192 int pdp_getgtp1(struct pdp_t
**pdp
, uint32_t tei
)
194 if ((tei
> PDP_MAX
) || (tei
< 1)) {
195 return EOF
; /* Not found */
197 *pdp
= &pdpa
[tei
- 1];
202 /* Context exists. We do no further validity checking. */
206 /* get a PDP based on the *peer* address + TEI-Data. Used for matching inbound Error Ind */
207 int pdp_getgtp1_peer_d(struct pdp_t
**pdp
, const struct sockaddr_in
*peer
, uint32_t teid_gn
)
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
))) {
224 int pdp_tidhash(uint64_t tid
)
226 return (lookup(&tid
, sizeof(tid
), 0) % PDP_MAX
);
229 int pdp_tidset(struct pdp_t
*pdp
, uint64_t tid
)
231 int hash
= pdp_tidhash(tid
);
233 struct pdp_t
*pdp_prev
= NULL
;
234 DEBUGP(DLGTP
, "Begin pdp_tidset tid = %"PRIx64
"\n", tid
);
237 for (pdp2
= hashtid
[hash
]; pdp2
; pdp2
= pdp2
->tidnext
)
242 pdp_prev
->tidnext
= pdp
;
243 DEBUGP(DLGTP
, "End pdp_tidset\n");
247 int pdp_tiddel(struct pdp_t
*pdp
)
249 int hash
= pdp_tidhash(pdp
->tid
);
251 struct pdp_t
*pdp_prev
= NULL
;
252 DEBUGP(DLGTP
, "Begin pdp_tiddel tid = %"PRIx64
"\n", pdp
->tid
);
253 for (pdp2
= hashtid
[hash
]; pdp2
; pdp2
= pdp2
->tidnext
) {
256 hashtid
[hash
] = pdp2
->tidnext
;
258 pdp_prev
->tidnext
= pdp2
->tidnext
;
259 DEBUGP(DLGTP
, "End pdp_tiddel: PDP found\n");
264 DEBUGP(DLGTP
, "End pdp_tiddel: PDP not found\n");
265 return EOF
; /* End of linked list and not found */
268 int pdp_tidget(struct pdp_t
**pdp
, uint64_t tid
)
270 int hash
= pdp_tidhash(tid
);
272 DEBUGP(DLGTP
, "Begin pdp_tidget tid = %"PRIx64
"\n", tid
);
273 for (pdp2
= hashtid
[hash
]; pdp2
; pdp2
= pdp2
->tidnext
) {
274 if (pdp2
->tid
== tid
) {
276 DEBUGP(DLGTP
, "Begin pdp_tidget. Found\n");
280 DEBUGP(DLGTP
, "Begin pdp_tidget. Not found\n");
281 return EOF
; /* End of linked list and not found */
284 int pdp_getimsi(struct pdp_t
**pdp
, uint64_t imsi
, uint8_t nsapi
)
286 return pdp_tidget(pdp
,
287 (imsi
& 0x0fffffffffffffffull
) +
288 ((uint64_t) nsapi
<< 60));
292 int pdp_iphash(void* ipif, struct ul66_t *eua) {
293 /#printf("IPhash %ld\n", lookup(eua->v, eua->l, ipif) % PDP_MAX);#/
294 return (lookup(eua->v, eua->l, ipif) % PDP_MAX);
297 int pdp_ipset(struct pdp_t *pdp, void* ipif, struct ul66_t *eua) {
300 struct pdp_t *pdp_prev = NULL;
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]);
310 memcpy(pdp->eua.v, eua->v, eua->l);
312 hash = pdp_iphash(pdp->ipif, &pdp->eua);
314 for (pdp2 = haship[hash]; pdp2; pdp2 = pdp2->ipnext)
319 pdp_prev->ipnext = pdp;
320 if (PDP_DEBUG) printf("End pdp_ipset\n");
324 int pdp_ipdel(struct pdp_t *pdp) {
325 int hash = pdp_iphash(pdp->ipif, &pdp->eua);
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) {
332 haship[hash] = pdp2->ipnext;
334 pdp_prev->ipnext = pdp2->ipnext;
335 if (PDP_DEBUG) printf("End pdp_ipdel: PDP found\n");
340 if (PDP_DEBUG) printf("End pdp_ipdel: PDP not found\n");
341 return EOF; /# End of linked list and not found #/
344 int pdp_ipget(struct pdp_t **pdp, void* ipif, struct ul66_t *eua) {
345 int hash = pdp_iphash(ipif, eua);
347 /#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]);#/
349 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)) {
353 /#printf("End pdp_ipget. Found\n");#/
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]);
359 return EOF; /# End of linked list and not found #/
362 /* Various conversion functions */
364 int pdp_ntoeua(struct in_addr
*src
, struct ul66_t
*eua
)
367 eua
->v
[0] = 0xf1; /* IETF */
368 eua
->v
[1] = 0x21; /* IPv4 */
369 memcpy(&eua
->v
[2], src
, 4); /* Copy a 4 byte address */
373 int pdp_euaton(struct ul66_t
*eua
, struct in_addr
*dst
)
375 if ((eua
->l
!= 6) || (eua
->v
[0] != 0xf1) || (eua
->v
[1] != 0x21)) {
378 memcpy(dst
, &eua
->v
[2], 4); /* Copy a 4 byte address */
382 uint64_t pdp_gettid(uint64_t imsi
, uint8_t nsapi
)
384 return (imsi
& 0x0fffffffffffffffull
) + ((uint64_t) nsapi
<< 60);
387 void pdp_set_imsi_nsapi(struct pdp_t
*pdp
, uint64_t teid
)
389 pdp
->imsi
= teid
& 0x0fffffffffffffffull
;
390 pdp
->nsapi
= (teid
& 0xf000000000000000ull
) >> 60;