diff options
Diffstat (limited to 'src/transport/gnunet-service-transport_ats.c')
-rw-r--r-- | src/transport/gnunet-service-transport_ats.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/transport/gnunet-service-transport_ats.c b/src/transport/gnunet-service-transport_ats.c new file mode 100644 index 000000000..c6975e526 --- /dev/null +++ b/src/transport/gnunet-service-transport_ats.c | |||
@@ -0,0 +1,441 @@ | |||
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 */ | ||