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 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
38 #include <sys/types.h>
41 #include "gnunet_common.h"
42 #include "gnunet_util_lib.h"
43 #include "gnunet_nat_lib.h"
47 /* Component name for logging */
48 #define COMP_NAT _("NAT")
51 struct GNUNET_NAT_Handle
55 enum GNUNET_NAT_port_forwarding natpmp_status;
56 enum GNUNET_NAT_port_forwarding upnp_status;
61 GNUNET_NAT_UPNP_Handle *upnp;
62 GNUNET_NAT_NATPMP_Handle *natpmp;
64 struct GNUNET_SCHEDULER_Handle *sched;
65 GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
67 struct sockaddr *local_addr; /* LAN address as passed by the caller */
68 struct sockaddr *ext_addr; /* External address as reported by NAT box */
69 struct sockaddr *contact_addr; /* External address and port where paquets are redirected*/
70 GNUNET_NAT_AddressCallback callback;
76 get_nat_state_str (int state)
80 /* we're in the process of trying to set up port forwarding */
81 case GNUNET_NAT_PORT_MAPPING:
84 /* we've successfully forwarded the port */
85 case GNUNET_NAT_PORT_MAPPED:
88 /* we're cancelling the port forwarding */
89 case GNUNET_NAT_PORT_UNMAPPING:
92 /* the port isn't forwarded */
93 case GNUNET_NAT_PORT_UNMAPPED:
94 return "Not forwarded";
96 case GNUNET_NAT_PORT_ERROR:
97 return "Redirection failed";
105 get_traversal_status (const struct GNUNET_NAT_Handle * s)
107 return MAX (s->natpmp_status, s->upnp_status);
111 * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
112 * @param a first sockaddr
113 * @param b second sockaddr
114 * @return 0 if addresses are equal, non-null value otherwise */
116 GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
121 if (a->sa_family == AF_INET && b->sa_family == AF_INET)
122 return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
123 &(((struct sockaddr_in *) b)->sin_addr),
124 sizeof (struct in_addr));
125 else if (a->sa_family == AF_INET6 && b->sa_family == AF_INET6)
126 return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
127 &(((struct sockaddr_in6 *) b)->sin6_addr),
128 sizeof (struct in6_addr));
133 /* Deal with a new IP address or port redirection:
134 * Send signals with the appropriate sockaddr (IP and port), free and changes
135 * or nullify the previous sockaddr. Change the port if needed.
138 notify_change (struct GNUNET_NAT_Handle *nat, struct sockaddr *addr, int new_port_mapped)
140 static int port_mapped = GNUNET_NO;
142 /* Nothing to do. We already check in nat_pulse() that addr has changed */
143 if (new_port_mapped == port_mapped)
146 port_mapped = new_port_mapped;
148 if (nat->contact_addr && nat->callback)
149 (*nat->callback) (nat->callback_cls, GNUNET_NO, (struct sockaddr *) &nat->contact_addr,
150 sizeof (nat->contact_addr));
152 /* At this point, we're sure contact_addr has changed */
153 if (nat->contact_addr)
155 GNUNET_free (nat->contact_addr);
156 nat->contact_addr = NULL;
159 /* No address, don't signal a new one */
163 GNUNET_free (nat->ext_addr);
164 nat->ext_addr = NULL;
167 /* Copy the new address and use it */
168 else if (addr != nat->ext_addr)
171 GNUNET_free (nat->ext_addr);
172 nat->ext_addr = GNUNET_malloc (sizeof (*addr));
173 memcpy (nat->ext_addr, addr, sizeof (*addr));
176 /* Recreate the ext_addr:public_port bogus address to pass to the callback */
177 if (nat->ext_addr->sa_family == AF_INET)
179 struct sockaddr_in *tmp_addr;
180 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
181 tmp_addr->sin_family = AF_INET;
182 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
183 tmp_addr->sin_len = sizeof (struct sockaddr_in);
185 tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0;
186 tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr;
187 nat->contact_addr = (struct sockaddr *) tmp_addr;
189 (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
190 sizeof (struct sockaddr_in));
192 else if (nat->ext_addr->sa_family == AF_INET6)
194 struct sockaddr_in6 *tmp_addr;
195 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
196 tmp_addr->sin6_family = AF_INET6;
197 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
198 tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
200 tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0;
201 tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr;
202 nat->contact_addr = (struct sockaddr *) tmp_addr;
204 (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
205 sizeof (struct sockaddr_in6));
210 nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
212 struct GNUNET_NAT_Handle *nat = cls;
213 static int first_warning = GNUNET_YES;
217 struct sockaddr *ext_addr_upnp = NULL;
218 struct sockaddr *ext_addr_natpmp = NULL;
220 old_status = get_traversal_status (nat);
222 /* Only update the protocol that has been successful until now */
223 if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
225 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
227 else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
229 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
234 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
237 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
241 new_status = get_traversal_status (nat);
243 if (old_status != new_status &&
244 (new_status == GNUNET_NAT_PORT_UNMAPPED || new_status == GNUNET_NAT_PORT_ERROR))
245 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
246 _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
249 if (new_status != old_status)
250 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT,
251 _("State changed from \"%s\" to \"%s\"\n"),
252 get_nat_state_str (old_status),
253 get_nat_state_str (new_status));
256 port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED);
257 if (!(ext_addr_upnp || ext_addr_natpmp))
259 /* Address has just changed and we could not get it, or it's the first try */
260 if (nat->ext_addr || first_warning)
262 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
263 _("Could not determine external IP address\n"));
264 first_warning = GNUNET_NO;
267 notify_change (nat, NULL, port_mapped);
269 else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0)
271 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
272 _("External IP address changed from %s to %s\n"),
273 GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
274 GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp)));
276 notify_change (nat, ext_addr_upnp, port_mapped);
278 else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 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_natpmp, sizeof (ext_addr_natpmp)));
285 notify_change (nat, ext_addr_natpmp, port_mapped);
288 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched,
289 GNUNET_TIME_UNIT_SECONDS,
293 struct GNUNET_NAT_Handle *
294 GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
295 const struct sockaddr *addr, socklen_t addrlen,
296 GNUNET_NAT_AddressCallback callback, void *callback_cls)
298 struct GNUNET_NAT_Handle *nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
302 GNUNET_assert (addr->sa_family == AF_INET
303 || addr->sa_family == AF_INET6);
304 nat->local_addr = GNUNET_malloc (addrlen);
305 memcpy (nat->local_addr, addr, addrlen);
307 if (addr->sa_family == AF_INET)
309 nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
310 ((struct sockaddr_in *) nat->local_addr)->sin_port = 0;
312 else if (addr->sa_family == AF_INET6)
314 nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
315 ((struct sockaddr_in6 *) nat->local_addr)->sin6_port = 0;
320 nat->local_addr = NULL;
321 nat->public_port = 0;
324 nat->should_change = GNUNET_YES;
326 nat->is_enabled = GNUNET_YES;
327 nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
328 nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
329 nat->callback = callback;
330 nat->callback_cls = callback_cls;
331 nat->ext_addr = NULL;
332 nat->contact_addr = NULL;
333 nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port);
334 nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port);
336 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched,
337 GNUNET_TIME_UNIT_SECONDS,
344 GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nat)
346 struct sockaddr *addr;
347 GNUNET_SCHEDULER_cancel (nat->sched, nat->pulse_timer);
350 GNUNET_NAT_UPNP_pulse (nat->upnp, GNUNET_NO, GNUNET_NO,
353 GNUNET_NAT_NATPMP_pulse (nat->natpmp, GNUNET_NO,
356 GNUNET_NAT_NATPMP_close (nat->natpmp);
357 GNUNET_NAT_UPNP_close (nat->upnp);
360 GNUNET_free (nat->local_addr);
362 GNUNET_free (nat->ext_addr);