minor fix
[oweals/gnunet.git] / src / sensor / gnunet-service-sensor_reporting_anomaly.c
1 /*
2      This file is part of GNUnet.
3      (C)
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  * @file sensor/gnunet-service-sensor_reporting_anomaly.c
23  * @brief sensor service anomaly reporting functionality
24  * @author Omar Tarabai
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "sensor.h"
29 #include "gnunet_peerstore_service.h"
30 #include "gnunet_core_service.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "sensor-reporting-anomaly",__VA_ARGS__)
33
34 struct AnomalyInfo
35 {
36
37   /**
38    * DLL
39    */
40   struct AnomalyInfo *prev;
41
42   /**
43    * DLL
44    */
45   struct AnomalyInfo *next;
46
47   /**
48    * Sensor information
49    */
50   struct GNUNET_SENSOR_SensorInfo *sensor;
51
52   /**
53    * Current anomalous status of sensor
54    */
55   int anomalous;
56
57   /**
58    * List of peers that reported an anomaly for this sensor
59    */
60   struct GNUNET_CONTAINER_MultiPeerMap *anomalous_neighbors;
61
62 };
63
64 /**
65  * Information about a connected CORE peer.
66  * Note that we only know about a connected peer if it is running the same
67  * application (sensor anomaly reporting) as us.
68  */
69 struct CorePeer
70 {
71
72   /**
73    * DLL
74    */
75   struct CorePeer *prev;
76
77   /**
78    * DLL
79    */
80   struct CorePeer *next;
81
82   /**
83    * Peer identity of connected peer
84    */
85   struct GNUNET_PeerIdentity *peerid;
86
87   /**
88    * Message queue for messages to be sent to this peer
89    */
90   struct GNUNET_MQ_Handle *mq;
91
92 };
93
94
95 /**
96  * Our configuration.
97  */
98 static const struct GNUNET_CONFIGURATION_Handle *cfg;
99
100 /**
101  * Multihashmap of loaded sensors
102  */
103 static struct GNUNET_CONTAINER_MultiHashMap *sensors;
104
105 /**
106  * Handle to core service
107  */
108 static struct GNUNET_CORE_Handle *core;
109
110 /**
111  * My peer id
112  */
113 static struct GNUNET_PeerIdentity mypeerid;
114
115 /**
116  * Head of DLL of anomaly info structs
117  */
118 static struct AnomalyInfo *ai_head;
119
120 /**
121  * Tail of DLL of anomaly info structs
122  */
123 static struct AnomalyInfo *ai_tail;
124
125 /**
126  * Head of DLL of CORE peers
127  */
128 static struct CorePeer *cp_head;
129
130 /**
131  * Tail of DLL of CORE peers
132  */
133 static struct CorePeer *cp_tail;
134
135 /**
136  * Is the module started?
137  */
138 static int module_running = GNUNET_NO;
139
140 /**
141  * Number of known neighborhood peers
142  */
143 static int neighborhood;
144
145
146 /**
147  * Destroy anomaly info struct
148  *
149  * @param ai struct to destroy
150  */
151 static void
152 destroy_anomaly_info (struct AnomalyInfo *ai)
153 {
154   if (NULL != ai->anomalous_neighbors)
155     GNUNET_CONTAINER_multipeermap_destroy (ai->anomalous_neighbors);
156   GNUNET_free (ai);
157 }
158
159
160 /**
161  * Destroy core peer struct
162  *
163  * @param cp struct to destroy
164  */
165 static void
166 destroy_core_peer (struct CorePeer *cp)
167 {
168   struct AnomalyInfo *ai;
169
170   if (NULL != cp->mq)
171   {
172     GNUNET_MQ_destroy (cp->mq);
173     cp->mq = NULL;
174   }
175   ai = ai_head;
176   while (NULL != ai)
177   {
178     GNUNET_assert (NULL != ai->anomalous_neighbors);
179     GNUNET_CONTAINER_multipeermap_remove_all (ai->anomalous_neighbors,
180                                               cp->peerid);
181     ai = ai->next;
182   }
183   GNUNET_free (cp);
184 }
185
186
187 /**
188  * Stop sensor anomaly reporting module
189  */
190 void
191 SENSOR_reporting_anomaly_stop ()
192 {
193   struct AnomalyInfo *ai;
194   struct CorePeer *cp;
195
196   LOG (GNUNET_ERROR_TYPE_DEBUG, "Stopping sensor anomaly reporting module.\n");
197   module_running = GNUNET_NO;
198   ai = ai_head;
199   while (NULL != ai)
200   {
201     GNUNET_CONTAINER_DLL_remove (ai_head, ai_tail, ai);
202     destroy_anomaly_info (ai);
203     ai = ai_head;
204   }
205   cp = cp_head;
206   while (NULL != cp)
207   {
208     GNUNET_CONTAINER_DLL_remove (cp_head, cp_tail, cp);
209     destroy_core_peer (cp);
210     cp = cp_head;
211   }
212   neighborhood = 0;
213   if (NULL != core)
214   {
215     GNUNET_CORE_disconnect (core);
216     core = NULL;
217   }
218 }
219
220
221 /**
222  * Gets the anomaly info struct related to the given sensor
223  *
224  * @param sensor Sensor to search by
225  */
226 static struct AnomalyInfo *
227 get_anomaly_info_by_sensor (struct GNUNET_SENSOR_SensorInfo *sensor)
228 {
229   struct AnomalyInfo *ai;
230
231   ai = ai_head;
232   while (NULL != ai)
233   {
234     if (ai->sensor == sensor)
235     {
236       return ai;
237     }
238     ai = ai->next;
239   }
240   return NULL;
241 }
242
243
244 /**
245  * Create an anomaly report message from a given anomaly info structb inside an
246  * MQ envelope.
247  *
248  * @param ai Anomaly info struct to use
249  * @return
250  */
251 static struct GNUNET_MQ_Envelope *
252 create_anomaly_report_message (struct AnomalyInfo *ai)
253 {
254   struct AnomalyReportMessage *arm;
255   struct GNUNET_MQ_Envelope *ev;
256
257   ev = GNUNET_MQ_msg (arm, GNUNET_MESSAGE_TYPE_SENSOR_ANOMALY_REPORT);
258   GNUNET_CRYPTO_hash (ai->sensor->name, strlen (ai->sensor->name) + 1,
259                       &arm->sensorname_hash);
260   arm->sensorversion_major = ai->sensor->version_major;
261   arm->sensorversion_minor = ai->sensor->version_minor;
262   arm->anomalous = ai->anomalous;
263   arm->anomalous_neighbors =
264       ((float) GNUNET_CONTAINER_multipeermap_size (ai->anomalous_neighbors)) /
265       neighborhood;
266   return ev;
267 }
268
269
270 /**
271  * Send given anomaly info report to given core peer.
272  *
273  * @param cp Core peer to send the report to
274  * @param ai Anomaly info to report
275  */
276 static void
277 send_anomaly_report (struct CorePeer *cp, struct AnomalyInfo *ai)
278 {
279   struct GNUNET_MQ_Envelope *ev;
280
281   GNUNET_assert (NULL != cp->mq);
282   ev = create_anomaly_report_message (ai);
283   GNUNET_MQ_send (cp->mq, ev);
284 }
285
286
287 /**
288  * An inbound anomaly report is received from a peer through CORE.
289  *
290  * @param cls closure (unused)
291  * @param peer the other peer involved
292  * @param message the actual message
293  * @return #GNUNET_OK to keep the connection open,
294  *         #GNUNET_SYSERR to close connection to the peer (signal serious error)
295  */
296 static int
297 handle_anomaly_report (void *cls, const struct GNUNET_PeerIdentity *other,
298                        const struct GNUNET_MessageHeader *message)
299 {
300   struct AnomalyReportMessage *arm;
301   struct GNUNET_SENSOR_SensorInfo *sensor;
302   struct AnomalyInfo *ai;
303   int peer_in_list;
304
305   arm = (struct AnomalyReportMessage *) message;
306   sensor = GNUNET_CONTAINER_multihashmap_get (sensors, &arm->sensorname_hash);
307   if (NULL == sensor || sensor->version_major != arm->sensorversion_major ||
308       sensor->version_minor != arm->sensorversion_minor)
309   {
310     LOG (GNUNET_ERROR_TYPE_WARNING,
311          "I don't have the sensor reported by the peer `%s'.\n",
312          GNUNET_i2s (other));
313     return GNUNET_OK;
314   }
315   ai = get_anomaly_info_by_sensor (sensor);
316   GNUNET_assert (NULL != ai);
317   peer_in_list =
318       GNUNET_CONTAINER_multipeermap_contains (ai->anomalous_neighbors, other);
319   if (GNUNET_YES == ai->anomalous)
320   {
321     if (GNUNET_YES == peer_in_list)
322       GNUNET_break_op (0);
323     else
324       GNUNET_CONTAINER_multipeermap_put (ai->anomalous_neighbors, other, NULL,
325                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
326   }
327   else
328   {
329     if (GNUNET_NO == peer_in_list)
330       GNUNET_break_op (0);
331     else
332       GNUNET_CONTAINER_multipeermap_remove_all (ai->anomalous_neighbors, other);
333   }
334   //TODO: report to collection point if anomalous neigbors jump up or down
335   // by a configurable percentage or is now 0% or 100%
336   return GNUNET_OK;
337 }
338
339
340 /**
341  * Method called whenever a CORE peer disconnects.
342  *
343  * @param cls closure (unused)
344  * @param peer peer identity this notification is about
345  */
346 static void
347 core_disconnect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
348 {
349   struct CorePeer *cp;
350
351   if (0 == GNUNET_CRYPTO_cmp_peer_identity (&mypeerid, peer))
352     return;
353   neighborhood--;
354   cp = cp_head;
355   while (NULL != cp)
356   {
357     if (peer == cp->peerid)
358     {
359       GNUNET_CONTAINER_DLL_remove (cp_head, cp_tail, cp);
360       destroy_core_peer (cp);
361       return;
362     }
363     cp = cp->next;
364   }
365   LOG (GNUNET_ERROR_TYPE_ERROR,
366        _("Received disconnect notification from CORE"
367          " for a peer we didn't know about.\n"));
368 }
369
370
371 /**
372  * Method called whenever a given peer connects through CORE.
373  *
374  * @param cls closure (unused)
375  * @param peer peer identity this notification is about
376  */
377 static void
378 core_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
379 {
380   struct CorePeer *cp;
381   struct AnomalyInfo *ai;
382
383   if (0 == GNUNET_CRYPTO_cmp_peer_identity (&mypeerid, peer))
384     return;
385   neighborhood++;
386   cp = GNUNET_new (struct CorePeer);
387   cp->peerid = (struct GNUNET_PeerIdentity *) peer;
388   cp->mq = GNUNET_CORE_mq_create (core, peer);
389   GNUNET_CONTAINER_DLL_insert (cp_head, cp_tail, cp);
390   /* Send any locally anomalous sensors to the new peer */
391   ai = ai_head;
392   while (NULL != ai)
393   {
394     if (GNUNET_YES == ai->anomalous)
395       send_anomaly_report (cp, ai);
396     ai = ai->next;
397   }
398 }
399
400
401 /**
402  * Function called after #GNUNET_CORE_connect has succeeded (or failed
403  * for good).  Note that the private key of the peer is intentionally
404  * not exposed here; if you need it, your process should try to read
405  * the private key file directly (which should work if you are
406  * authorized...).  Implementations of this function must not call
407  * #GNUNET_CORE_disconnect (other than by scheduling a new task to
408  * do this later).
409  *
410  * @param cls closure (unused)
411  * @param my_identity ID of this peer, NULL if we failed
412  */
413 static void
414 core_startup_cb (void *cls, const struct GNUNET_PeerIdentity *my_identity)
415 {
416   if (NULL == my_identity)
417   {
418     LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to connect to CORE service.\n"));
419     SENSOR_reporting_anomaly_stop ();
420     return;
421   }
422   if (0 != GNUNET_CRYPTO_cmp_peer_identity (&mypeerid, my_identity))
423   {
424     LOG (GNUNET_ERROR_TYPE_ERROR,
425          _("Peer identity received from CORE init doesn't match ours.\n"));
426     SENSOR_reporting_anomaly_stop ();
427     return;
428   }
429 }
430
431
432 /**
433  * Used by the analysis module to tell the reporting module about a change in
434  * the anomaly status of a sensor.
435  *
436  * @param sensor Related sensor
437  * @param anomalous The new sensor anomalous status
438  */
439 void
440 SENSOR_reporting_anomaly_update (struct GNUNET_SENSOR_SensorInfo *sensor,
441                                  int anomalous)
442 {
443   struct AnomalyInfo *ai;
444   struct CorePeer *cp;
445
446   if (GNUNET_NO == module_running)
447     return;
448   ai = get_anomaly_info_by_sensor (sensor);
449   GNUNET_assert (NULL != ai);
450   ai->anomalous = anomalous;
451   /* Report change to all neighbors */
452   cp = cp_head;
453   while (NULL != cp)
454   {
455     send_anomaly_report (cp, ai);
456     cp = cp->next;
457   }
458   //TODO: report change to collection point if report_anomalies
459 }
460
461
462 /**
463  * Iterator for defined sensors and creates anomaly info context
464  *
465  * @param cls unused
466  * @param key unused
467  * @param value a `struct GNUNET_SENSOR_SensorInfo *` with sensor information
468  * @return #GNUNET_YES to continue iterations
469  */
470 static int
471 init_sensor_reporting (void *cls, const struct GNUNET_HashCode *key,
472                        void *value)
473 {
474   struct GNUNET_SENSOR_SensorInfo *sensor = value;
475   struct AnomalyInfo *ai;
476
477   ai = GNUNET_new (struct AnomalyInfo);
478
479   ai->sensor = sensor;
480   ai->anomalous = GNUNET_NO;
481   ai->anomalous_neighbors =
482       GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
483   GNUNET_CONTAINER_DLL_insert (ai_head, ai_tail, ai);
484   return GNUNET_YES;
485 }
486
487
488 /**
489  * Start the sensor anomaly reporting module
490  *
491  * @param c our service configuration
492  * @param s multihashmap of loaded sensors
493  * @return #GNUNET_OK if started successfully, #GNUNET_SYSERR otherwise
494  */
495 int
496 SENSOR_reporting_anomaly_start (const struct GNUNET_CONFIGURATION_Handle *c,
497                                 struct GNUNET_CONTAINER_MultiHashMap *s)
498 {
499   static struct GNUNET_CORE_MessageHandler core_handlers[] = {
500     {&handle_anomaly_report, GNUNET_MESSAGE_TYPE_SENSOR_ANOMALY_REPORT,
501      sizeof (struct AnomalyReportMessage)},
502     {NULL, 0, 0}
503   };
504
505   LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting sensor anomaly reporting module.\n");
506   GNUNET_assert (NULL != s);
507   sensors = s;
508   cfg = c;
509   core =
510       GNUNET_CORE_connect (cfg, NULL, &core_startup_cb, core_connect_cb,
511                            &core_disconnect_cb, NULL, GNUNET_YES, NULL,
512                            GNUNET_YES, core_handlers);
513   if (NULL == core)
514   {
515     LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to connect to CORE service.\n"));
516     SENSOR_reporting_anomaly_stop ();
517     return GNUNET_SYSERR;
518   }
519   GNUNET_CRYPTO_get_peer_identity (cfg, &mypeerid);
520   GNUNET_CONTAINER_multihashmap_iterate (sensors, &init_sensor_reporting, NULL);
521   neighborhood = 0;
522   module_running = GNUNET_YES;
523   return GNUNET_OK;
524 }
525
526 /* end of gnunet-service-sensor_reporting_anomaly.c */