2 This file is part of GNUnet.
3 Copyright (C) 2011-2015, 2018 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @file ats/plugin_ats2_simple.c
20 * @brief ATS simple solver
21 * @author Matthias Wachs
22 * @author Christian Grothoff
25 * - subscribe to PEERSTORE when short on HELLOs (given application preferences!)
26 * - keep track of HELLOs and when we tried them last => re-suggest
27 * - sum up preferences per peer, keep totals! => PeerMap pid -> [preferences + sessions + addrs!]
28 * - sum up preferences overall, keep global sum => starting point for "proportional"
29 * - store DLL of available sessions per peer
32 #include "gnunet_ats_plugin_new.h"
33 #include "gnunet_peerstore_service.h"
35 #define LOG(kind,...) GNUNET_log_from (kind, "ats-simple",__VA_ARGS__)
39 * A handle for the proportional solver
45 * Entry in list of addresses we could try per peer.
61 * The address we could try.
66 * When did we try it last?
68 struct GNUNET_TIME_Absolute last_attempt;
71 * Current exponential backoff value.
73 struct GNUNET_TIME_Relative backoff;
76 * Is a session with this address already up?
77 * If not, set to NULL.
79 struct GNUNET_ATS_SessionHandle *sh;
85 * Information about preferences and sessions we track
92 * Internal representation of a session by the plugin.
93 * (If desired, plugin may just use NULL.)
95 struct GNUNET_ATS_SessionHandle
99 * Kept in DLL per peer.
101 struct GNUNET_ATS_SessionHandle *next;
104 * Kept in DLL per peer.
106 struct GNUNET_ATS_SessionHandle *prev;
109 * The session in the main ATS service.
111 struct GNUNET_ATS_Session *session;
114 * Current performance data for this @e session
116 const struct GNUNET_ATS_SessionData *data;
119 * Hello matching this session, or NULL for none.
124 * Peer this session is for.
129 * Address used by this session (largely for debugging).
134 * Last BW-in allocation given to the transport service.
136 struct GNUNET_BANDWIDTH_Value32NBO bw_in;
139 * Last BW-out allocation given to the transport service.
141 struct GNUNET_BANDWIDTH_Value32NBO bw_out;
147 * Information about preferences and sessions we track
154 * Kept in DLL per peer.
156 struct GNUNET_ATS_SessionHandle *sh_head;
159 * Kept in DLL per peer.
161 struct GNUNET_ATS_SessionHandle *sh_tail;
166 struct Hello *h_head;
171 struct Hello *h_tail;
174 * The handle for the proportional solver
176 struct SimpleHandle *h;
179 * Which peer is this for?
181 struct GNUNET_PeerIdentity pid;
184 * Array where we sum up the bandwidth requests received indexed
185 * by preference kind (see `enum GNUNET_MQ_PreferenceKind`)
187 uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT];
190 * Watch context where we are currently looking for HELLOs for
193 struct GNUNET_PEERSTORE_WatchContext *wc;
196 * Task used to try again to suggest an address for this peer.
198 struct GNUNET_SCHEDULER_Task *task;
204 * Representation of a network (to be expanded...)
210 * Total inbound quota
212 unsigned long long total_quota_in;
215 * Total outbound quota
217 unsigned long long total_quota_out;
222 enum GNUNET_NetworkType type;
228 * A handle for the proportional solver
234 * Our execution environment.
236 struct GNUNET_ATS_PluginEnvironment *env;
239 * Information we track for each peer.
241 struct GNUNET_CONTAINER_MultiPeerMap *peers;
244 * Information we track per network type (quotas).
246 struct Network networks[GNUNET_NT_COUNT];
249 * Handle to the peerstore service.
251 struct GNUNET_PEERSTORE_Handle *ps;
257 * Lookup peer in the peers map.
259 * @param h handle to look up in
260 * @param pid peer identity to look up by
261 * @return NULL for not found
264 lookup_peer (struct SimpleHandle *h,
265 const struct GNUNET_PeerIdentity *pid)
267 return GNUNET_CONTAINER_multipeermap_get (h->peers,
273 * Check if there is _any_ interesting information left we
274 * store about the peer in @a p.
276 * @param p peer to test if we can drop the data structure
277 * @return #GNUNET_YES if no information is left in @a p
280 peer_test_dead (struct Peer *p)
282 for (enum GNUNET_MQ_PreferenceKind pk = 0;
283 pk < GNUNET_MQ_PREFERENCE_COUNT;
285 if (0 != p->bw_by_pk[pk])
287 if (NULL != p->sh_head)
294 * Function called by PEERSTORE for each matching record.
296 * @param cls closure with a `struct Peer`
297 * @param record peerstore record information
298 * @param emsg error message, or NULL if no errors
302 const struct GNUNET_PEERSTORE_Record *record,
305 struct Peer *p = cls;
307 // FIXME: process hello!
308 // check for expiration
309 // (add to p's doubly-linked list)
313 // start suggestion task!
319 * Find or add peer if necessary.
321 * @param h our plugin handle
322 * @param pid the peer identity to add/look for
323 * @return a peer handle
326 peer_add (struct SimpleHandle *h,
327 const struct GNUNET_PeerIdentity *pid)
329 struct Peer *p = lookup_peer (h,
334 p = GNUNET_new (struct Peer);
337 p->wc = GNUNET_PEERSTORE_watch (h->ps,
343 GNUNET_assert (GNUNET_YES ==
344 GNUNET_CONTAINER_multipeermap_put (h->peers,
347 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
354 * Free the entry (and associated tasks) of peer @a p.
355 * Note that @a p must be dead already (see #peer_test_dead()).
357 * @param p the peer to free
360 peer_free (struct Peer *p)
362 struct SimpleHandle *h = p->h;
365 GNUNET_assert (NULL == p->sh_head);
366 while (NULL != (hello = p->h_head))
368 GNUNET_CONTAINER_DLL_remove (p->h_head,
371 GNUNET_assert (NULL == hello->sh);
376 GNUNET_SCHEDULER_cancel (p->task);
381 GNUNET_PEERSTORE_watch_cancel (p->wc);
384 GNUNET_assert (GNUNET_YES ==
385 GNUNET_CONTAINER_multipeermap_remove (h->peers,
393 * The world changed, recalculate our allocations.
396 update (struct SimpleHandle *h)
398 // recalculate allocations
399 // notify transport if it makes sense (delta significant)
404 * The plugin should begin to respect a new preference.
406 * @param cls the closure
407 * @param pref the preference to add
408 * @return plugin's internal representation, or NULL
410 static struct GNUNET_ATS_PreferenceHandle *
411 simple_preference_add (void *cls,
412 const struct GNUNET_ATS_Preference *pref)
414 struct SimpleHandle *h = cls;
415 struct Peer *p = peer_add (h,
418 GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
419 p->bw_by_pk[pref->pk] += ntohl (pref->bw.value__);
426 * The plugin should end respecting a preference.
428 * @param cls the closure
429 * @param ph whatever @e preference_add returned
430 * @param pref the preference to delete
431 * @return plugin's internal representation, or NULL
434 simple_preference_del (void *cls,
435 struct GNUNET_ATS_PreferenceHandle *ph,
436 const struct GNUNET_ATS_Preference *pref)
438 struct SimpleHandle *h = cls;
439 struct Peer *p = lookup_peer (h,
442 GNUNET_assert (NULL != p);
443 GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT);
444 p->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__);
445 if ( (0 == p->bw_by_pk[pref->pk]) &&
446 (GNUNET_YES == peer_test_dead (p)) )
453 * Transport established a new session with performance
454 * characteristics given in @a data.
457 * @param data performance characteristics of @a sh
458 * @param address address information (for debugging)
459 * @return handle by which the plugin will identify this session
461 static struct GNUNET_ATS_SessionHandle *
462 simple_session_add (void *cls,
463 const struct GNUNET_ATS_SessionData *data,
466 struct SimpleHandle *h = cls;
467 struct Peer *p = peer_add (h,
471 struct GNUNET_ATS_SessionHandle *sh;
473 /* setup session handle */
477 alen = strlen (address) + 1;
478 sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionHandle) + alen);
480 sh->session = data->session;
491 sh->address = (const char *) &sh[1];
493 GNUNET_CONTAINER_DLL_insert (p->sh_head,
498 while ( (NULL != hello) &&
499 (0 != strcmp (address,
513 * @a data changed for a given @a sh, solver should consider
514 * the updated performance characteristics.
517 * @param sh session this is about
518 * @param data performance characteristics of @a sh
521 simple_session_update (void *cls,
522 struct GNUNET_ATS_SessionHandle *sh,
523 const struct GNUNET_ATS_SessionData *data)
525 struct SimpleHandle *h = cls;
527 sh->data = data; /* this statement should not really do anything... */
533 * A session went away. Solver should update accordingly.
536 * @param sh session this is about
537 * @param data (last) performance characteristics of @a sh
540 simple_session_del (void *cls,
541 struct GNUNET_ATS_SessionHandle *sh,
542 const struct GNUNET_ATS_SessionData *data)
544 struct SimpleHandle *h = cls;
545 struct Peer *p = sh->peer;
547 // FIXME: tear down session
548 // del peer if otherwise dead
551 if ( (NULL == p->sh_head) &&
552 (GNUNET_YES == peer_test_dead (p)) )
558 #include "plugin_ats2_common.c"
562 * Function invoked when the plugin is loaded.
564 * @param[in,out] cls the `struct GNUNET_ATS_PluginEnvironment *` to use;
565 * modified to return the API functions (ugh).
566 * @return the `struct SimpleHandle` to pass as a closure
569 libgnunet_plugin_ats2_simple_init (void *cls)
571 static struct GNUNET_ATS_SolverFunctions sf;
572 struct GNUNET_ATS_PluginEnvironment *env = cls;
573 struct SimpleHandle *s;
575 s = GNUNET_new (struct SimpleHandle);
577 s->peers = GNUNET_CONTAINER_multipeermap_create (128,
579 s->ps = GNUNET_PEERSTORE_connect (env->cfg);
581 sf.preference_add = &simple_preference_add;
582 sf.preference_del = &simple_preference_del;
583 sf.session_add = &simple_session_add;
584 sf.session_update = &simple_session_update;
585 sf.session_del = &simple_session_del;
586 for (enum GNUNET_NetworkType nt = 0;
587 nt < GNUNET_NT_COUNT;
590 const char *name = GNUNET_NT_to_string (nt);
600 &s->networks[nt].total_quota_in);
604 &s->networks[nt].total_quota_out);
605 s->networks[nt].type = nt;
612 * Function used to unload the plugin.
614 * @param cls return value from #libgnunet_plugin_ats_proportional_init()
617 libgnunet_plugin_ats2_simple_done (void *cls)
619 struct GNUNET_ATS_SolverFunctions *sf = cls;
620 struct SimpleHandle *s = sf->cls;
622 // FIXME: iterate over peers and clean up!
623 GNUNET_CONTAINER_multipeermap_destroy (s->peers);
624 GNUNET_PEERSTORE_disconnect (s->ps,
631 /* end of plugin_ats2_simple.c */