expiry of udp clients
authorvenaas <venaas>
Tue, 30 Sep 2008 14:40:17 +0000 (14:40 +0000)
committervenaas <venaas@e88ac4ed-0b26-0410-9574-a7f39faa03bf>
Tue, 30 Sep 2008 14:40:17 +0000 (14:40 +0000)
git-svn-id: https://svn.testnett.uninett.no/radsecproxy/trunk@412 e88ac4ed-0b26-0410-9574-a7f39faa03bf

radsecproxy.c
radsecproxy.h
udp.c

index 533bf90..b89577a 100644 (file)
@@ -36,7 +36,6 @@
 
 /* Bugs:
  * May segfault when dtls connections go down? More testing needed
- * Need to remove UDP clients when no activity for a while...
  * Remove expired stuff from clients request list?
  * Multiple outgoing connections if not enough IDs? (multiple servers per conf?)
  * Useful for TCP accounting? Now we require separate server config for alt port
@@ -574,13 +573,10 @@ void removeclientrqs(struct client *client) {
     removeclientrqs_sendrq_freeserver_lock(0);
 }
 
-void removeclient(struct client *client) {
+void removelockedclient(struct client *client) {
     struct clsrvconf *conf;
     
-    if (!client)
-       return;
     conf = client->conf;
-    pthread_mutex_lock(conf->lock);
     if (conf->clients) {
        removeclientrqs(client);
        removequeue(client->replyq);
@@ -588,6 +584,17 @@ void removeclient(struct client *client) {
        free(client->addr);
        free(client);
     }
+}
+
+void removeclient(struct client *client) {
+    struct clsrvconf *conf;
+    
+    if (!client)
+       return;
+
+    conf = client->conf;
+    pthread_mutex_lock(conf->lock);
+    removelockedclient(client);
     pthread_mutex_unlock(conf->lock);
 }
 
@@ -2037,7 +2044,7 @@ void replyh(struct server *server, unsigned char *buf) {
        goto errunlock;
     }
 
-    debug(DBG_INFO, "replyh: passing reply to client %s", from->conf->name);
+    debug(DBG_INFO, "replyh: passing reply to client %s (%s)", from->conf->name, addr2string(from->addr));
     radmsg_free(rqout->rq->msg);
     rqout->rq->msg = msg;
     sendreply(newrqref(rqout->rq));
index 4be2274..520703f 100644 (file)
@@ -110,6 +110,7 @@ struct client {
     struct queue *replyq;
     struct queue *rbios; /* for dtls */
     struct sockaddr *addr;
+    time_t expiry; /* for udp */
 };
 
 struct server {
@@ -206,6 +207,7 @@ struct clsrvconf *find_clconf(uint8_t type, struct sockaddr *addr, struct list_n
 struct clsrvconf *find_srvconf(uint8_t type, struct sockaddr *addr, struct list_node **cur);
 struct clsrvconf *find_clconf_type(uint8_t type, struct list_node **cur);
 struct client *addclient(struct clsrvconf *conf, uint8_t lock);
+void removelockedclient(struct client *client);
 void removeclient(struct client *client);
 struct queue *newqueue();
 void freebios(struct queue *q);
diff --git a/udp.c b/udp.c
index bc239bc..14cc937 100644 (file)
--- a/udp.c
+++ b/udp.c
@@ -35,6 +35,20 @@ static int client4_sock = -1;
 static int client6_sock = -1;
 static struct queue *server_replyq = NULL;
 
+void removeudpclientfromreplyq(struct client *c) {
+    struct list_node *n;
+    struct request *r;
+    
+    /* lock the common queue and remove replies for this client */
+    pthread_mutex_lock(&c->replyq->mutex);
+    for (n = list_first(c->replyq->entries); n; n = list_next(n)) {
+       r = (struct request *)n->data;
+       if (r->from == c)
+           r->from = NULL;
+    }
+    pthread_mutex_unlock(&c->replyq->mutex);
+}      
+
 /* exactly one of client and server must be non-NULL */
 /* return who we received from in *client or *server */
 /* return from in sa if not NULL */
@@ -48,6 +62,7 @@ unsigned char *radudpget(int s, struct client **client, struct server **server,
     struct list_node *node;
     fd_set readfds;
     struct client *c = NULL;
+    struct timeval now;
     
     for (;;) {
        if (rad) {
@@ -103,13 +118,28 @@ unsigned char *radudpget(int s, struct client **client, struct server **server,
            debug(DBG_DBG, "radudpget: packet was padded with %d bytes", cnt - len);
 
        if (client) {
+           *client = NULL;
            pthread_mutex_lock(p->lock);
-           for (node = list_first(p->clients); node; node = list_next(node)) {
+           for (node = list_first(p->clients); node;) {
                c = (struct client *)node->data;
-               if (s == c->sock && addr_equal((struct sockaddr *)&from, c->addr))
-                   break;
+               node = list_next(node);
+               if (s != c->sock)
+                   continue;
+               gettimeofday(&now, NULL);
+               if (!*client && addr_equal((struct sockaddr *)&from, c->addr)) {
+                   c->expiry = now.tv_sec + 60;
+                   *client = c;
+               }
+               if (c->expiry >= now.tv_sec)
+                   continue;
+               
+               debug(DBG_DBG, "radudpget: removing expired client (%s)", addr2string(c->addr));
+               removeudpclientfromreplyq(c);
+               c->replyq = NULL; /* stop removeclient() from removing common udp replyq */
+               removelockedclient(c);
+               break;
            }
-           if (!node) {
+           if (!*client) {
                fromcopy = addr_copy((struct sockaddr *)&from);
                if (!fromcopy) {
                    pthread_mutex_unlock(p->lock);
@@ -123,8 +153,10 @@ unsigned char *radudpget(int s, struct client **client, struct server **server,
                }
                c->sock = s;
                c->addr = fromcopy;
+               gettimeofday(&now, NULL);
+               c->expiry = now.tv_sec + 60;
+               *client = c;
            }
-           *client = c;
            pthread_mutex_unlock(p->lock);
        } else if (server)
            *server = p->servers;
@@ -202,12 +234,15 @@ void *udpserverwr(void *arg) {
            pthread_cond_wait(&replyq->cond, &replyq->mutex);
            debug(DBG_DBG, "udp server writer, got signal");
        }
+       /* do this with lock, udpserverrd may set from = NULL if from expires */
+       if (reply->from)
+           memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
        pthread_mutex_unlock(&replyq->mutex);
-
-       memcpy(&to, reply->from->addr, SOCKADDRP_SIZE(reply->from->addr));
-       port_set((struct sockaddr *)&to, reply->udpport);
-       if (sendto(reply->udpsock, reply->replybuf, RADLEN(reply->replybuf), 0, (struct sockaddr *)&to, SOCKADDR_SIZE(to)) < 0)
-           debug(DBG_WARN, "udpserverwr: send failed");
+       if (reply->from) {
+           port_set((struct sockaddr *)&to, reply->udpport);
+           if (sendto(reply->udpsock, reply->replybuf, RADLEN(reply->replybuf), 0, (struct sockaddr *)&to, SOCKADDR_SIZE(to)) < 0)
+               debug(DBG_WARN, "udpserverwr: send failed");
+       }
        debug(DBG_DBG, "udpserverwr: refcount %d", reply->refcount);
        freerq(reply);
     }