optical changes, small fix, draft for notification
[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   // FIXME: tell ATS API, but not using this call:
258   GNUNET_ATS_address_update (ai->ar,
259                              session,
260                              NULL, 0);
261
262 }
263
264
265 /**
266  * Notify ATS that the session (but not the address) of
267  * a given address is no longer relevant.
268  *
269  * @param address the address
270  * @param session the session
271  */
272 void
273 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
274                      struct Session *session)
275 {
276   struct AddressInfo *ai;
277
278   if (NULL == session)
279   {
280     GNUNET_break (0);
281     return;
282   }
283   ai = find_ai (address, session);
284   if (NULL == ai)
285   {
286     /* We sometimes create sessions just for sending a PING,
287        and if those are destroyed they were never known to
288        ATS which means we end up here (however, in this
289        case, the address must be an outbound address). */
290     GNUNET_break (GNUNET_YES !=
291                   GNUNET_HELLO_address_check_option (address,
292                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
293
294     return;
295   }
296   GNUNET_assert (session == ai->session);
297   ai->session = NULL;
298   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
299                    "transport-ats",
300                    "Telling ATS to destroy session %p from peer %s\n",
301                    session,
302                    GNUNET_i2s (&address->peer));
303   /* FIXME: if this was an *inbound* address, destroy it
304      FULLY here well; but use different API, as looking up
305      inbound address without session is not great... */
306   GNUNET_ATS_address_destroyed (GST_ats, address, session);
307   if (GNUNET_YES ==
308       GNUNET_HELLO_address_check_option (address,
309                                          GNUNET_HELLO_ADDRESS_INFO_INBOUND))
310     GST_ats_expire_address (address);
311 }
312
313
314 /**
315  * Notify ATS about property changes to an address.
316  *
317  * @param address our information about the address
318  * @param session the session
319  * @param ats performance information
320  * @param ats_count number of elements in @a ats
321  */
322 void
323 GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address,
324                         struct Session *session,
325                         const struct GNUNET_ATS_Information *ats,
326                         uint32_t ats_count)
327 {
328   struct GNUNET_ATS_Information *ats_new;
329   struct AddressInfo *ai;
330
331   ai = find_ai (address, session);
332   if (NULL == ai)
333   {
334     /* We sometimes create sessions just for sending a PING,
335        and if we get metrics for those, they were never known to
336        ATS which means we end up here (however, in this
337        case, the address must be an outbound address). */
338     GNUNET_break (GNUNET_YES !=
339                   GNUNET_HELLO_address_check_option (address,
340                                                      GNUNET_HELLO_ADDRESS_INFO_INBOUND));
341
342     return;
343   }
344   /* Call to manipulation to manipulate ATS information */
345   GNUNET_assert (NULL != GST_ats);
346   if ((NULL == ats) || (0 == ats_count))
347     return;
348   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349               "Updating metrics for peer `%s' address %s session %p\n",
350               GNUNET_i2s (&address->peer),
351               GST_plugins_a2s (address),
352               session);
353   ats_new = GST_manipulation_manipulate_metrics (address,
354                                                  session,
355                                                  ats,
356                                                  ats_count);
357   GNUNET_ATS_address_update (ai->ar,
358                              session,
359                              ats_new, ats_count);
360   GNUNET_free_non_null (ats_new);
361 }
362
363
364 /**
365  * Notify ATS that the address has expired and thus cannot
366  * be used any longer.  This function must only be called
367  * if the corresponding session is already gone.
368  *
369  * @param address the address
370  */
371 void
372 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
373 {
374   struct AddressInfo *ai;
375
376   ai = find_ai (address, NULL);
377   if (NULL == ai)
378   {
379     GNUNET_break (0);
380     return;
381   }
382   GNUNET_assert (GNUNET_YES ==
383                  GNUNET_CONTAINER_multipeermap_remove (p2a,
384                                                        &address->peer,
385                                                        ai));
386   GNUNET_break (NULL == ai->session);
387   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
388                    "transport-ats",
389                    "Telling ATS to destroy address from peer %s\n",
390                    GNUNET_i2s (&address->peer));
391   GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
392   GNUNET_HELLO_address_free (ai->address);
393   GNUNET_free (ai);
394 }
395
396
397 /**
398  * Initialize ATS subsystem.
399  */
400 void
401 GST_ats_init ()
402 {
403   p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
404 }
405
406
407 /**
408  * Release memory used by the given address data.
409  *
410  * @param cls NULL
411  * @param key which peer is this about
412  * @param value the `struct AddressInfo`
413  * @return #GNUNET_OK (continue to iterate)
414  */
415 static int
416 destroy_ai (void *cls,
417             const struct GNUNET_PeerIdentity *key,
418             void *value)
419 {
420   struct AddressInfo *ai = value;
421
422   GNUNET_HELLO_address_free (ai->address);
423   GNUNET_free (ai);
424   return GNUNET_OK;
425 }
426
427
428 /**
429  * Shutdown ATS subsystem.
430  */
431 void
432 GST_ats_done ()
433 {
434   GNUNET_CONTAINER_multipeermap_iterate (p2a,
435                                          &destroy_ai,
436                                          NULL);
437   GNUNET_CONTAINER_multipeermap_destroy (p2a);
438   p2a = NULL;
439 }
440
441 /* end of gnunet-service-transport_ats.c */