- merge with master
[oweals/gnunet.git] / src / nat / gnunet-service-nat_externalip.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2015, 2016, 2017 GNUnet e.V.
4
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.
9
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.
14
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
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).
23  *
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.
27  *
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".
31  *
32  * @file nat/gnunet-service-nat_externalip.c
33  * @brief Functions for monitoring external IPv4 addresses
34  * @author Christian Grothoff
35  */
36 #include "platform.h"
37 #include <math.h>
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"
49 #include "nat.h"
50 #include <gcrypt.h>
51
52
53 /**
54  * How long do we wait until we re-try running `external-ip` if the
55  * command failed to terminate nicely?
56  */
57 #define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
58
59 /**
60  * How long do we wait until we re-try running `external-ip` if the
61  * command failed (but terminated)?
62  */
63 #define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
64
65 /**
66  * How long do we wait until we re-try running `external-ip` if the
67  * command succeeded?
68  */
69 #define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
70
71
72 /**
73  * Handle to monitor for external IP changes.
74  */
75 struct GN_ExternalIPMonitor
76 {
77   /**
78    * Kept in DLL.
79    */
80   struct GN_ExternalIPMonitor *next;
81
82   /**
83    * Kept in DLL.
84    */
85   struct GN_ExternalIPMonitor *prev;
86
87   /**
88    * Function to call when we believe our external IPv4 address changed.
89    */
90   GN_NotifyExternalIPv4Change cb;
91
92   /**
93    * Closure for @e cb.
94    */
95   void *cb_cls;
96
97 };
98
99
100 /**
101  * List of monitors, kept in DLL.
102  */
103 static struct GN_ExternalIPMonitor *mon_head;
104
105 /**
106  * List of monitors, kept in DLL.
107  */
108 static struct GN_ExternalIPMonitor *mon_tail;
109
110 /**
111  * Task run to obtain our external IP (if #enable_upnp is set
112  * and if we find we have a NATed IP address).
113  */
114 static struct GNUNET_SCHEDULER_Task *probe_external_ip_task;
115
116 /**
117  * Handle to our operation to run `external-ip`.
118  */
119 static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op;
120
121 /**
122  * What is our external IP address as claimed by `external-ip`?
123  * 0 for unknown.
124  */
125 static struct in_addr mini_external_ipv4;
126
127
128 /**
129  * Tell relevant clients about a change in our external
130  * IPv4 address.
131  *
132  * @param add #GNUNET_YES to add, #GNUNET_NO to remove
133  * @param v4 the external address that changed
134  */
135 static void
136 notify_monitors_external_ipv4_change (int add,
137                                       const struct in_addr *v4)
138 {
139   for (struct GN_ExternalIPMonitor *mon = mon_head;
140        NULL != mon;
141        mon = mon->next)
142     mon->cb (mon->cb_cls,
143              v4,
144              add);
145 }
146
147
148 /**
149  * Task used to run `external-ip` to get our external IPv4
150  * address and pass it to NATed clients if possible.
151  *
152  * @param cls NULL
153  */
154 static void
155 run_external_ip (void *cls);
156
157
158 /**
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.
162  *
163  * @param cls NULL
164  * @param addr the address, NULL on errors
165  * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
166  */
167 static void
168 handle_external_ip (void *cls,
169                     const struct in_addr *addr,
170                     enum GNUNET_NAT_StatusCode result)
171 {
172   char buf[INET_ADDRSTRLEN];
173
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,
180                                     &run_external_ip,
181                                     NULL);
182   switch (result)
183   {
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",
190                 inet_ntop (AF_INET,
191                            addr,
192                            buf,
193                            sizeof (buf)));
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);
200     break;
201   default:
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;
206     break;
207   }
208 }
209
210
211 /**
212  * Task used to run `external-ip` to get our external IPv4
213  * address and pass it to NATed clients if possible.
214  *
215  * @param cls NULL
216  */
217 static void
218 run_external_ip (void *cls)
219 {
220   probe_external_ip_task
221     = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
222                                     &run_external_ip,
223                                     NULL);
224   if (NULL != probe_external_ip_op)
225   {
226     GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
227     probe_external_ip_op = NULL;
228   }
229   probe_external_ip_op
230     = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
231                                           NULL);
232 }
233
234
235 /**
236  * We have changed our opinion about being NATed in the first
237  * place. Adapt our probing.
238  *
239  * @param have_nat #GNUNET_YES if we believe we are behind NAT
240  */
241 void
242 GN_nat_status_changed (int have_nat)
243 {
244   if (GNUNET_YES != enable_upnp)
245     return;
246   if ( (GNUNET_YES == have_nat) &&
247        (NULL == probe_external_ip_task) &&
248        (NULL == probe_external_ip_op) )
249   {
250     probe_external_ip_task
251       = GNUNET_SCHEDULER_add_now (&run_external_ip,
252                                   NULL);
253     return;
254   }
255   if (GNUNET_NO == have_nat)
256   {
257     if (NULL != probe_external_ip_task)
258     {
259       GNUNET_SCHEDULER_cancel (probe_external_ip_task);
260       probe_external_ip_task = NULL;
261     }
262     if (NULL != probe_external_ip_op)
263     {
264       GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
265       probe_external_ip_op = NULL;
266     }
267   }
268 }
269
270
271 /**
272  * Start monitoring external IPv4 addresses.
273  *
274  * @param cb function to call on changes
275  * @param cb_cls closure for @a cb
276  * @return handle to cancel
277  */
278 struct GN_ExternalIPMonitor *
279 GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb,
280                                 void *cb_cls)
281 {
282   struct GN_ExternalIPMonitor *mon;
283
284   mon = GNUNET_new (struct GN_ExternalIPMonitor);
285   mon->cb = cb;
286   mon->cb_cls = cb_cls;
287   GNUNET_CONTAINER_DLL_insert (mon_head,
288                                mon_tail,
289                                mon);
290   if (0 != mini_external_ipv4.s_addr)
291     cb (cb_cls,
292         &mini_external_ipv4,
293         GNUNET_YES);
294   return mon;
295 }
296
297
298 /**
299  * Stop calling monitor.
300  *
301  * @param mon monitor to call
302  */
303 void
304 GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon)
305 {
306   GNUNET_CONTAINER_DLL_remove (mon_head,
307                                mon_tail,
308                                mon);
309   GNUNET_free (mon);
310 }
311
312 /* end of gnunet-service-nat_externalip.c */