4fcf7c9dc54df22c20ba0c80be5f666fbf4282b1
[oweals/gnunet.git] / src / transport / gnunet-service-transport_ats.c
1 /*
2      This file is part of GNUnet.
3      (C) 2015 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file transport/gnunet-service-transport_ats.h
22  * @brief interfacing between transport and ATS service
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet-service-transport.h"
27 #include "gnunet-service-transport_ats.h"
28 #include "gnunet-service-transport_manipulation.h"
29 #include "gnunet-service-transport_plugins.h"
30 #include "gnunet_ats_service.h"
31
32
33 /**
34  * Information we track for each address known to ATS.
35  */
36 struct AddressInfo
37 {
38
39   /**
40    * The address (with peer identity).
41    */
42   struct GNUNET_HELLO_Address *address;
43
44   /**
45    * Session (can be NULL)
46    */
47   struct Session *session;
48
49   /**
50    * Record with ATS API for the address.
51    */
52   struct GNUNET_ATS_AddressRecord *ar;
53 };
54
55
56 /**
57  * Map from peer identities to one or more `struct AddressInfo` values
58  * for the peer.
59  */
60 static struct GNUNET_CONTAINER_MultiPeerMap *p2a;
61
62
63 /**
64  * Closure for #find_ai().
65  */
66 struct FindClosure
67 {
68
69   /**
70    * Session to look for (only used if the address is inbound).
71    */
72   struct Session *session;
73
74   /**
75    * Address to look for.
76    */
77   const struct GNUNET_HELLO_Address *address;
78
79   /**
80    * Where to store the result.
81    */
82   struct AddressInfo *ret;
83
84 };
85
86
87 /**
88  * Find matching address info.
89  *
90  * @param cls the `struct FindClosure`
91  * @param key which peer is this about
92  * @param value the `struct AddressInfo`
93  * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
94  */
95 static int
96 find_ai_cb (void *cls,
97             const struct GNUNET_PeerIdentity *key,
98             void *value)
99 {
100   struct FindClosure *fc = cls;
101   struct AddressInfo *ai = value;
102
103   if ( (0 ==
104         GNUNET_HELLO_address_cmp (fc->address,
105                                   ai->address) ) &&
106        (fc->session == ai->session) )
107   {
108     fc->ret = ai;
109     return GNUNET_NO;
110   }
111   return GNUNET_YES;
112 }
113
114
115 /**
116  * Find the address information struct for the
117  * given address and session.
118  *
119  * @param address address to look for
120  * @param session session to match for inbound connections
121  * @return NULL if this combination is unknown
122  */
123 static struct AddressInfo *
124 find_ai (const struct GNUNET_HELLO_Address *address,
125          struct Session *session)
126 {
127   struct FindClosure fc;
128
129   fc.address = address;
130   fc.session = session;
131   fc.ret = NULL;
132   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
133                                               &address->peer,
134                                               &find_ai_cb,
135                                               &fc);
136   return fc.ret;
137 }
138
139
140 /**
141  * Notify ATS about the new address including the network this address is
142  * located in.
143  *
144  * @param address the address
145  * @param session the session
146  * @param ats ats information
147  * @param ats_count number of @a ats information
148  */
149 void
150 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
151                      struct Session *session,
152                      const struct GNUNET_ATS_Information *ats,
153                      uint32_t ats_count)
154 {
155   struct GNUNET_TRANSPORT_PluginFunctions *papi;
156   struct GNUNET_ATS_Information ats2[ats_count + 1];
157   struct GNUNET_ATS_AddressRecord *ar;
158   struct AddressInfo *ai;
159   uint32_t net;
160
161   /* valid new address, let ATS know! */
162   if (NULL == address->transport_name)
163   {
164     GNUNET_break(0);
165     return;
166   }
167   if (GNUNET_YES ==
168       GNUNET_HELLO_address_check_option (address,
169                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
170   {
171     GNUNET_break (NULL != session);
172   }
173   else
174   {
175     GNUNET_break (NULL == session);
176   }
177   ai = find_ai (address, session);
178   if (NULL != ai)
179   {
180     GNUNET_break (0);
181     return;
182   }
183   if (NULL == (papi = GST_plugins_find (address->transport_name)))
184   {
185     /* we don't have the plugin for this address */
186     GNUNET_break(0);
187     return;
188   }
189   if (NULL != session)
190   {
191     net = papi->get_network (papi->cls, session);
192     if (GNUNET_ATS_NET_UNSPECIFIED == net)
193     {
194       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
195                   _ ("Could not obtain a valid network for `%s' %s (%s)\n"),
196                   GNUNET_i2s (&address->peer),
197                   GST_plugins_a2s (address),
198                   address->transport_name);
199       return;
200     }
201     ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE);
202     ats2[0].value = htonl (net);
203     memcpy (&ats2[1],
204             ats,
205             sizeof(struct GNUNET_ATS_Information) * ats_count);
206   }
207   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
208               "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n",
209               GNUNET_i2s (&address->peer),
210               (0 == address->address_length)
211               ? "<inbound>"
212               : GST_plugins_a2s (address),
213               session,
214               GNUNET_ATS_print_network_type (net));
215   ar = GNUNET_ATS_address_add (GST_ats,
216                                address,
217                                session,
218                                (NULL != session) ? ats2 : ats,
219                                (NULL != session) ? ats_count + 1 : ats_count);
220   ai = GNUNET_new (struct AddressInfo);
221   ai->address = GNUNET_HELLO_address_copy (address);
222   ai->session = session;
223   ai->ar = ar;
224   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
225                                             &ai->address->peer,
226                                             ai,
227                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
228 }
229
230
231 /**
232  * Notify ATS about a new session now existing for the given
233  * address.
234  *
235  * @param address the address
236  * @param session the session
237  */
238 void
239 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
240                      struct Session *session)
241 {
242   struct AddressInfo *ai;
243
244   ai = find_ai (address, NULL);
245   if (NULL == ai)
246   {
247     GNUNET_break (NULL != (find_ai (address, session)));
248     return;
249   }
250   GNUNET_break (NULL == ai->session);
251   ai->session = session;
252   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
253                    "transport-ats",
254                    "Telling ATS about new session %p for peer %s\n",
255                    session,
256                    GNUNET_i2s (&address->peer));
257   GNUNET_ATS_address_add_session (ai->ar,
258                                   session);
259 }
260
261
262 /**
263  * Notify ATS that the session (but not the address) of
264  * a given address is no longer relevant.
265  *
266  * @param address the address
267  * @param session the session
268  */
269 void
270 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
271                      struct Session *session)
272 {
273   struct AddressInfo *ai;
274
275   if (NULL == session)
276   {
277     GNUNET_break (0);
278     return;
279   }
280   ai = find_ai (address, session);
281   if (NULL == ai)
282   {
283     /* We sometimes create sessions just for sending a PING,
284        and if those are destroyed they were never known to
285        ATS which means we end up here (however, in this
286        case, the address must be an outbound address). */
287     GNUNET_break (GNUNET_YES !=
288                   GNUNET_HELLO_address_check_option (address,
289                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
290
291     return;
292   }
293   GNUNET_assert (session == ai->session);
294   ai->session = NULL;
295   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
296                    "transport-ats",
297                    "Telling ATS to destroy session %p from peer %s\n",
298                    session,
299                    GNUNET_i2s (&address->peer));
300   if (GNUNET_YES ==
301       GNUNET_ATS_address_del_session (ai->ar, session))
302   {
303     ai->ar = NULL;
304     GST_ats_expire_address (address);
305   }
306 }
307
308
309 /**
310  * Notify ATS about property changes to an address.
311  *
312  * @param address our information about the address
313  * @param session the session
314  * @param ats performance information
315  * @param ats_count number of elements in @a ats
316  */
317 void
318 GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address,
319                         struct Session *session,
320                         const struct GNUNET_ATS_Information *ats,
321                         uint32_t ats_count)
322 {
323   struct GNUNET_ATS_Information *ats_new;
324   struct AddressInfo *ai;
325
326   ai = find_ai (address, session);
327   if (NULL == ai)
328   {
329     /* We sometimes create sessions just for sending a PING,
330        and if we get metrics for those, they were never known to
331        ATS which means we end up here (however, in this
332        case, the address must be an outbound address). */
333     GNUNET_break (GNUNET_YES !=
334                   GNUNET_HELLO_address_check_option (address,
335                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
336
337     return;
338   }
339   /* Call to manipulation to manipulate ATS information */
340   GNUNET_assert (NULL != GST_ats);
341   if ((NULL == ats) || (0 == ats_count))
342     return;
343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344               "Updating metrics for peer `%s' address %s session %p\n",
345               GNUNET_i2s (&address->peer),
346               GST_plugins_a2s (address),
347               session);
348   ats_new = GST_manipulation_manipulate_metrics (address,
349                                                  session,
350                                                  ats,
351                                                  ats_count);
352   GNUNET_ATS_address_update (ai->ar,
353                              ats_new, ats_count);
354   GNUNET_free_non_null (ats_new);
355 }
356
357
358 /**
359  * Notify ATS about a new session now being in use (or not).
360  *
361  * @param address the address
362  * @param session the session
363  * @param in_use #GNUNET_YES or #GNUNET_NO
364  */
365 void
366 GST_ats_set_in_use (const struct GNUNET_HELLO_Address *address,
367                     struct Session *session,
368                     int in_use)
369 {
370   struct AddressInfo *ai;
371
372   ai = find_ai (address, session);
373   if (NULL == ai)
374   {
375     GNUNET_break (0);
376     return;
377   }
378   GNUNET_ATS_address_set_in_use (ai->ar, in_use);
379 }
380
381
382 /**
383  * Notify ATS that the address has expired and thus cannot
384  * be used any longer.  This function must only be called
385  * if the corresponding session is already gone.
386  *
387  * @param address the address
388  */
389 void
390 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
391 {
392   struct AddressInfo *ai;
393
394   ai = find_ai (address, NULL);
395   if (NULL == ai)
396   {
397     GNUNET_break (0);
398     return;
399   }
400   GNUNET_assert (GNUNET_YES ==
401                  GNUNET_CONTAINER_multipeermap_remove (p2a,
402                                                        &address->peer,
403                                                        ai));
404   GNUNET_break (NULL == ai->session);
405   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
406                    "transport-ats",
407                    "Telling ATS to destroy address from peer %s\n",
408                    GNUNET_i2s (&address->peer));
409   if (NULL != ai->ar)
410     GNUNET_ATS_address_destroy (ai->ar);
411   GNUNET_HELLO_address_free (ai->address);
412   GNUNET_free (ai);
413 }
414
415
416 /**
417  * Initialize ATS subsystem.
418  */
419 void
420 GST_ats_init ()
421 {
422   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
423 }
424
425
426 /**
427  * Release memory used by the given address data.
428  *
429  * @param cls NULL
430  * @param key which peer is this about
431  * @param value the `struct AddressInfo`
432  * @return #GNUNET_OK (continue to iterate)
433  */
434 static int
435 destroy_ai (void *cls,
436             const struct GNUNET_PeerIdentity *key,
437             void *value)
438 {
439   struct AddressInfo *ai = value;
440
441   GNUNET_HELLO_address_free (ai->address);
442   GNUNET_free (ai);
443   return GNUNET_OK;
444 }
445
446
447 /**
448  * Shutdown ATS subsystem.
449  */
450 void
451 GST_ats_done ()
452 {
453   GNUNET_CONTAINER_multipeermap_iterate (p2a,
454                                          &destroy_ai,
455                                          NULL);
456   GNUNET_CONTAINER_multipeermap_destroy (p2a);
457   p2a = NULL;
458 }
459
460 /* end of gnunet-service-transport_ats.c */