2 This file is part of GNUnet.
3 Copyright (C) 2009, 2015, 2016, 2017 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * Code to figure out what our external IPv4 address(es) might
22 * be (external IPv4s are what is seen on the rest of the Internet).
24 * This can be implemented using different methods, and we allow
25 * the main service to be notified about changes to what we believe
26 * is our external IPv4 address.
28 * Note that this is explicitly only about NATed systems; if one
29 * of our network interfaces has a global IP address this does
30 * not count as "external".
32 * @file nat/gnunet-service-nat_externalip.c
33 * @brief Functions for monitoring external IPv4 addresses
34 * @author Christian Grothoff
38 #include "gnunet_util_lib.h"
39 #include "gnunet_protocols.h"
40 #include "gnunet_signatures.h"
41 #include "gnunet_statistics_service.h"
42 #include "gnunet_resolver_service.h"
43 #include "gnunet_nat_service.h"
44 #include "gnunet-service-nat.h"
45 #include "gnunet-service-nat_externalip.h"
46 #include "gnunet-service-nat_stun.h"
47 #include "gnunet-service-nat_mini.h"
48 #include "gnunet-service-nat_helper.h"
54 * How long do we wait until we re-try running `external-ip` if the
55 * command failed to terminate nicely?
57 #define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
60 * How long do we wait until we re-try running `external-ip` if the
61 * command failed (but terminated)?
63 #define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
66 * How long do we wait until we re-try running `external-ip` if the
69 #define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
73 * Handle to monitor for external IP changes.
75 struct GN_ExternalIPMonitor
80 struct GN_ExternalIPMonitor *next;
85 struct GN_ExternalIPMonitor *prev;
88 * Function to call when we believe our external IPv4 address changed.
90 GN_NotifyExternalIPv4Change cb;
101 * List of monitors, kept in DLL.
103 static struct GN_ExternalIPMonitor *mon_head;
106 * List of monitors, kept in DLL.
108 static struct GN_ExternalIPMonitor *mon_tail;
111 * Task run to obtain our external IP (if #enable_upnp is set
112 * and if we find we have a NATed IP address).
114 static struct GNUNET_SCHEDULER_Task *probe_external_ip_task;
117 * Handle to our operation to run `external-ip`.
119 static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op;
122 * What is our external IP address as claimed by `external-ip`?
125 static struct in_addr mini_external_ipv4;
129 * Tell relevant clients about a change in our external
132 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
133 * @param v4 the external address that changed
136 notify_monitors_external_ipv4_change (int add,
137 const struct in_addr *v4)
139 for (struct GN_ExternalIPMonitor *mon = mon_head;
142 mon->cb (mon->cb_cls,
149 * Task used to run `external-ip` to get our external IPv4
150 * address and pass it to NATed clients if possible.
155 run_external_ip (void *cls);
159 * We learn our current external IP address. If it changed,
160 * notify all of our applicable clients. Also re-schedule
161 * #run_external_ip with an appropriate timeout.
164 * @param addr the address, NULL on errors
165 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
168 handle_external_ip (void *cls,
169 const struct in_addr *addr,
170 enum GNUNET_NAT_StatusCode result)
172 char buf[INET_ADDRSTRLEN];
174 probe_external_ip_op = NULL;
175 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
176 probe_external_ip_task
177 = GNUNET_SCHEDULER_add_delayed ((NULL == addr)
178 ? EXTERN_IP_RETRY_FAILURE
179 : EXTERN_IP_RETRY_SUCCESS,
184 case GNUNET_NAT_ERROR_SUCCESS:
185 GNUNET_assert (NULL != addr);
186 if (addr->s_addr == mini_external_ipv4.s_addr)
187 return; /* not change */
188 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
189 "Our external IP is now %s\n",
194 if (0 != mini_external_ipv4.s_addr)
195 notify_monitors_external_ipv4_change (GNUNET_NO,
196 &mini_external_ipv4);
197 mini_external_ipv4 = *addr;
198 notify_monitors_external_ipv4_change (GNUNET_YES,
199 &mini_external_ipv4);
202 if (0 != mini_external_ipv4.s_addr)
203 notify_monitors_external_ipv4_change (GNUNET_NO,
204 &mini_external_ipv4);
205 mini_external_ipv4.s_addr = 0;
212 * Task used to run `external-ip` to get our external IPv4
213 * address and pass it to NATed clients if possible.
218 run_external_ip (void *cls)
220 probe_external_ip_task
221 = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
224 if (NULL != probe_external_ip_op)
226 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
227 probe_external_ip_op = NULL;
230 = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
236 * We have changed our opinion about being NATed in the first
237 * place. Adapt our probing.
239 * @param have_nat #GNUNET_YES if we believe we are behind NAT
242 GN_nat_status_changed (int have_nat)
244 if (GNUNET_YES != enable_upnp)
246 if ( (GNUNET_YES == have_nat) &&
247 (NULL == probe_external_ip_task) &&
248 (NULL == probe_external_ip_op) )
250 probe_external_ip_task
251 = GNUNET_SCHEDULER_add_now (&run_external_ip,
255 if (GNUNET_NO == have_nat)
257 if (NULL != probe_external_ip_task)
259 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
260 probe_external_ip_task = NULL;
262 if (NULL != probe_external_ip_op)
264 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
265 probe_external_ip_op = NULL;
272 * Start monitoring external IPv4 addresses.
274 * @param cb function to call on changes
275 * @param cb_cls closure for @a cb
276 * @return handle to cancel
278 struct GN_ExternalIPMonitor *
279 GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb,
282 struct GN_ExternalIPMonitor *mon;
284 mon = GNUNET_new (struct GN_ExternalIPMonitor);
286 mon->cb_cls = cb_cls;
287 GNUNET_CONTAINER_DLL_insert (mon_head,
290 if (0 != mini_external_ipv4.s_addr)
299 * Stop calling monitor.
301 * @param mon monitor to call
304 GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon)
306 GNUNET_CONTAINER_DLL_remove (mon_head,
312 /* end of gnunet-service-nat_externalip.c */