LTP GCOV extension - code coverage report
Current view: directory - trunk/libteredo - peerlist.c
Test: lcov.info
Date: 2007-05-09 Instrumented lines: 169
Code covered: 74.6 % Executed lines: 126

       1                 : /*
       2                 :  * peerlist.c - Teredo relay internal peers list manipulation
       3                 :  * $Id: peerlist.c 1852 2006-12-15 16:42:20Z remi $
       4                 :  */
       5                 : 
       6                 : /***********************************************************************
       7                 :  *  Copyright © 2004-2006 Rémi Denis-Courmont.                         *
       8                 :  *  This program is free software; you can redistribute and/or modify  *
       9                 :  *  it under the terms of the GNU General Public License as published  *
      10                 :  *  by the Free Software Foundation; version 2 of the license.         *
      11                 :  *                                                                     *
      12                 :  *  This program is distributed in the hope that it will be useful,    *
      13                 :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
      14                 :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
      15                 :  *  See the GNU General Public License for more details.               *
      16                 :  *                                                                     *
      17                 :  *  You should have received a copy of the GNU General Public License  *
      18                 :  *  along with this program; if not, you can get it from:              *
      19                 :  *  http://www.gnu.org/copyleft/gpl.html                               *
      20                 :  ***********************************************************************/
      21                 : 
      22                 : #ifdef HAVE_CONFIG_H
      23                 : # include <config.h>
      24                 : #endif
      25                 : 
      26                 : #include <stdbool.h>
      27                 : #include <string.h>
      28                 : #include <time.h>
      29                 : #include <stdlib.h> /* malloc() / free() */
      30                 : #include <assert.h>
      31                 : 
      32                 : #include <inttypes.h>
      33                 : 
      34                 : #include <sys/types.h>
      35                 : #include <netinet/in.h>
      36                 : #include <pthread.h>
      37                 : #include <errno.h>
      38                 : 
      39                 : #ifndef NDEBUG
      40                 : # define JUDYERROR_NOTEST 1
      41                 : #endif
      42                 : #ifdef HAVE_JUDY_H
      43                 : # include <Judy.h>
      44                 : #endif
      45                 : 
      46                 : #include "teredo.h"
      47                 : #include "teredo-udp.h" // FIXME: ugly
      48                 : #include "debug.h"
      49                 : #include "clock.h"
      50                 : #include "peerlist.h"
      51                 : 
      52                 : /*
      53                 :  * Packets queueing
      54                 :  */
      55                 : struct teredo_queue
      56                 : {
      57                 :         teredo_queue *next;
      58                 :         size_t length;
      59                 :         uint32_t ipv4;
      60                 :         uint16_t port;
      61                 :         bool incoming;
      62                 :         uint8_t data[];
      63                 : };
      64                 : 
      65                 : static const unsigned teredo_MaxQueueBytes = 1280;
      66                 : 
      67                 : 
      68                 : static inline void teredo_peer_init (teredo_peer *peer)
      69          302301 : {
      70          302301 :         peer->queue = NULL;
      71          302301 :         peer->queue_left = teredo_MaxQueueBytes;
      72          302301 : }
      73                 : 
      74                 : 
      75                 : static inline void teredo_peer_destroy (teredo_peer *peer)
      76          302301 : {
      77          302301 :         teredo_queue *p = peer->queue;
      78                 : 
      79          604602 :         while (p != NULL)
      80                 :         {
      81                 :                 teredo_queue *buf;
      82                 : 
      83               0 :                 buf = p->next;
      84               0 :                 free (p);
      85               0 :                 p = buf;
      86                 :         }
      87          302301 : }
      88                 : 
      89                 : 
      90                 : static void teredo_peer_queue (teredo_peer *restrict peer,
      91                 :                                const void *restrict data, size_t len,
      92                 :                                uint32_t ip, uint16_t port, bool incoming)
      93               0 : {
      94                 :         teredo_queue *p;
      95                 : 
      96               0 :         if (len > peer->queue_left)
      97               0 :                 return;
      98               0 :         peer->queue_left -= len;
      99                 : 
     100               0 :         p = (teredo_queue *)malloc (sizeof (*p) + len);
     101               0 :         p->length = len;
     102               0 :         memcpy (p->data, data, len);
     103               0 :         p->ipv4 = ip;
     104               0 :         p->port = port;
     105               0 :         p->incoming = incoming;
     106                 : 
     107               0 :         p->next = peer->queue;
     108               0 :         peer->queue = p;
     109                 : }
     110                 : 
     111                 : 
     112                 : void teredo_enqueue_in (teredo_peer *restrict peer, const void *restrict data,
     113                 :                         size_t len, uint32_t ip, uint16_t port)
     114               0 : {
     115               0 :         teredo_peer_queue (peer, data, len, ip, port, true);
     116               0 : }
     117                 : 
     118                 : 
     119                 : void teredo_enqueue_out (teredo_peer *restrict peer,
     120                 :                          const void *restrict data, size_t len)
     121               0 : {
     122               0 :         teredo_peer_queue (peer, data, len, 0, 0, false);
     123               0 : }
     124                 : 
     125                 : 
     126                 : teredo_queue *teredo_peer_queue_yield (teredo_peer *peer)
     127               0 : {
     128               0 :         teredo_queue *q = peer->queue;
     129               0 :         peer->queue = NULL;
     130               0 :         peer->queue_left = teredo_MaxQueueBytes;
     131               0 :         return q;
     132                 : }
     133                 : 
     134                 : 
     135                 : void teredo_queue_emit (teredo_queue *q, int fd, uint32_t ipv4, uint16_t port,
     136                 :                         teredo_dequeue_cb cb, void *opaque)
     137               0 : {
     138               0 :         while (q != NULL)
     139                 :         {
     140                 :                 teredo_queue *buf;
     141                 : 
     142               0 :                 buf = q->next;
     143               0 :                 if (q->incoming)
     144                 :                 {
     145               0 :                         if ((ipv4 == q->ipv4) && (port == q->port))
     146               0 :                                 cb (opaque, q->data, q->length);
     147                 :                 }
     148                 :                 else
     149               0 :                         teredo_send (fd, q->data, q->length, ipv4, port);
     150               0 :                 free (q);
     151               0 :                 q = buf;
     152                 :         }
     153               0 : }
     154                 : 
     155                 : 
     156                 : /*** Peer list handling ***/
     157                 : typedef struct teredo_listitem
     158                 : {
     159                 :         struct teredo_listitem **pprev, *next;
     160                 :         teredo_peer peer;
     161                 :         union teredo_addr key;
     162                 : } teredo_listitem;
     163                 : 
     164                 : struct teredo_peerlist
     165                 : {
     166                 :         teredo_listitem *recent, *old;
     167                 :         unsigned left;
     168                 :         unsigned expiration;
     169                 :         pthread_t gc;
     170                 :         pthread_mutex_t lock;
     171                 : #ifdef HAVE_LIBJUDY
     172                 :         Pvoid_t PJHSArray;
     173                 : #endif
     174                 : };
     175                 : 
     176                 : 
     177                 : static inline teredo_listitem *listitem_create (void)
     178          302301 : {
     179          302301 :         teredo_listitem *entry = malloc (sizeof (*entry));
     180          302301 :         if (entry != NULL)
     181          302301 :                 teredo_peer_init (&entry->peer);
     182          302301 :         return entry;
     183                 : }
     184                 : 
     185                 : 
     186                 : static inline void listitem_destroy (teredo_listitem *entry)
     187          302301 : {
     188          302301 :         teredo_peer_destroy (&entry->peer);
     189          302301 :         free (entry);
     190          302301 : }
     191                 : 
     192                 : 
     193                 : static void listitem_recdestroy (teredo_listitem *entry)
     194              83 : {
     195          302467 :         while (entry != NULL)
     196                 :         {
     197          302301 :                 teredo_listitem *buf = entry->next;
     198          302301 :                 listitem_destroy (entry);
     199          302301 :                 entry = buf;
     200                 :         }
     201              83 : }
     202                 : 
     203                 : 
     204                 : /**
     205                 :  * Peer list garbage collector entry point.
     206                 :  *
     207                 :  * @return never ever.
     208                 :  */
     209                 : static LIBTEREDO_NORETURN void *garbage_collector (void *data)
     210              13 : {
     211              13 :         struct teredo_peerlist *l = (struct teredo_peerlist *)data;
     212                 : 
     213                 :         for (;;)
     214                 :         {
     215              52 :                 struct timespec delay = { .tv_sec = l->expiration };
     216              52 :                 while (clock_nanosleep (CLOCK_REALTIME, 0, &delay, &delay));
     217                 : 
     218                 :                 int state;
     219              39 :                 pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
     220                 :                 /* cancel-unsafe section starts */
     221              39 :                 pthread_mutex_lock (&l->lock);
     222                 : 
     223                 :                 // remove expired peers from hash table
     224            1953 :                 for (teredo_listitem *p = l->old; p != NULL; p = p->next)
     225                 :                 {
     226                 : #ifdef HAVE_LIBJUDY
     227                 :                         int Rc_int;
     228            1914 :                         JHSD (Rc_int, l->PJHSArray, (uint8_t *)&p->key, 16);
     229            1914 :                         assert (Rc_int);
     230                 : #endif
     231            1914 :                         l->left++;
     232                 :                 }
     233                 : 
     234                 :                 // unlinks old peers
     235              39 :                 teredo_listitem *old = l->old;
     236                 : 
     237                 :                 // moves recent peers to old peers area
     238              39 :                 l->old = l->recent;
     239              39 :                 l->recent = NULL;
     240              39 :                 if (l->old != NULL)
     241              16 :                         l->old->pprev = &l->old;
     242                 : 
     243              39 :                 pthread_mutex_unlock (&l->lock);
     244                 : 
     245                 :                 // Perform possibly expensive memory release without the lock
     246              39 :                 listitem_recdestroy (old);
     247                 : 
     248                 :                 /* cancel-unsafe section ends */
     249              39 :                 pthread_setcancelstate (state, NULL);
     250              39 :         }
     251                 : }
     252                 : 
     253                 : 
     254                 : /**
     255                 :  * Creates an empty peer list.
     256                 :  *
     257                 :  * @param max maximum number of peers in the list
     258                 :  * @param expiration minimum delay (seconds) before a peer can be removed
     259                 :  * by the garbage collector. Must not be 0.
     260                 :  *
     261                 :  * @return NULL on error (see errno for actual problem).
     262                 :  */
     263                 : teredo_peerlist *teredo_list_create (unsigned max, unsigned expiration)
     264              13 : {
     265                 :         /*printf ("Peer size: %u/%u bytes\n",sizeof (teredo_peer),
     266                 :                 sizeof (teredo_listitem));*/
     267              13 :         assert (expiration > 0);
     268                 : 
     269              13 :         teredo_peerlist *l = (teredo_peerlist *)malloc (sizeof (*l));
     270              13 :         if (l == NULL)
     271               0 :                 return NULL;
     272                 : 
     273              13 :         memset (l, 0, sizeof (l));
     274              13 :         pthread_mutex_init (&l->lock, NULL);
     275              13 :         l->recent = l->old = NULL;
     276              13 :         l->left = max;
     277              13 :         l->expiration = expiration;
     278                 : #ifdef HAVE_LIBJUDY
     279              13 :         l->PJHSArray = (Pvoid_t)NULL;
     280                 : #endif
     281                 : 
     282              13 :         if (pthread_create (&l->gc, NULL, garbage_collector, l))
     283                 :         {
     284               0 :                 pthread_mutex_destroy (&l->lock);
     285               0 :                 free (l);
     286               0 :                 return NULL;
     287                 :         }
     288                 : 
     289              13 :         return l;
     290                 : }
     291                 : 
     292                 : /**
     293                 :  * Empties an existing unlocked list. Always succeeds.
     294                 :  *
     295                 :  * @param max new value for maximum number of items allowed.
     296                 :  */
     297                 : void teredo_list_reset (teredo_peerlist *l, unsigned max)
     298              22 : {
     299              22 :         pthread_mutex_lock (&l->lock);
     300                 : 
     301                 : #ifdef HAVE_LIBJUDY
     302                 :         // detach old array
     303              22 :         Pvoid_t array = l->PJHSArray;
     304              22 :         l->PJHSArray = (Pvoid_t)NULL;
     305                 : #endif
     306                 : 
     307              22 :         teredo_listitem *recent = l->recent, *old = l->old;
     308                 :         // unlinks peers and resets lists
     309              22 :         l->recent = l->old = NULL;
     310              22 :         l->left = max;
     311                 : 
     312              22 :         pthread_mutex_unlock (&l->lock);
     313                 : 
     314                 :         /* the mutex is not needed for actual memory release */
     315              22 :         listitem_recdestroy (old);
     316              22 :         listitem_recdestroy (recent);
     317                 : 
     318                 : #ifdef HAVE_LIBJUDY
     319                 :         // destroy the old array that was detached before unlocking
     320                 :         Word_t Rc_word;
     321              22 :         JHSFA (Rc_word, array);
     322                 : #endif
     323              22 : }
     324                 : 
     325                 : /**
     326                 :  * Destroys an existing unlocked list.
     327                 :  */
     328                 : void teredo_list_destroy (teredo_peerlist *l)
     329              13 : {
     330              13 :         teredo_list_reset (l, 0);
     331                 : 
     332              13 :         pthread_cancel (l->gc);
     333              13 :         pthread_join (l->gc, NULL);
     334              13 :         pthread_mutex_destroy (&l->lock);
     335                 : 
     336              13 :         free (l);
     337              13 : }
     338                 : 
     339                 : /**
     340                 :  * Locks the list and looks up a peer in an unlocked list.
     341                 :  * On success, the list must be unlocked with teredo_list_release(), otherwise
     342                 :  * the next call to teredo_list_lookup will deadlock. Unlocking the list after
     343                 :  * a failure is not defined.
     344                 :  *
     345                 :  * @param create if not NULL, the peer will be added to the list if it is not
     346                 :  * present already, and *create will be true on return. If create is not NULL
     347                 :  * but the peer was already present, *create will be false on return.
     348                 :  * *create is undefined on return in case of error.
     349                 :  *
     350                 :  * @return The peer if found or created. NULL on error (when create is not
     351                 :  * NULL), or if the peer was not found (when create is NULL).
     352                 :  */
     353                 : teredo_peer *teredo_list_lookup (teredo_peerlist *restrict list,
     354                 :                                  const struct in6_addr *restrict addr,
     355                 :                                  bool *restrict create)
     356          611148 : {
     357                 :         teredo_listitem *p;
     358                 : 
     359          611148 :         pthread_mutex_lock (&list->lock);
     360                 : 
     361                 : #ifdef HAVE_LIBJUDY
     362          611148 :         teredo_listitem **pp = NULL;
     363                 : 
     364                 :         /* Judy dynamic array-based fast lookup */
     365                 :         {
     366                 :                 void *PValue;
     367                 : 
     368          611148 :                 if (create != NULL)
     369                 :                 {
     370          302316 :                         JHSI (PValue, list->PJHSArray, (uint8_t *)addr, 16);
     371          302316 :                         if (PValue == PJERR)
     372                 :                         {
     373               0 :                                 pthread_mutex_unlock (&list->lock);
     374               0 :                                 return NULL;
     375                 :                         }
     376          302316 :                         pp = (teredo_listitem **)PValue;
     377          302316 :                         p = *pp;
     378                 :                 }
     379                 :                 else
     380                 :                 {
     381          308832 :                         JHSG (PValue, list->PJHSArray, (uint8_t *)addr, 16);
     382          308832 :                         pp = (teredo_listitem **)PValue;
     383          308832 :                         p = (pp != NULL) ? *pp : NULL;
     384                 :                 }
     385                 : 
     386                 :         }
     387                 : #else
     388                 :         /* Slow O(n) simplistic peer lookup */
     389                 :         p = NULL;
     390                 : 
     391                 :         for (p = list->recent; p != NULL; p = p->next)
     392                 :                 if (memcmp (&p->key, addr, sizeof (*addr)) == 0)
     393                 :                         break;
     394                 : 
     395                 :         if (p == NULL)
     396                 :                 for (p = list->old; p != NULL; p = p->next)
     397                 :                         if (memcmp (&p->key, addr, sizeof (*addr)) == 0)
     398                 :                                 break;
     399                 : #endif
     400                 : 
     401          611148 :         if (p != NULL)
     402                 :         {
     403                 :                 /* peer was already in list */
     404          303060 :                 assert (*(p->pprev) == p);
     405          303060 :                 assert ((p->next == NULL) || (p->next->pprev == &p->next));
     406                 : 
     407          303060 :                 if (create != NULL)
     408               0 :                         *create = false;
     409                 : 
     410                 :                 /* move peer to the top of the head of the "recent" list */
     411          303060 :                 if (list->recent != p)
     412                 :                 {
     413                 :                         // unlinks
     414          303060 :                         if (p->next != NULL)
     415            1974 :                                 p->next->pprev = p->pprev;
     416          303060 :                         *(p->pprev) = p->next;
     417                 : 
     418                 :                         // inserts at head
     419          303060 :                         p->next = list->recent;
     420          303060 :                         if (p->next != NULL)
     421          303054 :                                 p->next->pprev = &p->next;
     422                 : 
     423          303060 :                         list->recent = p;
     424          303060 :                         p->pprev = &list->recent;
     425                 : 
     426          303060 :                         assert (*(p->pprev) == p);
     427          303060 :                         assert ((p->next == NULL) || (p->next->pprev == &p->next));
     428                 :                 }
     429                 : 
     430          303060 :                 return &p->peer;
     431                 :         }
     432                 : 
     433          308088 :         assert (p == NULL);
     434                 : 
     435                 :         /* otherwise, peer was not in list */
     436          308088 :         if (create == NULL)
     437                 :         {
     438            5772 :                 pthread_mutex_unlock (&list->lock);
     439            5772 :                 return NULL;
     440                 :         }
     441                 : 
     442          302316 :         *create = true;
     443                 : 
     444                 :         /* Allocates a new peer entry */
     445          302316 :         if (list->left > 0)
     446          302301 :                 p = listitem_create ();
     447                 : 
     448          302316 :         if (p == NULL)
     449                 :         {
     450                 : #ifdef HAVE_LIBJUDY
     451                 :                 int Rc_int;
     452              15 :                 JHSD (Rc_int, list->PJHSArray, (uint8_t *)addr, sizeof (*addr));
     453                 : #endif
     454              15 :                 pthread_mutex_unlock (&list->lock);
     455              15 :                 return NULL;
     456                 :         }
     457                 : 
     458                 :         /* Puts new entry at the head of the list */
     459          302301 :         p->next = list->recent;
     460          302301 :         if (p->next != NULL)
     461          302282 :                 p->next->pprev = &p->next;
     462                 : 
     463          302301 :         p->pprev = &list->recent;
     464          302301 :         list->recent = p;
     465          302301 :         p->pprev = &list->recent;
     466                 : 
     467          302301 :         list->left--;
     468                 : 
     469          302301 :         assert (*(p->pprev) == p);
     470          302301 :         assert ((p->next == NULL) || (p->next->pprev == &p->next));
     471                 : 
     472                 : #ifdef HAVE_LIBJUDY
     473          302301 :         *pp = p;
     474                 : #endif
     475          302301 :         memcpy (&p->key.ip6, addr, sizeof (struct in6_addr));
     476          302301 :         return &p->peer;
     477                 : }
     478                 : 
     479                 : 
     480                 : /**
     481                 :  * Unlocks a list that was locked by teredo_list_lookup().
     482                 :  */
     483                 : void teredo_list_release (teredo_peerlist *l)
     484          605361 : {
     485          605361 :         pthread_mutex_unlock (&l->lock);
     486          605361 : }

Generated by: LTP GCOV extension version 1.5