SingingCat  0
application
routing.c
Go to the documentation of this file.
1 #include "main-header.h"
2 #include "routing.h"
3 #include "espressif/esp8266_flash.h"
4 #include "user_app_exe.h"
5 #include "forwarding.h"
6 #include "networkif.h"
7 
8 static byte debug = 0;
9 static long last_run = 0;
10 static long last_announce_sent = 0;
11 
12 static void routing_update(const struct command *com, uint8_t signal_indicator);
13 static void peer_update(int added, struct hostroute *host);
14 
15 #define ROUTING_EVENT_LOOP_INTERVAL 30
16 #define ROUTING_ANNOUNCE_INTERVAL 120
17 
39 #define HOSTROUTE_ENTRIES 40
40 
46 #define MAX_ROUTE_AGE_SECONDS 600
48 static struct hostroute knownhosts[HOSTROUTE_ENTRIES];
49 
50 static void r_on_new_node(struct hostroute *host);
51 const char *decisionname(int decision);
57 void routing_init() {
58  memset(&knownhosts, 0, sizeof(knownhosts));
59 }
63 int is_device_online(int device) {
64  if (device == SOURCE_WIFI) {
65  return esp8266_is_enabled(); // for all but the first packet we should use this: esp_cloud_is_connected();
66  } else if (device == SOURCE_SERIAL) {
67  return 1;
68  }
69  return isonline_by_type(device);
70 }
71 
72 void bridge_packet(struct command *com) {
73  if (!config_get_flag(CONFIG_FLAGS_ROUTING_ENABLED)) {
74  printf("bridging of packets disabled\r\n");
75  return;
76  }
77 
78  if (com->flags & COMFLAGS_FORWARDED) {
79  printf("Not bridging packet. Flag FORWARDED is set already\r\n");
80  return;
81  }
82  com->recipient = BROADCAST;
83  com->flags = com->flags | COMFLAGS_FORWARDED;
84  com->sourcedev = SOURCE_WIFI;
85  send_command(com);
86  int i;
87 
88  // radio is dodgy? try several times?
89  for (i = 0; i < 5; i++) {
90  com->sourcedev = SOURCE_RADIO;
91  send_command(com);
92  Delay(20);
93  }
94 }
95 byte get_routing_debug() {
96  return debug;
97 }
98 void set_routing_debug(byte b) {
99  debug = b;
100  printf("Routing debug set to %i\r\n", debug);
101 }
111 byte got_new_packet(struct command *com, uint8_t signal_indicator) {
112  if (!is_command_valid(com)) {
113  error_com(com);
114  return 0;
115  }
116  if (debug) {
117  printf("***** ROUTING DEBUG (new packet received) *****\r\n");
118  command_print(com);
119  }
120  // we always 'remember' nodes (i.e. keep our routing tables uptodate)
121  routing_update(com, signal_indicator);
122 
123  byte action = 0;
124  long me = get_my_node_id();
125  long r = com->recipient;
126  long t = com->target;
127 
128  // never forward route-request replies! they MUST remain local.
129  if (com->com == 12) {
130  if ((r == BROADCAST) && (com->sender == CLOUD_SERVER)) {
131  send_command_reply(com, COMFLAGS_ACK | COMFLAGS_SUCCESS);
132  }
133  free_command(com);
134  return 0;
135  }
136  if (r == me) {
137  // *** DEAL WITH PACKETS TO MYSELF (as recipient)*** //
138  if (t == me) {
139  // we are the target (as well as the recipient) - process locally
140  process_command(com);
141  action = 3;
142  } else if (t == BROADCAST) {
143  bridge_packet(com);
144  process_command(com);
145  action = 2;
146  } else if (t == CLOUD_SERVER) {
147  forward_packet(com);
148  action = 1;
149  } else {
150  // we are recipient but not target, forward packet
151  int e = forward_packet(com);
152  if (e > 0) {
153  routing_error(com, e);
154  }
155  action = 1;
156  }
157  } else if (r == BROADCAST) {
158  // *** DEAL WITH BROADCASTS *** //
159  if ((t == me) || (t == BROADCAST)) {
160  process_command(com);
161  action = 3;
162  } else {
163  // discard
164  action = 0;
165  }
166  } else if (r == CLOUD_SERVER) {
167  // *** DEAL WITH PACKETS TO SERVER (but not me as receipient) *** //
168  // always discard ;-)
169  action = 0;
170  } else {
171  // *** DEAL WITH PACKETS TO ANYONE (but not me as recipient) *** //
172  // always discard ;-)
173  action = 0;
174  }
175 
176  // if cloud wanted us to forward it, tell it that we discarded it
177  if ((action == 0) && (com->sender == CLOUD_SERVER) && (r == me)) {
178  routing_error(com, 63);
179  }
180  printf("Routing decision completed: %s (%i) from %N to %N (via %N)\r\n", decisionname(action), (int)action, com->sender, com->target, com->recipient);
181  free_command(com);
182  return action;
183 }
184 
185 const char *decisionname(int decision) {
186  if (decision == 0) {
187  return "discard";
188  } else if (decision == 1) {
189  return "forward";
190  } else if (decision == 2) {
191  return "bridge";
192  } else if (decision == 3) {
193  return "local";
194  }
195  return "?";
196 }
197 
204  int i;
205  int res = 0;
206 
207  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
208  if (knownhosts[i].host != 0) {
209  res++;
210  }
211  }
212  return res;
213 }
219 struct hostroute *routing_get_node_by_index(const int index) {
220  int i;
221  int res = 0;
222 
223  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
224  if (knownhosts[i].host != 0) {
225  if (res == index) {
226  return &knownhosts[i];
227  }
228  res++;
229  }
230  }
231  return NULL;
232 }
233 
234 
235 
240  int i;
241 
242  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
243  if (knownhosts[i].host == 0) {
244  return &knownhosts[i];
245  }
246  }
247  return NULL;
248 }
249 
253 static int is_older_than(long now, long actual, int maxage) {
254  if (now >= actual) {
255  return ((now - actual) > maxage) ? 1 : 0;
256  }
257  return 1;
258 }
259 
265  int i;
266  long now = mculib_get_seconds_since_boot();
267 
268  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
269  struct hostroute *host = &knownhosts[i];
270  if (host->host == 0) {
271  continue;
272  }
273  if (is_older_than(now, host->lastseen, MAX_ROUTE_AGE_SECONDS)) {
274  peer_update(0, host);
275  host->host = 0;
276  }
277  }
278 }
279 
286 struct hostroute *routing_find_route(const long nodeid, const byte sourcedev) {
287  int i;
288 
289  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
290  if (knownhosts[i].device != sourcedev) {
291  continue;
292  }
293  if (knownhosts[i].host == nodeid) {
294  return &knownhosts[i];
295  }
296  }
297 
298  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
299  if (knownhosts[i].device != sourcedev) {
300  continue;
301  }
302  if (nodeid == CLOUD_SERVER) {
303  if (knownhosts[i].hops_to_server > 0) {
304  return &knownhosts[i];
305  }
306  }
307  }
308 
309  return NULL;
310 }
315 struct hostroute *routing_find_host(const long nodeid) {
316  int i;
317  struct hostroute *res;
318  struct hostroute *cur;
319 
320  res = NULL;
321  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
322  cur = &knownhosts[i];
323 #ifdef HACK_ROUTING
324  // here's a hack to make debugging easier. this has no impact if device 0x0F4D0F84 isn't unavailable
325  if ((cur->host == 0x0F4D0F84) && (is_device_online(cur->device))) {
326  return cur;
327  }
328 #endif
329  if ((res != NULL) && (cur->lastseen < res->lastseen)) {
330  continue;
331  }
332  if (cur->host == nodeid) {
333  if (!is_device_online(cur->device)) {
334  // this route isn't useful if the device it's on is offline
335  continue;
336  }
337  res = cur;
338  }
339  }
340 
341  if ((res == NULL) && (nodeid == CLOUD_SERVER)) {
342  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
343  cur = &knownhosts[i];
344  if ((res != NULL) && (cur->lastseen < res->lastseen)) {
345  continue;
346  }
347  if (cur->hops_to_server > 0) {
348  res = cur;
349  }
350  }
351  }
352 
353  return res;
354 }
359 void routing_request_reply(const struct command *com) {
360  int i;
361  struct hostroute *host;
362 
363  for (i = 0; i < com->argctr; i++) {
364  long nid = ascii_parse_hex((const byte *)get_arg((struct command *)com, i), 8, NULL);
365  printf("Route request reply from %N for %N\r\n", com->sender, nid);
366  host = routing_find_route(nid, com->sourcedev);
367  if (host == NULL) {
369  if (host == NULL) {
370  IPLOG("Routing table overflow!\r\n");
371  return;
372  }
373  host->host = nid;
374  host->nexthop = com->sender;
375  host->device = com->sourcedev;
376  host->lastseen = mculib_get_seconds_since_boot();
377  IPLOG("New cat: %N on (%i)\r\n", (void *)host->host, host->device);
378  r_on_new_node(host);
379  }
380  }
381 }
388 static void routing_update(const struct command *com, uint8_t signal_indicator) {
389  struct hostroute *host;
390 
391  if ((com->com == 12) && (com->target == get_my_node_id())) {
393  }
394  host = routing_find_route(com->sender, com->sourcedev);
395  if (host == NULL) {
396  // no route and not an announce: ignore
397  if (com->com != 23) {
398  return;
399  }
401  if (host == NULL) {
402  IPLOG("Routing table overflow!\r\n");
403  return;
404  }
405  host->host = com->sender;
406  host->device = com->sourcedev;
407  host->lastseen = mculib_get_seconds_since_boot();
408  IPLOG("New cat: %N on (%i)\r\n", (void *)host->host, host->device);
409  host->signal_indicator = signal_indicator;
410  r_on_new_node(host);
411  }
412 
413  host->signal_indicator = signal_indicator;
414 
415  if (com->sourcedev == SOURCE_WIFI) {
416  host->nexthop = com->sender; // no wifi multi-hop routing as of yet.
417  host->device = com->sourcedev;
418  } else if (com->sourcedev == SOURCE_SERIAL) {
419  host->nexthop = com->sender;
420  host->device = com->sourcedev;
421  } else {
422  if (com->sender != CLOUD_SERVER) {
423  host->nexthop = com->sender;
424  }
425  host->device = com->sourcedev;
426  }
427  if (com->com == 23) {
428  int b = atoi(get_arg(com, 0));
429  host->hops_to_server = (byte)b;
430  if (com->argctr > 1) {
431  host->hosttype = atoi(get_arg(com, 1));
432  }
433  printf("Updated routing table entry host=%p\r\n", (void *)host->host);
434  }
435  host->lastseen = mculib_get_seconds_since_boot();
436 }
437 
438 
439 
440 void routing_event_loop() {
441  long now = mculib_get_seconds_since_boot();
442 
443  if (mculib_has_time_passed(ROUTING_EVENT_LOOP_INTERVAL, &last_run)) {
444  print_node_id(config_get_ram_struct()->nodeid);
446  }
447  int iv = ROUTING_ANNOUNCE_INTERVAL;
448 
449  if (esp8266_is_accespoint()) {
450  iv = 2; // if accesspoint announce every 2 seconds
451  //printf("Announce because we are accesspoint!\r\n");
452  }
453  if (mculib_has_time_passed(iv, &last_announce_sent)) {
454  send_routing_update_now();
455  last_announce_sent = now;
456  }
457 }
458 
459 void send_routing_update_now() {
460  struct command *com;
461 
462  if (debug) {
463  printf("routing: sending periodic announcements now\r\n");
464  }
465  if (esp8266_is_enabled()) {
466  // wifi
467  com = alloc_command();
468  if (com == NULL) {
469  return;
470  }
471  com->sourcedev = SOURCE_WIFI;
472  com->com = 23; // announce
473  com->target = 0xFFFFFFFF;
474  com->recipient = 0xFFFFFFFF;
475  com->flags = COMFLAGS_ACK | COMFLAGS_SUCCESS;
476  if (config_get_flag(CONFIG_FLAGS_POWER_SAVE)) {
477  com->flags |= COMFLAGS_POWERSAVE;
478  command_add_arg(com, "%i", 200); // tell others that we are quite far away from server. we're powersaving sometimes, so not a good router at all.
479  } else {
480  command_add_arg(com, "%i", (int)get_hops_to_server());
481  }
482  command_add_arg(com, "1");
483  deliver_command(com, NULL);
484  }
485 
486  // radio
487  com = alloc_command();
488  if (com == NULL) {
489  return;
490  }
491  com->sourcedev = SOURCE_RADIO;
492  com->com = 23; // announce
493  com->target = 0xFFFFFFFF;
494  com->recipient = 0xFFFFFFFF;
495  com->index = -1;
496  com->flags = COMFLAGS_ACK | COMFLAGS_SUCCESS;
497  if (config_get_flag(CONFIG_FLAGS_POWER_SAVE)) {
498  command_add_arg(com, "%i", 200); // tell others that we are quite far away from server. we're powersaving sometimes, so not a good router at all.
499  } else {
500  command_add_arg(com, "%i", (int)get_hops_to_server());
501  }
502  command_add_arg(com, "1");
503 
504 //TODO: replace with list of active network interfaces
505  int xr = 0;
506 
507  if (isready_by_type(SOURCE_RADIO)) {
508  com->sourcedev = SOURCE_RADIO;
509  xr = send_command(com);
510  }
511  if (isready_by_type(SOURCE_LORA)) {
512  com->sourcedev = SOURCE_LORA;
513  xr = send_command(com);
514  }
515  if (xr != 0) {
516  printf("Send_command failed: %i\r\n", xr);
517  }
518  free_command(com);
519 }
520 
527  if (esp_cloud_is_connected()) {
528  return 1;
529  } else {
530  //TODO implement indirect routes
531  return 0;
532  }
533 }
534 
540  int i;
541  struct hostroute *host;
542 
543  long now = mculib_get_seconds_since_boot();
544 
545  printf("==== routing table ====\r\n");
546  printf("Hops to cloud: %i\r\n", get_hops_to_server());
547  for (i = 0; i < HOSTROUTE_ENTRIES; i++) {
548  host = &knownhosts[i];
549  if (host->host == 0) {
550  continue;
551  }
552  printf("Host: %p, nexthop: %p, dev: %i, type: %i, server: %i, lastseen: %i secs, sigind: %i\r\n",
553  (void *)host->host, (void *)host->nexthop,
554  (int)host->device,
555  (int)host->hosttype,
556  (int)knownhosts[i].hops_to_server,
557  (int)(now - host->lastseen),
558  (int)host->signal_indicator
559  );
560  }
561  printf("==== end routing table ====\r\n");
562 }
567 int send_command_one_arg(int command, long target, const byte *arg1, int arg1len) {
568  struct command *com = alloc_command();
569 
570  if (com == NULL) {
571  // in this case, if we have no spare commands, do nothing.
572  // user will press button again. And it should not happen anyways
573  return 1;
574  }
575  com->com = command;
576  com->recipient = 0; // automatically determine dev & hop please
577  com->target = target;
578  com->flags = 0;
579  // add some meta information about the button
580  command_add_binary_arg(com, arg1len, arg1);
581  // deliver it.
582  // Callback is NULL, because it either gets delivered or not.
583  // if not, there's little we can do about it anyways.
584  deliver_command(com, NULL);
585  return 0;
586 }
587 
588 /*
589  * \brief return a routing error message to sender, including the reason why we could not forward the packet
590  */
591 void routing_error(struct command *f_com, int errorcode) {
592  char tbuf[20];
593  struct command *com = alloc_command();
594 
595  if (com == NULL) {
596  return;
597  }
598  com->com = 51; // routing update (route-update)
599  com->target = f_com->sender;
600  snprintf(tbuf, sizeof(tbuf), "t=1"); // routing failure
601  command_add_arg(com, tbuf);
602  snprintf(tbuf, sizeof(tbuf), "fw=%i", errorcode);
603  command_add_arg(com, tbuf);
604  snprintf(tbuf, sizeof(tbuf), "rcpt=0x%N", f_com->recipient);
605  command_add_arg(com, tbuf);
606  snprintf(tbuf, sizeof(tbuf), "tgt=0x%N", f_com->target);
607  command_add_arg(com, tbuf);
608  snprintf(tbuf, sizeof(tbuf), "seq=%i", f_com->index);
609  command_add_arg(com, tbuf);
610  send_command(com); // try to deliver once. don't retry
611  free_command(com);
612 }
613 
614 /*
615  * \brief tell server that we have a new peer or one was removed
616  */
617 static void peer_update(int added, struct hostroute *host) {
618  char tbuf[20];
619  struct command *com = alloc_command();
620 
621  if (com == NULL) {
622  return;
623  }
624  com->com = 51; // routing update (route-update)
625  com->target = 1;
626  if (added) {
627  snprintf(tbuf, sizeof(tbuf), "t=2"); // peer added
628  } else {
629  snprintf(tbuf, sizeof(tbuf), "t=3"); // peer removed
630  }
631  command_add_arg(com, tbuf);
632  snprintf(tbuf, sizeof(tbuf), "peer=0x%N", host->host);
633  command_add_arg(com, tbuf);
634  snprintf(tbuf, sizeof(tbuf), "device=%i", host->device);
635  command_add_arg(com, tbuf);
636  snprintf(tbuf, sizeof(tbuf), "sigind=%i", host->signal_indicator);
637  command_add_arg(com, tbuf);
638  send_command(com); // try to deliver once. don't retry
639  free_command(com);
640 }
641 
642 void r_on_new_node(struct hostroute *host) {
643  if (user_app_executable()) {
644  invoke_on_new_node(host);
645  }
646  peer_update(1, host);
647 }
definitions of routing table structures
void routing_print_table()
print routing table
Definition: routing.c:539
struct hostroute * routing_find_route(const long nodeid, const byte sourcedev)
find specific hostroute to target or NULL if none known special case, if we ask for a route to server...
Definition: routing.c:286
struct hostroute * routing_find_empty_slot()
finds an empty slot in our routing entry list
Definition: routing.c:239
int send_command(struct command *com)
send a command to another module (or broadcast)
Definition: queue.c:372
void free_command(struct command *com)
free a command
Definition: queue.c:198
int deliver_command(struct command *com, pkt_callback)
deliver a command to a module
Definition: queue.c:649
void routing_init()
called when we power-up
Definition: routing.c:57
struct command * alloc_command()
allocate a free command
Definition: queue.c:171
void remove_stale_routes()
"old" routes are being removed this is the route garbage collector
Definition: routing.c:264
void command_add_arg(struct command *com, const char *format,...)
adds an arg to a partially initialised command structure
struct hostroute * routing_find_host(const long nodeid)
find route to host or NULL if none known
Definition: routing.c:315
long get_my_node_id()
get the id of my node
long host
Definition: routing.h:21
struct hostroute * routing_get_node_by_index(const int index)
finds a nodeid by index. [0...n]
Definition: routing.c:219
int esp8266_is_accespoint()
returns !=0 if we are currently an accesspoint
Definition: esp8266.c:572
int send_command_reply(struct command *com, byte flags)
send a reply to a command
Definition: queue.c:560
int routing_count_nodes()
determine number of nodes known
Definition: routing.c:203
int is_device_online(int device)
return 1 if the specified device is online and routing
Definition: routing.c:63
void process_command(struct command *com)
command is parsed, now execute it
byte got_new_packet(struct command *com, uint8_t signal_indicator)
stack received a new packet (signal indicator is a 0-255 byte value, interface specific)
Definition: routing.c:111
byte get_hops_to_server()
how many hops to the server?
Definition: routing.c:526
long lastseen
Definition: routing.h:22
int send_command_one_arg(int command, long target, const byte *arg1, int arg1len)
helper function to quickly and easily send a command somewhere return 0 if ok, else errorcode
Definition: routing.c:567
void command_print(struct command *com)
prints a command in human readable format to serial console
const char * get_arg(const struct command *com, int index)
given an argument by index[0..n], will return a pointer to the bytearray (excluding the fieldtype) th...
int command_add_binary_arg(struct command *com, const int len, const byte *srcbuf)
adds a binary parameter to command returns 0 if ok otherwise errorcode
int forward_packet(struct command *com)
a command is forwarded to target based on our hostroutes
Definition: forwarding.c:31
#define MAX_ROUTE_AGE_SECONDS
any route that has not been used or seen for longer than this is subject to removal (the garbage coll...
Definition: routing.c:46
void routing_request_reply(const struct command *com)
we call this when we receive a reply to a routing request this adds or updates a new route
Definition: routing.c:359
definitions of routing table structures
int com
Definition: command.h:22
long target
Definition: command.h:16
uint8_t sourcedev
Definition: command.h:17
long recipient
Definition: command.h:15
int index
Definition: command.h:13
uint8_t flags
Definition: command.h:23
uint8_t argctr
Definition: command.h:24
long sender
Definition: command.h:14