Tell core that we want to have this packet delivered
[oweals/gnunet.git] / src / nat / nat.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /*
22  * Parts of this file have been adapted from the Transmission project:
23  * Originally licensed by the GPL version 2.
24  * Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
25  */
26
27 /**
28  * @file nat/nat.c
29  * @brief Library handling UPnP and NAT-PMP port forwarding and
30  *     external IP address retrieval
31  *
32  * @author Milan Bouchet-Valat
33  */
34 #include "platform.h"
35 #include "gnunet_util_lib.h"
36 #include "gnunet_nat_lib.h"
37 #include "nat.h"
38 #include "natpmp.h"
39 #include "upnp.h"
40
41 /**
42  * Handle for active NAT registrations.
43  */
44 struct GNUNET_NAT_Handle
45 {
46   /**
47    * Handle for UPnP operations.
48    */
49   struct GNUNET_NAT_UPNP_Handle *upnp;
50
51   /**
52    * Handle for NAT PMP operations.
53    */
54   struct GNUNET_NAT_NATPMP_Handle *natpmp;
55
56   /**
57    * LAN address as passed by the caller 
58    */
59   struct sockaddr *local_addr;
60
61   /**
62    * External address as reported by found NAT box 
63    */
64   struct sockaddr *ext_addr;
65
66   /**
67    * External address as reported by each type of NAT box 
68    */
69   struct sockaddr *ext_addr_upnp;
70   struct sockaddr *ext_addr_natpmp;
71
72   /**
73    * External address and port where packets are redirected
74    */
75   struct sockaddr *contact_addr;
76
77   GNUNET_NAT_AddressCallback callback;
78
79   /**
80    * Closure for 'callback'.
81    */
82   void *callback_cls;
83
84   GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
85
86   enum GNUNET_NAT_PortState natpmp_status;
87
88   enum GNUNET_NAT_PortState upnp_status;
89
90   int is_enabled;
91
92   int should_change;
93
94   int port_mapped;
95
96   int old_status;
97
98   int new_status;
99
100   int did_warn;
101
102   int processing;
103
104   uint16_t public_port;
105
106 };
107
108 #ifdef DEBUG
109 static const char *
110 get_nat_state_str (enum GNUNET_NAT_PortState state)
111 {
112   switch (state)
113     {
114     case GNUNET_NAT_PORT_MAPPING:
115       return "Starting";
116     case GNUNET_NAT_PORT_MAPPED:
117       return "Forwarded";
118     case GNUNET_NAT_PORT_UNMAPPING:
119       return "Stopping";
120     case GNUNET_NAT_PORT_UNMAPPED:
121       return "Not forwarded";
122     case GNUNET_NAT_PORT_ERROR:
123       return "Redirection failed";
124     default:
125       return "not found";
126     }
127 }
128 #endif
129
130
131 static int
132 get_traversal_status (const struct GNUNET_NAT_Handle *h)
133 {
134   return GNUNET_MAX (h->natpmp_status, h->upnp_status);
135 }
136
137
138 /**
139  * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
140  * @param a first sockaddr
141  * @param b second sockaddr
142  * @return 0 if addresses are equal, non-null value otherwise */
143 int
144 GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
145 {
146   if (!(a && b))
147     return -1;
148   if ((a->sa_family == AF_INET) && (b->sa_family == AF_INET))
149     return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
150                    &(((struct sockaddr_in *) b)->sin_addr),
151                    sizeof (struct in_addr));
152   if ((a->sa_family == AF_INET6) && (b->sa_family == AF_INET6))
153     return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
154                    &(((struct sockaddr_in6 *) b)->sin6_addr),
155                    sizeof (struct in6_addr));
156   return -1;
157 }
158
159
160 /**
161  * Deal with a new IP address or port redirection:
162  * Send signals with the appropriate sockaddr (IP and port), free and changes
163  * or nullify the previous sockaddr. Change the port if needed.
164  */
165 static void
166 notify_change (struct GNUNET_NAT_Handle *h,
167                struct sockaddr *addr, size_t addrlen, int new_port_mapped)
168 {
169   if (new_port_mapped == h->port_mapped)
170     return;
171   h->port_mapped = new_port_mapped;
172
173   if ((NULL != h->contact_addr) && (NULL != h->callback))
174     h->callback (h->callback_cls,
175                  GNUNET_NO, h->contact_addr, sizeof (h->contact_addr));
176   GNUNET_free_non_null (h->contact_addr);
177   h->contact_addr = NULL;
178   GNUNET_free_non_null (h->ext_addr);
179   h->ext_addr = NULL;
180   if (NULL == addr)
181     return;
182   h->ext_addr = GNUNET_malloc (addrlen);
183   memcpy (h->ext_addr, addr, addrlen);
184
185   /* Recreate the ext_addr:public_port bogus address to pass to the callback */
186   if (h->ext_addr->sa_family == AF_INET)
187     {
188       struct sockaddr_in *tmp_addr;
189
190       tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
191       tmp_addr->sin_family = AF_INET;
192 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
193       tmp_addr->sin_len = sizeof (struct sockaddr_in);
194 #endif
195       tmp_addr->sin_port = h->port_mapped ? htons (h->public_port) : 0;
196       tmp_addr->sin_addr = ((struct sockaddr_in *) h->ext_addr)->sin_addr;
197       h->contact_addr = (struct sockaddr *) tmp_addr;
198
199       if (NULL != h->callback)
200         h->callback (h->callback_cls,
201                      GNUNET_YES,
202                      h->contact_addr, sizeof (struct sockaddr_in));
203     }
204   else if (h->ext_addr->sa_family == AF_INET6)
205     {
206       struct sockaddr_in6 *tmp_addr;
207
208       tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
209       tmp_addr->sin6_family = AF_INET6;
210 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
211       tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
212 #endif
213       tmp_addr->sin6_port = h->port_mapped ? htons (h->public_port) : 0;
214       tmp_addr->sin6_addr = ((struct sockaddr_in6 *) h->ext_addr)->sin6_addr;
215       h->contact_addr = (struct sockaddr *) tmp_addr;
216
217       if (NULL != h->callback)
218         h->callback (h->callback_cls,
219                      GNUNET_YES,
220                      h->contact_addr, sizeof (struct sockaddr_in6));
221     }
222   else
223     {
224       GNUNET_break (0);
225     }
226 }
227
228 static void nat_pulse (void *cls,
229                        const struct GNUNET_SCHEDULER_TaskContext *tc);
230
231 static void
232 pulse_cb (struct GNUNET_NAT_Handle *h)
233 {
234   socklen_t addrlen;
235   int port_mapped;
236
237   /* One of the protocols is still working, wait for it to complete */
238   if (h->processing)
239     return;
240
241   h->new_status = get_traversal_status (h);
242   if ((h->old_status != h->new_status) &&
243       ((h->new_status == GNUNET_NAT_PORT_UNMAPPED) ||
244        (h->new_status == GNUNET_NAT_PORT_ERROR)))
245     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
246                      "NAT",
247                      _
248                      ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
249 #ifdef DEBUG
250   if (h->new_status != h->old_status)
251     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT",
252                      _("State changed from `%s' to `%s'\n"),
253                      get_nat_state_str (h->old_status),
254                      get_nat_state_str (h->new_status));
255 #endif
256
257   port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED);
258   if (!(h->ext_addr_upnp || h->ext_addr_natpmp))
259     {
260       /* Address has just changed and we could not get it, or it's the first try,
261        * and we're not waiting for a reply from UPnP or NAT-PMP */
262       if (((NULL != h->ext_addr) ||
263            (GNUNET_NO == h->did_warn)) && h->processing != 0)
264         {
265           GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
266                            "NAT",
267                            _("Could not determine external IP address\n"));
268           h->did_warn = GNUNET_YES;
269         }
270       notify_change (h, NULL, 0, port_mapped);
271     }
272   else if (h->ext_addr_upnp
273            && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0)
274     {
275       addrlen = h->ext_addr_upnp->sa_family == AF_INET ?
276         sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
277       GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
278                        "NAT",
279                        _("External IP address changed to %s\n"),
280                        GNUNET_a2s (h->ext_addr_upnp, addrlen));
281       notify_change (h, h->ext_addr_upnp, addrlen, port_mapped);
282     }
283   else if (h->ext_addr_natpmp
284            && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0)
285     {
286       addrlen = h->ext_addr_natpmp->sa_family == AF_INET ?
287         sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
288       GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT",
289                        _("External IP address changed to `%s'\n"),
290                        GNUNET_a2s (h->ext_addr_natpmp, addrlen));
291       notify_change (h, h->ext_addr_natpmp, addrlen, port_mapped);
292     }
293
294   h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
295                                                  &nat_pulse, h);
296 }
297
298 static void
299 upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
300 {
301   struct GNUNET_NAT_Handle *h = cls;
302
303   h->upnp_status = status;
304   h->ext_addr_upnp = ext_addr;
305
306   h->processing--;
307   pulse_cb (h);
308 }
309
310 #if 0
311 static void
312 natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
313 {
314   struct GNUNET_NAT_Handle *h = cls;
315
316   h->natpmp_status = status;
317   h->ext_addr_natpmp = ext_addr;
318
319   h->processing--;
320   pulse_cb (h);
321 }
322 #endif
323
324 static void
325 nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
326 {
327   struct GNUNET_NAT_Handle *h = cls;
328
329   /* Stop if we're already waiting for an action to complete */
330   h->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
331   if (h->processing)
332     return;
333   h->old_status = get_traversal_status (h);
334
335   /* Only update the protocol that has been successful until now */
336   if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
337     {
338       h->processing = 1;
339       GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
340
341       /* Wait for the callback to call pulse_cb() to handle changes */
342       return;
343     }
344   else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
345     {
346       h->processing = 1;
347 #if 0
348       GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled);
349 #endif
350     }
351   else                          /* try both */
352     {
353       h->processing = 2;
354
355       GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
356 #if 0
357       GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, &natpmp_pulse_cb, h);
358 #endif
359     }
360 }
361
362
363 /**
364  * Attempt to enable port redirection and detect public IP address contacting
365  * UPnP or NAT-PMP routers on the local network. Use addr to specify to which
366  * of the local host's addresses should the external port be mapped. The port
367  * is taken from the corresponding sockaddr_in[6] field.
368  *
369  * @param addr the local address packets should be redirected to
370  * @param addrlen actual lenght of the address
371  * @param callback function to call everytime the public IP address changes
372  * @param callback_cls closure for callback
373  * @return NULL on error, otherwise handle that can be used to unregister 
374  */
375 struct GNUNET_NAT_Handle *
376 GNUNET_NAT_register (const struct sockaddr *addr,
377                      socklen_t addrlen,
378                      GNUNET_NAT_AddressCallback callback, void *callback_cls)
379 {
380   struct GNUNET_NAT_Handle *h;
381
382   h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
383
384   if (addr)
385     {
386       GNUNET_assert ((addr->sa_family == AF_INET) ||
387                      (addr->sa_family == AF_INET6));
388       h->local_addr = GNUNET_malloc (addrlen);
389       memcpy (h->local_addr, addr, addrlen);
390       if (addr->sa_family == AF_INET)
391         {
392           h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
393           ((struct sockaddr_in *) h->local_addr)->sin_port = 0;
394         }
395       else if (addr->sa_family == AF_INET6)
396         {
397           h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
398           ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0;
399         }
400     }
401   h->should_change = GNUNET_YES;
402   h->is_enabled = GNUNET_YES;
403   h->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
404   h->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
405   h->callback = callback;
406   h->callback_cls = callback_cls;
407   h->upnp =
408     GNUNET_NAT_UPNP_init (h->local_addr, addrlen, h->public_port,
409                           &upnp_pulse_cb, h);
410 #if 0
411   h->natpmp =
412     GNUNET_NAT_NATPMP_init (h->local_addr, addrlen, h->public_port,
413                             &natpmp_pulse_cb, h);
414 #endif
415   h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
416                                                  &nat_pulse, h);
417   return h;
418 }
419
420
421 /**
422  * Stop port redirection and public IP address detection for the given handle.
423  * This frees the handle, after having sent the needed commands to close open ports.
424  *
425  * @param h the handle to stop
426  */
427 void
428 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
429 {
430   GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO);
431   GNUNET_NAT_UPNP_close (h->upnp);
432
433 #if 0
434   GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO);
435   GNUNET_NAT_NATPMP_close (h->natpmp);
436 #endif
437
438   if (GNUNET_SCHEDULER_NO_TASK != h->pulse_timer)
439     GNUNET_SCHEDULER_cancel (h->pulse_timer);
440
441   GNUNET_free_non_null (h->local_addr);
442   GNUNET_free_non_null (h->ext_addr);
443   GNUNET_free_non_null (h->ext_addr_upnp);
444   GNUNET_free_non_null (h->ext_addr_natpmp);
445   GNUNET_free (h);
446 }
447
448 /* end of nat.c */