blob: 4c25913ffda05bba7ad97693f68b8e03bfe398cb [file] [log] [blame]
Pau Espin Pedrolf0829ff2019-06-20 16:58:55 +02001/*
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 Welteb841e572011-11-02 13:45:50 +01004 * Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
Harald Welte632e8432017-09-05 18:12:14 +02005 * Copyright (C) 2016 sysmocom - s.f.m.c. GmbH
Pau Espin Pedrolf0829ff2019-06-20 16:58:55 +02006 *
jjako52c24142002-12-16 13:33:51 +00007 * The contents of this file may be used under the terms of the GNU
8 * General Public License Version 2, provided that the above copyright
9 * notice and this permission notice is included in all copies or
10 * substantial portions of the software.
Pau Espin Pedrolf0829ff2019-06-20 16:58:55 +020011 *
jjako52c24142002-12-16 13:33:51 +000012 */
13
14/*
15 * Queue.c
16 * Reliable delivery of signalling messages
17 */
18
jjako0fe0df02004-09-17 11:30:40 +000019#include <../config.h>
20#ifdef HAVE_STDINT_H
21#include <stdint.h>
22#endif
23
jjako52c24142002-12-16 13:33:51 +000024#include <stdlib.h>
25#include <stdio.h>
26#include <sys/types.h>
27#include <sys/time.h>
28#include <netinet/in.h>
29#include <string.h>
30#include "pdp.h"
31#include "gtp.h"
32#include "queue.h"
33
Harald Welteb841e572011-11-02 13:45:50 +010034/*! \brief dump a queue_t to stdout */
Harald Weltebb47c352011-11-02 13:30:37 +010035static int queue_print(struct queue_t *queue)
Harald Weltebed35df2011-11-02 13:06:18 +010036{
37 int n;
Harald Weltee65c7392011-11-02 13:12:53 +010038 printf("Queue: %p Next: %d First: %d Last: %d\n", queue,
Harald Weltebed35df2011-11-02 13:06:18 +010039 queue->next, queue->first, queue->last);
40 printf("# State seq next prev timeout retrans\n");
41 for (n = 0; n < QUEUE_SIZE; n++) {
42 printf("%d %d %d %d %d %d %d\n",
43 n,
44 queue->qmsga[n].state,
45 queue->qmsga[n].seq,
46 queue->qmsga[n].next,
47 queue->qmsga[n].prev,
48 (int)queue->qmsga[n].timeout, queue->qmsga[n].retrans);
49 }
50 return 0;
jjako52c24142002-12-16 13:33:51 +000051}
52
Harald Welteb841e572011-11-02 13:45:50 +010053/*! \brief compute the hash function */
Harald Weltebb47c352011-11-02 13:30:37 +010054static int queue_seqhash(struct sockaddr_in *peer, uint16_t seq)
Harald Weltebed35df2011-11-02 13:06:18 +010055{
56 /* With QUEUE_HASH_SIZE = 2^16 this describes all possible
57 seq values. Thus we have perfect hash for the request queue.
58 For the response queue we might have collisions, but not very
59 often.
60 For performance optimisation we should remove the modulus
61 operator, but this is only valid for QUEUE_HASH_SIZE = 2^16 */
62 return seq % QUEUE_HASH_SIZE;
jjako52c24142002-12-16 13:33:51 +000063}
64
Pau Espin Pedrole725d872019-06-20 16:59:49 +020065/*! \brief Insert a message with given sequence number into the hash.
Harald Welteb841e572011-11-02 13:45:50 +010066 *
67 * This function sets the peer and the seq of the qmsg and then inserts
68 * the qmsg into the queue hash. To do so, it does a hashtable lookup
69 * and appends the new entry as the last into the double-linked list of
70 * entries for this sequence number.
71 */
Harald Weltebb47c352011-11-02 13:30:37 +010072static int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
73 struct sockaddr_in *peer, uint16_t seq)
Harald Weltebed35df2011-11-02 13:06:18 +010074{
75 int hash = queue_seqhash(peer, seq);
76 struct qmsg_t *qmsg2;
77 struct qmsg_t *qmsg_prev = NULL;
jjako52c24142002-12-16 13:33:51 +000078
Harald Weltebed35df2011-11-02 13:06:18 +010079 if (QUEUE_DEBUG)
80 printf("Begin queue_seqset seq = %d\n", (int)seq);
81 if (QUEUE_DEBUG)
Stefan Sperling3730c552018-11-21 15:54:45 +010082 printf("SIZEOF PEER %zu, *PEER %zu\n", sizeof(peer),
Harald Weltebed35df2011-11-02 13:06:18 +010083 sizeof(*peer));
jjako52c24142002-12-16 13:33:51 +000084
Harald Weltebed35df2011-11-02 13:06:18 +010085 qmsg->seq = seq;
86 memcpy(&qmsg->peer, peer, sizeof(*peer));
jjako52c24142002-12-16 13:33:51 +000087
Harald Weltebed35df2011-11-02 13:06:18 +010088 for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext)
89 qmsg_prev = qmsg2;
90 if (!qmsg_prev)
91 queue->hashseq[hash] = qmsg;
92 else
93 qmsg_prev->seqnext = qmsg;
94 if (QUEUE_DEBUG)
95 printf("End queue_seqset\n");
96 return 0;
jjako52c24142002-12-16 13:33:51 +000097}
jjako08d331d2003-10-13 20:33:30 +000098
Harald Welteb841e572011-11-02 13:45:50 +010099/*! \brief Remove a given qmsg_t from the queue hash */
Harald Weltebb47c352011-11-02 13:30:37 +0100100static int queue_seqdel(struct queue_t *queue, struct qmsg_t *qmsg)
Harald Weltebed35df2011-11-02 13:06:18 +0100101{
102 int hash = queue_seqhash(&qmsg->peer, qmsg->seq);
103 struct qmsg_t *qmsg2;
104 struct qmsg_t *qmsg_prev = NULL;
105 if (QUEUE_DEBUG)
106 printf("Begin queue_seqdel seq = %d\n", (int)qmsg->seq);
jjako08d331d2003-10-13 20:33:30 +0000107
Harald Weltebed35df2011-11-02 13:06:18 +0100108 for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
Alexander Couzens86540de2016-05-31 14:42:38 +0200109 if (qmsg == qmsg2) {
Harald Weltebed35df2011-11-02 13:06:18 +0100110 if (!qmsg_prev)
111 queue->hashseq[hash] = qmsg2->seqnext;
112 else
113 qmsg_prev->seqnext = qmsg2->seqnext;
114 if (QUEUE_DEBUG)
Harald Welteef711622011-11-02 18:07:02 +0100115 printf("End queue_seqdel: SEQ found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100116 return 0;
117 }
118 qmsg_prev = qmsg2;
119 }
Harald Welteef711622011-11-02 18:07:02 +0100120 printf("End queue_seqdel: SEQ not found\n");
Harald Weltebed35df2011-11-02 13:06:18 +0100121 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000122}
123
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200124/*! Allocates and initialises new queue structure.
125 * \param[out] queue pointer where to store the allocated object. Must be freed with queue_free
126 * \returns zero on success, non-zero on error
127 */
Harald Weltebed35df2011-11-02 13:06:18 +0100128int queue_new(struct queue_t **queue)
129{
130 if (QUEUE_DEBUG)
131 printf("queue_new\n");
132 *queue = calloc(1, sizeof(struct queue_t));
Neels Hofmeyrf89dc4e2016-04-25 13:00:10 +0200133 if (!(*queue))
134 return EOF;
Harald Weltebed35df2011-11-02 13:06:18 +0100135 (*queue)->next = 0;
136 (*queue)->first = -1;
137 (*queue)->last = -1;
jjako52c24142002-12-16 13:33:51 +0000138
Harald Weltebed35df2011-11-02 13:06:18 +0100139 if (QUEUE_DEBUG)
140 queue_print(*queue);
Neels Hofmeyrf89dc4e2016-04-25 13:00:10 +0200141 return 0;
jjako52c24142002-12-16 13:33:51 +0000142}
143
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200144/*! Deallocates queue structure.
145 * \param[in] queue pointer previously allocated by queue_new
146 * \returns zero on success, non-zero on error.
147 */
Harald Weltebed35df2011-11-02 13:06:18 +0100148int queue_free(struct queue_t *queue)
149{
150 if (QUEUE_DEBUG)
151 printf("queue_free\n");
152 if (QUEUE_DEBUG)
153 queue_print(queue);
154 free(queue);
155 return 0;
jjako52c24142002-12-16 13:33:51 +0000156}
157
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200158/*! Add a new message to the queue.
159 * \param[in] queue pointer previously allocated by queue_new
160 * \param[out] qmsg first message from the queue (if succeeds)
161 * \param[in] peer who sent the message to add
162 * \param[in] seq sequence number of the message to add
163 * \returns zero on success, non-zero on error.
164 */
jjako52c24142002-12-16 13:33:51 +0000165int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
Harald Weltebed35df2011-11-02 13:06:18 +0100166 struct sockaddr_in *peer, uint16_t seq)
167{
168 if (QUEUE_DEBUG)
169 printf("queue_newmsg %d\n", (int)seq);
170 if (queue->qmsga[queue->next].state == 1) {
171 return EOF; /* Queue is full */
172 } else {
173 *qmsg = &queue->qmsga[queue->next];
174 queue_seqset(queue, *qmsg, peer, seq);
Pau Espin Pedrol05ec2b32019-08-16 13:20:09 +0200175 INIT_LLIST_HEAD(&(*qmsg)->entry);
Harald Weltebed35df2011-11-02 13:06:18 +0100176 (*qmsg)->state = 1; /* Space taken */
177 (*qmsg)->this = queue->next;
178 (*qmsg)->next = -1; /* End of the queue */
179 (*qmsg)->prev = queue->last; /* Link to the previous */
180 if (queue->last != -1)
181 queue->qmsga[queue->last].next = queue->next; /* Link previous to us */
182 queue->last = queue->next; /* End of queue */
183 if (queue->first == -1)
184 queue->first = queue->next;
185 queue->next = (queue->next + 1) % QUEUE_SIZE; /* Increment */
186 if (QUEUE_DEBUG)
187 queue_print(queue);
188 return 0;
189 }
jjako52c24142002-12-16 13:33:51 +0000190}
191
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200192
193/*! Remove an element from the queue.
194 * \param[in] queue pointer previously allocated by queue_new
195 * \param[in] qmsg message to free
196 * \returns zero on success, non-zero on error.
Harald Welteb841e572011-11-02 13:45:50 +0100197 *
198 * Internally, we first delete the entry from the queue, and then update
199 * up our global queue->first / queue->last pointers. Finally,
200 * the qmsg_t is re-initialized with zero bytes. No memory is released.
201 */
Harald Weltebed35df2011-11-02 13:06:18 +0100202int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg)
203{
204 if (QUEUE_DEBUG)
205 printf("queue_freemsg\n");
206 if (qmsg->state != 1) {
207 return EOF; /* Not in queue */
208 }
jjako52c24142002-12-16 13:33:51 +0000209
Pau Espin Pedrol05ec2b32019-08-16 13:20:09 +0200210 llist_del(&qmsg->entry);
211
Harald Weltebed35df2011-11-02 13:06:18 +0100212 queue_seqdel(queue, qmsg);
jjako52c24142002-12-16 13:33:51 +0000213
Harald Weltebed35df2011-11-02 13:06:18 +0100214 if (qmsg->next == -1) /* Are we the last in queue? */
215 queue->last = qmsg->prev;
216 else
217 queue->qmsga[qmsg->next].prev = qmsg->prev;
jjako52c24142002-12-16 13:33:51 +0000218
Harald Weltebed35df2011-11-02 13:06:18 +0100219 if (qmsg->prev == -1) /* Are we the first in queue? */
220 queue->first = qmsg->next;
221 else
222 queue->qmsga[qmsg->prev].next = qmsg->next;
jjako52c24142002-12-16 13:33:51 +0000223
Harald Weltebed35df2011-11-02 13:06:18 +0100224 memset(qmsg, 0, sizeof(struct qmsg_t)); /* Just to be safe */
jjako52c24142002-12-16 13:33:51 +0000225
Harald Weltebed35df2011-11-02 13:06:18 +0100226 if (QUEUE_DEBUG)
227 queue_print(queue);
228
229 return 0;
jjako52c24142002-12-16 13:33:51 +0000230}
231
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200232/*! Move a given qmsg_t to the end of the queue.
233 * \param[in] queue pointer previously allocated by queue_new
234 * \param[in] qmsg message to move to the end of the queue
235 * \returns zero on success, non-zero on error.
236 */
Harald Weltebed35df2011-11-02 13:06:18 +0100237int queue_back(struct queue_t *queue, struct qmsg_t *qmsg)
238{
239 if (QUEUE_DEBUG)
240 printf("queue_back\n");
241 if (qmsg->state != 1) {
242 return EOF; /* Not in queue */
243 }
jjako52c24142002-12-16 13:33:51 +0000244
Harald Weltebed35df2011-11-02 13:06:18 +0100245 /* Insert stuff to maintain hash table */
jjako52c24142002-12-16 13:33:51 +0000246
Harald Weltebed35df2011-11-02 13:06:18 +0100247 if (qmsg->next != -1) { /* Only swop if there are others */
248 queue->qmsga[qmsg->next].prev = qmsg->prev;
249 queue->first = qmsg->next;
250
251 qmsg->next = -1;
252 qmsg->prev = queue->last;
253 if (queue->last != -1)
254 queue->qmsga[queue->last].next = qmsg->this;
255 queue->last = qmsg->this;
256 }
257 if (QUEUE_DEBUG)
258 queue_print(queue);
259 return 0;
jjako52c24142002-12-16 13:33:51 +0000260}
261
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200262/*! Get the first element in the entire queue.
263 * \param[in] queue pointer previously allocated by queue_new
264 * \param[out] qmsg first message from the queue (if succeeds)
265 * \returns zero on success, non-zero on error.
266 */
Harald Weltebed35df2011-11-02 13:06:18 +0100267int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg)
268{
269 /*printf("queue_getfirst\n"); */
270 if (queue->first == -1) {
271 *qmsg = NULL;
272 return EOF; /* End of queue = queue is empty. */
273 }
274 *qmsg = &queue->qmsga[queue->first];
275 if (QUEUE_DEBUG)
276 queue_print(queue);
277 return 0;
jjako52c24142002-12-16 13:33:51 +0000278}
279
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200280/*! Get a queue entry for a given peer + seq.
281 * \param[in] queue pointer previously allocated by queue_new
282 * \param[out] qmsg first message from the queue (if succeeds)
283 * \param[in] peer who sent the message to retrieve
284 * \param[in] seq sequence number of the message to retrive
285 * \returns zero on success, non-zero on error.
286 */
jjako52c24142002-12-16 13:33:51 +0000287int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
Harald Weltebed35df2011-11-02 13:06:18 +0100288 struct sockaddr_in *peer, uint16_t seq)
289{
290 int hash = queue_seqhash(peer, seq);
291 struct qmsg_t *qmsg2;
292 if (QUEUE_DEBUG)
293 printf("Begin queue_seqget seq = %d\n", (int)seq);
294 for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
295 if ((qmsg2->seq == seq) &&
296 (!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) {
297 *qmsg = qmsg2;
298 if (QUEUE_DEBUG)
299 printf("End queue_seqget. Found\n");
300 return 0;
301 }
302 }
303 if (QUEUE_DEBUG)
304 printf("End queue_seqget. Not found\n");
305 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000306}
307
Pau Espin Pedrole725d872019-06-20 16:59:49 +0200308/*! look-up a given seq/peer, return cbp + type and free entry.
309 * \param[in] queue pointer previously allocated by queue_new
310 * \param[in] peer who sent the message to retrieve
311 * \param[in] seq sequence number of the message to retrive
312 * \param[out] type GTP message type
313 * \param[out] type callback pointer of the message
314 * \returns zero on success, non-zero on error.
315 */
Harald Weltebed35df2011-11-02 13:06:18 +0100316int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
317 uint16_t seq, uint8_t * type, void **cbp)
318{
319 struct qmsg_t *qmsg;
320 if (queue_seqget(queue, &qmsg, peer, seq)) {
321 *cbp = NULL;
322 *type = 0;
323 return EOF;
324 }
325 *cbp = qmsg->cbp;
326 *type = qmsg->type;
327 if (queue_freemsg(queue, qmsg)) {
328 return EOF;
329 }
330 return 0;
jjako52c24142002-12-16 13:33:51 +0000331}