GNUnet 0.22.2
testbed.c
Go to the documentation of this file.
1/*
2 This file is part of GNUnet
3 Copyright (C) 2008, 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
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_testbed_lib.h"
30
31#define LOG(kind, ...) GNUNET_log_from (kind, "testing-api", __VA_ARGS__)
32
39#define LOW_PORT 12000
40
46#define HIGH_PORT 56000
47
48
54{
59 char *tmppath;
60
66
70 char *hostname;
71
80 uint32_t reserved_ports[65536 / 32];
81
90 uint32_t path_counter;
91
95 uint16_t lowport;
96
100 uint16_t highport;
101};
102
103
106 const char *testdir,
107 const char *trusted_ip,
108 const char *hostname,
109 uint16_t lowport,
110 uint16_t highport)
111{
112 struct GNUNET_TESTBED_System *system;
113
114 GNUNET_assert (NULL != testdir);
115 system = GNUNET_new (struct GNUNET_TESTBED_System);
116 if (NULL == (system->tmppath = getenv (GNUNET_TESTBED_PREFIX)))
117 system->tmppath = GNUNET_DISK_mkdtemp (testdir);
118 else
119 system->tmppath = GNUNET_strdup (system->tmppath);
120 system->lowport = lowport;
121 system->highport = highport;
122 if (NULL == system->tmppath)
123 {
124 GNUNET_free (system);
125 return NULL;
126 }
127 if (NULL != trusted_ip)
129 if (NULL != hostname)
130 system->hostname = GNUNET_strdup (hostname);
131 return system;
132}
133
134
137 const char *testdir,
138 const char *trusted_ip,
139 const char *hostname)
140{
143 hostname,
144 LOW_PORT,
145 HIGH_PORT);
146}
147
148
149void
151 bool remove_paths)
152{
153 if (remove_paths)
155 GNUNET_free (system->tmppath);
156 GNUNET_free (system->trusted_ip);
157 GNUNET_free (system->hostname);
158 GNUNET_free (system);
159}
160
161
162uint16_t
164{
165 struct GNUNET_NETWORK_Handle *socket;
166 struct addrinfo hint;
167 struct addrinfo *ret;
168 struct addrinfo *ai;
169 uint32_t *port_buckets;
170 char *open_port_str;
171 int bind_status;
172 uint32_t xor_image;
173 uint16_t index;
174 uint16_t open_port;
175 uint16_t pos;
176
177 /*
178 FIXME: Instead of using getaddrinfo we should try to determine the port
179 status by the following heurestics.
180
181 On systems which support both IPv4 and IPv6, only ports open on both
182 address families are considered open.
183 On system with either IPv4 or IPv6. A port is considered open if it's
184 open in the respective address family
185 */hint.ai_family = AF_UNSPEC; /* IPv4 and IPv6 */
186 hint.ai_socktype = 0;
187 hint.ai_protocol = 0;
188 hint.ai_addrlen = 0;
189 hint.ai_addr = NULL;
190 hint.ai_canonname = NULL;
191 hint.ai_next = NULL;
192 hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wild card address */
193 port_buckets = system->reserved_ports;
194 for (index = (system->lowport / 32) + 1; index < (system->highport / 32);
195 index++)
196 {
197 xor_image = (UINT32_MAX ^ port_buckets[index]);
198 if (0 == xor_image) /* Ports in the bucket are full */
199 continue;
200 pos = system->lowport % 32;
201 while (pos < 32)
202 {
203 if (0 == ((xor_image >> pos) & 1U))
204 {
205 pos++;
206 continue;
207 }
208 open_port = (index * 32) + pos;
209 if (open_port >= system->highport)
210 return 0;
211 GNUNET_asprintf (&open_port_str, "%u", (unsigned int) open_port);
212 ret = NULL;
213 GNUNET_assert (0 == getaddrinfo (NULL, open_port_str, &hint, &ret));
214 GNUNET_free (open_port_str);
215 bind_status = GNUNET_NO;
216 for (ai = ret; NULL != ai; ai = ai->ai_next)
217 {
218 socket = GNUNET_NETWORK_socket_create (ai->ai_family, SOCK_STREAM, 0);
219 if (NULL == socket)
220 continue;
221 bind_status =
222 GNUNET_NETWORK_socket_bind (socket, ai->ai_addr, ai->ai_addrlen);
224 if (GNUNET_OK != bind_status)
225 break;
226 socket = GNUNET_NETWORK_socket_create (ai->ai_family, SOCK_DGRAM, 0);
227 if (NULL == socket)
228 continue;
229 bind_status =
230 GNUNET_NETWORK_socket_bind (socket, ai->ai_addr, ai->ai_addrlen);
232 if (GNUNET_OK != bind_status)
233 break;
234 }
235 port_buckets[index] |= (1U << pos); /* Set the port bit */
236 freeaddrinfo (ret);
237 if (GNUNET_OK == bind_status)
238 {
240 "Found a free port %u\n",
241 (unsigned int) open_port);
242 return open_port;
243 }
244 pos++;
245 }
246 }
247 return 0;
248}
249
250
251void
253 uint16_t port)
254{
255 uint32_t *port_buckets;
256 uint16_t bucket;
257 uint16_t pos;
258
259 port_buckets = system->reserved_ports;
260 bucket = port / 32;
261 pos = port % 32;
262 LOG (GNUNET_ERROR_TYPE_DEBUG, "Releasing port %u\n", port);
263 if (0 == (port_buckets[bucket] & (1U << pos)))
264 {
265 GNUNET_break (0); /* Port was not reserved by us using reserve_port() */
266 return;
267 }
268 port_buckets[bucket] &= ~(1U << pos);
269}
270
271
276struct UpdateContext
277{
282
287
292
298 uint16_t *ports;
299
303 unsigned int nports;
304
309};
310
311
322static void
323update_config (void *cls,
324 const char *section,
325 const char *option,
326 const char *value)
327{
328 struct UpdateContext *uc = cls;
329 unsigned int ival;
330 char cval[12];
331 char uval[PATH_MAX];
332 char *single_variable;
333 char *per_host_variable;
334 unsigned long long num_per_host;
335 uint16_t new_port;
336
337 if (GNUNET_OK != uc->status)
338 return;
339 if (! ((0 == strcmp (option, "PORT")) || (0 == strcmp (option, "UNIXPATH")) ||
340 (0 == strcmp (option, "HOSTNAME"))))
341 return;
342 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
343 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
344 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
345 {
346 if ((ival != 0) &&
348 "testing",
349 single_variable)))
350 {
351 new_port = GNUNET_TESTBED_reserve_port (uc->system);
352 if (0 == new_port)
353 {
354 uc->status = GNUNET_SYSERR;
355 GNUNET_free (single_variable);
356 GNUNET_free (per_host_variable);
357 return;
358 }
359 GNUNET_snprintf (cval, sizeof(cval), "%u", new_port);
360 value = cval;
361 GNUNET_array_append (uc->ports, uc->nports, new_port);
362 }
363 else if ((ival != 0) &&
364 (GNUNET_YES ==
366 "testing",
367 single_variable)) &&
369 "testing",
370 per_host_variable,
371 &num_per_host))
372 {
373 /* GNUNET_snprintf (cval, sizeof (cval), "%u", */
374 /* ival + ctx->fdnum % num_per_host); */
375 /* value = cval; */
376 GNUNET_break (0); /* FIXME */
377 }
378 }
379 if (0 == strcmp (option, "UNIXPATH"))
380 {
382 "testing",
383 single_variable))
384 {
385 GNUNET_snprintf (uval,
386 sizeof(uval),
387 "%s/%s.sock",
388 uc->gnunet_home,
389 section);
390 value = uval;
391 }
392 else if ((GNUNET_YES ==
394 "testing",
395 per_host_variable,
396 &num_per_host)) &&
397 (num_per_host > 0))
398 {
399 GNUNET_break (0); /* FIXME */
400 }
401 }
402 if (0 == strcmp (option, "HOSTNAME"))
403 {
404 value = (NULL == uc->system->hostname) ? "localhost" : uc->system->hostname;
405 }
406 GNUNET_free (single_variable);
407 GNUNET_free (per_host_variable);
408 GNUNET_CONFIGURATION_set_value_string (uc->cfg, section, option, value);
409}
410
411
419static void
421 const char *section)
422{
423 struct UpdateContext *uc = cls;
424 char **ikeys;
425 char *val;
426 char *ptr;
427 char *orig_allowed_hosts;
428 char *allowed_hosts;
429 char *ACCEPT_FROM_key;
430 uint16_t ikeys_cnt;
431 uint16_t key;
432
433 ikeys_cnt = 0;
434 val = NULL;
435 /* Ignore certain options from sections. See
436 https://gnunet.org/bugs/view.php?id=2476 */
437 if (GNUNET_YES ==
439 section,
440 "TESTBED_IGNORE_KEYS"))
441 {
444 section,
445 "TESTBED_IGNORE_KEYS",
446 &val));
447 ptr = val;
448 for (ikeys_cnt = 0; NULL != (ptr = strstr (ptr, ";")); ikeys_cnt++)
449 ptr++;
450 if (0 == ikeys_cnt)
451 GNUNET_break (0);
452 else
453 {
454 ikeys = GNUNET_malloc ((sizeof(char *)) * ikeys_cnt);
455 ptr = val;
456 for (key = 0; key < ikeys_cnt; key++)
457 {
458 ikeys[key] = ptr;
459 ptr = strstr (ptr, ";");
460 GNUNET_assert (NULL != ptr); /* worked just before... */
461 *ptr = '\0';
462 ptr++;
463 }
464 }
465 }
466 if (0 != ikeys_cnt)
467 {
468 for (key = 0; key < ikeys_cnt; key++)
469 {
470 if (NULL != strstr (ikeys[key], "ADVERTISED_PORT"))
471 break;
472 }
473 if ((key == ikeys_cnt) &&
474 (GNUNET_YES ==
476 section,
477 "ADVERTISED_PORT")))
478 {
479 if (GNUNET_OK ==
481 section,
482 "PORT",
483 &ptr))
484 {
486 section,
487 "ADVERTISED_PORT",
488 ptr);
489 GNUNET_free (ptr);
490 }
491 }
492 for (key = 0; key < ikeys_cnt; key++)
493 {
494 if (NULL != strstr (ikeys[key], "ACCEPT_FROM"))
495 {
496 GNUNET_free (ikeys);
497 GNUNET_free (val);
498 return;
499 }
500 }
501 GNUNET_free (ikeys);
502 }
503 GNUNET_free (val);
504 ACCEPT_FROM_key = "ACCEPT_FROM";
505 if ((NULL != uc->system->trusted_ip) &&
506 (NULL != strstr (uc->system->trusted_ip, ":"))) /* IPv6 in use */
507 ACCEPT_FROM_key = "ACCEPT_FROM6";
509 section,
510 ACCEPT_FROM_key,
511 &orig_allowed_hosts))
512 {
513 orig_allowed_hosts = GNUNET_strdup ("127.0.0.1;");
514 }
515 if (NULL == uc->system->trusted_ip)
516 allowed_hosts = GNUNET_strdup (orig_allowed_hosts);
517 else
518 GNUNET_asprintf (&allowed_hosts,
519 "%s%s;",
520 orig_allowed_hosts,
521 uc->system->trusted_ip);
522 GNUNET_free (orig_allowed_hosts);
524 section,
525 ACCEPT_FROM_key,
526 allowed_hosts);
527 GNUNET_free (allowed_hosts);
528}
529
530
535 uint16_t **ports,
536 unsigned int *nports)
537{
538 struct UpdateContext uc;
539 char *default_config;
540
541 uc.system = system;
542 uc.cfg = cfg;
543 uc.status = GNUNET_OK;
544 uc.ports = NULL;
545 uc.nports = 0;
546 GNUNET_asprintf (&uc.gnunet_home,
547 "%s/%u",
550 GNUNET_asprintf (&default_config, "%s/config", uc.gnunet_home);
552 "PATHS",
553 "DEFAULTCONFIG",
554 default_config);
555 GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG", default_config);
556 GNUNET_free (default_config);
558 "PATHS",
559 "GNUNET_HOME",
560 uc.gnunet_home);
561 /* make PORTs and UNIXPATHs unique */
563 /* allow connections to services from system trusted_ip host */
565 /* enable loopback-based connections between peers */
566 GNUNET_CONFIGURATION_set_value_string (cfg, "nat", "USE_LOCALADDR", "YES");
567 GNUNET_free (uc.gnunet_home);
568 if ((NULL != ports) && (NULL != nports))
569 {
570 *ports = uc.ports;
571 *nports = uc.nports;
572 }
573 else
574 GNUNET_free (uc.ports);
575 return uc.status;
576}
577
578
579/* end of testbed.c */
char * getenv()
static int ret
Final status code.
Definition: gnunet-arm.c:93
static struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
Definition: gnunet-arm.c:108
static uint16_t port
Port number.
Definition: gnunet-bcd.c:146
static struct GNUNET_TRANSPORT_AddressIdentifier * ai
Handle to the operation that publishes our address.
struct GNUNET_HashCode key
The key used in the DHT.
static char * value
Value of the record to add/remove.
static struct GNUNET_FS_UnindexContext * uc
#define GNUNET_TESTBED_PREFIX
void GNUNET_CONFIGURATION_set_value_string(struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, const char *value)
Set a configuration value that should be a string.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_number(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, unsigned long long *number)
Get a configuration value that should be a number.
void GNUNET_CONFIGURATION_iterate_sections(const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_CONFIGURATION_SectionIterator iter, void *iter_cls)
Iterate over all sections in the configuration.
void GNUNET_CONFIGURATION_iterate(const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_CONFIGURATION_Iterator iter, void *iter_cls)
Iterate over all options in the configuration.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_yesno(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option)
Get a configuration value that should be in a set of "YES" or "NO".
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_get_value_string(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option, char **value)
Get a configuration value that should be a string.
enum GNUNET_GenericReturnValue GNUNET_CONFIGURATION_have_value(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section, const char *option)
Test if we have a value for a particular option.
char * GNUNET_DISK_mkdtemp(const char *t)
Create an (empty) temporary directory on disk.
Definition: disk.c:340
enum GNUNET_GenericReturnValue GNUNET_DISK_directory_remove(const char *filename)
Remove all files in a directory (rm -rf).
Definition: disk.c:1065
GNUNET_GenericReturnValue
Named constants for return values.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur.
@ GNUNET_ERROR_TYPE_DEBUG
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
int GNUNET_snprintf(char *buf, size_t size, const char *format,...) __attribute__((format(printf
Like snprintf, just aborts if the buffer is of insufficient size.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_array_append(arr, len, element)
Append an element to an array (growing the array by one).
#define GNUNET_free(ptr)
Wrapper around free.
enum GNUNET_GenericReturnValue GNUNET_NETWORK_socket_close(struct GNUNET_NETWORK_Handle *desc)
Close a socket.
Definition: network.c:508
struct GNUNET_NETWORK_Handle * GNUNET_NETWORK_socket_create(int domain, int type, int protocol)
Create a new socket.
Definition: network.c:833
enum GNUNET_GenericReturnValue GNUNET_NETWORK_socket_bind(struct GNUNET_NETWORK_Handle *desc, const struct sockaddr *address, socklen_t address_len)
Bind a socket to a particular address.
Definition: network.c:439
#define AI_NUMERICSERV
AI_NUMERICSERV not defined in windows.
Definition: platform.h:219
#define PATH_MAX
Assumed maximum path length.
Definition: platform.h:241
handle to a socket
Definition: network.c:53
Handle for a system on which GNUnet peers are executed; a system is used for reserving unique paths a...
Definition: testbed.c:54
uint32_t path_counter
Counter we use to make service home paths unique on this system; the full path consists of the tmppat...
Definition: testbed.c:90
uint16_t highport
Highest port we are allowed to use.
Definition: testbed.c:100
uint32_t reserved_ports[65536/32]
Bitmap where each port that has already been reserved for some GNUnet peer is recorded.
Definition: testbed.c:80
uint16_t lowport
Lowest port we are allowed to use.
Definition: testbed.c:95
char * hostname
our hostname
Definition: testbed.c:70
char * tmppath
Prefix (e.g.
Definition: testbed.c:59
char * trusted_ip
The trusted ip.
Definition: testbed.c:65
Closure for iterator for updating.
struct GNUNET_TESTBED_System * system
The system for which we are building configurations.
Definition: testbed.c:281
uint16_t * ports
Array of ports currently allocated to this peer.
Definition: testbed.c:298
char * gnunet_home
The customized service home path for this peer.
Definition: testbed.c:291
struct GNUNET_CONFIGURATION_Handle * cfg
The configuration we are building.
Definition: testbed.c:286
unsigned int nports
The number of ports in the above array.
Definition: testbed.c:303
int status
build status - to signal error while building a configuration
Definition: testbed.c:308
struct GNUNET_TESTBED_System * GNUNET_TESTBED_system_create_with_portrange(const char *testdir, const char *trusted_ip, const char *hostname, uint16_t lowport, uint16_t highport)
Create a system handle.
Definition: testbed.c:105
#define HIGH_PORT
Highest port used for GNUnet testing.
Definition: testbed.c:46
void GNUNET_TESTBED_release_port(struct GNUNET_TESTBED_System *system, uint16_t port)
Release reservation of a TCP or UDP port for a peer (used during #GNUNET_TESTBED_peer_destroy()).
Definition: testbed.c:252
struct GNUNET_TESTBED_System * GNUNET_TESTBED_system_create(const char *testdir, const char *trusted_ip, const char *hostname)
Create a system handle.
Definition: testbed.c:136
uint16_t GNUNET_TESTBED_reserve_port(struct GNUNET_TESTBED_System *system)
Reserve a TCP or UDP port for a peer.
Definition: testbed.c:163
static void update_config(void *cls, const char *section, const char *option, const char *value)
Function to iterate over options.
Definition: testbed.c:323
#define LOG(kind,...)
Definition: testbed.c:31
void GNUNET_TESTBED_system_destroy(struct GNUNET_TESTBED_System *system, bool remove_paths)
Free system resources.
Definition: testbed.c:150
static void update_config_sections(void *cls, const char *section)
Section iterator to set ACCEPT_FROM/ACCEPT_FROM6 to include the address of 'trusted_hosts' in all sec...
Definition: testbed.c:420
enum GNUNET_GenericReturnValue GNUNET_TESTBED_configuration_create(struct GNUNET_TESTBED_System *system, struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t **ports, unsigned int *nports)
Create a new configuration using the given configuration as a template; ports and paths will be modif...
Definition: testbed.c:532
#define LOW_PORT
Lowest port used for GNUnet testing.
Definition: testbed.c:39