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
8static byte debug = 0;
9static long last_run = 0;
10static long last_announce_sent = 0;
11
12static void routing_update(const struct command *com, uint8_t signal_indicator);
13static 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
48static struct hostroute knownhosts[HOSTROUTE_ENTRIES];
49
50static void r_on_new_node(struct hostroute *host);
51const char *decisionname(int decision);
58 memset(&knownhosts, 0, sizeof(knownhosts));
59}
63int 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
72void 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}
95byte get_routing_debug() {
96 return debug;
97}
98void set_routing_debug(byte b) {
99 debug = b;
100 printf("Routing debug set to %i\r\n", debug);
101}
111byte 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
185const 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}
219struct 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
253static 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
286struct 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}
315struct 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}
359void 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}
388static 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 }
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
440void 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
459void 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 {
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 {
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 }
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}
567int 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 */
591void 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
612}
613
614/*
615 * \brief tell server that we have a new peer or one was removed
616 */
617static 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
640}
641
642void 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
int send_command(struct command *com)
send a command to another module (or broadcast)
Definition: queue.c:374
void free_command(struct command *com)
free a command
Definition: queue.c:200
int deliver_command(struct command *com, pkt_callback)
deliver a command to a module
Definition: queue.c:651
void routing_init()
called when we power-up
Definition: routing.c:57
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:583
int send_command_reply(struct command *com, byte flags)
send a reply to a command
Definition: queue.c:562
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
struct hostroute * routing_find_empty_slot()
finds an empty slot in our routing entry list
Definition: routing.c:239
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
struct command * alloc_command()
allocate a free command
Definition: queue.c:173
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
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...
#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