LTP GCOV extension - code coverage report
Current view: directory - trunk/libteredo - maintain.c
Test: lcov.info
Date: 2007-05-09 Instrumented lines: 181
Code covered: 0.0 % Executed lines: 0

       1                 : /*
       2                 :  * maintain.c - Teredo client qualification & maintenance
       3                 :  * $Id: maintain.c 1943 2007-03-29 17:52:50Z remi $
       4                 :  *
       5                 :  * See "Teredo: Tunneling IPv6 over UDP through NATs"
       6                 :  * for more information
       7                 :  */
       8                 : 
       9                 : /***********************************************************************
      10                 :  *  Copyright © 2004-2007 Rémi Denis-Courmont.                         *
      11                 :  *  This program is free software; you can redistribute and/or modify  *
      12                 :  *  it under the terms of the GNU General Public License as published  *
      13                 :  *  by the Free Software Foundation; version 2 of the license.         *
      14                 :  *                                                                     *
      15                 :  *  This program is distributed in the hope that it will be useful,    *
      16                 :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
      17                 :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
      18                 :  *  See the GNU General Public License for more details.               *
      19                 :  *                                                                     *
      20                 :  *  You should have received a copy of the GNU General Public License  *
      21                 :  *  along with this program; if not, you can get it from:              *
      22                 :  *  http://www.gnu.org/copyleft/gpl.html                               *
      23                 :  ***********************************************************************/
      24                 : 
      25                 : #ifdef HAVE_CONFIG_H
      26                 : # include <config.h>
      27                 : #endif
      28                 : 
      29                 : #include <gettext.h>
      30                 : 
      31                 : #include <string.h> /* memcmp() */
      32                 : #include <assert.h>
      33                 : 
      34                 : #include <stdbool.h>
      35                 : #include <inttypes.h>
      36                 : #include <time.h> /* clock_gettime() */
      37                 : 
      38                 : #include <sys/types.h>
      39                 : #include <unistd.h> /* sysconf() */
      40                 : #include <netinet/in.h> /* struct in6_addr */
      41                 : #include <netdb.h> /* getaddrinfo(), gai_strerror() */
      42                 : #include <syslog.h>
      43                 : #include <stdlib.h> /* malloc(), free() */
      44                 : #include <errno.h> /* EINTR */
      45                 : #include <pthread.h>
      46                 : #include <arpa/nameser.h>
      47                 : #include <resolv.h> /* res_init() */
      48                 : 
      49                 : #include "teredo.h"
      50                 : #include "teredo-udp.h"
      51                 : #include "packets.h"
      52                 : 
      53                 : #include "security.h"
      54                 : #include "maintain.h"
      55                 : #include "v4global.h" // is_ipv4_global_unicast()
      56                 : #include "debug.h"
      57                 : 
      58                 : static inline void gettime (struct timespec *now)
      59               0 : {
      60                 : #if (_POSIX_CLOCK_SELECTION - 0 >= 0) && (_POSIX_MONOTONIC_CLOCK - 0 >= 0)
      61               0 :         if (clock_gettime (CLOCK_MONOTONIC, now) == 0)
      62               0 :                 return;
      63                 : #else
      64                 : # define pthread_condattr_setclock( a, c ) (((c) != CLOCK_REALTIME) ? EINVAL : 0)
      65                 : # ifndef CLOCK_MONOTONIC
      66                 : #  define CLOCK_MONOTONIC CLOCK_REALTIME
      67                 : # endif
      68                 : # warning Monotonic clock is needed for proper Teredo maintenance!
      69                 : #endif
      70               0 :         clock_gettime (CLOCK_REALTIME, now);
      71                 : }
      72                 : 
      73                 : 
      74                 : struct teredo_maintenance
      75                 : {
      76                 :         pthread_t thread;
      77                 :         pthread_mutex_t outer;
      78                 :         pthread_mutex_t inner;
      79                 :         pthread_cond_t received;
      80                 :         pthread_cond_t processed;
      81                 : 
      82                 :         const teredo_packet *incoming;
      83                 : 
      84                 :         int fd;
      85                 :         struct
      86                 :         {
      87                 :                 teredo_state state;
      88                 :                 teredo_state_cb cb;
      89                 :                 void *opaque;
      90                 :         } state;
      91                 :         char *server;
      92                 :         char *server2;
      93                 : 
      94                 :         unsigned qualification_delay;
      95                 :         unsigned qualification_retries;
      96                 :         unsigned refresh_delay;
      97                 :         unsigned restart_delay;
      98                 : };
      99                 : 
     100                 : 
     101                 : static int getipv4byname (const char *restrict name, uint32_t *restrict ipv4)
     102               0 : {
     103                 :         struct addrinfo hints =
     104                 :         {
     105                 :                 .ai_family = AF_INET,
     106                 :                 .ai_socktype = SOCK_DGRAM
     107               0 :         }, *res;
     108                 : 
     109               0 :         int val = getaddrinfo (name, NULL, &hints, &res);
     110               0 :         if (val)
     111               0 :                 return val;
     112                 : 
     113               0 :         memcpy (ipv4, &((const struct sockaddr_in *)(res->ai_addr))->sin_addr, 4);
     114               0 :         freeaddrinfo (res);
     115                 : 
     116               0 :         return 0;
     117                 : }
     118                 : 
     119                 : 
     120                 : /**
     121                 :  * Resolve Teredo server addresses.
     122                 :  *
     123                 :  * @return 0 on success, or an error value as defined for getaddrinfo().
     124                 :  */
     125                 : static int resolveServerIP (const char *server, uint32_t *restrict ip,
     126                 :                             const char *server2, uint32_t *restrict ip2)
     127               0 : {
     128               0 :         int val = getipv4byname (server, ip);
     129               0 :         if (val)
     130               0 :                 return val;
     131                 : 
     132               0 :         if ((server2 == NULL) || getipv4byname (server2, ip2) || (ip2 == ip))
     133                 :                 /*
     134                 :                  * NOTE:
     135                 :                  * While not specified anywhere, Windows XP/2003 seems to always
     136                 :                  * use the "next" IPv4 address as the secondary address.
     137                 :                  * We use as default, or as a replacement in case of error.
     138                 :                  */
     139               0 :                 *ip2 = htonl (ntohl (*ip) + 1);
     140                 : 
     141               0 :         if (!is_ipv4_global_unicast (*ip) || !is_ipv4_global_unicast (*ip2))
     142               0 :                 syslog (LOG_WARNING, _("Server has a non global IPv4 address. "
     143                 :                                        "It will most likely not work."));
     144                 : 
     145               0 :         return 0;
     146                 : }
     147                 : 
     148                 : 
     149                 : /**
     150                 :  * Checks and parses a received Router Advertisement.
     151                 :  *
     152                 :  * @return 0 if successful.
     153                 :  */
     154                 : static int
     155                 : maintenance_recv (const teredo_packet *restrict packet, uint32_t server_ip,
     156                 :                   uint8_t *restrict nonce, bool cone, uint16_t *restrict mtu,
     157                 :                   union teredo_addr *restrict newaddr)
     158               0 : {
     159               0 :         assert (packet->auth_nonce != NULL);
     160                 : 
     161               0 :         if (memcmp (packet->auth_nonce, nonce, 8))
     162               0 :                 return EPERM;
     163                 : 
     164                 :         /* TODO: fail instead of ignoring the packet? */
     165               0 :         if (packet->auth_conf_byte)
     166                 :         {
     167               0 :                 syslog (LOG_ERR, _("Authentication with server failed."));
     168               0 :                 return EACCES;
     169                 :         }
     170                 : 
     171               0 :         if (teredo_parse_ra (packet, newaddr, cone, mtu)
     172                 :         /* TODO: try to work-around incorrect server IP */
     173                 :          || (newaddr->teredo.server_ip != server_ip))
     174               0 :                 return EINVAL;
     175                 : 
     176                 :         /* Valid router advertisement received! */
     177               0 :         return 0;
     178                 : }
     179                 : 
     180                 : 
     181                 : /**
     182                 :  * Waits until the clock reaches deadline or a RS packet is received.
     183                 :  * @return 0 if a packet was received, ETIMEDOUT if deadline was reached.
     184                 :  */
     185                 : static int wait_reply (teredo_maintenance *restrict m,
     186                 :                        const struct timespec *restrict deadline)
     187               0 : {
     188               0 :         assert (m->incoming == NULL);
     189                 : 
     190                 :         /* Ignore EINTR */
     191                 :         for (;;)
     192                 :         {
     193               0 :                 int val = pthread_cond_timedwait (&m->received, &m->inner, deadline);
     194                 : 
     195               0 :                 switch (val)
     196                 :                 {
     197                 :                         case 0:
     198               0 :                                 if (m->incoming == NULL) // spurious wakeup
     199               0 :                                         continue;
     200                 :                                 /* fall through */
     201                 :                         case ETIMEDOUT:
     202               0 :                                 return val;
     203                 :                 }
     204               0 :         }
     205                 :         return 0; // dead code
     206                 : }
     207                 : 
     208                 : 
     209                 : /**
     210                 :  * Waits until the clock reaches deadline and ignore any RS packet received
     211                 :  * in the mean time.
     212                 :  */
     213                 : static void wait_reply_ignore (teredo_maintenance *restrict m,
     214                 :                                const struct timespec *restrict deadline)
     215               0 : {
     216               0 :         while (wait_reply (m, deadline) == 0)
     217                 :         {
     218               0 :                 m->incoming = NULL;
     219               0 :                 pthread_cond_signal (&m->processed);
     220                 :         }
     221               0 : }
     222                 : 
     223                 : 
     224                 : /**
     225                 :  * Make sure ts is in the future. If not, set it to the current time.
     226                 :  * @return false if (*ts) was changed, true otherwise.
     227                 :  */
     228                 : static bool
     229                 : checkTimeDrift (struct timespec *ts)
     230               0 : {
     231                 :         struct timespec now;
     232               0 :         gettime (&now);
     233                 : 
     234               0 :         if ((now.tv_sec > ts->tv_sec)
     235                 :          || ((now.tv_sec == ts->tv_sec) && (now.tv_nsec > ts->tv_nsec)))
     236                 :         {
     237                 :                 /* process stopped, CPU starved, or (ACPI, APM, etc) suspend */
     238               0 :                 syslog (LOG_WARNING, _("Too much time drift. Resynchronizing."));
     239               0 :                 memcpy (ts, &now, sizeof (*ts));
     240               0 :                 return false;
     241                 :         }
     242               0 :         return true;
     243                 : }
     244                 : 
     245                 : 
     246                 : static void
     247                 : cleanup_unlock (void *o)
     248               0 : {
     249               0 :         (void)pthread_mutex_unlock ((pthread_mutex_t *)o);
     250               0 : }
     251                 : 
     252                 : 
     253                 : /*
     254                 :  * Implementation notes:
     255                 :  * - Optional Teredo interval determination procedure was never implemented.
     256                 :  *   It adds NAT binding maintenance brittleness in addition to implementation
     257                 :  *   complexity, and is not necessary for RFC4380 compliance.
     258                 :  *   Also STUN RFC3489bis deprecates this type of behavior.
     259                 :  * - NAT cone type probing was removed in Miredo version 0.9.5. Since then,
     260                 :  *   Miredo qualification state machine became explicitly incompliant with
     261                 :  *   RFC4380. However, this made the startup much faster in many cases (many
     262                 :  *   NATs are restricted or symmetric), and is in accordance with deprecation
     263                 :  *   of NAT type determination in STUN RFC3489bis.
     264                 :  */
     265                 : 
     266                 : /**
     267                 :  * Teredo client maintenance procedure
     268                 :  */
     269                 : static inline LIBTEREDO_NORETURN
     270                 : void maintenance_thread (teredo_maintenance *m)
     271               0 : {
     272               0 :         struct timespec deadline = { 0, 0 };
     273               0 :         teredo_state *c_state = &m->state.state;
     274               0 :         uint32_t server_ip = 0;
     275               0 :         unsigned count = 0;
     276               0 :         bool error = false;
     277                 : 
     278               0 :         assert (!c_state->up);
     279               0 :         pthread_mutex_lock (&m->inner);
     280                 : 
     281                 :         /*
     282                 :          * Qualification/maintenance procedure
     283                 :          */
     284               0 :         pthread_cleanup_push (cleanup_unlock, &m->inner);
     285                 :         for (;;)
     286                 :         {
     287                 :                 /* Resolve server IPv4 addresses */
     288               0 :                 while (server_ip == 0)
     289                 :                 {
     290                 :                         /* FIXME: mutex kept while resolving - very bad */
     291                 :                         int val = resolveServerIP (m->server, &server_ip,
     292               0 :                                                    NULL, &(uint32_t){ 0 });
     293                 : 
     294               0 :                         gettime (&deadline);
     295                 : 
     296               0 :                         if (val)
     297                 :                         {
     298                 :                                 /* DNS resolution failed */
     299               0 :                                 syslog (LOG_ERR,
     300                 :                                         _("Cannot resolve Teredo server address \"%s\": %s"),
     301                 :                                         m->server, gai_strerror (val));
     302                 : 
     303                 :                                 /* wait some time before next resolution attempt */
     304               0 :                                 deadline.tv_sec += m->restart_delay;
     305               0 :                                 wait_reply_ignore (m, &deadline);
     306                 :                         }
     307                 :                         else
     308                 :                         {
     309                 :                                 /* DNS resolution succeeded */
     310                 :                                 /* Tells Teredo client about the new server's IP */
     311               0 :                                 c_state->addr.teredo.server_ip = server_ip;
     312               0 :                                 m->state.cb (c_state, m->state.opaque);
     313                 :                         }
     314                 :                 }
     315                 : 
     316                 :                 /* SEND ROUTER SOLICATION */
     317                 :                 do
     318               0 :                         deadline.tv_sec += m->qualification_delay;
     319               0 :                 while (!checkTimeDrift (&deadline));
     320                 : 
     321                 :                 uint8_t nonce[8];
     322               0 :                 teredo_get_nonce (deadline.tv_sec, server_ip, htons (IPPORT_TEREDO),
     323                 :                                   nonce);
     324               0 :                 teredo_send_rs (m->fd, server_ip, nonce, false);
     325                 : 
     326               0 :                 int val = 0;
     327                 :                 union teredo_addr newaddr;
     328               0 :                 uint16_t mtu = 1280;
     329                 : 
     330                 :                 /* RECEIVE ROUTER ADVERTISEMENT */
     331                 :                 do
     332                 :                 {
     333               0 :                         val = wait_reply (m, &deadline);
     334               0 :                         if (val)
     335               0 :                                 continue; // time out
     336                 : 
     337                 :                         /* check received packet */
     338               0 :                         val = maintenance_recv (m->incoming, server_ip,
     339                 :                                                 nonce, false, &mtu, &newaddr);
     340               0 :                         m->incoming = NULL;
     341               0 :                         pthread_cond_signal (&m->processed);
     342                 :                 }
     343               0 :                 while ((val != 0) && (val != ETIMEDOUT));
     344                 : 
     345               0 :                 unsigned delay = 0;
     346                 : 
     347                 :                 /* UPDATE FINITE STATE MACHINE */
     348               0 :                 if (val /* == ETIMEDOUT */)
     349                 :                 {
     350                 :                         /* no response */
     351               0 :                         count++;
     352                 : 
     353               0 :                         if (count >= m->qualification_retries)
     354                 :                         {
     355               0 :                                 count = 0;
     356                 :                                 /* No response from server */
     357               0 :                                 if (!error)
     358                 :                                 {
     359               0 :                                         syslog (LOG_INFO, _("No reply from Teredo server"));
     360               0 :                                         error = true;
     361                 :                                 }
     362                 : 
     363               0 :                                 if (c_state->up)
     364                 :                                 {
     365               0 :                                         syslog (LOG_NOTICE, _("Lost Teredo connectivity"));
     366               0 :                                         c_state->up = false;
     367               0 :                                         m->state.cb (c_state, m->state.opaque);
     368               0 :                                         server_ip = 0;
     369                 :                                 }
     370                 : 
     371                 :                                 /* Wait some time before retrying */
     372               0 :                                 delay = m->restart_delay;
     373                 :                         }
     374                 :                 }
     375                 :                 else
     376                 :                 {
     377                 :                         /* RA received and parsed succesfully */
     378               0 :                         count = 0;
     379                 : 
     380               0 :                         if ((!c_state->up)
     381                 :                          || memcmp (&c_state->addr, &newaddr, sizeof (c_state->addr))
     382                 :                          || (c_state->mtu != mtu))
     383                 :                         {
     384               0 :                                 memcpy (&c_state->addr, &newaddr, sizeof (c_state->addr));
     385               0 :                                 c_state->mtu = mtu;
     386               0 :                                 c_state->up = true;
     387                 : 
     388               0 :                                 syslog (LOG_NOTICE, _("New Teredo address/MTU"));
     389               0 :                                 m->state.cb (c_state, m->state.opaque);
     390                 :                         }
     391                 : 
     392                 :                         /* Success: schedule next NAT binding maintenance */
     393               0 :                         error = false;
     394               0 :                         delay = m->refresh_delay;
     395                 :                 }
     396                 : 
     397                 :                 /* WAIT UNTIL NEXT SOLICITATION */
     398                 :                 /* TODO: watch for new interface events
     399                 :                  * (netlink on Linux, PF_ROUTE on BSD) */
     400               0 :                 if (delay)
     401                 :                 {
     402               0 :                         deadline.tv_sec -= m->qualification_delay;
     403               0 :                         deadline.tv_sec += delay;
     404               0 :                         wait_reply_ignore (m, &deadline);
     405                 :                 }
     406               0 :         }
     407                 :         /* dead code */
     408                 :         pthread_cleanup_pop (1);
     409                 : }
     410                 : 
     411                 : 
     412                 : static LIBTEREDO_NORETURN void *do_maintenance (void *opaque)
     413               0 : {
     414               0 :         maintenance_thread ((teredo_maintenance *)opaque);
     415                 : }
     416                 : 
     417                 : 
     418                 : static const unsigned QualificationDelay = 4; // seconds
     419                 : static const unsigned QualificationRetries = 3;
     420                 : 
     421                 : static const unsigned RefreshDelay = 30; // seconds
     422                 : static const unsigned RestartDelay = 100; // seconds
     423                 : 
     424                 : /**
     425                 :  * Creates and starts a Teredo client maintenance procedure thread.
     426                 :  *
     427                 :  * @param fd socket to send router solicitation with
     428                 :  * @param cb status change notification callback
     429                 :  * @param opaque data for <cb> callback
     430                 :  * @param s1 primary server address/hostname
     431                 :  * @param s2 secondary server address/hostname
     432                 :  * @param q_sec qualification time out (seconds), 0 = default
     433                 :  * @param q_retries qualification retries, 0 = default
     434                 :  * @param refresh_sec qualification refresh interval (seconds), 0 = default
     435                 :  * @param restart_sec qualification failure interval (seconds), 0 = default
     436                 :  *
     437                 :  * @return NULL on error.
     438                 :  */
     439                 : teredo_maintenance *
     440                 : teredo_maintenance_start (int fd, teredo_state_cb cb, void *opaque,
     441                 :                           const char *s1, const char *s2,
     442                 :                           unsigned q_sec, unsigned q_retries,
     443                 :                           unsigned refresh_sec, unsigned restart_sec)
     444               0 : {
     445               0 :         teredo_maintenance *m = (teredo_maintenance *)malloc (sizeof (*m));
     446                 : 
     447               0 :         if (m == NULL)
     448               0 :                 return NULL;
     449                 : 
     450               0 :         memset (m, 0, sizeof (*m));
     451               0 :         m->fd = fd;
     452               0 :         m->state.cb = cb;
     453               0 :         m->state.opaque = opaque;
     454               0 :         m->server = strdup (s1);
     455                 : 
     456               0 :         m->qualification_delay = q_sec ?: QualificationDelay;
     457               0 :         m->qualification_retries = q_retries ?: QualificationRetries;
     458               0 :         m->refresh_delay = refresh_sec ?: RefreshDelay;
     459               0 :         m->restart_delay = restart_sec ?: RestartDelay;
     460                 : 
     461               0 :         if (m->server == NULL)
     462                 :         {
     463               0 :                 free (m);
     464               0 :                 return NULL;
     465                 :         }
     466                 : 
     467               0 :         m->server2 = (s2 != NULL) ? strdup (s2) : NULL;
     468               0 :         if ((s2 != NULL) != (m->server2 != NULL))
     469               0 :                 goto error;
     470                 :         else
     471                 :         {
     472                 :                 pthread_condattr_t attr;
     473                 : 
     474               0 :                 pthread_condattr_init (&attr);
     475               0 :                 (void)pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
     476                 :                 /* EINVAL: CLOCK_MONOTONIC unknown */
     477                 : 
     478               0 :                 pthread_cond_init (&m->received, &attr);
     479               0 :                 pthread_condattr_destroy (&attr);
     480                 :         }
     481                 : 
     482               0 :         pthread_cond_init (&m->processed, NULL);
     483               0 :         pthread_mutex_init (&m->outer, NULL);
     484               0 :         pthread_mutex_init (&m->inner, NULL);
     485                 : 
     486               0 :         int err = pthread_create (&m->thread, NULL, do_maintenance, m);
     487               0 :         if (err == 0)
     488               0 :                 return m;
     489                 : 
     490               0 :         syslog (LOG_ALERT, _("Error (%s): %s\n"), "pthread_create",
     491                 :                 strerror (err));
     492                 : 
     493               0 :         pthread_cond_destroy (&m->processed);
     494               0 :         pthread_cond_destroy (&m->received);
     495               0 :         pthread_mutex_destroy (&m->outer);
     496               0 :         pthread_mutex_destroy (&m->inner);
     497                 : 
     498               0 : error:
     499               0 :         if (m->server2 != NULL)
     500               0 :                 free (m->server2);
     501               0 :         free (m->server);
     502               0 :         free (m);
     503               0 :         return NULL;
     504                 : }
     505                 : 
     506                 : /**
     507                 :  * Stops and destroys a maintenance thread created by
     508                 :  * teredo_maintenance_start()
     509                 :  */
     510                 : void teredo_maintenance_stop (teredo_maintenance *m)
     511               0 : {
     512               0 :         pthread_cancel (m->thread);
     513               0 :         pthread_join (m->thread, NULL);
     514                 : 
     515               0 :         pthread_cond_destroy (&m->processed);
     516               0 :         pthread_cond_destroy (&m->received);
     517               0 :         pthread_mutex_destroy (&m->inner);
     518               0 :         pthread_mutex_destroy (&m->outer);
     519                 : 
     520               0 :         if (m->server2 != NULL)
     521               0 :                 free (m->server2);
     522               0 :         free (m->server);
     523               0 :         free (m);
     524               0 : }
     525                 : 
     526                 : 
     527                 : /**
     528                 :  * Passes a Teredo packet to a maintenance thread for processing.
     529                 :  * Thread-safe, not async-cancel safe.
     530                 :  *
     531                 :  * @return 0 if processed, -1 if not a valid router solicitation.
     532                 :  */
     533                 : int teredo_maintenance_process (teredo_maintenance *restrict m,
     534                 :                                 const teredo_packet *restrict packet)
     535               0 : {
     536               0 :         assert (m != NULL);
     537               0 :         assert (packet != NULL);
     538                 : 
     539                 :         /*
     540                 :          * We don't accept router advertisement without nonce.
     541                 :          * It is far too easy to spoof such packets.
     542                 :          */
     543               0 :         if ((packet->source_port != htons (IPPORT_TEREDO))
     544                 :             /* TODO: check for primary or secondary server address */
     545                 :          || (packet->auth_nonce == NULL)
     546                 :          || memcmp (packet->ip6 + 24, &teredo_restrict, 16))
     547               0 :                 return -1;
     548                 : 
     549               0 :         pthread_mutex_lock (&m->outer);
     550               0 :         pthread_mutex_lock (&m->inner);
     551                 : 
     552               0 :         m->incoming = packet;
     553               0 :         pthread_cond_signal (&m->received);
     554                 : 
     555                 :         /* Waits for maintenance thread to process packet... */
     556                 :         do
     557               0 :                 pthread_cond_wait (&m->processed, &m->inner);
     558               0 :         while (m->incoming != NULL);
     559                 : 
     560               0 :         pthread_mutex_unlock (&m->inner);
     561               0 :         pthread_mutex_unlock (&m->outer);
     562                 : 
     563               0 :         return 0;
     564                 : }

Generated by: LTP GCOV extension version 1.5