blob: 82cd7b44e8aff167e0dd3f3ed18cc2a27e7c0188 [file] [log] [blame]
jjako52c24142002-12-16 13:33:51 +00001/*
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
jjako52c24142002-12-16 13:33:51 +00006 *
7 * 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.
11 *
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
Harald Welteb841e572011-11-02 13:45:50 +010065/*! \brief Insert a message with given sequence number into the hash
66 *
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)
82 printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer),
83 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
Harald Welteb841e572011-11-02 13:45:50 +0100124/*! \brief Allocates and initialises new queue structure */
Harald Weltebed35df2011-11-02 13:06:18 +0100125int queue_new(struct queue_t **queue)
126{
127 if (QUEUE_DEBUG)
128 printf("queue_new\n");
129 *queue = calloc(1, sizeof(struct queue_t));
Neels Hofmeyrf89dc4e2016-04-25 13:00:10 +0200130 if (!(*queue))
131 return EOF;
Harald Weltebed35df2011-11-02 13:06:18 +0100132 (*queue)->next = 0;
133 (*queue)->first = -1;
134 (*queue)->last = -1;
jjako52c24142002-12-16 13:33:51 +0000135
Harald Weltebed35df2011-11-02 13:06:18 +0100136 if (QUEUE_DEBUG)
137 queue_print(*queue);
Neels Hofmeyrf89dc4e2016-04-25 13:00:10 +0200138 return 0;
jjako52c24142002-12-16 13:33:51 +0000139}
140
Harald Welteb841e572011-11-02 13:45:50 +0100141/*! \brief Deallocates queue structure */
Harald Weltebed35df2011-11-02 13:06:18 +0100142int queue_free(struct queue_t *queue)
143{
144 if (QUEUE_DEBUG)
145 printf("queue_free\n");
146 if (QUEUE_DEBUG)
147 queue_print(queue);
148 free(queue);
149 return 0;
jjako52c24142002-12-16 13:33:51 +0000150}
151
Harald Welteb841e572011-11-02 13:45:50 +0100152/*! \brief Add a new message to the queue */
jjako52c24142002-12-16 13:33:51 +0000153int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
Harald Weltebed35df2011-11-02 13:06:18 +0100154 struct sockaddr_in *peer, uint16_t seq)
155{
156 if (QUEUE_DEBUG)
157 printf("queue_newmsg %d\n", (int)seq);
158 if (queue->qmsga[queue->next].state == 1) {
159 return EOF; /* Queue is full */
160 } else {
161 *qmsg = &queue->qmsga[queue->next];
162 queue_seqset(queue, *qmsg, peer, seq);
163 (*qmsg)->state = 1; /* Space taken */
164 (*qmsg)->this = queue->next;
165 (*qmsg)->next = -1; /* End of the queue */
166 (*qmsg)->prev = queue->last; /* Link to the previous */
167 if (queue->last != -1)
168 queue->qmsga[queue->last].next = queue->next; /* Link previous to us */
169 queue->last = queue->next; /* End of queue */
170 if (queue->first == -1)
171 queue->first = queue->next;
172 queue->next = (queue->next + 1) % QUEUE_SIZE; /* Increment */
173 if (QUEUE_DEBUG)
174 queue_print(queue);
175 return 0;
176 }
jjako52c24142002-12-16 13:33:51 +0000177}
178
Harald Welteb841e572011-11-02 13:45:50 +0100179/*! \brief Simply remoev a given qmsg_t from the queue
180 *
181 * Internally, we first delete the entry from the queue, and then update
182 * up our global queue->first / queue->last pointers. Finally,
183 * the qmsg_t is re-initialized with zero bytes. No memory is released.
184 */
Harald Weltebed35df2011-11-02 13:06:18 +0100185int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg)
186{
187 if (QUEUE_DEBUG)
188 printf("queue_freemsg\n");
189 if (qmsg->state != 1) {
190 return EOF; /* Not in queue */
191 }
jjako52c24142002-12-16 13:33:51 +0000192
Harald Weltebed35df2011-11-02 13:06:18 +0100193 queue_seqdel(queue, qmsg);
jjako52c24142002-12-16 13:33:51 +0000194
Harald Weltebed35df2011-11-02 13:06:18 +0100195 if (qmsg->next == -1) /* Are we the last in queue? */
196 queue->last = qmsg->prev;
197 else
198 queue->qmsga[qmsg->next].prev = qmsg->prev;
jjako52c24142002-12-16 13:33:51 +0000199
Harald Weltebed35df2011-11-02 13:06:18 +0100200 if (qmsg->prev == -1) /* Are we the first in queue? */
201 queue->first = qmsg->next;
202 else
203 queue->qmsga[qmsg->prev].next = qmsg->next;
jjako52c24142002-12-16 13:33:51 +0000204
Harald Weltebed35df2011-11-02 13:06:18 +0100205 memset(qmsg, 0, sizeof(struct qmsg_t)); /* Just to be safe */
jjako52c24142002-12-16 13:33:51 +0000206
Harald Weltebed35df2011-11-02 13:06:18 +0100207 if (QUEUE_DEBUG)
208 queue_print(queue);
209
210 return 0;
jjako52c24142002-12-16 13:33:51 +0000211}
212
Harald Welteb841e572011-11-02 13:45:50 +0100213/*! \brief Move a given qmsg_t to the end of the queue ?!? */
Harald Weltebed35df2011-11-02 13:06:18 +0100214int queue_back(struct queue_t *queue, struct qmsg_t *qmsg)
215{
216 if (QUEUE_DEBUG)
217 printf("queue_back\n");
218 if (qmsg->state != 1) {
219 return EOF; /* Not in queue */
220 }
jjako52c24142002-12-16 13:33:51 +0000221
Harald Weltebed35df2011-11-02 13:06:18 +0100222 /* Insert stuff to maintain hash table */
jjako52c24142002-12-16 13:33:51 +0000223
Harald Weltebed35df2011-11-02 13:06:18 +0100224 if (qmsg->next != -1) { /* Only swop if there are others */
225 queue->qmsga[qmsg->next].prev = qmsg->prev;
226 queue->first = qmsg->next;
227
228 qmsg->next = -1;
229 qmsg->prev = queue->last;
230 if (queue->last != -1)
231 queue->qmsga[queue->last].next = qmsg->this;
232 queue->last = qmsg->this;
233 }
234 if (QUEUE_DEBUG)
235 queue_print(queue);
236 return 0;
jjako52c24142002-12-16 13:33:51 +0000237}
238
Harald Welteb841e572011-11-02 13:45:50 +0100239/*! \brief Get the first element in the entire queue */
Harald Weltebed35df2011-11-02 13:06:18 +0100240int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg)
241{
242 /*printf("queue_getfirst\n"); */
243 if (queue->first == -1) {
244 *qmsg = NULL;
245 return EOF; /* End of queue = queue is empty. */
246 }
247 *qmsg = &queue->qmsga[queue->first];
248 if (QUEUE_DEBUG)
249 queue_print(queue);
250 return 0;
jjako52c24142002-12-16 13:33:51 +0000251}
252
Harald Welteb841e572011-11-02 13:45:50 +0100253/*! \brief Get a queue entry for a given peer + seq */
jjako52c24142002-12-16 13:33:51 +0000254int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
Harald Weltebed35df2011-11-02 13:06:18 +0100255 struct sockaddr_in *peer, uint16_t seq)
256{
257 int hash = queue_seqhash(peer, seq);
258 struct qmsg_t *qmsg2;
259 if (QUEUE_DEBUG)
260 printf("Begin queue_seqget seq = %d\n", (int)seq);
261 for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
262 if ((qmsg2->seq == seq) &&
263 (!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) {
264 *qmsg = qmsg2;
265 if (QUEUE_DEBUG)
266 printf("End queue_seqget. Found\n");
267 return 0;
268 }
269 }
270 if (QUEUE_DEBUG)
271 printf("End queue_seqget. Not found\n");
272 return EOF; /* End of linked list and not found */
jjako52c24142002-12-16 13:33:51 +0000273}
274
Harald Welteb841e572011-11-02 13:45:50 +0100275/*! \brief look-up a given seq/peer, return cbp + type and free entry */
Harald Weltebed35df2011-11-02 13:06:18 +0100276int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
277 uint16_t seq, uint8_t * type, void **cbp)
278{
279 struct qmsg_t *qmsg;
280 if (queue_seqget(queue, &qmsg, peer, seq)) {
281 *cbp = NULL;
282 *type = 0;
283 return EOF;
284 }
285 *cbp = qmsg->cbp;
286 *type = qmsg->type;
287 if (queue_freemsg(queue, qmsg)) {
288 return EOF;
289 }
290 return 0;
jjako52c24142002-12-16 13:33:51 +0000291}