8df6f48a5381a3d3d89bd397bab5c6af9f94291a
[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     if (addr->s_addr == mini_external_ipv4.s_addr)
186       return; /* not change */
187     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188                 "Our external IP is now %s\n",
189                 inet_ntop (AF_INET,
190                            addr,
191                            buf,
192                            sizeof (buf)));
193     if (0 != mini_external_ipv4.s_addr)
194       notify_monitors_external_ipv4_change (GNUNET_NO,
195                                             &mini_external_ipv4);
196     mini_external_ipv4 = *addr;
197     notify_monitors_external_ipv4_change (GNUNET_YES,
198                                           &mini_external_ipv4);
199     break;
200   default:
201     if (0 != mini_external_ipv4.s_addr)
202       notify_monitors_external_ipv4_change (GNUNET_NO,
203                                             &mini_external_ipv4);
204     mini_external_ipv4.s_addr = 0;
205     break;
206   }
207 }
208
209
210 /**
211  * Task used to run `external-ip` to get our external IPv4
212  * address and pass it to NATed clients if possible.
213  *
214  * @param cls NULL
215  */
216 static void
217 run_external_ip (void *cls)
218 {
219   probe_external_ip_task
220     = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
221                                     &run_external_ip,
222                                     NULL);
223   if (NULL != probe_external_ip_op)
224   {
225     GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
226     probe_external_ip_op = NULL;
227   }
228   probe_external_ip_op
229     = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
230                                           NULL);
231 }
232
233
234 /**
235  * We have changed our opinion about being NATed in the first
236  * place. Adapt our probing.
237  *
238  * @param have_nat #GNUNET_YES if we believe we are behind NAT
239  */
240 void
241 GN_nat_status_changed (int have_nat)
242 {
243   if (GNUNET_YES != enable_upnp)
244     return;
245   if ( (GNUNET_YES == have_nat) &&
246        (NULL == probe_external_ip_task) &&
247        (NULL == probe_external_ip_op) )
248   {
249     probe_external_ip_task
250       = GNUNET_SCHEDULER_add_now (&run_external_ip,
251                                   NULL);
252     return;
253   }
254   if (GNUNET_NO == have_nat)
255   {
256     if (NULL != probe_external_ip_task)
257     {
258       GNUNET_SCHEDULER_cancel (probe_external_ip_task);
259       probe_external_ip_task = NULL;
260     }
261     if (NULL != probe_external_ip_op)
262     {
263       GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
264       probe_external_ip_op = NULL;
265     }
266   }
267 }
268
269
270 /**
271  * Start monitoring external IPv4 addresses.
272  *
273  * @param cb function to call on changes
274  * @param cb_cls closure for @a cb
275  * @return handle to cancel
276  */
277 struct GN_ExternalIPMonitor *
278 GN_external_ipv4_monitor_start (GN_NotifyExternalIPv4Change cb,
279                                 void *cb_cls)
280 {
281   struct GN_ExternalIPMonitor *mon;
282
283   mon = GNUNET_new (struct GN_ExternalIPMonitor);
284   mon->cb = cb;
285   mon->cb_cls = cb_cls;
286   GNUNET_CONTAINER_DLL_insert (mon_head,
287                                mon_tail,
288                                mon);
289   if (0 != mini_external_ipv4.s_addr)
290     cb (cb_cls,
291         &mini_external_ipv4,
292         GNUNET_YES);
293   return mon;
294 }
295
296
297 /**
298  * Stop calling monitor.
299  *
300  * @param mon monitor to call
301  */
302 void
303 GN_external_ipv4_monitor_stop (struct GN_ExternalIPMonitor *mon)
304 {
305   GNUNET_CONTAINER_DLL_remove (mon_head,
306                                mon_tail,
307                                mon);
308   GNUNET_free (mon);
309 }
310
311 /* end of gnunet-service-nat_externalip.c */