253 lines
7.1 KiB
C
253 lines
7.1 KiB
C
/*
|
|
* OpenGGSN - Gateway GPRS Support Node
|
|
* Copyright (C) 2002, 2003, 2004 Mondru AB.
|
|
*
|
|
* The contents of this file may be used under the terms of the GNU
|
|
* General Public License Version 2, provided that the above copyright
|
|
* notice and this permission notice is included in all copies or
|
|
* substantial portions of the software.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Queue.c
|
|
* Reliable delivery of signalling messages
|
|
*/
|
|
|
|
#include <../config.h>
|
|
#ifdef HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <netinet/in.h>
|
|
#include <string.h>
|
|
#include "pdp.h"
|
|
#include "gtp.h"
|
|
#include "queue.h"
|
|
|
|
int queue_print(struct queue_t *queue) {
|
|
int n;
|
|
printf("Queue: %x Next: %d First: %d Last: %d\n", (int) queue, queue->next, queue->first, queue->last);
|
|
printf("# State seq next prev timeout retrans\n");
|
|
for (n=0; n<QUEUE_SIZE; n++) {
|
|
printf("%d %d %d %d %d %d %d\n",
|
|
n,
|
|
queue->qmsga[n].state,
|
|
queue->qmsga[n].seq,
|
|
queue->qmsga[n].next,
|
|
queue->qmsga[n].prev,
|
|
(int) queue->qmsga[n].timeout,
|
|
queue->qmsga[n].retrans);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int queue_seqhash(struct sockaddr_in *peer, uint16_t seq) {
|
|
/* With QUEUE_HASH_SIZE = 2^16 this describes all possible
|
|
seq values. Thus we have perfect hash for the request queue.
|
|
For the response queue we might have collisions, but not very
|
|
often.
|
|
For performance optimisation we should remove the modulus
|
|
operator, but this is only valid for QUEUE_HASH_SIZE = 2^16 */
|
|
return seq % QUEUE_HASH_SIZE;
|
|
}
|
|
|
|
int queue_seqset(struct queue_t *queue, struct qmsg_t *qmsg,
|
|
struct sockaddr_in *peer, uint16_t seq) {
|
|
int hash = queue_seqhash(peer, seq);
|
|
struct qmsg_t *qmsg2;
|
|
struct qmsg_t *qmsg_prev = NULL;
|
|
|
|
if (QUEUE_DEBUG) printf("Begin queue_seqset seq = %d\n", (int) seq);
|
|
if (QUEUE_DEBUG) printf("SIZEOF PEER %d, *PEER %d\n", sizeof(peer), sizeof(*peer));
|
|
|
|
qmsg->seq = seq;
|
|
memcpy(&qmsg->peer, peer, sizeof(*peer));
|
|
|
|
for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext)
|
|
qmsg_prev = qmsg2;
|
|
if (!qmsg_prev)
|
|
queue->hashseq[hash] = qmsg;
|
|
else
|
|
qmsg_prev->seqnext = qmsg;
|
|
if (QUEUE_DEBUG) printf("End queue_seqset\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
int queue_seqdel(struct queue_t *queue, struct qmsg_t *qmsg) {
|
|
int hash = queue_seqhash(&qmsg->peer, qmsg->seq);
|
|
struct qmsg_t *qmsg2;
|
|
struct qmsg_t *qmsg_prev = NULL;
|
|
if (QUEUE_DEBUG) printf("Begin queue_seqdel seq = %d\n", (int) qmsg->seq);
|
|
|
|
for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
|
|
if (qmsg == qmsg) {
|
|
if (!qmsg_prev)
|
|
queue->hashseq[hash] = qmsg2->seqnext;
|
|
else
|
|
qmsg_prev->seqnext = qmsg2->seqnext;
|
|
if (QUEUE_DEBUG) printf("End queue_seqset: SEQ found\n");
|
|
return 0;
|
|
}
|
|
qmsg_prev = qmsg2;
|
|
}
|
|
printf("End queue_seqset: SEQ not found\n");
|
|
return EOF; /* End of linked list and not found */
|
|
}
|
|
|
|
|
|
/* Allocates and initialises new queue structure */
|
|
int queue_new(struct queue_t **queue) {
|
|
if (QUEUE_DEBUG) printf("queue_new\n");
|
|
*queue = calloc(1, sizeof(struct queue_t));
|
|
(*queue)->next = 0;
|
|
(*queue)->first = -1;
|
|
(*queue)->last = -1;
|
|
|
|
if (QUEUE_DEBUG) queue_print(*queue);
|
|
if (*queue) return 0;
|
|
else return EOF;
|
|
}
|
|
|
|
/* Deallocates queue structure */
|
|
int queue_free(struct queue_t *queue) {
|
|
if (QUEUE_DEBUG) printf("queue_free\n");
|
|
if (QUEUE_DEBUG) queue_print(queue);
|
|
free(queue);
|
|
return 0;
|
|
}
|
|
|
|
int queue_newmsg(struct queue_t *queue, struct qmsg_t **qmsg,
|
|
struct sockaddr_in *peer, uint16_t seq) {
|
|
if (QUEUE_DEBUG) printf("queue_newmsg %d\n", (int) seq);
|
|
if (queue->qmsga[queue->next].state == 1) {
|
|
return EOF; /* Queue is full */
|
|
}
|
|
else {
|
|
*qmsg = &queue->qmsga[queue->next];
|
|
queue_seqset(queue, *qmsg, peer, seq);
|
|
(*qmsg)->state = 1; /* Space taken */
|
|
(*qmsg)->this = queue->next;
|
|
(*qmsg)->next=-1; /* End of the queue */
|
|
(*qmsg)->prev=queue->last; /* Link to the previous */
|
|
if (queue->last != -1)
|
|
queue->qmsga[queue->last].next=queue->next; /* Link previous to us */
|
|
queue->last = queue->next; /* End of queue */
|
|
if (queue->first == -1) queue->first = queue->next;
|
|
queue->next = (queue->next+1) % QUEUE_SIZE; /* Increment */
|
|
if (QUEUE_DEBUG) queue_print(queue);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int queue_freemsg(struct queue_t *queue, struct qmsg_t *qmsg) {
|
|
if (QUEUE_DEBUG) printf("queue_freemsg\n");
|
|
if (qmsg->state != 1) {
|
|
return EOF; /* Not in queue */
|
|
}
|
|
|
|
queue_seqdel(queue, qmsg);
|
|
|
|
if (qmsg->next == -1) /* Are we the last in queue? */
|
|
queue->last = qmsg->prev;
|
|
else
|
|
queue->qmsga[qmsg->next].prev = qmsg->prev;
|
|
|
|
if (qmsg->prev == -1) /* Are we the first in queue? */
|
|
queue->first = qmsg->next;
|
|
else
|
|
queue->qmsga[qmsg->prev].next = qmsg->next;
|
|
|
|
memset(qmsg, 0, sizeof(struct qmsg_t)); /* Just to be safe */
|
|
|
|
if (QUEUE_DEBUG) queue_print(queue);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int queue_back(struct queue_t *queue, struct qmsg_t *qmsg) {
|
|
if (QUEUE_DEBUG) printf("queue_back\n");
|
|
if (qmsg->state != 1) {
|
|
return EOF; /* Not in queue */
|
|
}
|
|
|
|
/* Insert stuff to maintain hash table */
|
|
|
|
if (qmsg->next != -1) {/* Only swop if there are others */
|
|
queue->qmsga[qmsg->next].prev = qmsg->prev;
|
|
queue->first = qmsg->next;
|
|
|
|
qmsg->next = -1;
|
|
qmsg->prev = queue->last;
|
|
if (queue->last != -1) queue->qmsga[queue->last].next = qmsg->this;
|
|
queue->last = qmsg->this;
|
|
}
|
|
if (QUEUE_DEBUG) queue_print(queue);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the element with a particular sequence number */
|
|
int queue_getfirst(struct queue_t *queue, struct qmsg_t **qmsg) {
|
|
/*printf("queue_getfirst\n");*/
|
|
if (queue->first == -1) {
|
|
*qmsg = NULL;
|
|
return EOF; /* End of queue = queue is empty. */
|
|
}
|
|
*qmsg = &queue->qmsga[queue->first];
|
|
if (QUEUE_DEBUG) queue_print(queue);
|
|
return 0;
|
|
}
|
|
|
|
int queue_getseqx(struct queue_t *queue, struct qmsg_t **qmsg,
|
|
struct sockaddr_in *peer, uint16_t seq) {
|
|
int n;
|
|
if (QUEUE_DEBUG) printf("queue_getseq, %d\n", (int) seq);
|
|
if (QUEUE_DEBUG) queue_print(queue);
|
|
for (n=0; n<QUEUE_SIZE; n++) {
|
|
if ((queue->qmsga[n].seq == seq) &&
|
|
(!memcmp(&queue->qmsga[n].peer, peer, sizeof(*peer)))) {
|
|
*qmsg = &queue->qmsga[n];
|
|
return 0;
|
|
}
|
|
}
|
|
return EOF; /* Not found */
|
|
}
|
|
|
|
int queue_seqget(struct queue_t *queue, struct qmsg_t **qmsg,
|
|
struct sockaddr_in *peer, uint16_t seq) {
|
|
int hash = queue_seqhash(peer, seq);
|
|
struct qmsg_t *qmsg2;
|
|
if (QUEUE_DEBUG) printf("Begin queue_seqget seq = %d\n", (int) seq);
|
|
for (qmsg2 = queue->hashseq[hash]; qmsg2; qmsg2 = qmsg2->seqnext) {
|
|
if ((qmsg2->seq == seq) &&
|
|
(!memcmp(&qmsg2->peer, peer, sizeof(*peer)))) {
|
|
*qmsg = qmsg2;
|
|
if (QUEUE_DEBUG) printf("End queue_seqget. Found\n");
|
|
return 0;
|
|
}
|
|
}
|
|
if (QUEUE_DEBUG) printf("End queue_seqget. Not found\n");
|
|
return EOF; /* End of linked list and not found */
|
|
}
|
|
|
|
int queue_freemsg_seq(struct queue_t *queue, struct sockaddr_in *peer,
|
|
uint16_t seq, uint8_t *type, void **cbp) {
|
|
struct qmsg_t *qmsg;
|
|
if (queue_seqget(queue, &qmsg, peer, seq)) {
|
|
*cbp = NULL;
|
|
*type = 0;
|
|
return EOF;
|
|
}
|
|
*cbp = qmsg->cbp;
|
|
*type = qmsg->type;
|
|
if (queue_freemsg(queue, qmsg)) {
|
|
return EOF;
|
|
}
|
|
return 0;
|
|
}
|