aboutsummaryrefslogtreecommitdiff
path: root/src/topology
diff options
context:
space:
mode:
Diffstat (limited to 'src/topology')
-rw-r--r--src/topology/.gitignore2
-rw-r--r--src/topology/Makefile.am61
-rw-r--r--src/topology/friends.c246
-rw-r--r--src/topology/gnunet-daemon-topology.c1155
-rw-r--r--src/topology/test_gnunet_daemon_topology.c298
-rw-r--r--src/topology/test_gnunet_daemon_topology_data.conf34
-rw-r--r--src/topology/topology.conf8
7 files changed, 0 insertions, 1804 deletions
diff --git a/src/topology/.gitignore b/src/topology/.gitignore
deleted file mode 100644
index cfa95ec7e..000000000
--- a/src/topology/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
1gnunet-daemon-topology
2test_gnunet_daemon_topology
diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am
deleted file mode 100644
index e5920116b..000000000
--- a/src/topology/Makefile.am
+++ /dev/null
@@ -1,61 +0,0 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6endif
7
8pkgcfgdir= $(pkgdatadir)/config.d/
9
10libexecdir= $(pkglibdir)/libexec/
11
12dist_pkgcfg_DATA = \
13 topology.conf
14
15
16lib_LTLIBRARIES = libgnunetfriends.la
17
18libgnunetfriends_la_SOURCES = \
19 friends.c
20libgnunetfriends_la_LIBADD = \
21 $(top_builddir)/src/util/libgnunetutil.la \
22 $(GN_LIBINTL) $(XLIB)
23libgnunetfriends_la_LDFLAGS = \
24 $(GN_LIB_LDFLAGS) \
25 -version-info 0:0:0
26
27
28libexec_PROGRAMS = \
29 gnunet-daemon-topology
30
31gnunet_daemon_topology_SOURCES = \
32 gnunet-daemon-topology.c
33gnunet_daemon_topology_LDADD = \
34 libgnunetfriends.la \
35 $(top_builddir)/src/core/libgnunetcore.la \
36 $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \
37 $(top_builddir)/src/statistics/libgnunetstatistics.la \
38 $(top_builddir)/src/transport/libgnunettransport.la \
39 $(top_builddir)/src/ats/libgnunetats.la \
40 $(top_builddir)/src/hello/libgnunethello.la \
41 $(top_builddir)/src/util/libgnunetutil.la \
42 $(GN_LIBINTL)
43
44
45check_PROGRAMS = \
46 test_gnunet_daemon_topology
47
48if ENABLE_TEST_RUN
49AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
50TESTS = $(check_PROGRAMS)
51endif
52
53test_gnunet_daemon_topology_SOURCES = \
54 test_gnunet_daemon_topology.c
55test_gnunet_daemon_topology_LDADD = \
56 $(top_builddir)/src/testbed/libgnunettestbed.la \
57 $(top_builddir)/src/statistics/libgnunetstatistics.la \
58 $(top_builddir)/src/util/libgnunetutil.la
59
60EXTRA_DIST = \
61 test_gnunet_daemon_topology_data.conf
diff --git a/src/topology/friends.c b/src/topology/friends.c
deleted file mode 100644
index 65c7e81d7..000000000
--- a/src/topology/friends.c
+++ /dev/null
@@ -1,246 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 Christian Grothoff
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/friends.c
23 * @brief library to read and write the FRIENDS file
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_friends_lib.h"
28
29
30/**
31 * Parse the FRIENDS file.
32 *
33 * @param cfg our configuration
34 * @param cb function to call on each friend found
35 * @param cb_cls closure for @a cb
36 * @return #GNUNET_OK on success, #GNUNET_SYSERR on parsing errors
37 */
38int
39GNUNET_FRIENDS_parse (const struct GNUNET_CONFIGURATION_Handle *cfg,
40 GNUNET_FRIENDS_Callback cb,
41 void *cb_cls)
42{
43 char *fn;
44 char *data;
45 size_t pos;
46 size_t start;
47 struct GNUNET_PeerIdentity pid;
48 uint64_t fsize;
49 ssize_t ssize;
50
51 if (GNUNET_OK !=
52 GNUNET_CONFIGURATION_get_value_filename (cfg,
53 "TOPOLOGY",
54 "FRIENDS",
55 &fn))
56 {
57 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
58 "topology",
59 "FRIENDS");
60 return GNUNET_SYSERR;
61 }
62 if ((GNUNET_OK !=
63 GNUNET_DISK_file_test (fn)) &&
64 (GNUNET_OK !=
65 GNUNET_DISK_fn_write (fn,
66 NULL,
67 0,
68 GNUNET_DISK_PERM_USER_READ
69 | GNUNET_DISK_PERM_USER_WRITE
70 | GNUNET_DISK_OPEN_CREATE)))
71 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
72 "write",
73 fn);
74 if ((GNUNET_OK !=
75 GNUNET_DISK_file_size (fn,
76 &fsize,
77 GNUNET_NO,
78 GNUNET_YES)) ||
79 (0 == fsize))
80 {
81 GNUNET_free (fn);
82 return GNUNET_OK;
83 }
84 data = GNUNET_malloc_large (fsize);
85 if (NULL == data)
86 {
87 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "malloc");
88 GNUNET_free (fn);
89 return GNUNET_SYSERR;
90 }
91 ssize = GNUNET_DISK_fn_read (fn,
92 data,
93 fsize);
94 if ((ssize < 0) ||
95 (fsize != (uint64_t) ssize))
96 {
97 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
98 "read",
99 "fn");
100 GNUNET_free (fn);
101 GNUNET_free (data);
102 return GNUNET_SYSERR;
103 }
104 start = 0;
105 pos = 0;
106 while (pos < fsize)
107 {
108 while ((pos < fsize) &&
109 (! isspace ((unsigned char) data[pos])))
110 pos++;
111 if (GNUNET_OK !=
112 GNUNET_CRYPTO_eddsa_public_key_from_string (&data[start],
113 pos - start,
114 &pid.public_key))
115 {
116 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
117 _ (
118 "Syntax error in FRIENDS file at offset %llu, skipping bytes `%.*s'.\n"),
119 (unsigned long long) pos,
120 (int) (pos - start),
121 &data[start]);
122 pos++;
123 start = pos;
124 continue;
125 }
126 pos++;
127 start = pos;
128 cb (cb_cls, &pid);
129 }
130 GNUNET_free (data);
131 GNUNET_free (fn);
132 return GNUNET_OK;
133}
134
135
136/**
137 * Handle for writing a friends file.
138 */
139struct GNUNET_FRIENDS_Writer
140{
141 /**
142 * Handle to the file.
143 */
144 struct GNUNET_DISK_FileHandle *fh;
145};
146
147
148/**
149 * Start writing a fresh FRIENDS file. Will make a backup of the
150 * old one.
151 *
152 * @param cfg configuration to use.
153 * @return NULL on error
154 */
155struct GNUNET_FRIENDS_Writer *
156GNUNET_FRIENDS_write_start (const struct GNUNET_CONFIGURATION_Handle *cfg)
157{
158 struct GNUNET_FRIENDS_Writer *w;
159 char *fn;
160
161 if (GNUNET_OK !=
162 GNUNET_CONFIGURATION_get_value_filename (cfg, "TOPOLOGY", "FRIENDS", &fn))
163 {
164 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
165 "topology", "FRIENDS");
166 return NULL;
167 }
168 if (GNUNET_OK !=
169 GNUNET_DISK_directory_create_for_file (fn))
170 {
171 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
172 _ ("Directory for file `%s' does not seem to be writable.\n"),
173 fn);
174 GNUNET_free (fn);
175 return NULL;
176 }
177 if (GNUNET_OK == GNUNET_DISK_file_test (fn))
178 GNUNET_DISK_file_backup (fn);
179 w = GNUNET_new (struct GNUNET_FRIENDS_Writer);
180 w->fh = GNUNET_DISK_file_open (fn,
181 GNUNET_DISK_OPEN_CREATE
182 | GNUNET_DISK_OPEN_WRITE
183 | GNUNET_DISK_OPEN_FAILIFEXISTS,
184 GNUNET_DISK_PERM_USER_READ);
185 GNUNET_free (fn);
186 if (NULL == w->fh)
187 {
188 GNUNET_free (w);
189 return NULL;
190 }
191 return w;
192}
193
194
195/**
196 * Finish writing out the friends file.
197 *
198 * @param w write handle
199 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
200 */
201int
202GNUNET_FRIENDS_write_stop (struct GNUNET_FRIENDS_Writer *w)
203{
204 int ret;
205
206 ret = GNUNET_DISK_file_close (w->fh);
207 GNUNET_free (w);
208 return ret;
209}
210
211
212/**
213 * Add a friend to the friends file.
214 *
215 * @param w write handle
216 * @param friend_id friend to add
217 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
218 */
219int
220GNUNET_FRIENDS_write (struct GNUNET_FRIENDS_Writer *w,
221 const struct GNUNET_PeerIdentity *friend_id)
222{
223 char *buf;
224 char *ret;
225 size_t slen;
226
227 ret = GNUNET_CRYPTO_eddsa_public_key_to_string (&friend_id->public_key);
228 GNUNET_asprintf (&buf,
229 "%s\n",
230 ret);
231 GNUNET_free (ret);
232 slen = strlen (buf);
233 if (slen !=
234 GNUNET_DISK_file_write (w->fh,
235 buf,
236 slen))
237 {
238 GNUNET_free (buf);
239 return GNUNET_SYSERR;
240 }
241 GNUNET_free (buf);
242 return GNUNET_OK;
243}
244
245
246/* end of friends.c */
diff --git a/src/topology/gnunet-daemon-topology.c b/src/topology/gnunet-daemon-topology.c
deleted file mode 100644
index b380c0bd4..000000000
--- a/src/topology/gnunet-daemon-topology.c
+++ /dev/null
@@ -1,1155 +0,0 @@
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 three functions:
27 * - suggesting to ATS which peers we might want to connect to
28 * - enforcing the F2F restrictions (by blacklisting)
29 * - gossping HELLOs
30 *
31 * All three require similar information (who are our friends
32 * impacts connectivity suggestions; connectivity suggestions
33 * should consider blacklisting; connectivity suggestions
34 * should consider available/known HELLOs; gossip requires
35 * connectivity data; connectivity suggestions require
36 * connectivity data), which is why they are combined in this
37 * program.
38 */
39#include "platform.h"
40#include "gnunet_util_lib.h"
41#include "gnunet_friends_lib.h"
42#include "gnunet_constants.h"
43#include "gnunet_core_service.h"
44#include "gnunet_protocols.h"
45#include "gnunet_peerinfo_service.h"
46#include "gnunet_statistics_service.h"
47#include "gnunet_transport_service.h"
48#include "gnunet_ats_service.h"
49
50
51/**
52 * At what frequency do we sent HELLOs to a peer?
53 */
54#define HELLO_ADVERTISEMENT_MIN_FREQUENCY \
55 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
56
57/**
58 * After what time period do we expire the HELLO Bloom filter?
59 */
60#define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY \
61 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
62
63
64/**
65 * Record for neighbours, friends and blacklisted peers.
66 */
67struct Peer
68{
69 /**
70 * Which peer is this entry about?
71 */
72 struct GNUNET_PeerIdentity pid;
73
74 /**
75 * Our handle for transmitting to this peer; NULL
76 * if peer is not connected.
77 */
78 struct GNUNET_MQ_Handle *mq;
79
80 /**
81 * Pointer to the HELLO message of this peer; can be NULL.
82 */
83 struct GNUNET_HELLO_Message *hello;
84
85 /**
86 * Bloom filter used to mark which peers already got the HELLO
87 * from this peer.
88 */
89 struct GNUNET_CONTAINER_BloomFilter *filter;
90
91 /**
92 * Next time we are allowed to transmit a HELLO to this peer?
93 */
94 struct GNUNET_TIME_Absolute next_hello_allowed;
95
96 /**
97 * When should we reset the bloom filter of this entry?
98 */
99 struct GNUNET_TIME_Absolute filter_expiration;
100
101 /**
102 * ID of task we use to wait for the time to send the next HELLO
103 * to this peer.
104 */
105 struct GNUNET_SCHEDULER_Task *hello_delay_task;
106
107 /**
108 * Handle for our connectivity suggestion for this peer.
109 */
110 struct GNUNET_ATS_ConnectivitySuggestHandle *sh;
111
112 /**
113 * How much would we like to connect to this peer?
114 */
115 uint32_t strength;
116
117 /**
118 * Is this peer listed here because it is a friend?
119 */
120 int is_friend;
121};
122
123
124/**
125 * Our peerinfo notification context. We use notification
126 * to instantly learn about new peers as they are discovered.
127 */
128static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
129
130/**
131 * Our configuration.
132 */
133static const struct GNUNET_CONFIGURATION_Handle *cfg;
134
135/**
136 * Handle to the CORE service.
137 */
138static struct GNUNET_CORE_Handle *handle;
139
140/**
141 * Handle to the PEERINFO service.
142 */
143static struct GNUNET_PEERINFO_Handle *pi;
144
145/**
146 * Handle to the ATS service.
147 */
148static struct GNUNET_ATS_ConnectivityHandle *ats;
149
150/**
151 * Identity of this peer.
152 */
153static struct GNUNET_PeerIdentity my_identity;
154
155/**
156 * All of our friends, all of our current neighbours and all peers for
157 * which we have HELLOs. So pretty much everyone. Maps peer identities
158 * to `struct Peer *` values.
159 */
160static struct GNUNET_CONTAINER_MultiPeerMap *peers;
161
162/**
163 * Handle for reporting statistics.
164 */
165static struct GNUNET_STATISTICS_Handle *stats;
166
167/**
168 * Blacklist (NULL if we have none).
169 */
170static struct GNUNET_TRANSPORT_Blacklist *blacklist;
171
172/**
173 * Task scheduled to asynchronously reconsider adding/removing
174 * peer connectivity suggestions.
175 */
176static struct GNUNET_SCHEDULER_Task *add_task;
177
178/**
179 * Active HELLO offering to transport service.
180 */
181static struct GNUNET_TRANSPORT_OfferHelloHandle *oh;
182
183/**
184 * Flag to disallow non-friend connections (pure F2F mode).
185 */
186static int friends_only;
187
188/**
189 * Minimum number of friends to have in the
190 * connection set before we allow non-friends.
191 */
192static unsigned int minimum_friend_count;
193
194/**
195 * Number of peers (friends and others) that we are currently connected to.
196 */
197static unsigned int connection_count;
198
199/**
200 * Target number of connections.
201 */
202static unsigned int target_connection_count;
203
204/**
205 * Number of friends that we are currently connected to.
206 */
207static unsigned int friend_count;
208
209
210/**
211 * Function that decides if a connection is acceptable or not.
212 * If we have a blacklist, only friends are allowed, so the check
213 * is rather simple.
214 *
215 * @param cls closure
216 * @param pid peer to approve or disapprove
217 * @return #GNUNET_OK if the connection is allowed
218 */
219static int
220blacklist_check (void *cls, const struct GNUNET_PeerIdentity *pid)
221{
222 struct Peer *pos;
223
224 pos = GNUNET_CONTAINER_multipeermap_get (peers, pid);
225 if ((NULL != pos) && (GNUNET_YES == pos->is_friend))
226 return GNUNET_OK;
227 GNUNET_STATISTICS_update (stats,
228 gettext_noop ("# peers blacklisted"),
229 1,
230 GNUNET_NO);
231 return GNUNET_SYSERR;
232}
233
234
235/**
236 * Whitelist all peers that we blacklisted; we've passed
237 * the minimum number of friends.
238 */
239static void
240whitelist_peers ()
241{
242 if (NULL != blacklist)
243 {
244 GNUNET_TRANSPORT_blacklist_cancel (blacklist);
245 blacklist = NULL;
246 }
247}
248
249
250/**
251 * Free all resources associated with the given peer.
252 *
253 * @param cls closure (not used)
254 * @param pid identity of the peer
255 * @param value peer to free
256 * @return #GNUNET_YES (always: continue to iterate)
257 */
258static int
259free_peer (void *cls, const struct GNUNET_PeerIdentity *pid, void *value)
260{
261 struct Peer *pos = value;
262
263 GNUNET_break (NULL == pos->mq);
264 GNUNET_break (GNUNET_OK ==
265 GNUNET_CONTAINER_multipeermap_remove (peers, pid, pos));
266 if (NULL != pos->hello_delay_task)
267 {
268 GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
269 pos->hello_delay_task = NULL;
270 }
271 if (NULL != pos->sh)
272 {
273 GNUNET_ATS_connectivity_suggest_cancel (pos->sh);
274 pos->sh = NULL;
275 }
276 if (NULL != pos->hello)
277 {
278 GNUNET_free (pos->hello);
279 pos->hello = NULL;
280 }
281 if (NULL != pos->filter)
282 {
283 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
284 pos->filter = NULL;
285 }
286 GNUNET_free (pos);
287 return GNUNET_YES;
288}
289
290
291/**
292 * Recalculate how much we want to be connected to the specified peer
293 * and let ATS know about the result.
294 *
295 * @param pos peer to consider connecting to
296 */
297static void
298attempt_connect (struct Peer *pos)
299{
300 uint32_t strength;
301
302 if (0 == GNUNET_memcmp (&my_identity, &pos->pid))
303 return; /* This is myself, nothing to do. */
304 if (connection_count < target_connection_count)
305 strength = 1;
306 else
307 strength = 0;
308 if ((friend_count < minimum_friend_count) || (GNUNET_YES == friends_only))
309 {
310 if (pos->is_friend)
311 strength += 10; /* urgently needed */
312 else
313 strength = 0; /* disallowed */
314 }
315 if (pos->is_friend)
316 strength *= 2; /* friends always count more */
317 if (NULL != pos->mq)
318 strength *= 2; /* existing connections preferred */
319 if (strength == pos->strength)
320 return; /* nothing to do */
321 if (NULL != pos->sh)
322 {
323 GNUNET_ATS_connectivity_suggest_cancel (pos->sh);
324 pos->sh = NULL;
325 }
326 pos->strength = strength;
327 if (0 != strength)
328 {
329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330 "Asking to connect to `%s' with strength %u\n",
331 GNUNET_i2s (&pos->pid),
332 (unsigned int) strength);
333 GNUNET_STATISTICS_update (stats,
334 gettext_noop ("# connect requests issued to ATS"),
335 1,
336 GNUNET_NO);
337 pos->sh = GNUNET_ATS_connectivity_suggest (ats, &pos->pid, strength);
338 }
339}
340
341
342/**
343 * Create a new entry in the peer list.
344 *
345 * @param peer identity of the new entry
346 * @param hello hello message, can be NULL
347 * @param is_friend is the new entry for a friend?
348 * @return the new entry
349 */
350static struct Peer *
351make_peer (const struct GNUNET_PeerIdentity *peer,
352 const struct GNUNET_HELLO_Message *hello,
353 int is_friend)
354{
355 struct Peer *ret;
356
357 ret = GNUNET_new (struct Peer);
358 ret->pid = *peer;
359 ret->is_friend = is_friend;
360 if (NULL != hello)
361 {
362 ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
363 GNUNET_memcpy (ret->hello, hello, GNUNET_HELLO_size (hello));
364 }
365 GNUNET_break (GNUNET_OK ==
366 GNUNET_CONTAINER_multipeermap_put (
367 peers,
368 peer,
369 ret,
370 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
371 return ret;
372}
373
374
375/**
376 * Setup bloom filter for the given peer entry.
377 *
378 * @param peer entry to initialize
379 */
380static void
381setup_filter (struct Peer *peer)
382{
383 struct GNUNET_HashCode hc;
384
385 /* 2^{-5} chance of not sending a HELLO to a peer is
386 * acceptably small (if the filter is 50% full);
387 * 64 bytes of memory are small compared to the rest
388 * of the data structure and would only really become
389 * "useless" once a HELLO has been passed on to ~100
390 * other peers, which is likely more than enough in
391 * any case; hence 64, 5 as bloomfilter parameters. */peer->filter = GNUNET_CONTAINER_bloomfilter_init (NULL, 64, 5);
392 peer->filter_expiration =
393 GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
394 /* never send a peer its own HELLO */
395 GNUNET_CRYPTO_hash (&peer->pid, sizeof(struct GNUNET_PeerIdentity), &hc);
396 GNUNET_CONTAINER_bloomfilter_add (peer->filter, &hc);
397}
398
399
400/**
401 * Closure for #find_advertisable_hello().
402 */
403struct FindAdvHelloContext
404{
405 /**
406 * Peer we want to advertise to.
407 */
408 struct Peer *peer;
409
410 /**
411 * Where to store the result (peer selected for advertising).
412 */
413 struct Peer *result;
414
415 /**
416 * Maximum HELLO size we can use right now.
417 */
418 size_t max_size;
419
420 struct GNUNET_TIME_Relative next_adv;
421};
422
423
424/**
425 * Find a peer that would be reasonable for advertising.
426 *
427 * @param cls closure
428 * @param pid identity of a peer
429 * @param value 'struct Peer*' for the peer we are considering
430 * @return #GNUNET_YES (continue iteration)
431 */
432static int
433find_advertisable_hello (void *cls,
434 const struct GNUNET_PeerIdentity *pid,
435 void *value)
436{
437 struct FindAdvHelloContext *fah = cls;
438 struct Peer *pos = value;
439 struct GNUNET_TIME_Relative rst_time;
440 struct GNUNET_HashCode hc;
441 size_t hs;
442
443 if (pos == fah->peer)
444 return GNUNET_YES;
445 if (pos->hello == NULL)
446 return GNUNET_YES;
447 rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
448 if (0 == rst_time.rel_value_us)
449 {
450 /* time to discard... */
451 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
452 setup_filter (pos);
453 }
454 fah->next_adv = GNUNET_TIME_relative_min (rst_time, fah->next_adv);
455 hs = GNUNET_HELLO_size (pos->hello);
456 if (hs > fah->max_size)
457 return GNUNET_YES;
458 GNUNET_CRYPTO_hash (&fah->peer->pid,
459 sizeof(struct GNUNET_PeerIdentity),
460 &hc);
461 if (GNUNET_NO == GNUNET_CONTAINER_bloomfilter_test (pos->filter, &hc))
462 fah->result = pos;
463 return GNUNET_YES;
464}
465
466
467/**
468 * Calculate when we would like to send the next HELLO to this
469 * peer and ask for it.
470 *
471 * @param cls for which peer to schedule the HELLO
472 */
473static void
474schedule_next_hello (void *cls)
475{
476 struct Peer *pl = cls;
477 struct FindAdvHelloContext fah;
478 struct GNUNET_MQ_Envelope *env;
479 size_t want;
480 struct GNUNET_TIME_Relative delay;
481 struct GNUNET_HashCode hc;
482
483 pl->hello_delay_task = NULL;
484 GNUNET_assert (NULL != pl->mq);
485 /* find applicable HELLOs */
486 fah.peer = pl;
487 fah.result = NULL;
488 fah.max_size = GNUNET_MAX_MESSAGE_SIZE - 1;
489 fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
490 GNUNET_CONTAINER_multipeermap_iterate (peers, &find_advertisable_hello, &fah);
491 pl->hello_delay_task =
492 GNUNET_SCHEDULER_add_delayed (fah.next_adv, &schedule_next_hello, pl);
493 if (NULL == fah.result)
494 return;
495 delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
496 if (0 != delay.rel_value_us)
497 return;
498
499 want = GNUNET_HELLO_size (fah.result->hello);
500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501 "Sending HELLO with %u bytes",
502 (unsigned int) want);
503 env = GNUNET_MQ_msg_copy (&fah.result->hello->header);
504 GNUNET_MQ_send (pl->mq, env);
505
506 /* avoid sending this one again soon */
507 GNUNET_CRYPTO_hash (&pl->pid, sizeof(struct GNUNET_PeerIdentity), &hc);
508 GNUNET_CONTAINER_bloomfilter_add (fah.result->filter, &hc);
509
510 GNUNET_STATISTICS_update (stats,
511 gettext_noop ("# HELLO messages gossipped"),
512 1,
513 GNUNET_NO);
514 /* prepare to send the next one */
515 pl->next_hello_allowed =
516 GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
517 if (NULL != pl->hello_delay_task)
518 GNUNET_SCHEDULER_cancel (pl->hello_delay_task);
519 pl->hello_delay_task = GNUNET_SCHEDULER_add_now (&schedule_next_hello, pl);
520}
521
522
523/**
524 * Cancel existing requests for sending HELLOs to this peer
525 * and recalculate when we should send HELLOs to it based
526 * on our current state (something changed!).
527 *
528 * @param cls closure `struct Peer` to skip, or NULL
529 * @param pid identity of a peer
530 * @param value `struct Peer *` for the peer
531 * @return #GNUNET_YES (always)
532 */
533static int
534reschedule_hellos (void *cls,
535 const struct GNUNET_PeerIdentity *pid,
536 void *value)
537{
538 struct Peer *peer = value;
539 struct Peer *skip = cls;
540
541 if (skip == peer)
542 return GNUNET_YES;
543 if (NULL == peer->mq)
544 return GNUNET_YES;
545 if (NULL != peer->hello_delay_task)
546 {
547 GNUNET_SCHEDULER_cancel (peer->hello_delay_task);
548 peer->hello_delay_task = NULL;
549 }
550 peer->hello_delay_task =
551 GNUNET_SCHEDULER_add_now (&schedule_next_hello, peer);
552 return GNUNET_YES;
553}
554
555
556/**
557 * Method called whenever a peer connects.
558 *
559 * @param cls closure
560 * @param peer peer identity this notification is about
561 * @param mq message queue for communicating with @a peer
562 * @return our `struct Peer` for @a peer
563 */
564static void *
565connect_notify (void *cls,
566 const struct GNUNET_PeerIdentity *peer,
567 struct GNUNET_MQ_Handle *mq)
568{
569 struct Peer *pos;
570
571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572 "Core told us that we are connecting to `%s'\n",
573 GNUNET_i2s (peer));
574 if (0 == GNUNET_memcmp (&my_identity, peer))
575 return NULL;
576 GNUNET_MQ_set_options (mq, GNUNET_MQ_PRIO_BEST_EFFORT);
577 connection_count++;
578 GNUNET_STATISTICS_set (stats,
579 gettext_noop ("# peers connected"),
580 connection_count,
581 GNUNET_NO);
582 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
583 if (NULL == pos)
584 {
585 pos = make_peer (peer, NULL, GNUNET_NO);
586 }
587 else
588 {
589 GNUNET_assert (NULL == pos->mq);
590 }
591 pos->mq = mq;
592 if (pos->is_friend)
593 {
594 friend_count++;
595 if ((friend_count == minimum_friend_count) && (GNUNET_YES != friends_only))
596 whitelist_peers ();
597 GNUNET_STATISTICS_set (stats,
598 gettext_noop ("# friends connected"),
599 friend_count,
600 GNUNET_NO);
601 }
602 reschedule_hellos (NULL, peer, pos);
603 return pos;
604}
605
606
607/**
608 * Try to add more peers to our connection set.
609 *
610 * @param cls closure, not used
611 * @param pid identity of a peer
612 * @param value `struct Peer *` for the peer
613 * @return #GNUNET_YES (continue to iterate)
614 */
615static int
616try_add_peers (void *cls, const struct GNUNET_PeerIdentity *pid, void *value)
617{
618 struct Peer *pos = value;
619
620 attempt_connect (pos);
621 return GNUNET_YES;
622}
623
624
625/**
626 * Add peers and schedule connection attempt
627 *
628 * @param cls unused, NULL
629 */
630static void
631add_peer_task (void *cls)
632{
633 add_task = NULL;
634
635 GNUNET_CONTAINER_multipeermap_iterate (peers, &try_add_peers, NULL);
636}
637
638
639/**
640 * Method called whenever a peer disconnects.
641 *
642 * @param cls closure
643 * @param peer peer identity this notification is about
644 * @param internal_cls the `struct Peer` for this peer
645 */
646static void
647disconnect_notify (void *cls,
648 const struct GNUNET_PeerIdentity *peer,
649 void *internal_cls)
650{
651 struct Peer *pos = internal_cls;
652
653 if (NULL == pos)
654 return; /* myself, we're shutting down */
655 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
656 "Core told us that we disconnected from `%s'\n",
657 GNUNET_i2s (peer));
658 if (NULL == pos->mq)
659 {
660 GNUNET_break (0);
661 return;
662 }
663 pos->mq = NULL;
664 connection_count--;
665 if (NULL != pos->hello_delay_task)
666 {
667 GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
668 pos->hello_delay_task = NULL;
669 }
670 GNUNET_STATISTICS_set (stats,
671 gettext_noop ("# peers connected"),
672 connection_count,
673 GNUNET_NO);
674 if (pos->is_friend)
675 {
676 friend_count--;
677 GNUNET_STATISTICS_set (stats,
678 gettext_noop ("# friends connected"),
679 friend_count,
680 GNUNET_NO);
681 }
682 if (((connection_count < target_connection_count) ||
683 (friend_count < minimum_friend_count)) &&
684 (NULL == add_task))
685 add_task = GNUNET_SCHEDULER_add_now (&add_peer_task, NULL);
686 if ((friend_count < minimum_friend_count) && (NULL == blacklist))
687 blacklist = GNUNET_TRANSPORT_blacklist (cfg, &blacklist_check, NULL);
688}
689
690
691/**
692 * Iterator called on each address.
693 *
694 * @param cls flag that we will set if we see any addresses
695 * @param address the address of the peer
696 * @param expiration when will the given address expire
697 * @return #GNUNET_SYSERR always, to terminate iteration
698 */
699static int
700address_iterator (void *cls,
701 const struct GNUNET_HELLO_Address *address,
702 struct GNUNET_TIME_Absolute expiration)
703{
704 int *flag = cls;
705
706 *flag = GNUNET_YES;
707 return GNUNET_SYSERR;
708}
709
710
711/**
712 * We've gotten a HELLO from another peer. Consider it for
713 * advertising.
714 *
715 * @param hello the HELLO we got
716 */
717static void
718consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
719{
720 int have_address;
721 struct GNUNET_PeerIdentity pid;
722 struct GNUNET_TIME_Absolute dt;
723 struct GNUNET_HELLO_Message *nh;
724 struct Peer *peer;
725 uint16_t size;
726
727 if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
728 {
729 GNUNET_break (0);
730 return;
731 }
732 if (0 == GNUNET_memcmp (&pid, &my_identity))
733 return; /* that's me! */
734 have_address = GNUNET_NO;
735 GNUNET_HELLO_iterate_addresses (hello,
736 GNUNET_NO,
737 &address_iterator,
738 &have_address);
739 if (GNUNET_NO == have_address)
740 return; /* no point in advertising this one... */
741 peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
742 if (NULL == peer)
743 {
744 peer = make_peer (&pid, hello, GNUNET_NO);
745 }
746 else if (NULL != peer->hello)
747 {
748 dt = GNUNET_HELLO_equals (peer->hello, hello, GNUNET_TIME_absolute_get ());
749 if (dt.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
750 return; /* nothing new here */
751 }
752 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
753 "Found HELLO from peer `%s' for advertising\n",
754 GNUNET_i2s (&pid));
755 if (NULL != peer->hello)
756 {
757 nh = GNUNET_HELLO_merge (peer->hello, hello);
758 GNUNET_free (peer->hello);
759 peer->hello = nh;
760 }
761 else
762 {
763 size = GNUNET_HELLO_size (hello);
764 peer->hello = GNUNET_malloc (size);
765 GNUNET_memcpy (peer->hello, hello, size);
766 }
767 if (NULL != peer->filter)
768 {
769 GNUNET_CONTAINER_bloomfilter_free (peer->filter);
770 peer->filter = NULL;
771 }
772 setup_filter (peer);
773 /* since we have a new HELLO to pick from, re-schedule all
774 * HELLO requests that are not bound by the HELLO send rate! */
775 GNUNET_CONTAINER_multipeermap_iterate (peers, &reschedule_hellos, peer);
776}
777
778
779/**
780 * PEERINFO calls this function to let us know about a possible peer
781 * that we might want to connect to.
782 *
783 * @param cls closure (not used)
784 * @param peer potential peer to connect to
785 * @param hello HELLO for this peer (or NULL)
786 * @param err_msg NULL if successful, otherwise contains error message
787 */
788static void
789process_peer (void *cls,
790 const struct GNUNET_PeerIdentity *peer,
791 const struct GNUNET_HELLO_Message *hello,
792 const char *err_msg)
793{
794 struct Peer *pos;
795
796 if (NULL != err_msg)
797 {
798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799 _ ("Error in communication with PEERINFO service: %s\n"),
800 err_msg);
801 GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
802 peerinfo_notify =
803 GNUNET_PEERINFO_notify (cfg, GNUNET_NO, &process_peer, NULL);
804 return;
805 }
806 GNUNET_assert (NULL != peer);
807 if (0 == GNUNET_memcmp (&my_identity, peer))
808 return; /* that's me! */
809 if (NULL == hello)
810 {
811 /* free existing HELLO, if any */
812 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
813 if (NULL != pos)
814 {
815 GNUNET_free (pos->hello);
816 pos->hello = NULL;
817 if (NULL != pos->filter)
818 {
819 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
820 pos->filter = NULL;
821 }
822 if ((NULL == pos->mq) && (GNUNET_NO == pos->is_friend))
823 free_peer (NULL, &pos->pid, pos);
824 }
825 return;
826 }
827 consider_for_advertising (hello);
828 pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
829 if (NULL == pos)
830 pos = make_peer (peer, hello, GNUNET_NO);
831 attempt_connect (pos);
832}
833
834
835/**
836 * Function called after #GNUNET_CORE_connect has succeeded
837 * (or failed for good).
838 *
839 * @param cls closure
840 * @param my_id ID of this peer, NULL if we failed
841 */
842static void
843core_init (void *cls, const struct GNUNET_PeerIdentity *my_id)
844{
845 if (NULL == my_id)
846 {
847 GNUNET_log (
848 GNUNET_ERROR_TYPE_ERROR,
849 _ ("Failed to connect to core service, can not manage topology!\n"));
850 GNUNET_SCHEDULER_shutdown ();
851 return;
852 }
853 my_identity = *my_id;
854 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "I am peer `%s'\n", GNUNET_i2s (my_id));
855 peerinfo_notify =
856 GNUNET_PEERINFO_notify (cfg, GNUNET_NO, &process_peer, NULL);
857}
858
859
860/**
861 * Process friend found in FRIENDS file.
862 *
863 * @param cls pointer to an `unsigned int` to be incremented per friend found
864 * @param pid identity of the friend
865 */
866static void
867handle_friend (void *cls, const struct GNUNET_PeerIdentity *pid)
868{
869 unsigned int *entries_found = cls;
870 struct Peer *fl;
871
872 if (0 == GNUNET_memcmp (pid, &my_identity))
873 {
874 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
875 _ ("Found myself `%s' in friend list (useless, ignored)\n"),
876 GNUNET_i2s (pid));
877 return;
878 }
879 (*entries_found)++;
880 fl = make_peer (pid, NULL, GNUNET_YES);
881 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
882 _ ("Found friend `%s' in configuration\n"),
883 GNUNET_i2s (&fl->pid));
884}
885
886
887/**
888 * Read the friends file.
889 */
890static void
891read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
892{
893 unsigned int entries_found;
894
895 entries_found = 0;
896 if (GNUNET_OK != GNUNET_FRIENDS_parse (cfg, &handle_friend, &entries_found))
897 {
898 if ((GNUNET_YES == friends_only) || (minimum_friend_count > 0))
899 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
900 _ ("Encountered errors parsing friends list!\n"));
901 }
902 GNUNET_STATISTICS_update (stats,
903 gettext_noop ("# friends in configuration"),
904 entries_found,
905 GNUNET_NO);
906 if ((minimum_friend_count > entries_found) && (GNUNET_NO == friends_only))
907 {
908 GNUNET_log (
909 GNUNET_ERROR_TYPE_WARNING,
910 _ (
911 "Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
912 }
913 if ((minimum_friend_count > target_connection_count) &&
914 (GNUNET_NO == friends_only))
915 {
916 GNUNET_log (
917 GNUNET_ERROR_TYPE_WARNING,
918 _ (
919 "More friendly connections required than target total number of connections.\n"));
920 }
921}
922
923
924/**
925 * This function is called whenever an encrypted HELLO message is
926 * received.
927 *
928 * @param cls closure with the peer identity of the sender
929 * @param message the actual HELLO message
930 * @return #GNUNET_OK if @a message is well-formed
931 * #GNUNET_SYSERR if @a message is invalid
932 */
933static int
934check_hello (void *cls, const struct GNUNET_HELLO_Message *message)
935{
936 struct GNUNET_PeerIdentity pid;
937
938 if (GNUNET_OK != GNUNET_HELLO_get_id (message, &pid))
939 {
940 GNUNET_break_op (0);
941 return GNUNET_SYSERR;
942 }
943 return GNUNET_OK;
944}
945
946
947/**
948 * This function is called whenever an encrypted HELLO message is
949 * received.
950 *
951 * @param cls closure with the peer identity of the sender
952 * @param message the actual HELLO message
953 */
954static void
955handle_hello (void *cls, const struct GNUNET_HELLO_Message *message)
956{
957 const struct GNUNET_PeerIdentity *other = cls;
958 struct Peer *peer;
959 struct GNUNET_PeerIdentity pid;
960
961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 "Received encrypted HELLO from peer `%s'",
963 GNUNET_i2s (other));
964 GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (message, &pid));
965 GNUNET_STATISTICS_update (stats,
966 gettext_noop ("# HELLO messages received"),
967 1,
968 GNUNET_NO);
969 peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
970 if (NULL == peer)
971 {
972 if ((GNUNET_YES == friends_only) || (friend_count < minimum_friend_count))
973 return;
974 }
975 else
976 {
977 if ((GNUNET_YES != peer->is_friend) && (GNUNET_YES == friends_only))
978 return;
979 if ((GNUNET_YES != peer->is_friend) &&
980 (friend_count < minimum_friend_count))
981 return;
982 }
983 (void) GNUNET_PEERINFO_add_peer (pi, message, NULL, NULL);
984}
985
986
987/**
988 * Last task run during shutdown. Disconnects us from
989 * the transport and core.
990 *
991 * @param cls unused, NULL
992 */
993static void
994cleaning_task (void *cls)
995{
996 if (NULL != peerinfo_notify)
997 {
998 GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
999 peerinfo_notify = NULL;
1000 }
1001 if (NULL != handle)
1002 {
1003 GNUNET_CORE_disconnect (handle);
1004 handle = NULL;
1005 }
1006 whitelist_peers ();
1007 if (NULL != add_task)
1008 {
1009 GNUNET_SCHEDULER_cancel (add_task);
1010 add_task = NULL;
1011 }
1012 if (NULL != oh)
1013 {
1014 GNUNET_TRANSPORT_offer_hello_cancel (oh);
1015 oh = NULL;
1016 }
1017 GNUNET_CONTAINER_multipeermap_iterate (peers, &free_peer, NULL);
1018 GNUNET_CONTAINER_multipeermap_destroy (peers);
1019 peers = NULL;
1020 if (NULL != ats)
1021 {
1022 GNUNET_ATS_connectivity_done (ats);
1023 ats = NULL;
1024 }
1025 if (NULL != pi)
1026 {
1027 GNUNET_PEERINFO_disconnect (pi);
1028 pi = NULL;
1029 }
1030 if (NULL != stats)
1031 {
1032 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1033 stats = NULL;
1034 }
1035}
1036
1037
1038/**
1039 * Main function that will be run.
1040 *
1041 * @param cls closure
1042 * @param args remaining command-line arguments
1043 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1044 * @param c configuration
1045 */
1046static void
1047run (void *cls,
1048 char *const *args,
1049 const char *cfgfile,
1050 const struct GNUNET_CONFIGURATION_Handle *c)
1051{
1052 struct GNUNET_MQ_MessageHandler handlers[] =
1053 { GNUNET_MQ_hd_var_size (hello,
1054 GNUNET_MESSAGE_TYPE_HELLO,
1055 struct GNUNET_HELLO_Message,
1056 NULL),
1057 GNUNET_MQ_handler_end () };
1058 unsigned long long opt;
1059
1060 cfg = c;
1061 stats = GNUNET_STATISTICS_create ("topology", cfg);
1062 friends_only =
1063 GNUNET_CONFIGURATION_get_value_yesno (cfg, "TOPOLOGY", "FRIENDS-ONLY");
1064 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1065 "TOPOLOGY",
1066 "MINIMUM-FRIENDS",
1067 &opt))
1068 opt = 0;
1069 minimum_friend_count = (unsigned int) opt;
1070 if (GNUNET_OK !=
1071 GNUNET_CONFIGURATION_get_value_number (cfg,
1072 "TOPOLOGY",
1073 "TARGET-CONNECTION-COUNT",
1074 &opt))
1075 opt = 16;
1076 target_connection_count = (unsigned int) opt;
1077 peers = GNUNET_CONTAINER_multipeermap_create (target_connection_count * 2,
1078 GNUNET_NO);
1079 read_friends_file (cfg);
1080 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1081 "Topology would like %u connections with at least %u friends\n",
1082 target_connection_count,
1083 minimum_friend_count);
1084 if ((GNUNET_YES == friends_only) || (minimum_friend_count > 0))
1085 blacklist = GNUNET_TRANSPORT_blacklist (cfg, &blacklist_check, NULL);
1086 ats = GNUNET_ATS_connectivity_init (cfg);
1087 pi = GNUNET_PEERINFO_connect (cfg);
1088 handle = GNUNET_CORE_connect (cfg,
1089 NULL,
1090 &core_init,
1091 &connect_notify,
1092 &disconnect_notify,
1093 handlers);
1094 GNUNET_SCHEDULER_add_shutdown (&cleaning_task, NULL);
1095 if (NULL == handle)
1096 {
1097 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1098 _ ("Failed to connect to `%s' service.\n"),
1099 "core");
1100 GNUNET_SCHEDULER_shutdown ();
1101 return;
1102 }
1103}
1104
1105
1106/**
1107 * The main function for the topology daemon.
1108 *
1109 * @param argc number of arguments from the command line
1110 * @param argv command line arguments
1111 * @return 0 ok, 1 on error
1112 */
1113int
1114main (int argc, char *const *argv)
1115{
1116 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1117 GNUNET_GETOPT_OPTION_END
1118 };
1119 int ret;
1120
1121 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1122 return 2;
1123
1124 ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc,
1125 argv,
1126 "gnunet-daemon-topology",
1127 _ ("GNUnet topology control"),
1128 options,
1129 &run,
1130 NULL))
1131 ? 0
1132 : 1;
1133 GNUNET_free_nz ((void *) argv);
1134 return ret;
1135}
1136
1137
1138#if defined(__linux__) && defined(__GLIBC__)
1139#include <malloc.h>
1140
1141/**
1142 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1143 */
1144void __attribute__ ((constructor))
1145GNUNET_ARM_memory_init ()
1146{
1147 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1148 mallopt (M_TOP_PAD, 1 * 1024);
1149 malloc_trim (0);
1150}
1151
1152
1153#endif
1154
1155/* end of gnunet-daemon-topology.c */
diff --git a/src/topology/test_gnunet_daemon_topology.c b/src/topology/test_gnunet_daemon_topology.c
deleted file mode 100644
index d1081515c..000000000
--- a/src/topology/test_gnunet_daemon_topology.c
+++ /dev/null
@@ -1,298 +0,0 @@
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 */
52static unsigned int connect_left;
53
54/*
55 * Result of the testcase.
56 */
57static int result;
58
59/*
60 * Peers that reached the threshold of connections.
61 */
62static int checked_peers;
63
64/*
65 * Testbed operations.
66 */
67struct GNUNET_TESTBED_Operation *op[NUM_PEERS];
68
69/*
70 * Timeout for testcase.
71 */
72static struct GNUNET_SCHEDULER_Task *timeout_tid;
73
74/*
75 * Peer context for every testbed peer.
76 */
77struct peerctx
78{
79 int index;
80 struct GNUNET_STATISTICS_Handle *statistics;
81 int connections;
82 int reported; /* GNUNET_NO | GNUNET_YES */
83};
84
85
86static void
87shutdown_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
105static void
106timeout_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 */
121int
122statistics_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
162static void *
163ca_statistics (void *cls,
164 const struct GNUNET_CONFIGURATION_Handle *cfg)
165{
166 return GNUNET_STATISTICS_create ("topology", cfg);
167}
168
169
170void
171da_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
187static void
188service_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
213static void
214notify_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
230static void
231do_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 &notify_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
281int
282main (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_directory_remove ("/tmp/test-gnunet-topology");
293
294 return (GNUNET_OK != result) ? 1 : 0;
295}
296
297
298/* end of test_gnunet_daemon_topology.c */
diff --git a/src/topology/test_gnunet_daemon_topology_data.conf b/src/topology/test_gnunet_daemon_topology_data.conf
deleted file mode 100644
index b1b3e9b88..000000000
--- a/src/topology/test_gnunet_daemon_topology_data.conf
+++ /dev/null
@@ -1,34 +0,0 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-topology/
3
4[transport]
5PLUGINS = 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]
10OVERLAY_TOPOLOGY=LINE
11
12[transport-tcp]
13BINDTO = 127.0.0.1
14
15# Do not try to connect to external network
16[hostlist]
17OPTIONS =
18
19[nat]
20DISABLEV6 = YES
21ENABLE_UPNP = NO
22BEHIND_NAT = NO
23ALLOW_NAT = NO
24INTERNAL_ADDRESS = 127.0.0.1
25EXTERNAL_ADDRESS = 127.0.0.1
26USE_LOCALADDR = NO
27USE_HOSTNAME = NO
28
29[nse]
30WORKBITS = 0
31
32[rps]
33START_ON_DEMAND = NO
34IMMEDIATE_START = NO
diff --git a/src/topology/topology.conf b/src/topology/topology.conf
deleted file mode 100644
index 45d8dd0c7..000000000
--- a/src/topology/topology.conf
+++ /dev/null
@@ -1,8 +0,0 @@
1[topology]
2IMMEDIATE_START = YES
3NOARMBIND = YES
4MINIMUM-FRIENDS = 0
5FRIENDS-ONLY = NO
6TARGET-CONNECTION-COUNT = 16
7FRIENDS = $GNUNET_CONFIG_HOME/topology/friends.txt
8BINARY = gnunet-daemon-topology