f862f8c47a0c5695876a2408a592fbf453cdf853
[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   GNUNET_assert ( (fc->session != ai->session) ||
112                   (NULL == ai->session) );
113   return GNUNET_YES;
114 }
115
116
117 /**
118  * Find the address information struct for the
119  * given address and session.
120  *
121  * @param address address to look for
122  * @param session session to match for inbound connections
123  * @return NULL if this combination is unknown
124  */
125 static struct AddressInfo *
126 find_ai (const struct GNUNET_HELLO_Address *address,
127          struct Session *session)
128 {
129   struct FindClosure fc;
130
131   fc.address = address;
132   fc.session = session;
133   fc.ret = NULL;
134   GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
135                                               &address->peer,
136                                               &find_ai_cb,
137                                               &fc);
138   return fc.ret;
139 }
140
141
142 /**
143  * Test if ATS knows about this address.
144  *
145  * @param address the address
146  * @param session the session
147  * @return #GNUNET_YES if address is known, #GNUNET_NO if not.
148  */
149 int
150 GST_ats_is_known (const struct GNUNET_HELLO_Address *address,
151                   struct Session *session)
152 {
153   return (NULL != find_ai (address, session)) ? GNUNET_YES : GNUNET_NO;
154 }
155
156
157 /**
158  * Notify ATS about the new address including the network this address is
159  * located in.
160  *
161  * @param address the address
162  * @param session the session
163  * @param ats ats information
164  * @param ats_count number of @a ats information
165  */
166 void
167 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
168                      struct Session *session,
169                      const struct GNUNET_ATS_Information *ats,
170                      uint32_t ats_count)
171 {
172   struct GNUNET_TRANSPORT_PluginFunctions *papi;
173   struct GNUNET_ATS_Information ats2[ats_count + 1];
174   struct GNUNET_ATS_AddressRecord *ar;
175   struct AddressInfo *ai;
176   uint32_t net;
177
178   /* valid new address, let ATS know! */
179   if (NULL == address->transport_name)
180   {
181     GNUNET_break(0);
182     return;
183   }
184   if (GNUNET_YES ==
185       GNUNET_HELLO_address_check_option (address,
186                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
187   {
188     GNUNET_break (NULL != session);
189   }
190   ai = find_ai (address, session);
191   if (NULL != ai)
192   {
193     GNUNET_break (0);
194     return;
195   }
196   if (NULL == (papi = GST_plugins_find (address->transport_name)))
197   {
198     /* we don't have the plugin for this address */
199     GNUNET_assert (0);
200     return;
201   }
202   if (NULL != session)
203   {
204     net = papi->get_network (papi->cls, session);
205     if (GNUNET_ATS_NET_UNSPECIFIED == net)
206     {
207       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
208                   _ ("Could not obtain a valid network for `%s' %s (%s)\n"),
209                   GNUNET_i2s (&address->peer),
210                   GST_plugins_a2s (address),
211                   address->transport_name);
212       return;
213     }
214     ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE);
215     ats2[0].value = htonl (net);
216     memcpy (&ats2[1],
217             ats,
218             sizeof(struct GNUNET_ATS_Information) * ats_count);
219   }
220   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
221               "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n",
222               GNUNET_i2s (&address->peer),
223               (0 == address->address_length)
224               ? "<inbound>"
225               : GST_plugins_a2s (address),
226               session,
227               GNUNET_ATS_print_network_type (net));
228   ar = GNUNET_ATS_address_add (GST_ats,
229                                address,
230                                session,
231                                (NULL != session) ? ats2 : ats,
232                                (NULL != session) ? ats_count + 1 : ats_count);
233   ai = GNUNET_new (struct AddressInfo);
234   ai->address = GNUNET_HELLO_address_copy (address);
235   ai->session = session;
236   ai->ar = ar;
237   (void) GNUNET_CONTAINER_multipeermap_put (p2a,
238                                             &ai->address->peer,
239                                             ai,
240                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
241 }
242
243
244 /**
245  * Notify ATS about a new session now existing for the given
246  * address.
247  *
248  * @param address the address
249  * @param session the session
250  */
251 void
252 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
253                      struct Session *session)
254 {
255   struct AddressInfo *ai;
256
257   ai = find_ai (address, NULL);
258   if (NULL == ai)
259   {
260     /* We may already be aware of the session, even if some other part
261        of the code could not tell if it just created a new session or
262        just got one recycled from the plugin; hence, we may be called
263        with "new" session even for an "old" session; in that case,
264        check that this is the case, but just ignore it. */
265     GNUNET_assert (NULL != (find_ai (address, session)));
266     return;
267   }
268   GNUNET_break (NULL == ai->session);
269   ai->session = session;
270   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
271                    "transport-ats",
272                    "Telling ATS about new session %p for peer %s\n",
273                    session,
274                    GNUNET_i2s (&address->peer));
275   GNUNET_ATS_address_add_session (ai->ar,
276                                   session);
277 }
278
279
280 /**
281  * Notify ATS that the session (but not the address) of
282  * a given address is no longer relevant.
283  *
284  * @param address the address
285  * @param session the session
286  */
287 void
288 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
289                      struct Session *session)
290 {
291   struct AddressInfo *ai;
292
293   if (NULL == session)
294   {
295     GNUNET_break (0);
296     return;
297   }
298   ai = find_ai (address, session);
299   if (NULL == ai)
300   {
301     /* We sometimes create sessions just for sending a PING,
302        and if those are destroyed they were never known to
303        ATS which means we end up here (however, in this
304        case, the address must be an outbound address). */
305     GNUNET_break (GNUNET_YES !=
306                   GNUNET_HELLO_address_check_option (address,
307                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
308
309     return;
310   }
311   GNUNET_assert (session == ai->session);
312   ai->session = NULL;
313   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
314                    "transport-ats",
315                    "Telling ATS to destroy session %p from peer %s\n",
316                    session,
317                    GNUNET_i2s (&address->peer));
318   if (GNUNET_YES ==
319       GNUNET_ATS_address_del_session (ai->ar, session))
320   {
321     ai->ar = NULL;
322     GST_ats_expire_address (address);
323   }
324 }
325
326
327 /**
328  * Notify ATS about property changes to an address.
329  *
330  * @param address our information about the address
331  * @param session the session
332  * @param ats performance information
333  * @param ats_count number of elements in @a ats
334  */
335 void
336 GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address,
337                         struct Session *session,
338                         const struct GNUNET_ATS_Information *ats,
339                         uint32_t ats_count)
340 {
341   struct GNUNET_ATS_Information *ats_new;
342   struct AddressInfo *ai;
343
344   ai = find_ai (address, session);
345   if (NULL == ai)
346   {
347     /* We sometimes create sessions just for sending a PING,
348        and if we get metrics for those, they were never known to
349        ATS which means we end up here (however, in this
350        case, the address must be an outbound address). */
351     GNUNET_assert (GNUNET_YES !=
352                    GNUNET_HELLO_address_check_option (address,
353                                                       GNUNET_HELLO_ADDRESS_INFO_INBOUND));
354
355     return;
356   }
357   /* Call to manipulation to manipulate ATS information */
358   GNUNET_assert (NULL != GST_ats);
359   if ((NULL == ats) || (0 == ats_count))
360     return;
361   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
362               "Updating metrics for peer `%s' address %s session %p\n",
363               GNUNET_i2s (&address->peer),
364               GST_plugins_a2s (address),
365               session);
366   ats_new = GST_manipulation_manipulate_metrics (address,
367                                                  session,
368                                                  ats,
369                                                  ats_count);
370   GNUNET_ATS_address_update (ai->ar,
371                              ats_new, ats_count);
372   GNUNET_free_non_null (ats_new);
373 }
374
375
376 /**
377  * Notify ATS about a new session now being in use (or not).
378  *
379  * @param address the address
380  * @param session the session
381  * @param in_use #GNUNET_YES or #GNUNET_NO
382  */
383 void
384 GST_ats_set_in_use (const struct GNUNET_HELLO_Address *address,
385                     struct Session *session,
386                     int in_use)
387 {
388   struct AddressInfo *ai;
389
390   ai = find_ai (address, session);
391   if (NULL == ai)
392   {
393     GNUNET_break (0);
394     return;
395   }
396   GNUNET_ATS_address_set_in_use (ai->ar, in_use);
397 }
398
399
400 /**
401  * Notify ATS that the address has expired and thus cannot
402  * be used any longer.  This function must only be called
403  * if the corresponding session is already gone.
404  *
405  * @param address the address
406  */
407 void
408 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
409 {
410   struct AddressInfo *ai;
411
412   ai = find_ai (address, NULL);
413   if (NULL == ai)
414   {
415     GNUNET_assert (0);
416     return;
417   }
418   GNUNET_assert (GNUNET_YES ==
419                  GNUNET_CONTAINER_multipeermap_remove (p2a,
420                                                        &address->peer,
421                                                        ai));
422   GNUNET_break (NULL == ai->session);
423   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
424                    "transport-ats",
425                    "Telling ATS to destroy address from peer %s\n",
426                    GNUNET_i2s (&address->peer));
427   if (NULL != ai->ar)
428     GNUNET_ATS_address_destroy (ai->ar);
429   GNUNET_HELLO_address_free (ai->address);
430   GNUNET_free (ai);
431 }
432
433
434 /**
435  * Initialize ATS subsystem.
436  */
437 void
438 GST_ats_init ()
439 {
440   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
441 }
442
443
444 /**
445  * Release memory used by the given address data.
446  *
447  * @param cls NULL
448  * @param key which peer is this about
449  * @param value the `struct AddressInfo`
450  * @return #GNUNET_OK (continue to iterate)
451  */
452 static int
453 destroy_ai (void *cls,
454             const struct GNUNET_PeerIdentity *key,
455             void *value)
456 {
457   struct AddressInfo *ai = value;
458
459   GNUNET_HELLO_address_free (ai->address);
460   GNUNET_free (ai);
461   return GNUNET_OK;
462 }
463
464
465 /**
466  * Shutdown ATS subsystem.
467  */
468 void
469 GST_ats_done ()
470 {
471   GNUNET_CONTAINER_multipeermap_iterate (p2a,
472                                          &destroy_ai,
473                                          NULL);
474   GNUNET_CONTAINER_multipeermap_destroy (p2a);
475   p2a = NULL;
476 }
477
478 /* end of gnunet-service-transport_ats.c */