- unique constraint
[oweals/gnunet.git] / src / testbed / gnunet-daemon-testbed-underlay.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2013 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
19 */
20
21
22 /**
23  * @file testbed/gnunet-daemon-testbed-blacklist.c
24  * @brief daemon to restrict incoming connections from other peers at the
25  *          transport layer of a peer
26  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
27  */
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_transport_service.h"
32 #include "gnunet_ats_service.h"
33 #include "gnunet_testing_lib.h"
34 #include <sqlite3.h>
35
36 /**
37  * Logging shorthand
38  */
39 #define LOG(type,...)                           \
40   GNUNET_log (type, __VA_ARGS__)
41
42 /**
43  * Debug logging shorthand
44  */
45 #define DEBUG(...)                              \
46   LOG (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
47
48 /**
49  * Log an error message at log-level 'level' that indicates
50  * a failure of the command 'cmd' on file 'filename'
51  * with the message given by strerror(errno).
52  */
53 #define LOG_SQLITE(db, msg, level, cmd)                                 \
54   do {                                                                  \
55     GNUNET_log_from (level, "sqlite", _("`%s' failed at %s:%d with error: %s\n"), \
56                      cmd, __FILE__,__LINE__, sqlite3_errmsg(db));  \
57     if (msg != NULL)                                                    \
58       GNUNET_asprintf(msg, _("`%s' failed at %s:%u with error: %s"), cmd, \
59                       __FILE__, __LINE__, sqlite3_errmsg(db));     \
60   } while(0)
61
62
63 /**
64  * Allow access from the peers read from the whitelist
65  */
66 #define ACCESS_ALLOW 1
67
68 /**
69  * Deny access from the peers read from the blacklist
70  */
71 #define ACCESS_DENY 0
72
73 /**
74  * The map to store the peer identities to allow/deny
75  */
76 static struct GNUNET_CONTAINER_MultiPeerMap *map;
77
78
79 /**
80  * The map to store the peer identities to allow/deny
81  */
82 static struct GNUNET_CONTAINER_MultiPeerMap *blacklist_map;
83
84 /**
85  * The database connection
86  */
87 static struct sqlite3 *db;
88
89 /**
90  * The blacklist handle we obtain from transport when we register ourselves for
91  * access control
92  */
93 struct GNUNET_TRANSPORT_Blacklist *bh;
94
95 /**
96  * The peer ID map
97  */
98 static struct GNUNET_DISK_MapHandle *idmap;
99
100 /**
101  * The hostkeys data
102  */
103 static char *hostkeys_data;
104
105 /**
106  * Handle to the transport service.  This is used for setting link metrics
107  */
108 static struct GNUNET_TRANSPORT_Handle *transport;
109
110 /**
111  * The number of hostkeys in the hostkeys array
112  */
113 static unsigned int num_hostkeys;
114
115 /**
116  * Task for shutdown
117  */
118 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
119
120 /**
121  * Are we allowing or denying access from peers
122  */
123 static int mode;
124
125
126 /**
127  * @ingroup hashmap
128  * Iterator over hash map entries.
129  *
130  * @param cls closure
131  * @param key current key code
132  * @param value value in the hash map
133  * @return #GNUNET_YES if we should continue to
134  *         iterate,
135  *         #GNUNET_NO if not.
136  */
137 static int
138 iterator (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
139 {
140   GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (map, key,
141                                                                      value));
142   return GNUNET_YES;
143 }
144
145
146 /**
147  * Cleaup and destroy the map
148  */
149 static void
150 cleanup_map ()
151 {
152   if (NULL != map)
153   {
154     GNUNET_assert (GNUNET_SYSERR != GNUNET_CONTAINER_multipeermap_iterate (map,
155                                                                            &iterator,
156                                                                            NULL));
157     GNUNET_CONTAINER_multipeermap_destroy (map);
158     map = NULL;
159   }
160 }
161
162
163 /**
164  * Shutdown task to cleanup our resources and exit.
165  *
166  * @param cls NULL
167  * @param tc scheduler task context
168  */
169 static void
170 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
171 {
172   cleanup_map ();
173   if (NULL != bh)
174     GNUNET_TRANSPORT_blacklist_cancel (bh);
175 }
176
177
178 /**
179  * Function that decides if a connection is acceptable or not.
180  *
181  * @param cls closure
182  * @param pid peer to approve or disapproave
183  * @return GNUNET_OK if the connection is allowed, GNUNET_SYSERR if not
184  */
185 static int
186 check_access (void *cls, const struct GNUNET_PeerIdentity * pid)
187 {
188   int contains;
189
190   if (NULL != map)
191     contains = GNUNET_CONTAINER_multipeermap_contains (map, pid);
192   else
193     contains = GNUNET_NO;
194   if (ACCESS_DENY == mode)
195     return (contains) ? GNUNET_SYSERR : GNUNET_OK;
196   return (contains) ? GNUNET_OK : GNUNET_SYSERR;
197 }
198
199
200 static int
201 get_identity (unsigned int offset, struct GNUNET_PeerIdentity *id)
202 {
203   struct GNUNET_CRYPTO_EddsaPrivateKey private_key;
204
205   if (offset >= num_hostkeys)
206     return GNUNET_SYSERR;
207   (void) memcpy (&private_key,
208                  hostkeys_data + (offset * GNUNET_TESTING_HOSTKEYFILESIZE),
209                  GNUNET_TESTING_HOSTKEYFILESIZE);
210   GNUNET_CRYPTO_eddsa_key_get_public (&private_key, &id->public_key);
211   return GNUNET_OK;
212 }
213
214
215 /**
216  * Function to blacklist a peer
217  *
218  * @param offset the offset where to find the peer's hostkey in the array of hostkeys
219  */
220 static void
221 blacklist_peer (unsigned int offset)
222 {
223   struct GNUNET_PeerIdentity id;
224
225   GNUNET_assert (offset < num_hostkeys);
226   GNUNET_assert (GNUNET_OK == get_identity (offset, &id));
227   GNUNET_break (GNUNET_OK ==
228                 GNUNET_CONTAINER_multipeermap_put (map, &id, &id,
229                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
230
231 }
232
233 /**
234  * Blacklist peer
235  */
236 struct BlackListRow
237 {
238   /**
239    * Next ptr
240    */
241   struct BlackListRow *next;
242
243   /**
244    * The offset where to find the hostkey for the peer
245    */
246   unsigned int id;
247 };
248
249
250 /**
251  * Whilelist entry
252  */
253 struct WhiteListRow
254 {
255   /**
256    * Next ptr
257    */
258   struct WhiteListRow *next;
259   
260   /**
261    * The offset where to find the hostkey for the peer
262    */
263   unsigned int id;
264   
265   /**
266    * Bandwidth to be assigned to the link
267    */
268   int bandwidth;
269
270   /**
271    * Latency to be assigned to the link
272    */
273   int latency;
274
275   /**
276    * Loss to be assigned to the link
277    */
278   int loss;
279 };
280
281
282 /**
283  * Function to load keys
284  */
285 static int
286 load_keys (const struct GNUNET_CONFIGURATION_Handle *c)
287 {
288   char *data_dir;
289   char *idfile;
290   struct GNUNET_DISK_FileHandle *fd;
291   uint64_t fsize;
292
293   data_dir = NULL;
294   idfile = NULL;
295   fd = NULL;
296   fsize = 0;
297   data_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
298   GNUNET_asprintf (&idfile, "%s/testing_hostkeys.ecc", data_dir);
299   GNUNET_free (data_dir);
300   data_dir = NULL;
301   if (GNUNET_OK !=
302       GNUNET_DISK_file_size (idfile, &fsize, GNUNET_YES, GNUNET_YES))
303   {
304     GNUNET_free (idfile);
305     return GNUNET_SYSERR;
306   }
307   if (0 != (fsize % GNUNET_TESTING_HOSTKEYFILESIZE))
308   {
309     LOG (GNUNET_ERROR_TYPE_ERROR,
310          _("Incorrect hostkey file format: %s\n"), idfile);
311     GNUNET_free (idfile);
312     return GNUNET_SYSERR;
313   }
314   fd = GNUNET_DISK_file_open (idfile, GNUNET_DISK_OPEN_READ,
315                               GNUNET_DISK_PERM_NONE);
316   if (NULL == fd)
317   {
318     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", idfile);
319     GNUNET_free (idfile);
320     return GNUNET_SYSERR;
321   }
322   GNUNET_free (idfile);
323   idfile = NULL;
324   hostkeys_data = GNUNET_DISK_file_map (fd,
325                                         &idmap,
326                                         GNUNET_DISK_MAP_TYPE_READ,
327                                         fsize);
328   if (NULL != hostkeys_data)
329     num_hostkeys = fsize / GNUNET_TESTING_HOSTKEYFILESIZE;
330   return GNUNET_OK;
331 }
332
333
334 /**
335  * Function to read blacklist rows from the database
336  *
337  * @param db the database connection
338  * @param pid the identity of this peer
339  * @param bl_rows where to store the retrieved blacklist rows
340  * @return GNUNET_SYSERR upon error OR the number of rows retrieved
341  */
342 static int
343 db_read_blacklist (struct sqlite3 *db, unsigned int pid, struct BlackListRow **bl_rows)
344 {
345   static const char *query_bl = "SELECT (oid) FROM blacklist WHERE (id == ?);";
346   struct sqlite3_stmt *stmt_bl;
347   struct BlackListRow *lr;
348   int nrows;
349   int peer_id;
350   int ret;
351
352   if (SQLITE_OK != (ret = sqlite3_prepare_v2 (db, query_bl, -1, &stmt_bl, NULL)))
353   {
354     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
355     return GNUNET_SYSERR;
356   }
357   if (SQLITE_OK != (ret = sqlite3_bind_int (stmt_bl, 1, pid)))
358   {
359     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_int");
360     sqlite3_finalize (stmt_bl);
361     return GNUNET_SYSERR;
362   }
363   nrows = 0;
364   do
365   {
366     ret = sqlite3_step (stmt_bl);
367     if (SQLITE_ROW != ret)
368       break;
369     peer_id = sqlite3_column_int (stmt_bl, 1);
370     lr = GNUNET_new (struct BlackListRow);
371     lr->id = peer_id;
372     lr->next = *bl_rows;
373     *bl_rows = lr;
374     nrows++;
375   } while (1);
376   sqlite3_finalize (stmt_bl);
377   stmt_bl = NULL;
378   return nrows;
379 }
380
381
382 /**
383  * Function to read whitelist rows from the database
384  *
385  * @param db the database connection
386  * @param pid the identity of this peer
387  * @param wl_rows where to store the retrieved whitelist rows
388  * @return GNUNET_SYSERR upon error OR the number of rows retrieved
389  */
390 static int
391 db_read_whitelist (struct sqlite3 *db, unsigned int pid, struct WhiteListRow **wl_rows)
392 {
393   static const char *query_wl = "SELECT (oid, bandwidth, latency, loss) FROM whitelist WHERE (id == ?);";
394   struct sqlite3_stmt *stmt_wl;
395   struct WhiteListRow *lr;
396   int nrows;
397   int ret;
398   
399   if (SQLITE_OK != (ret = sqlite3_prepare_v2 (db, query_wl, -1, &stmt_wl, NULL)))
400   {
401     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
402     return GNUNET_SYSERR;
403   }
404   if (SQLITE_OK != (ret = sqlite3_bind_int (stmt_wl, 1, pid)))
405   {
406     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_int");
407     sqlite3_finalize (stmt_wl);
408     return GNUNET_SYSERR;
409   }
410   nrows = 0;
411   do
412   {
413     ret = sqlite3_step (stmt_wl);
414     if (SQLITE_ROW != ret)
415       break;
416     nrows++;
417     lr = GNUNET_new (struct WhiteListRow);
418     lr->id = sqlite3_column_int (stmt_wl, 1);
419     lr->bandwidth = sqlite3_column_int (stmt_wl, 2);
420     lr->latency = sqlite3_column_int (stmt_wl, 3);
421     lr->loss = sqlite3_column_int (stmt_wl, 4);
422     lr->next = *wl_rows;
423     *wl_rows = lr;
424   } while (1);
425   sqlite3_finalize (stmt_wl);
426   return nrows;
427 }
428
429
430 /**
431  * Main function that will be run.
432  *
433  * @param cls closure
434  * @param args remaining command-line arguments
435  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
436  * @param c configuration
437  */
438 static void
439 run (void *cls, char *const *args, const char *cfgfile,
440      const struct GNUNET_CONFIGURATION_Handle *c)
441 {
442   char *dbfile;
443   struct BlackListRow *bl_head;
444   struct BlackListRow *bl_entry;
445   struct WhiteListRow *wl_head;
446   struct WhiteListRow *wl_entry;
447   struct GNUNET_PeerIdentity identity;
448   struct GNUNET_ATS_Information triplet[3];
449   unsigned long long pid;
450   unsigned int nrows;
451   int ret;
452
453   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (c, "TESTBED",
454                                                             "PEERID", &pid))
455   {
456     GNUNET_break (0);
457     return;
458   }
459   transport = GNUNET_TRANSPORT_connect (c, NULL, NULL, NULL, NULL, NULL);
460   if (NULL == transport)
461   {
462     GNUNET_break (0);
463     return;
464   }
465   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "TESTBED",
466                                                             "UNDERLAY_DB",
467                                                             &dbfile))
468   {
469     GNUNET_break (0);
470     return;
471   }
472   if (SQLITE_OK != (ret = sqlite3_open_v2 (dbfile, &db, SQLITE_OPEN_READONLY, NULL)))
473   {
474     if (NULL != db)
475     {
476       LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite_open_v2");
477       sqlite3_close (db);
478     }
479     else
480       LOG (GNUNET_ERROR_TYPE_ERROR, "Cannot open sqlite file %s\n", dbfile);
481     GNUNET_free (dbfile);
482     return;
483   }
484   DEBUG ("Opened database %s\n", dbfile);
485   GNUNET_free (dbfile);
486   dbfile = NULL;
487   bl_head = NULL;
488   wl_head = NULL;
489   nrows = db_read_blacklist (db, pid, &bl_head);
490   if (GNUNET_SYSERR == nrows)
491     goto close_db;
492   if (nrows > 0)
493   {
494     blacklist_map = GNUNET_CONTAINER_multipeermap_create (nrows, GNUNET_YES);
495     if (GNUNET_OK != load_keys (c))
496       goto close_db;
497   }
498   while (NULL != (bl_entry = bl_head))
499   {
500     bl_head = bl_entry->next;
501     blacklist_peer (bl_entry->id);
502     GNUNET_free (bl_entry);
503   }
504   if (NULL != blacklist_map)
505   {
506     bh = GNUNET_TRANSPORT_blacklist (c, &check_access, NULL);
507     shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
508                                                   &do_shutdown, NULL);
509   }
510   /* read and process whitelist */
511   nrows = 0;
512   wl_head = NULL;
513   nrows = db_read_whitelist (db, pid, &wl_head);
514   if ((GNUNET_SYSERR == nrows) || (0 == nrows))
515     goto close_db;
516   triplet[0].type = 0; //FIXME: not implemented: GNUNET_ATS_QUALITY_NET_THROUGHPUT
517   triplet[1].type = GNUNET_ATS_QUALITY_NET_DELAY;
518   triplet[2].type =  0; //FIXME: not implemented: GNUNET_ATS_QUALITY_NET_LOSSRATE;
519   while (NULL != (wl_entry = wl_head))
520   {
521     wl_head = wl_entry->next;
522     triplet[0].value = wl_entry->bandwidth; //FIXME: bandwidth != throughput !!
523     triplet[1].value = wl_entry->latency;
524     triplet[2].value = wl_entry->loss;
525     GNUNET_assert (GNUNET_OK == get_identity (wl_entry->id, &identity));
526     GNUNET_TRANSPORT_set_traffic_metric (transport,
527                                          &identity,
528                                          GNUNET_YES,
529                                          GNUNET_YES, /* FIXME: Separate inbound, outboud metrics */
530                                          triplet, 3);
531     GNUNET_free (wl_entry);
532   }
533
534  close_db:
535   while (NULL != (bl_entry = bl_head))
536   {
537     bl_head = bl_entry->next;
538     GNUNET_free (bl_entry);
539   }
540   GNUNET_break (GNUNET_OK == sqlite3_close (db));
541   return;
542 }
543
544
545 /**
546  * The main function.
547  *
548  * @param argc number of arguments from the command line
549  * @param argv command line arguments
550  * @return 0 ok, 1 on error
551  */
552 int
553 main (int argc, char *const *argv)
554 {
555   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
556     GNUNET_GETOPT_OPTION_END
557   };
558   int ret;
559
560   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
561     return 2;
562 #ifdef SQLITE_CONFIG_MMAP_SIZE
563   (void) sqlite3_config (SQLITE_CONFIG_MMAP_SIZE, 512000, 256000000);
564 #endif
565   ret =
566       (GNUNET_OK ==
567        GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-testbed-underlay",
568                            _
569                            ("Daemon to restrict underlay network in testbed deployments"),
570                            options, &run, NULL)) ? 0 : 1;
571   GNUNET_free ((void*) argv);
572   return ret;
573 }