reduce overhead
[corutils.git] / src / routed / nodedb.c
blobc081882846f5d82dbca12e5fc1170315cf140319
1 /**
2 * Connection oriented routing user space utils
3 * Copyright (C) 2009-2020
4 * Authors:
5 * Michael Blizek
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA.
23 #include "routed.h"
26 struct route_list localneighs;
27 struct service_list localservices;
29 __u8 local_query_status = QUERY_STATUS_NOTQUERIED;
30 __u64 local_last_query_time = 0;
32 struct list_head node_list_reachable;
33 struct list_head node_list_unreachable;
34 struct list_head node_list_recalcneeded;
37 #warning todo refcnt of struct node
39 int service_list_contains(struct service_list *services, __be32 port)
41 __u32 u;
43 for (u=0;u<services->numports;u++) {
44 if (services->ports[u] == port) {
45 return 1;
49 return 0;
52 struct node * try_find_neigh_byaddr(__be64 addr)
54 struct list_head *curr = node_list_reachable.next;
56 while (curr != &node_list_reachable) {
57 struct node *currnode = container_of(curr, struct node,
58 node_list);
60 if (currnode->addr == addr)
61 return currnode;
63 curr = curr->next;
66 return 0;
69 struct node * try_find_neigh_byservice(__be32 serviceport)
71 struct list_head *curr = node_list_reachable.next;
73 while (curr != &node_list_reachable) {
74 struct node *currnode = container_of(curr, struct node,
75 node_list);
77 if (service_list_contains(&(currnode->services), serviceport)) {
78 return currnode;
81 curr = curr->next;
84 return 0;
87 struct e2eroute get_e2eroute_localhost(void)
89 struct e2eroute ret;
90 bzero(&ret, sizeof(struct e2eroute));
91 ret.numhops = 0;
92 return ret;
95 struct e2eroute get_e2eroute(struct node *node)
97 struct e2eroute ret;
99 struct node *currnode;
100 __u32 u;
102 ASSERT(node != 0);
104 bzero(&ret, sizeof(struct e2eroute));
106 if (get_static_route(node->addr, &ret) == 0)
107 return ret;
109 ret.numhops = 0;
110 for (currnode = node;currnode != 0;currnode = currnode->prevhop) {
111 ret.numhops += 1;
112 ASSERT(currnode->prevhop != 0 || currnode->is_neighbor == 1);
115 ret.addr = calloc(8, ret.numhops);
116 u = ret.numhops;
117 for (currnode = node;currnode != 0;currnode = currnode->prevhop) {
118 ASSERT(u > 0);
119 ret.addr[u-1] = node->addr;
120 u--;
122 ASSERT(currnode->prevhop != 0 || currnode->is_neighbor == 1);
124 ASSERT(u == 0);
126 return ret;
129 #warning todo call from rds_connect
130 void free_e2eroute_addr(struct e2eroute *e2er)
132 if (e2er->numhops == 0) {
133 ASSERT(e2er->addr == 0);
134 } else {
135 ASSERT(e2er->addr != 0);
136 free(e2er->addr);
137 e2er->addr = 0;
139 e2er->numhops = 0;
142 bzero(e2er, sizeof(struct e2eroute));
146 int connect_to_host_recv(int fd, struct libcor_nonblock_resumeinfo *lnr,
147 struct nonblock_resumeinfo_connect_to_host_recv *rcr,
148 struct e2eroute e2er)
150 if (rcr->state == 0) {
151 rcr->fd = fd;
152 rcr->numhops = e2er.numhops;
154 rcr->state = 1;
155 rcr->u=0;
158 ASSERT(rcr->fd == fd);
159 ASSERT(rcr->numhops == e2er.numhops);
161 for (;rcr->u < rcr->numhops;rcr->u++) {
162 int rc = read_resp_nonblock(fd, 0, lnr, 0, 0, 0);
163 if (rc != RC_OK)
164 return rc;
166 bzero(rcr, sizeof(struct nonblock_resumeinfo_connect_to_host_recv));
167 return RC_OK;
170 int connect_to_host_send(int fd,
171 struct libcor_nonblock_resumeinfo *lnr,
172 struct nonblock_resumeinfo_connect_to_host_send *cth,
173 struct e2eroute e2er, int flush)
175 if (cth->state == 0) {
176 cth->fd = fd;
178 cth->e2er = e2er;
180 cth->state = 1;
181 cth->u=0;
184 ASSERT(cth->fd == fd);
185 ASSERT(cth->e2er.numhops == e2er.numhops);
187 for (;cth->u < e2er.numhops;) {
188 int flush1 = (flush && cth->u == e2er.numhops - 1);
189 int rc = send_connect_neigh_nonblock(fd, lnr,
190 e2er.addr[cth->u], flush1);
191 cth->u++;
192 if (rc != RC_OK)
193 return rc;
195 bzero(cth, sizeof(struct nonblock_resumeinfo_connect_to_host_send));
196 return RC_OK;
199 static void add_neigh(void *ptr, __be64 addr, __u32 latency_us)
201 struct route_list *list = (struct route_list *) ptr;
202 struct node *node;
204 if (verbosity >= 2)
205 printf("add neighbor %llx latency %u\n", ntohll(addr), latency_us);
207 if (addr == 0)
208 return;
210 if (list->numroutes >= list->rows_alloc)
211 return;
213 ASSERT(list->routes[list->numroutes].dst == 0);
215 node = try_find_neigh_byaddr(addr);
217 if (node == 0) {
218 node = calloc(1, sizeof(struct node));
219 node->addr = addr;
220 list_add_tail(&(node->node_list), &node_list_reachable);
223 list->routes[list->numroutes].dst = node;
224 list->routes[list->numroutes].latency_us = latency_us;
225 list->numroutes++;
228 static void init_neighlist(void *ptr, __u32 numneighs)
230 struct route_list *list = (struct route_list *) ptr;
232 if (verbosity >= 2)
233 printf("init neighlist %u\n", numneighs);
235 if (list->routes != 0) {
236 free(list->routes);
239 if (numneighs > 16)
240 numneighs = 16;
241 #warning todo limit
242 list->rows_alloc = (__u16) numneighs;
243 list->numroutes = 0;
244 list->routes = calloc(numneighs, sizeof(struct route));
247 static void add_service(void *ptr, __be32 port)
249 struct service_list *list = (struct service_list *) ptr;
251 if (verbosity >= 2)
252 printf("add service %u\n", ntohs(port));
254 if (list->numports >= list->rows_alloc)
255 return;
257 list->ports[list->numports] = port;
258 list->numports++;
261 static void init_servicelist(void *ptr, __u32 numservices)
263 struct service_list *list = (struct service_list *) ptr;
265 if (verbosity >= 2)
266 printf("init servicelist %u\n", numservices);
268 if (list->ports != 0) {
269 free(list->ports);
272 if (numservices > 16)
273 numservices = 16;
274 list->rows_alloc = (__u16) numservices;
275 list->numports = 0;
276 list->ports = calloc(numservices, 4);
280 static int _export_servicelist(int fd, __be64 addr, __u32 cost,
281 struct service_list *services)
283 __u32 u;
285 for (u=0;u<services->numports;u++) {
286 int rc = dprintf(fd, "%llx:%u:%u\n", ntohll(addr),
287 ntohl(services->ports[u]), cost);
289 if (rc < 0) {
290 return 1;
294 return 0;
297 #warning todo move to /run/
298 static void export_servicelist(void)
300 char *servicelist_txtfile = "/var/lib/cor/services/txt";
301 char *servicelist_tmpfile = "/var/lib/cor/services/tmp";
303 int fd = 0;
304 struct list_head *curr;
306 if (export_servicelist_enabled == 0)
307 return;
309 fd = open(servicelist_tmpfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
310 if (fd <= 0) {
311 perror("export servicelist");
312 return;
315 if (_export_servicelist(fd, 0, 0, &localservices) != 0)
316 goto err;
318 curr = node_list_reachable.next;
319 while (curr != &node_list_reachable) {
320 struct node *currnode = container_of(curr, struct node,
321 node_list);
323 curr = curr->next;
325 ASSERT(has_localaddr != 0 || localaddr == 0);
326 if (currnode->addr == 0)
327 continue;
328 if (currnode->addr == localaddr)
329 continue;
331 if (_export_servicelist(fd, currnode->addr,
332 currnode->metric, &(currnode->services)) != 0)
333 goto err;
336 fsync(fd);
337 close(fd);
339 if (rename(servicelist_tmpfile, servicelist_txtfile) != 0) {
340 perror("export servicelist");
341 goto err2;
344 if (0) {
345 err:
346 close(fd);
347 err2:
348 unlink(servicelist_tmpfile);
352 static void _recalc_routes_add_recalcneeded(struct node *node)
354 struct list_head *curr;
356 curr = node_list_recalcneeded.prev;
357 while (curr != &node_list_recalcneeded) {
358 struct node *currnode = container_of(curr, struct node,
359 node_list);
361 if (currnode->metric > node->metric) {
362 list_add(&(node->node_list), &(currnode->node_list));
363 return;
366 curr = curr->next;
369 list_add(&(node->node_list), &node_list_recalcneeded);
372 static void _recalc_routes(struct node *node)
374 struct route_list *routes = &(node->routes);
376 __u32 u;
378 for (u=0;u<routes->numroutes;u++) {
379 struct node *target = routes->routes[u].dst;
381 __u32 metric = node->metric +
382 routes->routes[u].latency_us +
383 EXTRA_LATENCY_PERHOP_US;
385 ASSERT(node->metric != 0);
386 ASSERT(node->is_neighbor == 1 || node->prevhop != 0);
388 if (metric < node->metric)
389 continue;
391 if (target->metric <= metric)
392 continue;
394 target->metric = metric;
395 target->prevhop = node;
397 list_del(&(target->node_list));
398 _recalc_routes_add_recalcneeded(target);
401 list_del(&(node->node_list));
402 list_add_tail(&(node->node_list), &node_list_reachable);
405 static void _recalc_routes_localneighs(void)
407 __u32 u;
408 for (u=0;u<localneighs.numroutes;u++) {
409 struct node *target = localneighs.routes[u].dst;
411 target->is_neighbor = 1;
412 target->metric = localneighs.routes[u].latency_us +
413 EXTRA_LATENCY_PERHOP_US;
414 target->prevhop = 0;
416 list_del(&(target->node_list));
417 _recalc_routes_add_recalcneeded(target);
421 static void _recalc_routes_reset_reachable(void)
423 struct list_head *curr = node_list_reachable.next;
424 while (curr != &node_list_reachable) {
425 struct node *currnode = container_of(curr, struct node,
426 node_list);
428 currnode->is_neighbor = 0;
429 currnode->prevhop = 0;
430 currnode->metric = 0;
432 curr = curr->next;
435 list_add(&node_list_unreachable, &node_list_reachable);
436 list_del(&node_list_reachable);
437 init_list_head(&node_list_reachable);
440 static void recalc_routes(void)
442 if (verbosity >= 2)
443 printf("recalc_routes\n");
445 _recalc_routes_reset_reachable();
447 _recalc_routes_localneighs();
449 while (!list_empty(&node_list_recalcneeded)) {
450 struct node *currnode = container_of(
451 node_list_recalcneeded.next, struct node,
452 node_list);
454 _recalc_routes(currnode);
457 /* we cannot free struct node here, because nonblock_resumeinfo may still have a pointer */
458 /*while (!list_empty(&node_list_unreachable)) {
459 struct node *currnode = container_of(
460 node_list_unreachable.next, struct node,
461 node_list);
463 list_del(&(currnode->node_list));
465 if (currnode->addr != 0) {
466 free(currnode->addr);
467 currnode->addr = 0;
470 free(currnode);
473 export_servicelist();
477 static void discover_localhost()
479 int fd;
480 int rc;
482 __u8 bindata_noresp = 0;
484 local_last_query_time = get_time_nsec();
486 fd = socket(PF_COR, SOCK_RAW, PROTO_COR_RAW);
487 if (fd < 0) {
488 perror("socket");
489 goto err_noclose;
492 if (setsockopts_tos(fd, COR_TOS_HIGH_LATENCY) != 0) {
493 perror("setsockopt");
494 goto err;
497 if (connect(fd, 0, 0) != 0) {
498 perror("connect");
499 goto err;
503 rc = send_list_neigh(fd, 1);
504 if (rc != RC_OK)
505 goto err;
507 rc = read_resp(fd, 1, &bindata_noresp);
508 if (rc != RC_OK)
509 goto err;
511 if (bindata_noresp != 0) {
512 init_neighlist(&localneighs, 0);
513 } else {
514 char *resp_bin = 0;
515 __u32 resp_bin_len = 0;
517 rc = read_resp_bin(fd, &resp_bin, &resp_bin_len);
518 if (rc != RC_OK)
519 goto err;
521 rc = parse_neigh_list(resp_bin, resp_bin_len, DEFAULT_LATENCY_US,
522 &localneighs, init_neighlist, add_neigh);
523 free(resp_bin);
524 resp_bin = 0;
525 resp_bin_len = 0;
526 if (rc != RC_OK)
527 goto err;
531 rc = send_list_services(fd, 1);
532 if (rc != RC_OK)
533 goto err;
535 rc = read_resp(fd, 1, &bindata_noresp);
536 if (rc != RC_OK)
537 goto err;
539 if (bindata_noresp != 0) {
540 init_servicelist(&localservices, 0);
541 } else {
542 char *resp_bin = 0;
543 __u32 resp_bin_len = 0;
545 rc = read_resp_bin(fd, &resp_bin, &resp_bin_len);
546 if (rc != RC_OK)
547 goto err;
549 rc = parse_service_list(resp_bin, resp_bin_len, &localservices,
550 init_servicelist, add_service);
551 free(resp_bin);
552 resp_bin = 0;
553 resp_bin_len = 0;
554 if (rc != RC_OK)
555 goto err;
558 close(fd);
560 local_query_status = QUERY_STATUS_QUERIED;
562 recalc_routes();
564 if (0) {
565 err:
566 close(fd);
567 err_noclose:
568 local_query_status = QUERY_STATUS_ERROR;
572 void _discover_network(int fd, struct nonblock_resumeinfo *nr)
574 struct node *node;
575 struct libcor_nonblock_resumeinfo *lnr;
576 int rc = RC_CONNBROKEN;
578 __u8 bindata_noresp = 0;
580 lnr = &(nr->data.discover_network.lnr);
582 ASSERT(nr->type == EPOLLDATA_DISCOVERNETWORK);
584 node = nr->data.discover_network.node;
586 if (nr->data.discover_network.state == 1) {
587 goto state_1;
588 } else if (nr->data.discover_network.state == 2) {
589 goto state_2;
590 } else if (nr->data.discover_network.state == 3) {
591 goto state_3;
592 } else if (nr->data.discover_network.state == 4) {
593 goto state_4;
594 } else if (nr->data.discover_network.state == 5) {
595 goto state_5;
596 } else if (nr->data.discover_network.state == 6) {
597 goto state_6;
598 } else if (nr->data.discover_network.state == 7) {
599 goto state_7;
600 } else if (nr->data.discover_network.state != 0) {
601 ASSERT(0);
604 if (verbosity >= 2)
605 printf("discover resume neighbor %llx\n", ntohll(node->addr));
607 nr->data.discover_network.e2er = get_e2eroute(node);
609 nr->data.discover_network.state = 1;
610 state_1:
611 rc = connect_to_host_send(fd, lnr,
612 &(nr->data.discover_network.connect_send),
613 nr->data.discover_network.e2er, 0);
615 if (check_rc(rc, "load_neigh_list: connect_to_host_send error"))
616 goto out;
618 rc = send_list_neigh_nonblock(fd, lnr, 0);
619 nr->data.discover_network.state = 2;
620 if (check_rc(rc, "load_neigh_list: send_list_neigh error"))
621 goto out;
623 state_2:
624 rc = send_list_services_nonblock(fd, lnr, 1);
625 nr->data.discover_network.state = 3;
626 if (check_rc(rc, "load_neigh_list: send_list_services error"))
627 goto out;
629 state_3:
630 rc = connect_to_host_recv(fd, lnr,
631 &(nr->data.discover_network.connect_recv),
632 nr->data.discover_network.e2er);
633 if (check_rc(rc, "load_neigh_list: connect_to_host_recv error"))
634 goto out;
636 free_e2eroute_addr(&nr->data.discover_network.e2er);
638 nr->data.discover_network.state = 4;
639 state_4:
640 rc = read_resp_nonblock(fd, 1, lnr, 0, 0, &bindata_noresp);
641 if (check_rc(rc, "load_neigh_list: read_resp error"))
642 goto out;
644 if (bindata_noresp != 0) {
645 init_neighlist(&(node->routes), 0);
646 } else {
647 char *resp_bin = 0;
648 __u32 resp_bin_len = 0;
650 nr->data.discover_network.state = 5;
651 state_5:
652 rc = read_resp_bin_nonblock(fd, lnr, &resp_bin, &resp_bin_len);
653 if (check_rc(rc, "load_neigh_list: read_resp_bin_nonblock (neighborlest) error"))
654 goto out;
656 rc = parse_neigh_list(resp_bin, resp_bin_len, DEFAULT_LATENCY_US,
657 &(node->routes), init_neighlist, add_neigh);
659 free(resp_bin);
660 resp_bin = 0;
661 resp_bin_len = 0;
663 ASSERT(rc != RC_WOULDBLOCK);
664 if (check_rc(rc, "load_neigh_list: parse_neigh_list error"))
665 goto out;
668 nr->data.discover_network.state = 6;
669 state_6:
670 rc = read_resp_nonblock(fd, 1, lnr, 0, 0, &bindata_noresp);
671 if (check_rc(rc, "load_neigh_list: read_resp error"))
672 goto out;
674 if (bindata_noresp != 0) {
675 char *resp_bin = 0;
676 __u32 resp_bin_len = 0;
678 nr->data.discover_network.state = 7;
679 state_7:
680 rc = read_resp_bin_nonblock(fd, lnr, &resp_bin, &resp_bin_len);
681 if (check_rc(rc, "load_neigh_list: read_resp_bin_nonblock (servicelist) error"))
682 goto out;
684 rc = parse_service_list(resp_bin, resp_bin_len, &(node->services),
685 init_servicelist, add_service);
687 free(resp_bin);
688 resp_bin = 0;
689 resp_bin_len = 0;
691 ASSERT(rc != RC_WOULDBLOCK);
692 if (check_rc(rc, "load_neigh_list: parse_service_list error"))
693 goto out;
696 node->query_status = QUERY_STATUS_QUERIED;
697 recalc_routes();
698 out:
699 //printf("load_neigh_list state %d\n", nr->data.discover_network.state);
700 if (rc != RC_WOULDBLOCK) {
701 free_e2eroute_addr(&nr->data.discover_network.e2er);
703 //printf("load_neigh_list rc %d\n", rc);
704 close(fd);
705 if (nr != 0)
706 free(nr);
708 if (rc == RC_CONNBROKEN)
709 node->query_status = QUERY_STATUS_ERROR;
710 node->query_in_progress = 0;
714 static int _discover_network_start_load_node(struct node *node)
716 int fd;
717 int rc;
718 struct nonblock_resumeinfo *nr;
720 struct epoll_event epe;
722 if (verbosity >= 2)
723 printf("discover start neighbor %llx\n", ntohll(node->addr));
725 ASSERT(node->query_in_progress == 0);
726 node->query_in_progress = 1;
727 node->last_query_time = get_time_nsec();
729 fd = socket(PF_COR, SOCK_RAW, PROTO_COR_RAW);
730 if (fd < 0) {
731 perror("socket");
732 goto err;
735 if (setsockopts_tos(fd, COR_TOS_HIGH_LATENCY) != 0) {
736 close(fd);
737 perror("setsockopt");
738 goto err;
741 if (connect(fd, 0, 0) != 0) {
742 close(fd);
743 perror("connect");
744 goto err;
747 set_nonblock(fd, 1);
749 nr = (struct nonblock_resumeinfo *)
750 malloc(sizeof(struct nonblock_resumeinfo));
751 bzero(nr, sizeof(struct nonblock_resumeinfo));
752 nr->fd = fd;
753 nr->type = EPOLLDATA_DISCOVERNETWORK;
754 nr->data.discover_network.node = node;
756 bzero(&epe, sizeof(struct epoll_event));
757 epe.events = (EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLET);
758 epe.data.ptr = nr;
759 epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &epe);
761 if (0) {
762 err:
763 node->query_status = QUERY_STATUS_ERROR;
764 node->query_in_progress = 0;
768 void discover_network(void)
770 struct list_head *curr;
772 __u64 time_nsec = get_time_nsec();
774 if (local_query_status != QUERY_STATUS_QUERIED ||
775 local_last_query_time + 1000000L < time_nsec) {
776 if (verbosity >= 2)
777 printf("discover localhost\n");
778 discover_localhost();
781 curr = node_list_reachable.next;
783 while (curr != &node_list_reachable) {
784 struct node *currnode = container_of(curr, struct node,
785 node_list);
787 if (currnode->query_in_progress == 0 && (
788 currnode->query_status ==
789 QUERY_STATUS_NOTQUERIED ||
790 currnode->last_query_time + 30000000L <
791 time_nsec)) {
792 _discover_network_start_load_node(currnode);
795 curr = curr->next;
799 int initial_discovery_inprogress(void)
801 struct list_head *curr = node_list_reachable.next;
803 while (curr != &node_list_reachable) {
804 struct node *currnode = container_of(curr, struct node,
805 node_list);
807 if (currnode->query_status == QUERY_STATUS_NOTQUERIED &&
808 currnode->query_in_progress != 0)
809 return 1;
811 curr = curr->next;
814 return 0;
817 void init_nodedb(void)
819 init_list_head(&node_list_reachable);
820 init_list_head(&node_list_unreachable);
821 init_list_head(&node_list_recalcneeded);