diff options
Diffstat (limited to 'src/service/topology')
-rw-r--r-- | src/service/topology/.gitignore | 2 | ||||
-rw-r--r-- | src/service/topology/Makefile.am | 47 | ||||
-rw-r--r-- | src/service/topology/gnunet-daemon-topology.c | 1119 | ||||
-rw-r--r-- | src/service/topology/meson.build | 29 | ||||
-rw-r--r-- | src/service/topology/test_gnunet_daemon_topology.c | 300 | ||||
-rw-r--r-- | src/service/topology/test_gnunet_daemon_topology_data.conf | 34 | ||||
-rw-r--r-- | src/service/topology/topology.conf | 5 |
7 files changed, 1536 insertions, 0 deletions
diff --git a/src/service/topology/.gitignore b/src/service/topology/.gitignore new file mode 100644 index 000000000..cfa95ec7e --- /dev/null +++ b/src/service/topology/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | gnunet-daemon-topology | ||
2 | test_gnunet_daemon_topology | ||
diff --git a/src/service/topology/Makefile.am b/src/service/topology/Makefile.am new file mode 100644 index 000000000..559e55174 --- /dev/null +++ b/src/service/topology/Makefile.am | |||
@@ -0,0 +1,47 @@ | |||
1 | # This Makefile.am is in the public domain | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | endif | ||
7 | |||
8 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
9 | |||
10 | libexecdir= $(pkglibdir)/libexec/ | ||
11 | |||
12 | dist_pkgcfg_DATA = \ | ||
13 | topology.conf | ||
14 | |||
15 | |||
16 | libexec_PROGRAMS = \ | ||
17 | gnunet-daemon-topology | ||
18 | |||
19 | gnunet_daemon_topology_SOURCES = \ | ||
20 | gnunet-daemon-topology.c | ||
21 | gnunet_daemon_topology_LDADD = \ | ||
22 | $(top_builddir)/src/service/core/libgnunetcore.la \ | ||
23 | $(top_builddir)/src/service/peerstore/libgnunetpeerstore.la \ | ||
24 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
25 | $(top_builddir)/src/service/transport/libgnunettransportapplication.la \ | ||
26 | $(top_builddir)/src/lib/hello/libgnunethello.la \ | ||
27 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
28 | $(GN_LIBINTL) | ||
29 | |||
30 | |||
31 | #check_PROGRAMS = \ | ||
32 | # test_gnunet_daemon_topology | ||
33 | |||
34 | # if ENABLE_TEST_RUN | ||
35 | # AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
36 | # TESTS = $(check_PROGRAMS) | ||
37 | # endif | ||
38 | |||
39 | test_gnunet_daemon_topology_SOURCES = \ | ||
40 | test_gnunet_daemon_topology.c | ||
41 | test_gnunet_daemon_topology_LDADD = \ | ||
42 | $(top_builddir)/src/testbed/libgnunettestbed.la \ | ||
43 | $(top_builddir)/src/service/statistics/libgnunetstatistics.la \ | ||
44 | $(top_builddir)/src/lib/util/libgnunetutil.la | ||
45 | |||
46 | EXTRA_DIST = \ | ||
47 | test_gnunet_daemon_topology_data.conf | ||
diff --git a/src/service/topology/gnunet-daemon-topology.c b/src/service/topology/gnunet-daemon-topology.c new file mode 100644 index 000000000..a95b67908 --- /dev/null +++ b/src/service/topology/gnunet-daemon-topology.c | |||
@@ -0,0 +1,1119 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2007-2016 GNUnet e.V. | ||
4 | |||
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. | ||
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 | Affero General Public License for more details. | ||
14 | |||
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/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file topology/gnunet-daemon-topology.c | ||
23 | * @brief code for maintaining the overlay topology | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * This daemon combines one Function: | ||
27 | * - gossping HELLOs | ||
28 | * | ||
29 | */ | ||
30 | #include "platform.h" | ||
31 | #include "gnunet_util_lib.h" | ||
32 | #include "gnunet_hello_uri_lib.h" | ||
33 | #include "gnunet_constants.h" | ||
34 | #include "gnunet_core_service.h" | ||
35 | #include "gnunet_protocols.h" | ||
36 | #include "gnunet_peerstore_service.h" | ||
37 | #include "gnunet_statistics_service.h" | ||
38 | #include "gnunet_transport_application_service.h" | ||
39 | #include <assert.h> | ||
40 | |||
41 | |||
42 | /** | ||
43 | * At what frequency do we sent HELLOs to a peer? | ||
44 | */ | ||
45 | #define HELLO_ADVERTISEMENT_MIN_FREQUENCY \ | ||
46 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) | ||
47 | |||
48 | /** | ||
49 | * After what time period do we expire the HELLO Bloom filter? | ||
50 | */ | ||
51 | #define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY \ | ||
52 | GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Record for neighbours and blacklisted peers. | ||
57 | */ | ||
58 | struct Peer | ||
59 | { | ||
60 | /** | ||
61 | * Which peer is this entry about? | ||
62 | */ | ||
63 | struct GNUNET_PeerIdentity pid; | ||
64 | |||
65 | /** | ||
66 | * Our handle for transmitting to this peer; NULL | ||
67 | * if peer is not connected. | ||
68 | */ | ||
69 | struct GNUNET_MQ_Handle *mq; | ||
70 | |||
71 | /** | ||
72 | * Pointer to the hello uri of this peer; can be NULL. | ||
73 | */ | ||
74 | struct GNUNET_MessageHeader *hello; | ||
75 | |||
76 | /** | ||
77 | * Bloom filter used to mark which peers already got the HELLO | ||
78 | * from this peer. | ||
79 | */ | ||
80 | struct GNUNET_CONTAINER_BloomFilter *filter; | ||
81 | |||
82 | /** | ||
83 | * Next time we are allowed to transmit a HELLO to this peer? | ||
84 | */ | ||
85 | struct GNUNET_TIME_Absolute next_hello_allowed; | ||
86 | |||
87 | /** | ||
88 | * When should we reset the bloom filter of this entry? | ||
89 | */ | ||
90 | struct GNUNET_TIME_Absolute filter_expiration; | ||
91 | |||
92 | /** | ||
93 | * ID of task we use to wait for the time to send the next HELLO | ||
94 | * to this peer. | ||
95 | */ | ||
96 | struct GNUNET_SCHEDULER_Task *hello_delay_task; | ||
97 | |||
98 | /** | ||
99 | * Transport suggest handle. | ||
100 | */ | ||
101 | struct GNUNET_TRANSPORT_ApplicationSuggestHandle *ash; | ||
102 | |||
103 | /** | ||
104 | * How much would we like to connect to this peer? | ||
105 | */ | ||
106 | uint32_t strength; | ||
107 | |||
108 | }; | ||
109 | |||
110 | /** | ||
111 | * Context for a add hello uri request. | ||
112 | */ | ||
113 | struct StoreHelloEntry | ||
114 | { | ||
115 | /** | ||
116 | * Kept (also) in a DLL. | ||
117 | */ | ||
118 | struct StoreHelloEntry *prev; | ||
119 | |||
120 | /** | ||
121 | * Kept (also) in a DLL. | ||
122 | */ | ||
123 | struct StoreHelloEntry *next; | ||
124 | |||
125 | /** | ||
126 | * Store hello ctx | ||
127 | */ | ||
128 | struct GNUNET_PEERSTORE_StoreHelloContext *sc; | ||
129 | }; | ||
130 | |||
131 | /** | ||
132 | * The task to delayed start the notification process intially. | ||
133 | * We like to give transport some time to give us our hello to distribute it. | ||
134 | */ | ||
135 | struct GNUNET_SCHEDULER_Task *peerstore_notify_task; | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Our peerstore notification context. We use notification | ||
140 | * to instantly learn about new peers as they are discovered. | ||
141 | */ | ||
142 | static struct GNUNET_PEERSTORE_Monitor *peerstore_notify; | ||
143 | |||
144 | /** | ||
145 | * Our configuration. | ||
146 | */ | ||
147 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
148 | |||
149 | /** | ||
150 | * Handle to the CORE service. | ||
151 | */ | ||
152 | static struct GNUNET_CORE_Handle *handle; | ||
153 | |||
154 | /** | ||
155 | * Handle to the PEERSTORE service. | ||
156 | */ | ||
157 | static struct GNUNET_PEERSTORE_Handle *ps; | ||
158 | |||
159 | /** | ||
160 | * Handle to Transport service. | ||
161 | */ | ||
162 | struct GNUNET_TRANSPORT_ApplicationHandle *transport; | ||
163 | |||
164 | /** | ||
165 | * Identity of this peer. | ||
166 | */ | ||
167 | static struct GNUNET_PeerIdentity my_identity; | ||
168 | |||
169 | /** | ||
170 | * Our private key. | ||
171 | */ | ||
172 | static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; | ||
173 | |||
174 | /** | ||
175 | * All of our current neighbours and all peers for | ||
176 | * which we have HELLOs. So pretty much everyone. Maps peer identities | ||
177 | * to `struct Peer *` values. | ||
178 | */ | ||
179 | static struct GNUNET_CONTAINER_MultiPeerMap *peers; | ||
180 | |||
181 | /** | ||
182 | * Handle for reporting statistics. | ||
183 | */ | ||
184 | static struct GNUNET_STATISTICS_Handle *stats; | ||
185 | |||
186 | /** | ||
187 | * Task scheduled to asynchronously reconsider adding/removing | ||
188 | * peer connectivity suggestions. | ||
189 | */ | ||
190 | static struct GNUNET_SCHEDULER_Task *add_task; | ||
191 | |||
192 | /** | ||
193 | * Number of peers that we are currently connected to. | ||
194 | */ | ||
195 | static unsigned int connection_count; | ||
196 | |||
197 | /** | ||
198 | * Target number of connections. | ||
199 | */ | ||
200 | static unsigned int target_connection_count; | ||
201 | |||
202 | /** | ||
203 | * Head of the linkd list to store the store context for hellos. | ||
204 | */ | ||
205 | static struct StoreHelloEntry *she_head; | ||
206 | |||
207 | /** | ||
208 | * Tail of the linkd list to store the store context for hellos. | ||
209 | */ | ||
210 | static struct StoreHelloEntry *she_tail; | ||
211 | |||
212 | /** | ||
213 | * Free all resources associated with the given peer. | ||
214 | * | ||
215 | * @param cls closure (not used) | ||
216 | * @param pid identity of the peer | ||
217 | * @param value peer to free | ||
218 | * @return #GNUNET_YES (always: continue to iterate) | ||
219 | */ | ||
220 | static int | ||
221 | free_peer (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) | ||
222 | { | ||
223 | struct Peer *pos = value; | ||
224 | |||
225 | GNUNET_break (NULL == pos->mq); | ||
226 | GNUNET_break (GNUNET_OK == | ||
227 | GNUNET_CONTAINER_multipeermap_remove (peers, pid, pos)); | ||
228 | if (NULL != pos->hello_delay_task) | ||
229 | { | ||
230 | GNUNET_SCHEDULER_cancel (pos->hello_delay_task); | ||
231 | pos->hello_delay_task = NULL; | ||
232 | } | ||
233 | if (NULL != pos->ash) | ||
234 | { | ||
235 | GNUNET_TRANSPORT_application_suggest_cancel (pos->ash); | ||
236 | pos->ash = NULL; | ||
237 | } | ||
238 | if (NULL != pos->hello) | ||
239 | { | ||
240 | GNUNET_free (pos->hello); | ||
241 | pos->hello = NULL; | ||
242 | } | ||
243 | if (NULL != pos->filter) | ||
244 | { | ||
245 | GNUNET_CONTAINER_bloomfilter_free (pos->filter); | ||
246 | pos->filter = NULL; | ||
247 | } | ||
248 | GNUNET_free (pos); | ||
249 | return GNUNET_YES; | ||
250 | } | ||
251 | |||
252 | |||
253 | /** | ||
254 | * Recalculate how much we want to be connected to the specified peer | ||
255 | * and let ATS know about the result. | ||
256 | * | ||
257 | * @param pos peer to consider connecting to | ||
258 | */ | ||
259 | static void | ||
260 | attempt_connect (struct Peer *pos) | ||
261 | { | ||
262 | uint32_t strength; | ||
263 | struct GNUNET_BANDWIDTH_Value32NBO bw; | ||
264 | |||
265 | if (0 == GNUNET_memcmp (&my_identity, &pos->pid)) | ||
266 | return; /* This is myself, nothing to do. */ | ||
267 | if (connection_count < target_connection_count) | ||
268 | strength = 1; | ||
269 | else | ||
270 | strength = 0; | ||
271 | if (NULL != pos->mq) | ||
272 | strength *= 2; /* existing connections preferred */ | ||
273 | if (NULL != pos->ash) | ||
274 | { | ||
275 | GNUNET_TRANSPORT_application_suggest_cancel (pos->ash); | ||
276 | pos->ash = NULL; | ||
277 | } | ||
278 | pos->strength = strength; | ||
279 | if (0 != strength) | ||
280 | { | ||
281 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
282 | "Asking to connect to `%s' with strength %u\n", | ||
283 | GNUNET_i2s (&pos->pid), | ||
284 | (unsigned int) strength); | ||
285 | GNUNET_STATISTICS_update (stats, | ||
286 | gettext_noop ("# connect requests issued to ATS"), | ||
287 | 1, | ||
288 | GNUNET_NO); | ||
289 | // TODO Use strength somehow. | ||
290 | bw.value__ = 0; | ||
291 | pos->ash = GNUNET_TRANSPORT_application_suggest (transport, | ||
292 | &pos->pid, | ||
293 | GNUNET_MQ_PRIO_BEST_EFFORT, | ||
294 | bw); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | |||
299 | /** | ||
300 | * Create a new entry in the peer list. | ||
301 | * | ||
302 | * @param peer identity of the new entry | ||
303 | * @param hello hello message, can be NULL | ||
304 | * @return the new entry | ||
305 | */ | ||
306 | static struct Peer * | ||
307 | make_peer (const struct GNUNET_PeerIdentity *peer, | ||
308 | const struct GNUNET_MessageHeader *hello) | ||
309 | { | ||
310 | struct Peer *ret; | ||
311 | |||
312 | ret = GNUNET_new (struct Peer); | ||
313 | ret->pid = *peer; | ||
314 | if (NULL != hello) | ||
315 | { | ||
316 | ret->hello = GNUNET_malloc (ntohs (hello->size)); | ||
317 | GNUNET_memcpy (ret->hello, hello, ntohs (hello->size)); | ||
318 | } | ||
319 | GNUNET_break (GNUNET_OK == | ||
320 | GNUNET_CONTAINER_multipeermap_put ( | ||
321 | peers, | ||
322 | peer, | ||
323 | ret, | ||
324 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); | ||
325 | return ret; | ||
326 | } | ||
327 | |||
328 | |||
329 | /** | ||
330 | * Setup bloom filter for the given peer entry. | ||
331 | * | ||
332 | * @param peer entry to initialize | ||
333 | */ | ||
334 | static void | ||
335 | setup_filter (struct Peer *peer) | ||
336 | { | ||
337 | struct GNUNET_HashCode hc; | ||
338 | |||
339 | /* 2^{-5} chance of not sending a HELLO to a peer is | ||
340 | * acceptably small (if the filter is 50% full); | ||
341 | * 64 bytes of memory are small compared to the rest | ||
342 | * of the data structure and would only really become | ||
343 | * "useless" once a HELLO has been passed on to ~100 | ||
344 | * other peers, which is likely more than enough in | ||
345 | * any case; hence 64, 5 as bloomfilter parameters. */peer->filter = GNUNET_CONTAINER_bloomfilter_init (NULL, 64, 5); | ||
346 | peer->filter_expiration = | ||
347 | GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY); | ||
348 | /* never send a peer its own HELLO */ | ||
349 | GNUNET_CRYPTO_hash (&peer->pid, sizeof(struct GNUNET_PeerIdentity), &hc); | ||
350 | GNUNET_CONTAINER_bloomfilter_add (peer->filter, &hc); | ||
351 | } | ||
352 | |||
353 | |||
354 | /** | ||
355 | * Closure for #find_advertisable_hello(). | ||
356 | */ | ||
357 | struct FindAdvHelloContext | ||
358 | { | ||
359 | /** | ||
360 | * Peer we want to advertise to. | ||
361 | */ | ||
362 | struct Peer *peer; | ||
363 | |||
364 | /** | ||
365 | * Where to store the result (peer selected for advertising). | ||
366 | */ | ||
367 | struct Peer *result; | ||
368 | |||
369 | /** | ||
370 | * Maximum HELLO size we can use right now. | ||
371 | */ | ||
372 | size_t max_size; | ||
373 | |||
374 | struct GNUNET_TIME_Relative next_adv; | ||
375 | }; | ||
376 | |||
377 | |||
378 | /** | ||
379 | * Find a peer that would be reasonable for advertising. | ||
380 | * | ||
381 | * @param cls closure | ||
382 | * @param pid identity of a peer | ||
383 | * @param value 'struct Peer*' for the peer we are considering | ||
384 | * @return #GNUNET_YES (continue iteration) | ||
385 | */ | ||
386 | static int | ||
387 | find_advertisable_hello (void *cls, | ||
388 | const struct GNUNET_PeerIdentity *pid, | ||
389 | void *value) | ||
390 | { | ||
391 | struct FindAdvHelloContext *fah = cls; | ||
392 | struct Peer *pos = value; | ||
393 | struct GNUNET_TIME_Relative rst_time; | ||
394 | struct GNUNET_HashCode hc; | ||
395 | size_t hs; | ||
396 | |||
397 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
398 | "find_advertisable_hello\n"); | ||
399 | if (pos == fah->peer) | ||
400 | return GNUNET_YES; | ||
401 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
402 | "find_advertisable_hello 2\n"); | ||
403 | if (pos->hello == NULL) | ||
404 | return GNUNET_YES; | ||
405 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
406 | "find_advertisable_hello 3\n"); | ||
407 | rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration); | ||
408 | if (0 == rst_time.rel_value_us) | ||
409 | { | ||
410 | /* time to discard... */ | ||
411 | GNUNET_CONTAINER_bloomfilter_free (pos->filter); | ||
412 | setup_filter (pos); | ||
413 | } | ||
414 | fah->next_adv = GNUNET_TIME_relative_min (rst_time, fah->next_adv); | ||
415 | hs = pos->hello->size; | ||
416 | if (hs > fah->max_size) | ||
417 | return GNUNET_YES; | ||
418 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
419 | "find_advertisable_hello 4\n"); | ||
420 | GNUNET_CRYPTO_hash (&fah->peer->pid, | ||
421 | sizeof(struct GNUNET_PeerIdentity), | ||
422 | &hc); | ||
423 | if (GNUNET_NO == GNUNET_CONTAINER_bloomfilter_test (pos->filter, &hc)) | ||
424 | fah->result = pos; | ||
425 | return GNUNET_YES; | ||
426 | } | ||
427 | |||
428 | |||
429 | /** | ||
430 | * Calculate when we would like to send the next HELLO to this | ||
431 | * peer and ask for it. | ||
432 | * | ||
433 | * @param cls for which peer to schedule the HELLO | ||
434 | */ | ||
435 | static void | ||
436 | schedule_next_hello (void *cls) | ||
437 | { | ||
438 | struct Peer *pl = cls; | ||
439 | struct FindAdvHelloContext fah; | ||
440 | struct GNUNET_MQ_Envelope *env; | ||
441 | size_t want; | ||
442 | struct GNUNET_TIME_Relative delay; | ||
443 | struct GNUNET_HashCode hc; | ||
444 | |||
445 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
446 | "schedule_next_hello\n"); | ||
447 | pl->hello_delay_task = NULL; | ||
448 | GNUNET_assert (NULL != pl->mq); | ||
449 | /* find applicable HELLOs */ | ||
450 | fah.peer = pl; | ||
451 | fah.result = NULL; | ||
452 | fah.max_size = GNUNET_MAX_MESSAGE_SIZE - 1; | ||
453 | fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL; | ||
454 | GNUNET_CONTAINER_multipeermap_iterate (peers, &find_advertisable_hello, &fah); | ||
455 | if (NULL == fah.result) | ||
456 | { | ||
457 | pl->hello_delay_task = | ||
458 | GNUNET_SCHEDULER_add_delayed (fah.next_adv, &schedule_next_hello, pl); | ||
459 | |||
460 | return; | ||
461 | } | ||
462 | want = ntohs (fah.result->hello->size); | ||
463 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
464 | "Sending HELLO with %u bytes for peer %s\n", | ||
465 | (unsigned int) want, | ||
466 | GNUNET_i2s (&pl->pid)); | ||
467 | env = GNUNET_MQ_msg_copy (fah.result->hello); | ||
468 | GNUNET_MQ_send (pl->mq, env); | ||
469 | |||
470 | /* avoid sending this one again soon */ | ||
471 | GNUNET_CRYPTO_hash (&pl->pid, sizeof(struct GNUNET_PeerIdentity), &hc); | ||
472 | GNUNET_CONTAINER_bloomfilter_add (fah.result->filter, &hc); | ||
473 | |||
474 | GNUNET_STATISTICS_update (stats, | ||
475 | gettext_noop ("# HELLO messages gossipped"), | ||
476 | 1, | ||
477 | GNUNET_NO); | ||
478 | /* prepare to send the next one */ | ||
479 | pl->next_hello_allowed = | ||
480 | GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY); | ||
481 | delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed); | ||
482 | pl->hello_delay_task = GNUNET_SCHEDULER_add_delayed (delay, &schedule_next_hello, pl); | ||
483 | } | ||
484 | |||
485 | |||
486 | /** | ||
487 | * Cancel existing requests for sending HELLOs to this peer | ||
488 | * and recalculate when we should send HELLOs to it based | ||
489 | * on our current state (something changed!). | ||
490 | * | ||
491 | * @param cls closure `struct Peer` to skip, or NULL | ||
492 | * @param pid identity of a peer | ||
493 | * @param value `struct Peer *` for the peer | ||
494 | * @return #GNUNET_YES (always) | ||
495 | */ | ||
496 | static int | ||
497 | reschedule_hellos (void *cls, | ||
498 | const struct GNUNET_PeerIdentity *pid, | ||
499 | void *value) | ||
500 | { | ||
501 | (void) cls; | ||
502 | struct Peer *peer = value; | ||
503 | |||
504 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
505 | "Reschedule for `%s'\n", | ||
506 | GNUNET_i2s (&peer->pid)); | ||
507 | if (NULL == peer->mq) | ||
508 | return GNUNET_YES; | ||
509 | if (NULL != peer->hello_delay_task) | ||
510 | { | ||
511 | GNUNET_SCHEDULER_cancel (peer->hello_delay_task); | ||
512 | peer->hello_delay_task = NULL; | ||
513 | } | ||
514 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
515 | "Schedule_next_hello\n"); | ||
516 | peer->hello_delay_task = | ||
517 | GNUNET_SCHEDULER_add_now (&schedule_next_hello, peer); | ||
518 | return GNUNET_YES; | ||
519 | } | ||
520 | |||
521 | |||
522 | /** | ||
523 | * Method called whenever a peer connects. | ||
524 | * | ||
525 | * @param cls closure | ||
526 | * @param peer peer identity this notification is about | ||
527 | * @param mq message queue for communicating with @a peer | ||
528 | * @return our `struct Peer` for @a peer | ||
529 | */ | ||
530 | static void * | ||
531 | connect_notify (void *cls, | ||
532 | const struct GNUNET_PeerIdentity *peer, | ||
533 | struct GNUNET_MQ_Handle *mq) | ||
534 | { | ||
535 | struct Peer *pos; | ||
536 | |||
537 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
538 | "Core told us that we are connecting to `%s'\n", | ||
539 | GNUNET_i2s (peer)); | ||
540 | if (0 == GNUNET_memcmp (&my_identity, peer)) | ||
541 | return NULL; | ||
542 | GNUNET_MQ_set_options (mq, GNUNET_MQ_PRIO_BEST_EFFORT); | ||
543 | connection_count++; | ||
544 | GNUNET_STATISTICS_set (stats, | ||
545 | gettext_noop ("# peers connected"), | ||
546 | connection_count, | ||
547 | GNUNET_NO); | ||
548 | pos = GNUNET_CONTAINER_multipeermap_get (peers, peer); | ||
549 | if (NULL == pos) | ||
550 | { | ||
551 | pos = make_peer (peer, NULL); | ||
552 | } | ||
553 | else | ||
554 | { | ||
555 | GNUNET_assert (NULL == pos->mq); | ||
556 | } | ||
557 | pos->mq = mq; | ||
558 | reschedule_hellos (NULL, peer, pos); | ||
559 | return pos; | ||
560 | } | ||
561 | |||
562 | |||
563 | /** | ||
564 | * Try to add more peers to our connection set. | ||
565 | * | ||
566 | * @param cls closure, not used | ||
567 | * @param pid identity of a peer | ||
568 | * @param value `struct Peer *` for the peer | ||
569 | * @return #GNUNET_YES (continue to iterate) | ||
570 | */ | ||
571 | static int | ||
572 | try_add_peers (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) | ||
573 | { | ||
574 | struct Peer *pos = value; | ||
575 | |||
576 | attempt_connect (pos); | ||
577 | return GNUNET_YES; | ||
578 | } | ||
579 | |||
580 | |||
581 | /** | ||
582 | * Add peers and schedule connection attempt | ||
583 | * | ||
584 | * @param cls unused, NULL | ||
585 | */ | ||
586 | static void | ||
587 | add_peer_task (void *cls) | ||
588 | { | ||
589 | add_task = NULL; | ||
590 | |||
591 | GNUNET_CONTAINER_multipeermap_iterate (peers, &try_add_peers, NULL); | ||
592 | } | ||
593 | |||
594 | |||
595 | /** | ||
596 | * Method called whenever a peer disconnects. | ||
597 | * | ||
598 | * @param cls closure | ||
599 | * @param peer peer identity this notification is about | ||
600 | * @param internal_cls the `struct Peer` for this peer | ||
601 | */ | ||
602 | static void | ||
603 | disconnect_notify (void *cls, | ||
604 | const struct GNUNET_PeerIdentity *peer, | ||
605 | void *internal_cls) | ||
606 | { | ||
607 | struct Peer *pos = internal_cls; | ||
608 | |||
609 | if (NULL == pos) | ||
610 | return; /* myself, we're shutting down */ | ||
611 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
612 | "Core told us that we disconnected from `%s'\n", | ||
613 | GNUNET_i2s (peer)); | ||
614 | if (NULL == pos->mq) | ||
615 | { | ||
616 | GNUNET_break (0); | ||
617 | return; | ||
618 | } | ||
619 | pos->mq = NULL; | ||
620 | connection_count--; | ||
621 | if (NULL != pos->hello_delay_task) | ||
622 | { | ||
623 | GNUNET_SCHEDULER_cancel (pos->hello_delay_task); | ||
624 | pos->hello_delay_task = NULL; | ||
625 | } | ||
626 | GNUNET_STATISTICS_set (stats, | ||
627 | gettext_noop ("# peers connected"), | ||
628 | connection_count, | ||
629 | GNUNET_NO); | ||
630 | if ((connection_count < target_connection_count) && | ||
631 | (NULL == add_task)) | ||
632 | add_task = GNUNET_SCHEDULER_add_now (&add_peer_task, NULL); | ||
633 | |||
634 | } | ||
635 | |||
636 | |||
637 | /** | ||
638 | * Iterator called on each address. | ||
639 | * | ||
640 | * @param cls flag that we will set if we see any addresses | ||
641 | * @param address the address of the peer | ||
642 | * @return #GNUNET_SYSERR always, to terminate iteration | ||
643 | */ | ||
644 | static void | ||
645 | address_iterator (void *cls, | ||
646 | const struct GNUNET_PeerIdentity *pid, | ||
647 | const char *uri) | ||
648 | { | ||
649 | (void) pid; | ||
650 | int *flag = cls; | ||
651 | |||
652 | *flag = *flag + 1; | ||
653 | // *flag = GNUNET_YES; | ||
654 | } | ||
655 | |||
656 | |||
657 | /** | ||
658 | * We've gotten a HELLO from another peer. Consider it for | ||
659 | * advertising. | ||
660 | * | ||
661 | * @param hello the HELLO we got | ||
662 | */ | ||
663 | static void | ||
664 | consider_for_advertising (const struct GNUNET_MessageHeader *hello) | ||
665 | { | ||
666 | int num_addresses_old = 0; | ||
667 | int num_addresses_new = 0; | ||
668 | struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg (hello); | ||
669 | const struct GNUNET_PeerIdentity *pid; | ||
670 | struct Peer *peer; | ||
671 | uint16_t size; | ||
672 | |||
673 | pid = GNUNET_HELLO_builder_iterate (builder, | ||
674 | &address_iterator, | ||
675 | &num_addresses_new); | ||
676 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
677 | "consider 0 for %s\n", | ||
678 | GNUNET_i2s (pid)); | ||
679 | if (0 == num_addresses_new) | ||
680 | { | ||
681 | GNUNET_HELLO_builder_free (builder); | ||
682 | return; /* no point in advertising this one... */ | ||
683 | } | ||
684 | peer = GNUNET_CONTAINER_multipeermap_get (peers, pid); | ||
685 | if (NULL == peer) | ||
686 | { | ||
687 | peer = make_peer (pid, hello); | ||
688 | } | ||
689 | else if (NULL != peer->hello) | ||
690 | { | ||
691 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
692 | struct GNUNET_TIME_Absolute new_hello_exp = | ||
693 | GNUNET_HELLO_builder_get_expiration_time (hello); | ||
694 | struct GNUNET_TIME_Absolute old_hello_exp = | ||
695 | GNUNET_HELLO_builder_get_expiration_time (peer->hello); | ||
696 | struct GNUNET_HELLO_Builder *builder_old = GNUNET_HELLO_builder_from_msg ( | ||
697 | peer->hello); | ||
698 | |||
699 | GNUNET_HELLO_builder_iterate (builder_old, | ||
700 | &address_iterator, | ||
701 | &num_addresses_old); | ||
702 | GNUNET_HELLO_builder_free (builder_old); | ||
703 | if (GNUNET_TIME_absolute_cmp (new_hello_exp, >, now) && | ||
704 | (GNUNET_TIME_absolute_cmp (new_hello_exp, >, old_hello_exp) || | ||
705 | num_addresses_old < num_addresses_new)) | ||
706 | { | ||
707 | GNUNET_free (peer->hello); | ||
708 | size = ntohs (hello->size); | ||
709 | peer->hello = GNUNET_malloc (size); | ||
710 | GNUNET_memcpy (peer->hello, hello, size); | ||
711 | } | ||
712 | else | ||
713 | { | ||
714 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
715 | "consider 3\n"); | ||
716 | GNUNET_HELLO_builder_free (builder); | ||
717 | return; | ||
718 | } | ||
719 | } | ||
720 | else | ||
721 | { | ||
722 | size = ntohs (hello->size); | ||
723 | peer->hello = GNUNET_malloc (size); | ||
724 | GNUNET_memcpy (peer->hello, hello, size); | ||
725 | } | ||
726 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
727 | "Found HELLO from peer `%s' for advertising\n", | ||
728 | GNUNET_i2s (pid)); | ||
729 | if (NULL != peer->filter) | ||
730 | { | ||
731 | GNUNET_CONTAINER_bloomfilter_free (peer->filter); | ||
732 | peer->filter = NULL; | ||
733 | } | ||
734 | setup_filter (peer); | ||
735 | /* since we have a new HELLO to pick from, re-schedule all | ||
736 | * HELLO requests that are not bound by the HELLO send rate! */ | ||
737 | GNUNET_CONTAINER_multipeermap_iterate (peers, &reschedule_hellos, NULL); | ||
738 | GNUNET_HELLO_builder_free (builder); | ||
739 | } | ||
740 | |||
741 | |||
742 | static void | ||
743 | error_cb (void *cls) | ||
744 | { | ||
745 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
746 | _ ( | ||
747 | "Error in communication with PEERSTORE service to monitor.\n")); | ||
748 | return; | ||
749 | } | ||
750 | |||
751 | |||
752 | static void | ||
753 | sync_cb (void *cls) | ||
754 | { | ||
755 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
756 | _ ("Finished initial PEERSTORE iteration in monitor.\n")); | ||
757 | return; | ||
758 | } | ||
759 | |||
760 | |||
761 | /** | ||
762 | * PEERSTORE calls this function to let us know about a possible peer | ||
763 | * that we might want to connect to. | ||
764 | * | ||
765 | * @param cls closure (not used) | ||
766 | * @param peer potential peer to connect to | ||
767 | * @param hello HELLO for this peer (or NULL) | ||
768 | * @param err_msg NULL if successful, otherwise contains error message | ||
769 | */ | ||
770 | static void | ||
771 | process_peer (void *cls, | ||
772 | const struct GNUNET_PEERSTORE_Record *record, | ||
773 | const char *err_msg) | ||
774 | { | ||
775 | struct Peer *pos; | ||
776 | struct GNUNET_MessageHeader *hello; | ||
777 | |||
778 | if (NULL != err_msg) | ||
779 | { | ||
780 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
781 | _ ("Error in communication with PEERSTORE service: %s\n"), | ||
782 | err_msg); | ||
783 | GNUNET_PEERSTORE_monitor_stop (peerstore_notify); | ||
784 | peerstore_notify = | ||
785 | GNUNET_PEERSTORE_monitor_start (cfg, | ||
786 | GNUNET_YES, | ||
787 | "peerstore", | ||
788 | NULL, | ||
789 | GNUNET_PEERSTORE_HELLO_KEY, | ||
790 | error_cb, | ||
791 | NULL, | ||
792 | sync_cb, | ||
793 | NULL, | ||
794 | &process_peer, | ||
795 | NULL); | ||
796 | return; | ||
797 | } | ||
798 | GNUNET_assert (NULL != record); | ||
799 | hello = record->value; | ||
800 | if (NULL == hello) | ||
801 | { | ||
802 | /* free existing HELLO, if any */ | ||
803 | pos = GNUNET_CONTAINER_multipeermap_get (peers, &record->peer); | ||
804 | if (NULL != pos) | ||
805 | { | ||
806 | GNUNET_free (pos->hello); | ||
807 | pos->hello = NULL; | ||
808 | if (NULL != pos->filter) | ||
809 | { | ||
810 | GNUNET_CONTAINER_bloomfilter_free (pos->filter); | ||
811 | pos->filter = NULL; | ||
812 | } | ||
813 | if (NULL == pos->mq) | ||
814 | free_peer (NULL, &pos->pid, pos); | ||
815 | } | ||
816 | GNUNET_PEERSTORE_monitor_next (peerstore_notify, 1); | ||
817 | return; | ||
818 | } | ||
819 | consider_for_advertising (hello); | ||
820 | pos = GNUNET_CONTAINER_multipeermap_get (peers, &record->peer); | ||
821 | if (NULL == pos) | ||
822 | pos = make_peer (&record->peer, hello); | ||
823 | attempt_connect (pos); | ||
824 | GNUNET_PEERSTORE_monitor_next (peerstore_notify, 1); | ||
825 | } | ||
826 | |||
827 | |||
828 | static void | ||
829 | start_notify (void *cls) | ||
830 | { | ||
831 | (void) cls; | ||
832 | |||
833 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
834 | "Starting to process new hellos for gossiping.\n"); | ||
835 | peerstore_notify = | ||
836 | GNUNET_PEERSTORE_monitor_start (cfg, | ||
837 | GNUNET_YES, | ||
838 | "peerstore", | ||
839 | NULL, | ||
840 | GNUNET_PEERSTORE_HELLO_KEY, | ||
841 | &error_cb, | ||
842 | NULL, | ||
843 | &sync_cb, | ||
844 | NULL, | ||
845 | &process_peer, | ||
846 | NULL); | ||
847 | } | ||
848 | |||
849 | |||
850 | /** | ||
851 | * Function called after #GNUNET_CORE_connect has succeeded | ||
852 | * (or failed for good). | ||
853 | * | ||
854 | * @param cls closure | ||
855 | * @param my_id ID of this peer, NULL if we failed | ||
856 | */ | ||
857 | static void | ||
858 | core_init (void *cls, const struct GNUNET_PeerIdentity *my_id) | ||
859 | { | ||
860 | if (NULL == my_id) | ||
861 | { | ||
862 | GNUNET_log ( | ||
863 | GNUNET_ERROR_TYPE_ERROR, | ||
864 | _ ("Failed to connect to core service, can not manage topology!\n")); | ||
865 | GNUNET_SCHEDULER_shutdown (); | ||
866 | return; | ||
867 | } | ||
868 | my_identity = *my_id; | ||
869 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "I am peer `%s'\n", GNUNET_i2s (my_id)); | ||
870 | peerstore_notify_task = GNUNET_SCHEDULER_add_delayed ( | ||
871 | GNUNET_TIME_UNIT_MINUTES, | ||
872 | start_notify, | ||
873 | NULL); | ||
874 | } | ||
875 | |||
876 | |||
877 | /** | ||
878 | * This function is called whenever an encrypted HELLO message is | ||
879 | * received. | ||
880 | * | ||
881 | * @param cls closure with the peer identity of the sender | ||
882 | * @param message the actual HELLO message | ||
883 | * @return #GNUNET_OK if @a message is well-formed | ||
884 | * #GNUNET_SYSERR if @a message is invalid | ||
885 | */ | ||
886 | static int | ||
887 | check_hello (void *cls, const struct GNUNET_MessageHeader *message) | ||
888 | { | ||
889 | struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg ( | ||
890 | message); | ||
891 | const struct GNUNET_PeerIdentity *pid = GNUNET_HELLO_builder_get_id (builder); | ||
892 | |||
893 | if (NULL == pid) | ||
894 | { | ||
895 | GNUNET_break_op (0); | ||
896 | return GNUNET_SYSERR; | ||
897 | } | ||
898 | return GNUNET_OK; | ||
899 | } | ||
900 | |||
901 | |||
902 | static void | ||
903 | shc_cont (void *cls, int success) | ||
904 | { | ||
905 | struct StoreHelloEntry *she = cls; | ||
906 | |||
907 | she->sc = NULL; | ||
908 | if (GNUNET_YES == success) | ||
909 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
910 | "Hello stored successfully!\n"); | ||
911 | else | ||
912 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
913 | "Error storing hello!\n"); | ||
914 | GNUNET_CONTAINER_DLL_remove (she_head, she_tail, she); | ||
915 | GNUNET_free (she); | ||
916 | } | ||
917 | |||
918 | |||
919 | /** | ||
920 | * This function is called whenever an encrypted HELLO message is | ||
921 | * received. | ||
922 | * | ||
923 | * @param cls closure with the peer identity of the sender | ||
924 | * @param message the actual HELLO message | ||
925 | */ | ||
926 | static void | ||
927 | handle_hello (void *cls, const struct GNUNET_MessageHeader *message) | ||
928 | { | ||
929 | struct StoreHelloEntry *she; | ||
930 | const struct GNUNET_PeerIdentity *other = cls; | ||
931 | struct GNUNET_HELLO_Builder *builder = GNUNET_HELLO_builder_from_msg ( | ||
932 | message); | ||
933 | |||
934 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
935 | "Received encrypted HELLO from peer `%s'\n", | ||
936 | GNUNET_i2s (other)); | ||
937 | GNUNET_STATISTICS_update (stats, | ||
938 | gettext_noop ("# HELLO messages received"), | ||
939 | 1, | ||
940 | GNUNET_NO); | ||
941 | GNUNET_HELLO_builder_from_msg (message); | ||
942 | she = GNUNET_new (struct StoreHelloEntry); | ||
943 | she->sc = GNUNET_PEERSTORE_hello_add (ps, message, &shc_cont, she); | ||
944 | if (NULL != she->sc) | ||
945 | { | ||
946 | GNUNET_CONTAINER_DLL_insert (she_head, she_tail, she); | ||
947 | } | ||
948 | else | ||
949 | GNUNET_free (she); | ||
950 | GNUNET_HELLO_builder_free (builder); | ||
951 | } | ||
952 | |||
953 | |||
954 | /** | ||
955 | * Last task run during shutdown. Disconnects us from | ||
956 | * the transport and core. | ||
957 | * | ||
958 | * @param cls unused, NULL | ||
959 | */ | ||
960 | static void | ||
961 | cleaning_task (void *cls) | ||
962 | { | ||
963 | struct StoreHelloEntry *pos; | ||
964 | |||
965 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Topology shutdown\n"); | ||
966 | while (NULL != (pos = she_head)) | ||
967 | { | ||
968 | GNUNET_CONTAINER_DLL_remove (she_head, she_tail, pos); | ||
969 | if (NULL != pos->sc) | ||
970 | GNUNET_PEERSTORE_hello_add_cancel (pos->sc); | ||
971 | GNUNET_free (pos); | ||
972 | } | ||
973 | if (NULL != peerstore_notify) | ||
974 | { | ||
975 | GNUNET_PEERSTORE_monitor_stop (peerstore_notify); | ||
976 | peerstore_notify = NULL; | ||
977 | } | ||
978 | else if (NULL != peerstore_notify_task) | ||
979 | { | ||
980 | GNUNET_SCHEDULER_cancel (peerstore_notify_task); | ||
981 | } | ||
982 | if (NULL != handle) | ||
983 | { | ||
984 | GNUNET_CORE_disconnect (handle); | ||
985 | handle = NULL; | ||
986 | } | ||
987 | if (NULL != add_task) | ||
988 | { | ||
989 | GNUNET_SCHEDULER_cancel (add_task); | ||
990 | add_task = NULL; | ||
991 | } | ||
992 | GNUNET_CONTAINER_multipeermap_iterate (peers, &free_peer, NULL); | ||
993 | GNUNET_CONTAINER_multipeermap_destroy (peers); | ||
994 | peers = NULL; | ||
995 | if (NULL != transport) | ||
996 | { | ||
997 | GNUNET_TRANSPORT_application_done (transport); | ||
998 | transport = NULL; | ||
999 | } | ||
1000 | if (NULL != ps) | ||
1001 | { | ||
1002 | GNUNET_PEERSTORE_disconnect (ps); | ||
1003 | ps = NULL; | ||
1004 | } | ||
1005 | if (NULL != stats) | ||
1006 | { | ||
1007 | GNUNET_STATISTICS_destroy (stats, GNUNET_NO); | ||
1008 | stats = NULL; | ||
1009 | } | ||
1010 | GNUNET_free (my_private_key); | ||
1011 | } | ||
1012 | |||
1013 | |||
1014 | /** | ||
1015 | * Main function that will be run. | ||
1016 | * | ||
1017 | * @param cls closure | ||
1018 | * @param args remaining command-line arguments | ||
1019 | * @param cfgfile name of the configuration file used (for saving, can be NULL!) | ||
1020 | * @param c configuration | ||
1021 | */ | ||
1022 | static void | ||
1023 | run (void *cls, | ||
1024 | char *const *args, | ||
1025 | const char *cfgfile, | ||
1026 | const struct GNUNET_CONFIGURATION_Handle *c) | ||
1027 | { | ||
1028 | struct GNUNET_MQ_MessageHandler handlers[] = | ||
1029 | { GNUNET_MQ_hd_var_size (hello, | ||
1030 | GNUNET_MESSAGE_TYPE_HELLO_URI, | ||
1031 | struct GNUNET_MessageHeader, | ||
1032 | NULL), | ||
1033 | GNUNET_MQ_handler_end () }; | ||
1034 | unsigned long long opt; | ||
1035 | |||
1036 | cfg = c; | ||
1037 | my_private_key = | ||
1038 | GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg); | ||
1039 | stats = GNUNET_STATISTICS_create ("topology", cfg); | ||
1040 | |||
1041 | if (GNUNET_OK != | ||
1042 | GNUNET_CONFIGURATION_get_value_number (cfg, | ||
1043 | "TOPOLOGY", | ||
1044 | "TARGET-CONNECTION-COUNT", | ||
1045 | &opt)) | ||
1046 | opt = 16; | ||
1047 | target_connection_count = (unsigned int) opt; | ||
1048 | peers = GNUNET_CONTAINER_multipeermap_create (target_connection_count * 2, | ||
1049 | GNUNET_NO); | ||
1050 | transport = GNUNET_TRANSPORT_application_init (cfg); | ||
1051 | ps = GNUNET_PEERSTORE_connect (cfg); | ||
1052 | handle = GNUNET_CORE_connect (cfg, | ||
1053 | NULL, | ||
1054 | &core_init, | ||
1055 | &connect_notify, | ||
1056 | &disconnect_notify, | ||
1057 | handlers); | ||
1058 | GNUNET_SCHEDULER_add_shutdown (&cleaning_task, NULL); | ||
1059 | if (NULL == handle) | ||
1060 | { | ||
1061 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1062 | _ ("Failed to connect to `%s' service.\n"), | ||
1063 | "core"); | ||
1064 | GNUNET_SCHEDULER_shutdown (); | ||
1065 | return; | ||
1066 | } | ||
1067 | } | ||
1068 | |||
1069 | |||
1070 | /** | ||
1071 | * The main function for the topology daemon. | ||
1072 | * | ||
1073 | * @param argc number of arguments from the command line | ||
1074 | * @param argv command line arguments | ||
1075 | * @return 0 ok, 1 on error | ||
1076 | */ | ||
1077 | int | ||
1078 | main (int argc, char *const *argv) | ||
1079 | { | ||
1080 | static const struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
1081 | GNUNET_GETOPT_OPTION_END | ||
1082 | }; | ||
1083 | int ret; | ||
1084 | |||
1085 | if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) | ||
1086 | return 2; | ||
1087 | |||
1088 | ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, | ||
1089 | argv, | ||
1090 | "gnunet-daemon-topology", | ||
1091 | _ ("GNUnet topology control"), | ||
1092 | options, | ||
1093 | &run, | ||
1094 | NULL)) | ||
1095 | ? 0 | ||
1096 | : 1; | ||
1097 | GNUNET_free_nz ((void *) argv); | ||
1098 | return ret; | ||
1099 | } | ||
1100 | |||
1101 | |||
1102 | #if defined(__linux__) && defined(__GLIBC__) | ||
1103 | #include <malloc.h> | ||
1104 | |||
1105 | /** | ||
1106 | * MINIMIZE heap size (way below 128k) since this process doesn't need much. | ||
1107 | */ | ||
1108 | void __attribute__ ((constructor)) | ||
1109 | GNUNET_TOPOLOGY_memory_init () | ||
1110 | { | ||
1111 | mallopt (M_TRIM_THRESHOLD, 4 * 1024); | ||
1112 | mallopt (M_TOP_PAD, 1 * 1024); | ||
1113 | malloc_trim (0); | ||
1114 | } | ||
1115 | |||
1116 | |||
1117 | #endif | ||
1118 | |||
1119 | /* end of gnunet-daemon-topology.c */ | ||
diff --git a/src/service/topology/meson.build b/src/service/topology/meson.build new file mode 100644 index 000000000..ca3cc6935 --- /dev/null +++ b/src/service/topology/meson.build | |||
@@ -0,0 +1,29 @@ | |||
1 | gnunetdaemontopology_src = ['gnunet-daemon-topology.c'] | ||
2 | |||
3 | configure_file(input : 'topology.conf', | ||
4 | output : 'topology.conf', | ||
5 | configuration : cdata, | ||
6 | install: true, | ||
7 | install_dir: pkgcfgdir) | ||
8 | |||
9 | |||
10 | if get_option('monolith') | ||
11 | # FIXME add daemon when new daemon macro is ported/ready for it. | ||
12 | #foreach p : libgnunetfriends_src | ||
13 | # gnunet_src += 'topology/' + p | ||
14 | #endforeach | ||
15 | endif | ||
16 | |||
17 | executable ('gnunet-daemon-topology', | ||
18 | gnunetdaemontopology_src, | ||
19 | dependencies: [ | ||
20 | libgnunetutil_dep, | ||
21 | libgnunetcore_dep, | ||
22 | libgnunetpeerstore_dep, | ||
23 | libgnunetstatistics_dep, | ||
24 | libgnunettransportapplication_dep, | ||
25 | libgnunethello_dep], | ||
26 | include_directories: [incdir, configuration_inc], | ||
27 | install: true, | ||
28 | install_dir: get_option('libdir') / 'gnunet' / 'libexec') | ||
29 | |||
diff --git a/src/service/topology/test_gnunet_daemon_topology.c b/src/service/topology/test_gnunet_daemon_topology.c new file mode 100644 index 000000000..6f9758b09 --- /dev/null +++ b/src/service/topology/test_gnunet_daemon_topology.c | |||
@@ -0,0 +1,300 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2012 GNUnet e.V. | ||
4 | |||
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. | ||
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 | Affero General Public License for more details. | ||
14 | |||
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/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file topology/test_gnunet_daemon_topology.c | ||
22 | * @brief testcase for topology maintenance code | ||
23 | * @author Christian Grothoff | ||
24 | * @author xrs | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_testbed_service.h" | ||
28 | #include "gnunet_statistics_service.h" | ||
29 | |||
30 | |||
31 | #define NUM_PEERS 8 | ||
32 | |||
33 | /* | ||
34 | * The threshold defines the number of connection that are needed | ||
35 | * for one peer to pass the test. Be aware that setting NUM_PEERS | ||
36 | * too high can cause bandwidth problems for the testing peers. | ||
37 | * Normal should be 5KB/s per peer. See gnunet-config -s ats. | ||
38 | * schanzen 12/2019: This _only_ makes sense if we connect to the | ||
39 | * actual network as in the test we do not connect to more than 1 peer. | ||
40 | * => reducing to 1 for now, was NUM_PEERS / 2 | ||
41 | */ | ||
42 | #define THRESHOLD 1 | ||
43 | |||
44 | /** | ||
45 | * How long until we give up on connecting the peers? | ||
46 | */ | ||
47 | #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) | ||
48 | |||
49 | /* | ||
50 | * Store manual connections. | ||
51 | */ | ||
52 | static unsigned int connect_left; | ||
53 | |||
54 | /* | ||
55 | * Result of the testcase. | ||
56 | */ | ||
57 | static int result; | ||
58 | |||
59 | /* | ||
60 | * Peers that reached the threshold of connections. | ||
61 | */ | ||
62 | static int checked_peers; | ||
63 | |||
64 | /* | ||
65 | * Testbed operations. | ||
66 | */ | ||
67 | struct GNUNET_TESTBED_Operation *op[NUM_PEERS]; | ||
68 | |||
69 | /* | ||
70 | * Timeout for testcase. | ||
71 | */ | ||
72 | static struct GNUNET_SCHEDULER_Task *timeout_tid; | ||
73 | |||
74 | /* | ||
75 | * Peer context for every testbed peer. | ||
76 | */ | ||
77 | struct peerctx | ||
78 | { | ||
79 | int index; | ||
80 | struct GNUNET_STATISTICS_Handle *statistics; | ||
81 | int connections; | ||
82 | int reported; /* GNUNET_NO | GNUNET_YES */ | ||
83 | }; | ||
84 | |||
85 | |||
86 | static void | ||
87 | shutdown_task (void *cls) | ||
88 | { | ||
89 | unsigned int i; | ||
90 | |||
91 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
92 | "Shutting down testcase\n"); | ||
93 | |||
94 | for (i = 0; i < NUM_PEERS; i++) | ||
95 | { | ||
96 | if (NULL != op[i]) | ||
97 | GNUNET_TESTBED_operation_done (op[i]); | ||
98 | } | ||
99 | |||
100 | if (NULL != timeout_tid) | ||
101 | GNUNET_SCHEDULER_cancel (timeout_tid); | ||
102 | } | ||
103 | |||
104 | |||
105 | static void | ||
106 | timeout_task (void *cls) | ||
107 | { | ||
108 | timeout_tid = NULL; | ||
109 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
110 | "Testcase timeout\n"); | ||
111 | |||
112 | result = GNUNET_SYSERR; | ||
113 | GNUNET_SCHEDULER_shutdown (); | ||
114 | } | ||
115 | |||
116 | |||
117 | /* | ||
118 | * The function is called every time the topology of connected | ||
119 | * peers to a peer changes. | ||
120 | */ | ||
121 | int | ||
122 | statistics_iterator (void *cls, | ||
123 | const char *subsystem, | ||
124 | const char *name, | ||
125 | uint64_t value, | ||
126 | int is_persistent) | ||
127 | { | ||
128 | struct peerctx *p_ctx = (struct peerctx*) cls; | ||
129 | |||
130 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
131 | "Peer %d: %s = %llu\n", | ||
132 | p_ctx->index, | ||
133 | name, | ||
134 | (unsigned long long) value); | ||
135 | |||
136 | if (p_ctx->connections < value) | ||
137 | p_ctx->connections = value; | ||
138 | |||
139 | if ((THRESHOLD <= value) && (GNUNET_NO == p_ctx->reported)) | ||
140 | { | ||
141 | p_ctx->reported = GNUNET_YES; | ||
142 | checked_peers++; | ||
143 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
144 | "Peer %d successfully connected to at least %d peers once.\n", | ||
145 | p_ctx->index, | ||
146 | THRESHOLD); | ||
147 | |||
148 | if (checked_peers == NUM_PEERS) | ||
149 | { | ||
150 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
151 | "Test OK: All peers have connected to %d peers once.\n", | ||
152 | THRESHOLD); | ||
153 | result = GNUNET_YES; | ||
154 | GNUNET_SCHEDULER_shutdown (); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return GNUNET_YES; | ||
159 | } | ||
160 | |||
161 | |||
162 | static void * | ||
163 | ca_statistics (void *cls, | ||
164 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
165 | { | ||
166 | return GNUNET_STATISTICS_create ("topology", cfg); | ||
167 | } | ||
168 | |||
169 | |||
170 | void | ||
171 | da_statistics (void *cls, | ||
172 | void *op_result) | ||
173 | { | ||
174 | struct peerctx *p_ctx = (struct peerctx *) cls; | ||
175 | |||
176 | GNUNET_break (GNUNET_OK == GNUNET_STATISTICS_watch_cancel | ||
177 | (p_ctx->statistics, "topology", "# peers connected", | ||
178 | statistics_iterator, p_ctx)); | ||
179 | |||
180 | GNUNET_STATISTICS_destroy (p_ctx->statistics, GNUNET_NO); | ||
181 | p_ctx->statistics = NULL; | ||
182 | |||
183 | GNUNET_free (p_ctx); | ||
184 | } | ||
185 | |||
186 | |||
187 | static void | ||
188 | service_connect_complete (void *cls, | ||
189 | struct GNUNET_TESTBED_Operation *op, | ||
190 | void *ca_result, | ||
191 | const char *emsg) | ||
192 | { | ||
193 | int ret; | ||
194 | struct peerctx *p_ctx = (struct peerctx*) cls; | ||
195 | |||
196 | if (NULL == ca_result) | ||
197 | GNUNET_SCHEDULER_shutdown (); | ||
198 | |||
199 | p_ctx->statistics = ca_result; | ||
200 | |||
201 | ret = GNUNET_STATISTICS_watch (ca_result, | ||
202 | "topology", | ||
203 | "# peers connected", | ||
204 | statistics_iterator, | ||
205 | p_ctx); | ||
206 | |||
207 | if (GNUNET_NO == ret) | ||
208 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
209 | "call to GNUNET_STATISTICS_watch() failed\n"); | ||
210 | } | ||
211 | |||
212 | |||
213 | static void | ||
214 | notify_connect_complete (void *cls, | ||
215 | struct GNUNET_TESTBED_Operation *op, | ||
216 | const char *emsg) | ||
217 | { | ||
218 | GNUNET_TESTBED_operation_done (op); | ||
219 | if (NULL != emsg) | ||
220 | { | ||
221 | fprintf (stderr, "Failed to connect two peers: %s\n", emsg); | ||
222 | result = GNUNET_SYSERR; | ||
223 | GNUNET_SCHEDULER_shutdown (); | ||
224 | return; | ||
225 | } | ||
226 | connect_left--; | ||
227 | } | ||
228 | |||
229 | |||
230 | static void | ||
231 | do_connect (void *cls, | ||
232 | struct GNUNET_TESTBED_RunHandle *h, | ||
233 | unsigned int num_peers, | ||
234 | struct GNUNET_TESTBED_Peer **peers, | ||
235 | unsigned int links_succeeded, | ||
236 | unsigned int links_failed) | ||
237 | { | ||
238 | unsigned int i; | ||
239 | struct peerctx *p_ctx; | ||
240 | |||
241 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
242 | "Threshold is set to %d.\n", | ||
243 | THRESHOLD); | ||
244 | |||
245 | GNUNET_assert (NUM_PEERS == num_peers); | ||
246 | |||
247 | for (i = 0; i < NUM_PEERS; i++) | ||
248 | { | ||
249 | p_ctx = GNUNET_new (struct peerctx); | ||
250 | p_ctx->index = i; | ||
251 | p_ctx->connections = 0; | ||
252 | p_ctx->reported = GNUNET_NO; | ||
253 | |||
254 | if (i < NUM_PEERS - 1) | ||
255 | { | ||
256 | connect_left++; | ||
257 | GNUNET_TESTBED_overlay_connect (NULL, | ||
258 | ¬ify_connect_complete, NULL, | ||
259 | peers[i], peers[i + 1]); | ||
260 | } | ||
261 | |||
262 | op[i] = | ||
263 | GNUNET_TESTBED_service_connect (cls, | ||
264 | peers[i], | ||
265 | "statistics", | ||
266 | service_connect_complete, | ||
267 | p_ctx, /* cls of completion cb */ | ||
268 | ca_statistics, /* connect adapter */ | ||
269 | da_statistics, /* disconnect adapter */ | ||
270 | p_ctx); | ||
271 | } | ||
272 | |||
273 | GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); | ||
274 | timeout_tid = | ||
275 | GNUNET_SCHEDULER_add_delayed (TIMEOUT, | ||
276 | &timeout_task, | ||
277 | NULL); | ||
278 | } | ||
279 | |||
280 | |||
281 | int | ||
282 | main (int argc, char *argv[]) | ||
283 | { | ||
284 | result = GNUNET_SYSERR; | ||
285 | checked_peers = 0; | ||
286 | |||
287 | (void) GNUNET_TESTBED_test_run ("test-gnunet-daemon-topology", | ||
288 | "test_gnunet_daemon_topology_data.conf", | ||
289 | NUM_PEERS, | ||
290 | 0, NULL, NULL, | ||
291 | &do_connect, NULL); | ||
292 | GNUNET_DISK_purge_cfg_dir | ||
293 | ("test_gnunet_daemon_topology_data.conf", | ||
294 | "GNUNET_TEST_HOME"); | ||
295 | |||
296 | return (GNUNET_OK != result) ? 1 : 0; | ||
297 | } | ||
298 | |||
299 | |||
300 | /* end of test_gnunet_daemon_topology.c */ | ||
diff --git a/src/service/topology/test_gnunet_daemon_topology_data.conf b/src/service/topology/test_gnunet_daemon_topology_data.conf new file mode 100644 index 000000000..b1b3e9b88 --- /dev/null +++ b/src/service/topology/test_gnunet_daemon_topology_data.conf | |||
@@ -0,0 +1,34 @@ | |||
1 | [PATHS] | ||
2 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-topology/ | ||
3 | |||
4 | [transport] | ||
5 | PLUGINS = tcp | ||
6 | #PREFIX = xterm -e xterm -T transport -e gdb -x cmd --args | ||
7 | #PREFIX = valgrind --tool=memcheck --log-file=logs%p | ||
8 | |||
9 | [testbed] | ||
10 | OVERLAY_TOPOLOGY=LINE | ||
11 | |||
12 | [transport-tcp] | ||
13 | BINDTO = 127.0.0.1 | ||
14 | |||
15 | # Do not try to connect to external network | ||
16 | [hostlist] | ||
17 | OPTIONS = | ||
18 | |||
19 | [nat] | ||
20 | DISABLEV6 = YES | ||
21 | ENABLE_UPNP = NO | ||
22 | BEHIND_NAT = NO | ||
23 | ALLOW_NAT = NO | ||
24 | INTERNAL_ADDRESS = 127.0.0.1 | ||
25 | EXTERNAL_ADDRESS = 127.0.0.1 | ||
26 | USE_LOCALADDR = NO | ||
27 | USE_HOSTNAME = NO | ||
28 | |||
29 | [nse] | ||
30 | WORKBITS = 0 | ||
31 | |||
32 | [rps] | ||
33 | START_ON_DEMAND = NO | ||
34 | IMMEDIATE_START = NO | ||
diff --git a/src/service/topology/topology.conf b/src/service/topology/topology.conf new file mode 100644 index 000000000..9bcfb479c --- /dev/null +++ b/src/service/topology/topology.conf | |||
@@ -0,0 +1,5 @@ | |||
1 | [topology] | ||
2 | IMMEDIATE_START = YES | ||
3 | NOARMBIND = YES | ||
4 | TARGET-CONNECTION-COUNT = 16 | ||
5 | BINARY = gnunet-daemon-topology | ||