2 This file is part of GNUnet.
3 (C) 2009 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 2, 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
38 #if 0 /* keep Emacsens' auto-indent happy */
47 #include <sys/types.h>
50 #include "gnunet_common.h"
51 #include "gnunet_util_lib.h"
52 #include "gnunet_nat_lib.h"
56 /* Component name for logging */
57 #define COMP_NAT _("NAT")
60 struct GNUNET_NAT_Handle
64 GNUNET_NAT_port_forwarding natpmp_status;
65 GNUNET_NAT_port_forwarding upnp_status;
70 GNUNET_NAT_UPNP_Handle *upnp;
71 GNUNET_NAT_NATPMP_Handle *natpmp;
73 struct GNUNET_SCHEDULER_Handle *sched;
74 GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
76 struct sockaddr *local_addr; /* LAN address as passed by the caller */
77 struct sockaddr *ext_addr; /* External address as reported by NAT box */
78 struct sockaddr *contact_addr; /* External address and port where paquets are redirected*/
79 GNUNET_NAT_AddressCallback callback;
85 get_nat_state_str (int state)
89 /* we're in the process of trying to set up port forwarding */
90 case GNUNET_NAT_PORT_MAPPING:
93 /* we've successfully forwarded the port */
94 case GNUNET_NAT_PORT_MAPPED:
97 /* we're cancelling the port forwarding */
98 case GNUNET_NAT_PORT_UNMAPPING:
101 /* the port isn't forwarded */
102 case GNUNET_NAT_PORT_UNMAPPED:
103 return "Not forwarded";
105 case GNUNET_NAT_PORT_ERROR:
106 return "Redirection failed";
114 get_traversal_status (const GNUNET_NAT_Handle * s)
116 return MAX (s->natpmp_status, s->upnp_status);
120 * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
121 * @param a first sockaddr
122 * @param second sockaddr
123 * @returns 0 if addresses are equal, non-null value otherwise */
125 GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
130 if (a->sa_family == AF_INET && b->sa_family == AF_INET)
131 return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
132 &(((struct sockaddr_in *) b)->sin_addr),
133 sizeof (struct in_addr));
134 else if (a->sa_family == AF_INET6 && b->sa_family == AF_INET6)
135 return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
136 &(((struct sockaddr_in6 *) b)->sin6_addr),
137 sizeof (struct in6_addr));
142 /* Deal with a new IP address or port redirection:
143 * Send signals with the appropriate sockaddr (IP and port), free and changes
144 * or nullify the previous sockaddr. Change the port if needed.
147 notify_change (GNUNET_NAT_Handle *nat, struct sockaddr *addr, int new_port_mapped)
149 static int port_mapped = GNUNET_NO;
151 /* Nothing to do. We already check in nat_pulse() that addr has changed */
152 if (new_port_mapped == port_mapped)
155 port_mapped = new_port_mapped;
157 if (nat->contact_addr && nat->callback)
158 (*nat->callback) (nat->callback_cls, GNUNET_NO, (struct sockaddr *) &nat->contact_addr,
159 sizeof (nat->contact_addr));
161 /* At this point, we're sure contact_addr has changed */
162 if (nat->contact_addr)
164 GNUNET_free (nat->contact_addr);
165 nat->contact_addr = NULL;
168 /* No address, don't signal a new one */
172 GNUNET_free (nat->ext_addr);
173 nat->ext_addr = NULL;
176 /* Copy the new address and use it */
177 else if (addr != nat->ext_addr)
180 GNUNET_free (nat->ext_addr);
181 nat->ext_addr = GNUNET_malloc (sizeof (*addr));
182 memcpy (nat->ext_addr, addr, sizeof (*addr));
185 /* Recreate the ext_addr:public_port bogus address to pass to the callback */
186 if (nat->ext_addr->sa_family == AF_INET)
188 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;
198 (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
199 sizeof (struct sockaddr_in));
201 else if (nat->ext_addr->sa_family == AF_INET6)
203 struct sockaddr_in6 *tmp_addr;
204 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
205 tmp_addr->sin6_family = AF_INET6;
206 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
207 tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
209 tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0;
210 tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr;
211 nat->contact_addr = (struct sockaddr *) tmp_addr;
213 (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
214 sizeof (struct sockaddr_in6));
219 nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
221 GNUNET_NAT_Handle *nat = cls;
222 static int first_warning = GNUNET_YES;
226 struct sockaddr *ext_addr_upnp = NULL;
227 struct sockaddr *ext_addr_natpmp = NULL;
229 old_status = get_traversal_status (nat);
231 /* Only update the protocol that has been successful until now */
232 if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
234 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
236 else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
238 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
243 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
246 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
250 new_status = get_traversal_status (nat);
252 if (old_status != new_status &&
253 (new_status == GNUNET_NAT_PORT_UNMAPPED || new_status == GNUNET_NAT_PORT_ERROR))
254 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
255 _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
258 if (new_status != old_status)
259 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT,
260 _("State changed from \"%s\" to \"%s\"\n"),
261 get_nat_state_str (old_status),
262 get_nat_state_str (new_status));
265 port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED);
266 if (!(ext_addr_upnp || ext_addr_natpmp))
268 /* Address has just changed and we could not get it, or it's the first try */
269 if (nat->ext_addr || first_warning)
271 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
272 _("Could not determine external IP address\n"));
273 first_warning = GNUNET_NO;
276 notify_change (nat, NULL, port_mapped);
278 else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0)
280 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
281 _("External IP address changed from %s to %s\n"),
282 GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
283 GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp)));
285 notify_change (nat, ext_addr_upnp, port_mapped);
287 else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0)
289 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
290 _("External IP address changed from %s to %s\n"),
291 GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
292 GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp)));
294 notify_change (nat, ext_addr_natpmp, port_mapped);
297 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched, GNUNET_NO,
298 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
299 GNUNET_SCHEDULER_NO_TASK,
300 GNUNET_TIME_UNIT_SECONDS,
304 struct GNUNET_NAT_Handle *
305 GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
306 const struct sockaddr *addr, socklen_t addrlen,
307 GNUNET_NAT_AddressCallback callback, void *callback_cls)
309 GNUNET_NAT_Handle *nat = GNUNET_malloc (sizeof (GNUNET_NAT_Handle));
313 GNUNET_assert (addr->sa_family == AF_INET
314 || addr->sa_family == AF_INET6);
315 nat->local_addr = GNUNET_malloc (addrlen);
316 memcpy (nat->local_addr, addr, addrlen);
318 if (addr->sa_family == AF_INET)
320 nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
321 ((struct sockaddr_in *) nat->local_addr)->sin_port = 0;
323 else if (addr->sa_family == AF_INET6)
325 nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
326 ((struct sockaddr_in6 *) nat->local_addr)->sin6_port = 0;
331 nat->local_addr = NULL;
332 nat->public_port = 0;
335 nat->should_change = GNUNET_YES;
337 nat->is_enabled = GNUNET_YES;
338 nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
339 nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
340 nat->callback = callback;
341 nat->callback_cls = callback_cls;
342 nat->ext_addr = NULL;
343 nat->contact_addr = NULL;
344 nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port);
345 nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port);
347 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO,
348 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
349 GNUNET_SCHEDULER_NO_TASK,
350 GNUNET_TIME_UNIT_SECONDS,
357 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nat)
359 struct sockaddr *addr;
360 GNUNET_SCHEDULER_cancel (nat->sched, nat->pulse_timer);
363 GNUNET_NAT_UPNP_pulse (nat->upnp, GNUNET_NO, GNUNET_NO,
366 GNUNET_NAT_NATPMP_pulse (nat->natpmp, GNUNET_NO,
369 GNUNET_NAT_NATPMP_close (nat->natpmp);
370 GNUNET_NAT_UPNP_close (nat->upnp);
372 GNUNET_free (nat->ext_addr);
376 #if 0 /* keep Emacsens' auto-indent happy */