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