Contiki 2.5
discovery_ipnd.c
Go to the documentation of this file.
1 /**
2  * \addtogroup discovery
3  * @{
4  */
5 
6 /**
7  * \defgroup discovery_ipnd IP-ND discovery module
8  *
9  * @{
10  */
11 
12 /**
13  * \file
14  * \brief IP-ND compliant discovery module
15  * IP-ND = http://tools.ietf.org/html/draft-irtf-dtnrg-ipnd-01
16  *
17  * \author Wolf-Bastian Poettner <poettner@ibr.cs.tu-bs.de>
18  */
19 
20 #include <string.h> // for memset
21 
22 #include "net/netstack.h"
23 #include "net/packetbuf.h"
24 #include "net/rime/rimeaddr.h"
25 #include "clock.h"
26 #include "net/mac/frame802154.h" // for IEEE802154_PANID
27 #include "logging.h"
28 
29 #include "dtn_network.h"
30 #include "agent.h"
31 #include "sdnv.h"
32 #include "statistics.h"
33 #include "convergence_layer.h"
34 #include "eid.h"
35 
36 #include "discovery.h"
37 
38 void discovery_ipnd_refresh_neighbour(rimeaddr_t * neighbour);
39 void discovery_ipnd_save_neighbour(rimeaddr_t * neighbour);
40 
41 #define DISCOVERY_NEIGHBOUR_CACHE 3
42 #define DISCOVERY_CYCLE 5
43 #define DISCOVERY_NEIGHBOUR_TIMEOUT (5*DISCOVERY_CYCLE)
44 #define DISCOVERY_IPND_SERVICE "lowpancl"
45 #define DISCOVERY_IPND_BUFFER_LEN 60
46 #define DISCOVERY_IPND_WHITELIST 0
47 
48 
49 #define IPND_FLAGS_SOURCE_EID (1<<0)
50 #define IPND_FLAGS_SERVICE_BLOCK (1<<1)
51 #define IPND_FLAGS_BLOOMFILTER (1<<2)
52 
53 /**
54  * This "internal" struct extends the discovery_neighbour_list_entry struct with
55  * more attributes for internal use
56  */
57 struct discovery_basic_neighbour_list_entry {
58  struct discovery_basic_neighbour_list_entry *next;
59  rimeaddr_t neighbour;
60  unsigned long timestamp_last;
61  unsigned long timestamp_discovered;
62  uint8_t active;
63 };
64 
65 PROCESS(discovery_process, "DISCOVERY process");
66 
67 /**
68  * List and memory blocks to save information about neighbours
69  */
70 LIST(neighbour_list);
71 MEMB(neighbour_mem, struct discovery_basic_neighbour_list_entry, DISCOVERY_NEIGHBOUR_CACHE);
72 
73 uint8_t discovery_status = 0;
74 static struct etimer discovery_timeout_timer;
75 static struct etimer discovery_cycle_timer;
76 uint16_t discovery_sequencenumber = 0;
77 
78 rimeaddr_t discovery_whitelist[DISCOVERY_IPND_WHITELIST];
79 
80 /**
81  * \brief IPND Discovery init function (called by agent)
82  */
84 {
85  // Enable discovery module
86  discovery_status = 1;
87 
88  // Initialize the neighbour list
89  list_init(neighbour_list);
90 
91  // Initialize the neighbour memory block
92  memb_init(&neighbour_mem);
93 
94 #if DISCOVERY_IPND_WHITELIST > 0
95  // Clear the discovery whitelist
96  memset(&discovery_whitelist, 0, sizeof(rimeaddr_t) * DISCOVERY_IPND_WHITELIST);
97 
98  // Fill the datastructure here
99 
100  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_WRN, "Whitelist enabled");
101 #endif
102 
103  // Start discovery process
104  process_start(&discovery_process, NULL);
105 }
106 
107 /**
108  * \brief Checks if address is currently listed a neighbour
109  * \param dest Address of potential neighbour
110  * \return 1 if neighbour, 0 otherwise
111  */
112 uint8_t discovery_ipnd_is_neighbour(rimeaddr_t * dest)
113 {
114  struct discovery_basic_neighbour_list_entry * entry;
115 
116  if( discovery_status == 0 ) {
117  // Not initialized yet
118  return 0;
119  }
120 
121  for(entry = list_head(neighbour_list);
122  entry != NULL;
123  entry = entry->next) {
124  if( entry->active &&
125  rimeaddr_cmp(&entry->neighbour, dest) ) {
126  return 1;
127  }
128  }
129 
130  return 0;
131 }
132 
133 /**
134  * \brief Enable discovery functionality
135  */
137 {
138  discovery_status = 1;
139 }
140 
141 /**
142  * \brief Disable discovery functionality
143  * Prevents outgoing packets from being sent
144  */
146 {
147  discovery_status = 0;
148 }
149 
150 /**
151  * \brief Parses an incoming EID in an IPND frame
152  * \param eid Pointer where the EID will be stored
153  * \param buffer Pointer to the incoming EID
154  * \param length Length of the Pointer
155  * \return Length of the parsed EID
156  */
157 uint8_t discovery_ipnd_parse_eid(uint32_t * eid, uint8_t * buffer, uint8_t length) {
158  int ret = 0;
159 
160  /* Parse EID */
161  ret = eid_parse_host_length(buffer, length, eid);
162  if( ret < 0 ) {
163  return 0;
164  }
165 
166  return ret;
167 }
168 
169 /**
170  * \brief Dummy function that could parse the IPND service block
171  * \param buffer Pointer to the block
172  * \param length Length of the block
173  * \return dummy
174  */
175 uint8_t discovery_ipnd_parse_service_block(uint8_t * buffer, uint8_t length) {
176  return 0;
177 }
178 
179 /**
180  * \brief Dummy function that could parse the IPND bloom filter
181  * \param buffer Pointer to the filter
182  * \param length Length of the filter
183  * \return dummy
184  */
185 uint8_t discovery_ipnd_parse_bloomfilter(uint8_t * buffer, uint8_t length) {
186  return 0;
187 }
188 
189 /**
190  * \brief DTN Network has received an incoming discovery packet
191  * \param source Source address of the packet
192  * \param payload Payload pointer of the packet
193  * \param length Length of the payload
194  */
195 void discovery_ipnd_receive(rimeaddr_t * source, uint8_t * payload, uint8_t length)
196 {
197  int offset = 0;
198 
199  if( discovery_status == 0 ) {
200  // Not initialized yet
201  return;
202  }
203 
204  if( length < 3 ) {
205  // IPND must have at least 3 bytes
206  return;
207  }
208 
209  // Save all peer from which we receive packets to the active neighbours list
211 
212  // Version
213  if( payload[offset++] != 0x02 ) {
214  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_WRN, "IPND version mismatch");
215  return;
216  }
217 
218  // Flags
219  uint8_t flags = payload[offset++];
220 
221  // Sequence Number
222  uint16_t sequenceNumber = ((payload[offset] & 0xFF) << 8) + (payload[offset+1] & 0xFF);
223  offset += 2;
224 
225  uint32_t eid = 0;
226  if( flags & IPND_FLAGS_SOURCE_EID ) {
227  offset += discovery_ipnd_parse_eid(&eid, &payload[offset], length - offset);
228  }
229 
230  if( flags & IPND_FLAGS_SERVICE_BLOCK ) {
231  offset += discovery_ipnd_parse_service_block(&payload[offset], length - offset);
232  }
233 
234  if( flags & IPND_FLAGS_BLOOMFILTER ) {
235  offset += discovery_ipnd_parse_bloomfilter(&payload[offset], length - offset);
236  }
237 
238  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_DBG, "Discovery from %lu with flags %02X and seqNo %u", eid, flags, sequenceNumber);
239 }
240 
241 /**
242  * \brief Send out IPND beacon
243  */
245  uint8_t ipnd_buffer[DISCOVERY_IPND_BUFFER_LEN];
246  char string_buffer[20];
247  int offset = 0;
248  int len;
249 
250  // Clear the ipnd_buffer
251  memset(ipnd_buffer, 0, DISCOVERY_IPND_BUFFER_LEN);
252 
253  // Version
254  ipnd_buffer[offset++] = 0x02;
255 
256  // Flags
257  ipnd_buffer[offset++] = IPND_FLAGS_SOURCE_EID | IPND_FLAGS_SERVICE_BLOCK;
258 
259  // Beacon Sequence Number
260  ipnd_buffer[offset++] = (discovery_sequencenumber & 0xFF00) << 8;
261  ipnd_buffer[offset++] = (discovery_sequencenumber & 0x00FF) << 0;
262  discovery_sequencenumber ++;
263 
264  /**
265  * Add node's EID
266  */
267  offset += eid_create_host_length(dtn_node_id, &ipnd_buffer[offset], DISCOVERY_IPND_BUFFER_LEN - offset);
268 
269  /**
270  * Add static Service block
271  */
272  // We have one static service
273  ipnd_buffer[offset++] = 1; // Number of services
274 
275  // The service is called DISCOVERY_IPND_SERVICE
276  len = sprintf(string_buffer, DISCOVERY_IPND_SERVICE);
277  offset += sdnv_encode(len, &ipnd_buffer[offset], DISCOVERY_IPND_BUFFER_LEN - offset);
278  memcpy(&ipnd_buffer[offset], string_buffer, len);
279  offset += len;
280 
281  // We exploit ip and port here
282  len = sprintf(string_buffer, "ip=%lu;port=%u;", dtn_node_id, IEEE802154_PANID);
283  offset += sdnv_encode(len, &ipnd_buffer[offset], DISCOVERY_IPND_BUFFER_LEN - offset);
284  memcpy(&ipnd_buffer[offset], string_buffer, len);
285  offset += len;
286 
287  // Now: Send it
288  rimeaddr_t destination = {{0, 0}}; // Broadcast
289  convergence_layer_send_discovery(ipnd_buffer, offset, &destination);
290 }
291 
292 /**
293  * \brief Checks if ''neighbours'' is already known
294  * Yes: refresh timestamp
295  * No: Create entry
296  *
297  * \param neighbour Address of the neighbour that should be refreshed
298  */
299 void discovery_ipnd_refresh_neighbour(rimeaddr_t * neighbour)
300 {
301 #if DISCOVERY_IPND_WHITELIST > 0
302  int i;
303  int found = 0;
304  for(i=0; i<DISCOVERY_IPND_WHITELIST; i++) {
305  if( rimeaddr_cmp(&discovery_whitelist[i], neighbour) ) {
306  found = 1;
307  break;
308  }
309  }
310 
311  if( !found ) {
312  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_WRN, "Ignoring peer %u.%u, not on whitelist", neighbour->u8[0], neighbour->u8[1]);
313  return;
314  }
315 #endif
316 
317  struct discovery_basic_neighbour_list_entry * entry;
318 
319  if( discovery_status == 0 ) {
320  // Not initialized yet
321  return;
322  }
323 
324  for(entry = list_head(neighbour_list);
325  entry != NULL;
326  entry = entry->next) {
327  if( entry->active &&
328  rimeaddr_cmp(&entry->neighbour, neighbour) ) {
329  entry->timestamp_last = clock_seconds();
330  return;
331  }
332  }
333 
335 }
336 
337 /**
338  * \brief Marks a neighbour as 'dead' after multiple transmission attempts have failed
339  * \param neighbour Address of the neighbour
340  */
341 void discovery_ipnd_delete_neighbour(rimeaddr_t * neighbour)
342 {
343  struct discovery_basic_neighbour_list_entry * entry;
344 
345  if( discovery_status == 0 ) {
346  // Not initialized yet
347  return;
348  }
349 
350  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_INF, "Neighbour %u.%u disappeared", neighbour->u8[0], neighbour->u8[1]);
351 
352  // Tell the CL that this neighbour has disappeared
353  convergence_layer_neighbour_down(neighbour);
354 
355  for(entry = list_head(neighbour_list);
356  entry != NULL;
357  entry = entry->next) {
358  if( entry->active &&
359  rimeaddr_cmp(&entry->neighbour, neighbour) ) {
360 
361  // Notify the statistics module
362  statistics_contacts_down(&entry->neighbour, entry->timestamp_last - entry->timestamp_discovered);
363 
364  memb_free(&neighbour_mem, entry);
365  list_remove(neighbour_list, entry);
366 
367  break;
368  }
369  }
370 }
371 
372 /**
373  * \brief Save neighbour to local cache
374  * \param neighbour Address of the neighbour
375  */
376 void discovery_ipnd_save_neighbour(rimeaddr_t * neighbour)
377 {
378  if( discovery_status == 0 ) {
379  // Not initialized yet
380  return;
381  }
382 
383  // If we know that neighbour already, no need to re-add it
384  if( discovery_ipnd_is_neighbour(neighbour) ) {
385  return;
386  }
387 
388  struct discovery_basic_neighbour_list_entry * entry;
389  entry = memb_alloc(&neighbour_mem);
390 
391  if( entry == NULL ) {
392  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_WRN, "no more space for neighbours");
393  return;
394  }
395 
396  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_INF, "Found new neighbour %u.%u", neighbour->u8[0], neighbour->u8[1]);
397 
398  // Clean the entry struct, so that "active" becomes zero
399  memset(entry, 0, sizeof(struct discovery_basic_neighbour_list_entry));
400 
401  entry->active = 1;
402  rimeaddr_copy(&entry->neighbour, neighbour);
403  entry->timestamp_last = clock_seconds();
404  entry->timestamp_discovered = clock_seconds();
405 
406  // Notify the statistics module
407  statistics_contacts_up(neighbour);
408 
409  list_add(neighbour_list, entry);
410 
411  // We have found a new neighbour, now go and notify the agent
412  process_post(&agent_process, dtn_beacon_event, &entry->neighbour);
413 }
414 
415 /**
416  * \brief Returns the list of currently known neighbours
417  * \return Pointer to list with neighbours
418  */
419 struct discovery_neighbour_list_entry * discovery_ipnd_list_neighbours()
420 {
421  return list_head(neighbour_list);
422 }
423 
424 /**
425  * \brief Stops pending discoveries
426  */
428 {
429 
430 }
431 
432 /**
433  * \brief IPND Discovery Persistent Process
434  */
435 PROCESS_THREAD(discovery_process, ev, data)
436 {
437  PROCESS_BEGIN();
438 
439  etimer_set(&discovery_timeout_timer, DISCOVERY_NEIGHBOUR_TIMEOUT * CLOCK_SECOND);
440  etimer_set(&discovery_cycle_timer, DISCOVERY_CYCLE * CLOCK_SECOND);
441  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_INF, "Discovery process running");
442 
443  while(1) {
445 
446  if( etimer_expired(&discovery_timeout_timer) ) {
447  struct discovery_basic_neighbour_list_entry * entry;
448 
449  for(entry = list_head(neighbour_list);
450  entry != NULL;
451  entry = entry->next) {
452  if( entry->active && (clock_seconds() - entry->timestamp_last) > DISCOVERY_NEIGHBOUR_TIMEOUT ) {
453  LOG(LOGD_DTN, LOG_DISCOVERY, LOGL_DBG, "Neighbour %u.%u timed out: %lu vs. %lu = %lu", entry->neighbour.u8[0], entry->neighbour.u8[1], clock_time(), entry->timestamp_last, clock_time() - entry->timestamp_last);
454  discovery_ipnd_delete_neighbour(&entry->neighbour);
455  }
456  }
457 
458  etimer_restart(&discovery_timeout_timer);
459  }
460 
461  /**
462  * Regularly send out our discovery beacon
463  */
464  if( etimer_expired(&discovery_cycle_timer) ) {
466  etimer_restart(&discovery_cycle_timer);
467  }
468  }
469 
470  PROCESS_END();
471 }
472 
473 const struct discovery_driver discovery_ipnd = {
474  "IPND_DISCOVERY",
485 };
486 /** @} */
487 /** @} */
488