2 This file is part of GNUnet.
3 (C) 2009, 2010 Christian Grothoff (and other contributing authors)
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.
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.
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.
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>
29 * @brief Library handling UPnP and NAT-PMP port forwarding and
30 * external IP address retrieval
32 * @author Milan Bouchet-Valat
35 #include "gnunet_util_lib.h"
36 #include "gnunet_nat_lib.h"
42 * Handle for active NAT registrations.
44 struct GNUNET_NAT_Handle
47 * Handle for UPnP operations.
49 struct GNUNET_NAT_UPNP_Handle *upnp;
52 * Handle for NAT PMP operations.
54 struct GNUNET_NAT_NATPMP_Handle *natpmp;
57 * LAN address as passed by the caller
59 struct sockaddr *local_addr;
62 * External address as reported by found NAT box
64 struct sockaddr *ext_addr;
67 * External address as reported by each type of NAT box
69 struct sockaddr *ext_addr_upnp;
70 struct sockaddr *ext_addr_natpmp;
73 * External address and port where packets are redirected
75 struct sockaddr *contact_addr;
77 GNUNET_NAT_AddressCallback callback;
80 * Closure for 'callback'.
84 GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
86 enum GNUNET_NAT_PortState natpmp_status;
88 enum GNUNET_NAT_PortState upnp_status;
104 uint16_t public_port;
110 get_nat_state_str (enum GNUNET_NAT_PortState state)
114 case GNUNET_NAT_PORT_MAPPING:
116 case GNUNET_NAT_PORT_MAPPED:
118 case GNUNET_NAT_PORT_UNMAPPING:
120 case GNUNET_NAT_PORT_UNMAPPED:
121 return "Not forwarded";
122 case GNUNET_NAT_PORT_ERROR:
123 return "Redirection failed";
132 get_traversal_status (const struct GNUNET_NAT_Handle *h)
134 return GNUNET_MAX (h->natpmp_status, h->upnp_status);
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 */
144 GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
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));
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.
166 notify_change (struct GNUNET_NAT_Handle *h,
167 struct sockaddr *addr, size_t addrlen, int new_port_mapped)
169 if (new_port_mapped == h->port_mapped)
171 h->port_mapped = new_port_mapped;
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);
182 h->ext_addr = GNUNET_malloc (addrlen);
183 memcpy (h->ext_addr, addr, addrlen);
185 /* Recreate the ext_addr:public_port bogus address to pass to the callback */
186 if (h->ext_addr->sa_family == AF_INET)
188 struct sockaddr_in *tmp_addr;
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);
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;
199 if (NULL != h->callback)
200 h->callback (h->callback_cls,
202 h->contact_addr, sizeof (struct sockaddr_in));
204 else if (h->ext_addr->sa_family == AF_INET6)
206 struct sockaddr_in6 *tmp_addr;
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);
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;
217 if (NULL != h->callback)
218 h->callback (h->callback_cls,
220 h->contact_addr, sizeof (struct sockaddr_in6));
228 static void nat_pulse (void *cls,
229 const struct GNUNET_SCHEDULER_TaskContext *tc);
232 pulse_cb (struct GNUNET_NAT_Handle *h)
237 /* One of the protocols is still working, wait for it to complete */
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,
248 ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
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));
257 port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED);
258 if (!(h->ext_addr_upnp || h->ext_addr_natpmp))
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)
265 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
267 _("Could not determine external IP address\n"));
268 h->did_warn = GNUNET_YES;
270 notify_change (h, NULL, 0, port_mapped);
272 else if (h->ext_addr_upnp
273 && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0)
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,
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);
283 else if (h->ext_addr_natpmp
284 && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0)
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);
294 h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
299 upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
301 struct GNUNET_NAT_Handle *h = cls;
303 h->upnp_status = status;
304 h->ext_addr_upnp = ext_addr;
312 natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
314 struct GNUNET_NAT_Handle *h = cls;
316 h->natpmp_status = status;
317 h->ext_addr_natpmp = ext_addr;
325 nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
327 struct GNUNET_NAT_Handle *h = cls;
329 /* Stop if we're already waiting for an action to complete */
330 h->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
333 h->old_status = get_traversal_status (h);
335 /* Only update the protocol that has been successful until now */
336 if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
339 GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
341 /* Wait for the callback to call pulse_cb() to handle changes */
344 else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
348 GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled);
355 GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
357 GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, &natpmp_pulse_cb, h);
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.
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
375 struct GNUNET_NAT_Handle *
376 GNUNET_NAT_register (const struct sockaddr *addr,
378 GNUNET_NAT_AddressCallback callback, void *callback_cls)
380 struct GNUNET_NAT_Handle *h;
382 h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
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)
392 h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
393 ((struct sockaddr_in *) h->local_addr)->sin_port = 0;
395 else if (addr->sa_family == AF_INET6)
397 h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
398 ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0;
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;
408 GNUNET_NAT_UPNP_init (h->local_addr, addrlen, h->public_port,
412 GNUNET_NAT_NATPMP_init (h->local_addr, addrlen, h->public_port,
413 &natpmp_pulse_cb, h);
415 h->pulse_timer = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
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.
425 * @param h the handle to stop
428 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
430 GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO);
431 GNUNET_NAT_UPNP_close (h->upnp);
434 GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO);
435 GNUNET_NAT_NATPMP_close (h->natpmp);
438 if (GNUNET_SCHEDULER_NO_TASK != h->pulse_timer)
439 GNUNET_SCHEDULER_cancel (h->pulse_timer);
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);