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 : }
|