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 GNUNET_NAT_UPNP_Handle *upnp;
52 * Handle for NAT PMP operations.
54 GNUNET_NAT_NATPMP_Handle *natpmp;
59 struct GNUNET_SCHEDULER_Handle *sched;
62 * LAN address as passed by the caller
64 struct sockaddr *local_addr;
67 * External address as reported by NAT box
69 struct sockaddr *ext_addr;
72 * External address and port where packets are redirected
74 struct sockaddr *contact_addr;
76 GNUNET_NAT_AddressCallback callback;
79 * Closure for 'callback'.
83 GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
85 enum GNUNET_NAT_PortState natpmp_status;
87 enum GNUNET_NAT_PortState upnp_status;
103 get_nat_state_str (enum GNUNET_NAT_PortState state)
107 case GNUNET_NAT_PORT_MAPPING:
109 case GNUNET_NAT_PORT_MAPPED:
111 case GNUNET_NAT_PORT_UNMAPPING:
113 case GNUNET_NAT_PORT_UNMAPPED:
114 return "Not forwarded";
115 case GNUNET_NAT_PORT_ERROR:
116 return "Redirection failed";
125 get_traversal_status (const struct GNUNET_NAT_Handle * s)
127 return MAX (s->natpmp_status, s->upnp_status);
132 * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
133 * @param a first sockaddr
134 * @param b second sockaddr
135 * @return 0 if addresses are equal, non-null value otherwise */
137 GNUNET_NAT_cmp_addr (const struct sockaddr *a,
138 const struct sockaddr *b)
142 if ( (a->sa_family == AF_INET) && (b->sa_family == AF_INET) )
143 return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
144 &(((struct sockaddr_in *) b)->sin_addr),
145 sizeof (struct in_addr));
146 if ( (a->sa_family == AF_INET6) && (b->sa_family == AF_INET6) )
147 return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
148 &(((struct sockaddr_in6 *) b)->sin6_addr),
149 sizeof (struct in6_addr));
155 * Deal with a new IP address or port redirection:
156 * Send signals with the appropriate sockaddr (IP and port), free and changes
157 * or nullify the previous sockaddr. Change the port if needed.
160 notify_change (struct GNUNET_NAT_Handle *nat,
161 struct sockaddr *addr,
165 if (new_port_mapped == nat->port_mapped)
167 nat->port_mapped = new_port_mapped;
169 if ( (NULL != nat->contact_addr) &&
170 (NULL != nat->callback) )
171 nat->callback (nat->callback_cls,
174 sizeof (nat->contact_addr));
175 GNUNET_free_non_null (nat->contact_addr);
176 nat->contact_addr = NULL;
177 GNUNET_free_non_null (nat->ext_addr);
178 nat->ext_addr = NULL;
181 nat->ext_addr = GNUNET_malloc (addrlen);
182 memcpy (nat->ext_addr, addr, addrlen);
184 /* Recreate the ext_addr:public_port bogus address to pass to the callback */
185 if (nat->ext_addr->sa_family == AF_INET)
187 struct sockaddr_in tmp_addr;
189 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
190 tmp_addr->sin_family = AF_INET;
191 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
192 tmp_addr->sin_len = sizeof (struct sockaddr_in);
194 tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0;
195 tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr;
196 nat->contact_addr = (struct sockaddr *) tmp_addr;
197 if (NULL != nat->callback)
198 nat->callback (nat->callback_cls,
201 sizeof (struct sockaddr_in));
203 else if (nat->ext_addr->sa_family == AF_INET6)
205 struct sockaddr_in6 *tmp_addr;
207 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
208 tmp_addr->sin6_family = AF_INET6;
209 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
210 tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
212 tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0;
213 tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr;
214 nat->contact_addr = (struct sockaddr *) tmp_addr;
215 if (NULL != nat->callback)
216 nat->callback (nat->callback_cls,
219 sizeof (struct sockaddr_in6));
229 nat_pulse (void *cls,
230 const struct GNUNET_SCHEDULER_TaskContext *tc)
232 struct GNUNET_NAT_Handle *nat = cls;
236 struct sockaddr *ext_addr_upnp = NULL;
237 struct sockaddr *ext_addr_natpmp = NULL;
239 nat->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
240 old_status = get_traversal_status (nat);
242 /* Only update the protocol that has been successful until now */
243 if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
245 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
247 else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
249 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
255 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
258 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
261 new_status = get_traversal_status (nat);
262 if ( (old_status != new_status) &&
263 ( (new_status == GNUNET_NAT_PORT_UNMAPPED) ||
264 (new_status == GNUNET_NAT_PORT_ERROR) ) )
265 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
267 _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
269 if (new_status != old_status)
270 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT",
271 _("State changed from `%s' to `%s'\n"),
272 get_nat_state_str (old_status),
273 get_nat_state_str (new_status));
276 port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED);
277 if (!(ext_addr_upnp || ext_addr_natpmp))
279 /* Address has just changed and we could not get it, or it's the first try */
280 if ( (NULL != nat->ext_addr) ||
281 (GNUNET_NO == nat->did_warn) )
283 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
285 _("Could not determine external IP address\n"));
286 nat->did_warn = GNUNET_YES;
288 notify_change (nat, NULL, port_mapped);
290 else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0)
292 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
294 _("External IP address changed to %s\n"),
295 GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp)));
296 notify_change (nat, ext_addr_upnp, port_mapped);
298 else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0)
300 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT",
301 _("External IP address changed to `%s'\n"),
302 GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp)));
303 notify_change (nat, ext_addr_natpmp, port_mapped);
305 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched,
306 GNUNET_TIME_UNIT_SECONDS,
312 * Attempt to enable port redirection and detect public IP address contacting
313 * UPnP or NAT-PMP routers on the local network. Use addr to specify to which
314 * of the local host's addresses should the external port be mapped. The port
315 * is taken from the corresponding sockaddr_in[6] field.
317 * @param sched the sheduler used in the program
318 * @param addr the local address packets should be redirected to
319 * @param addrlen actual lenght of the address
320 * @param callback function to call everytime the public IP address changes
321 * @param callback_cls closure for callback
322 * @return NULL on error, otherwise handle that can be used to unregister
324 struct GNUNET_NAT_Handle *
325 GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
326 const struct sockaddr *addr, socklen_t addrlen,
327 GNUNET_NAT_AddressCallback callback, void *callback_cls)
329 struct GNUNET_NAT_Handle *nat;
331 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
334 GNUNET_assert ( (addr->sa_family == AF_INET) ||
335 (addr->sa_family == AF_INET6) );
336 nat->local_addr = GNUNET_malloc (addrlen);
337 memcpy (nat->local_addr, addr, addrlen);
338 if (addr->sa_family == AF_INET)
340 nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
341 ((struct sockaddr_in *) nat->local_addr)->sin_port = 0;
343 else if (addr->sa_family == AF_INET6)
345 nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
346 ((struct sockaddr_in6 *) nat->local_addr)->sin6_port = 0;
349 nat->should_change = GNUNET_YES;
351 nat->is_enabled = GNUNET_YES;
352 nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
353 nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
354 nat->callback = callback;
355 nat->callback_cls = callback_cls;
356 nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port);
357 nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port);
358 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched,
359 GNUNET_TIME_UNIT_SECONDS,
366 * Stop port redirection and public IP address detection for the given handle.
367 * This frees the handle, after having sent the needed commands to close open ports.
369 * @param h the handle to stop
372 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
374 struct sockaddr *addr;
376 GNUNET_SCHEDULER_cancel (h->sched,
379 GNUNET_NAT_UPNP_pulse (h->upnp,
380 GNUNET_NO, GNUNET_NO,
383 GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO,
385 GNUNET_NAT_NATPMP_close (h->natpmp);
386 GNUNET_NAT_UPNP_close (h->upnp);
387 GNUNET_free_non_null (h->local_addr);
388 GNUNET_free_non_null (h->ext_addr);