/* This file is part of Netsukuku * (c) Copyright 2005 Andrea Lo Pumo aka AlpT * * This source code is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * This source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * Please refer to the GNU Public License for more details. * * You should have received a copy of the GNU Public License along with * this source code; if not, write to: * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * -- * * radar.c * * The radar sends in broadcast a bouquet of MAX_RADAR_SCANS# packets and waits * for the ECHO_REPLY of the nodes which are alive. It then recollects the * replies and builds a small statistic, updates, if necessary, the internal * maps, the bnode maps and the qspn buffer. * A radar is fired periodically by the radar_daemon(), which is started as a * thread. */ #include "includes.h" #include "llist.c" #include "endianness.h" #include "if.h" #include "bmap.h" #include "route.h" #include "request.h" #include "pkts.h" #include "qspn.h" #include "radar.h" #include "netsukuku.h" #include "common.h" pthread_attr_t radar_qspn_send_t_attr; void first_init_radar(void) { max_radar_wait = MAX_RADAR_WAIT; pthread_attr_init(&radar_qspn_send_t_attr); pthread_attr_setdetachstate(&radar_qspn_send_t_attr, PTHREAD_CREATE_DETACHED); /* register the radar's ops in the pkt_op_table */ add_pkt_op(ECHO_ME, SKT_BCAST, ntk_udp_radar_port, radard); add_pkt_op(ECHO_REPLY, SKT_UDP, ntk_udp_radar_port, radar_recv_reply); rlist = (struct rnode_list *) clist_init(&rlist_counter); alwd_rnodes = (struct allowed_rnode *) clist_init(&alwd_rnodes_counter); radar_daemon_ctl = 0; init_radar(); } void last_close_radar(void) { close_radar(); rnl_reset(&rlist, &rlist_counter); } void init_radar(void) { hook_retry = 0; my_echo_id = 0; total_radar_scans = 0; setzero(radar_scans, sizeof(radar_scans)); radar_scan_mutex = 0; radar_q = (struct radar_queue *) clist_init(&radar_q_counter); setzero(send_qspn_now, sizeof(u_char) * MAX_LEVELS); } void close_radar(void) { if (radar_q_counter) clist_destroy(&radar_q, &radar_q_counter); } void reset_radar(void) { if (me.cur_node->flags & MAP_HNODE) { free_new_node(); rnl_reset(&rlist, &rlist_counter); } close_radar(); init_radar(); } /* * free_new_node * * frees all the temporary alloced rq->node structs used at the * hook time. */ void free_new_node(void) { struct radar_queue *rq; rq = radar_q; list_for(rq) if (rq->node && ((int) rq->node != RADQ_EXT_RNODE)) { xfree(rq->node); rq->node = 0; } } /* * find_node_radar_q * * returns the first radar_queue struct which has the * rq->node pointer equal to `node'. */ struct radar_queue * find_node_radar_q(map_node * node) { struct radar_queue *rq; rq = radar_q; list_for(rq) if (rq->node == node) return rq; return 0; } /* * find_ip_radar_q * * returns the first radar_queue struct which has the rq->ip * member equal to the given `ip'. */ struct radar_queue * find_ip_radar_q(inet_prefix * ip) { struct radar_queue *rq; rq = radar_q; list_for(rq) if (!memcmp(rq->ip.data, ip->data, MAX_IP_SZ)) return rq; return 0; } /* * rnl_add * * adds a new rnode_list struct in the `*rnlist' list. The new * allocated struct will be filled respectively with `rnode' and `dev'. * It returns the added `rnode_list' struct. */ struct rnode_list * rnl_add(struct rnode_list **rnlist, int *rnlist_counter, map_node * rnode, interface * dev) { struct rnode_list *rnl; rnl = xzalloc(sizeof(struct rnode_list)); rnl->node = (map_node *) rnode; rnl->dev[0] = dev; rnl->dev_n++; clist_add(rnlist, rnlist_counter, rnl); return rnl; } /* * rnl_del * * deletes the `rnl' struct from the `rnlist' rnode_list. * If `close_socket' is not zero, `rnl'->tcp_sk will be closed. */ void rnl_del(struct rnode_list **rnlist, int *rnlist_counter, struct rnode_list *rnl, int close_socket) { if (rnl) { if (close_socket && rnl->tcp_sk) inet_close(&rnl->tcp_sk); clist_del(rnlist, rnlist_counter, rnl); } if (!(*rnlist_counter)) *rnlist = 0; } /* * rnl_reset * * reset the whole rnode_list */ void rnl_reset(struct rnode_list **rnlist, int *rnlist_counter) { struct rnode_list *rnl = *rnlist, *next; list_safe_for(rnl, next) rnl_del(rnlist, rnlist_counter, rnl, 1); *rnlist = (struct rnode_list *) clist_init(rnlist_counter); } /* * rnl_del_dead_rnode * * it removes all the rnode_list structs which are related * to a rnode which doesn't exist anymore in `root_node' * It returns the number of delete rnodes_list structs. */ int rnl_del_dead_rnode(struct rnode_list **rnlist, int *rnlist_counter, map_node * root_node) { struct rnode_list *rnl = *rnlist, *next; int i = 0; list_safe_for(rnl, next) if (rnode_find(root_node, rnl->node) < 0) { rnl_del(rnlist, rnlist_counter, rnl, 1); i++; } return i; } /* * rnl_find_rpos * * returns the first rnode_list struct, contained in * `rnlist', which has rnl->node equal to `node'. */ struct rnode_list * rnl_find_node(struct rnode_list *rnlist, map_node * node) { struct rnode_list *rnl = rnlist; list_for(rnl) if (rnl->node == node) return rnl; return 0; } /* * rnl_add_dev * * If `rnl' is 0 a new struct is added in `*rnlist' using `node'. * In both cases the `new_dev' is added in the rnl->dev[] array of * pointers (if it isn't already present there) and rnl->dev_n is * incremented. * On error -1 is returned. */ int rnl_add_dev(struct rnode_list **rnlist, int *rnlist_counter, struct rnode_list *rnl, map_node * node, interface * new_dev) { int i; if (!rnl) { rnl = rnl_add(rnlist, rnlist_counter, node, new_dev); return 0; } if (rnl->dev_n >= MAX_INTERFACES) return -1; for (i = 0; i < rnl->dev_n; i++) if (rnl->dev[i] == new_dev) return 0; rnl->dev[rnl->dev_n++] = new_dev; return 0; } /* * rnl_del_dev * * It searches a pointer in the rnl->dev[] array equal to * `del_dev'. If it is found, it is set to 0 and rnl->dev_n is decremented, * otherwise 0 is returned. * If rnlist->dev_n is 0, the found rnlist struct is deleted from the llist. * On error -1 is returned. */ int rnl_del_dev(struct rnode_list **rnlist, int *rnlist_counter, struct rnode_list *rnl, interface * del_dev) { int i; if (!rnl) return 0; if (rnl->dev_n <= 0) return -1; for (i = 0; i < rnl->dev_n; i++) { if (rnl->dev[i] == del_dev) { if (i == rnl->dev_n - 1) rnl->dev[i] = 0; else { rnl->dev[i] = rnl->dev[rnl->dev_n - 1]; rnl->dev[rnl->dev_n - 1] = 0; } rnl->dev_n--; break; } } if (!rnl->dev_n) rnl_del(rnlist, rnlist_counter, rnl, 1); return 0; } /* * rnl_update_devs * * it updates the device array present in the rnode_list struct of `node'. * It searches in rnlist a struct which have rnlist->node == `node', * then it substitutes rnlist->dev with `devs' and rnlist->dev_n with `dev_n'. * If there is a difference between the new `devs' array and the old one, 1 is * returned. */ int rnl_update_devs(struct rnode_list **rnlist, int *rnlist_counter, map_node * node, interface ** devs, int dev_n) { struct rnode_list *old_rnl, *new_rnl; int i, dev_pos, update = 0; old_rnl = rnl_find_node(*rnlist, node); if (!dev_n) { /* * The new `devs' array is empty, therefore delete old_rnl */ rnl_del(rnlist, rnlist_counter, old_rnl, 1); return 0; } if (old_rnl) /* * Diff old_rnl->dev and `devs' */ for (i = 0; i < dev_n; i++) { dev_pos = FIND_PTR(devs[i], old_rnl->dev, old_rnl->dev_n); if (dev_pos < 0) { update = 1; break; } } else if (!old_rnl) update = 1; if (update) { new_rnl = rnl_add(rnlist, rnlist_counter, node, devs[0]); for (i = 1; i < dev_n; i++) rnl_add_dev(rnlist, rnlist_counter, new_rnl, node, devs[i]); new_rnl->tcp_sk = (old_rnl) ? old_rnl->tcp_sk : 0; rnl_del(rnlist, rnlist_counter, old_rnl, 0); } return update; } interface ** rnl_get_dev(struct rnode_list * rnlist, map_node * node) { struct rnode_list *rnl; rnl = rnl_find_node(rnlist, node); return !rnl ? 0 : rnl->dev; } interface * rnl_get_rand_dev(struct rnode_list * rnlist, map_node * node) { struct rnode_list *rnl; return !(rnl = rnl_find_node(rnlist, node)) ? 0 : rnl->dev[rand_range(0, rnl->dev_n - 1)]; } /* * rnl_get_sk * * It returns the tcp socket associated to rnode `node'. * If the socket is set to zero, it tries to create a tcp connection to * `node' to the `ntk_tcp_port' port. * * On error -1 is returned. */ int rnl_get_sk(struct rnode_list *rnlist, map_node * node) { struct rnode_list *rnl; if (!(rnl = rnl_find_node(rnlist, node))) return -1; if (!rnl->tcp_sk) { inet_prefix to; int i; if (me.cur_node->flags & MAP_HNODE) { struct radar_queue *rq; /* If we are hooking, get the IP from the radar * queue */ if (!(rq = find_node_radar_q(rnl->node))) return -1; inet_copy(&to, &rq->ip); } else { rnodetoip((u_int) me.int_map, (u_int) node, me.cur_quadg.ipstart[1], &to); } /* Try to connect using the `i'th device. If it fails, try * another device */ for (i = 0; i < rnl->dev_n && rnl->tcp_sk <= 0; i++) rnl->tcp_sk = pkt_tcp_connect(&to, ntk_tcp_port, rnl->dev[i]); /* If the socket is connected, set it to keepalive */ if ((rnl->tcp_sk = (rnl->tcp_sk <= 0) ? 0 : rnl->tcp_sk)) set_keepalive_sk(rnl->tcp_sk); } return rnl->tcp_sk > 0 ? rnl->tcp_sk : -1; } /* * rnl_set_sk * * It sets the socket associated to rnode `node' to `sk' */ void rnl_set_sk(struct rnode_list *rnlist, map_node * node, int sk) { struct rnode_list *rnl; if (!(rnl = rnl_find_node(rnlist, node))) return; rnl->tcp_sk = sk; } /* * rnl_close_all_sk * * It closes all the opened tcp_sk of the `rnlist' llist */ void rnl_close_all_sk(struct rnode_list *rnlist) { struct rnode_list *rnl = rnlist; list_for(rnl) if (rnl->tcp_sk) inet_close(&rnl->tcp_sk); } /* * rnl_fill_rq * * It sets the `pkt'->sk and `pkt'->to variables. * The `pkt'->sk is retrieved using rnl_get_sk() * * On error -1 is returned. */ int rnl_fill_rq(map_node * rnode, PACKET * pkt) { int tries = 0; retry: if (!pkt->sk && (pkt->sk = rnl_get_sk(rlist, rnode)) <= 0) { error(ERROR_MSG "Couldn't get the socket associated " "to dst_rnode", ERROR_FUNC); return -1; } if (inet_getpeername(pkt->sk, &pkt->to, 0) < 0) { tries++; if (tries < 2) goto retry; return -1; } return 0; } /* * rnl_send_rq * * It is a wrapper to send_rq. It is used to send or receive a packet to/from * the specified `rnode'. * * On error -1 is returned. * * Note: the pkt->sk must not be closed. */ int rnl_send_rq(map_node * rnode, PACKET * pkt, int pkt_flags, u_char rq, int rq_id, u_char re, int check_ack, PACKET * rpkt) { int ret, tries = 0; retry: if (!pkt->sk && rnl_fill_rq(rnode, pkt) < 0) return -1; ret = send_rq(pkt, pkt_flags, rq, rq_id, re, check_ack, rpkt); if ((ret == SEND_RQ_ERR_CONNECT || ret == SEND_RQ_ERR_SEND || ret == SEND_RQ_ERR_RECV)) { /* The socket has been corrupted, set it to 0 and try again */ inet_close(&pkt->sk); rnl_set_sk(rlist, rnode, 0); tries++; if (tries < 2) goto retry; } return ret; } /* * is_rnode_allowed * * it verifies if the rnode described by the `rip' IP is * present in the `alr' llist. If it is 1 is returned, otherwise 0. */ int is_rnode_allowed(inet_prefix rip, struct allowed_rnode *alr) { int i, e, gid[MAX_LEVELS]; iptogids(&rip, gid, FAMILY_LVLS); list_for(alr) { for (e = 0, i = alr->min_level; i < alr->tot_level; i++) if (gid[i] != alr->gid[i]) { e = 1; break; } if (!e) return 1; } return 0; } /* * new_rnode_allowed * * add a new allowed rnode in the `alr' llist which has * already `*alr_counter' members. `gid', `min_lvl', and `tot_lvl' are the * respective field of the new allowed_rnode struct. */ void new_rnode_allowed(struct allowed_rnode **alr, int *alr_counter, int *gid, int min_lvl, int tot_lvl) { struct allowed_rnode *new_alr; new_alr = xmalloc(sizeof(struct allowed_rnode)); new_alr->min_level = min_lvl; new_alr->tot_level = tot_lvl; setzero(new_alr->gid, sizeof(int) * MAX_LEVELS); memcpy(&new_alr->gid[min_lvl], &gid[min_lvl], sizeof(int) * (tot_lvl - min_lvl)); debug(DBG_SOFT, "new_rnode_allowed: %d, %d, %d, %d. min_lvl: %d, tot_lvl: %d", gid[0], gid[1], gid[2], gid[3], min_lvl, tot_lvl); clist_add(alr, alr_counter, new_alr); } void reset_rnode_allowed(struct allowed_rnode **alr, int *alr_counter) { if (*alr) list_destroy((*alr)); *alr = (struct allowed_rnode *) clist_init(alr_counter); } /* * count_hooking_nodes * * returns the number of hooking nodes, which are stored * in the radar_queue. */ int count_hooking_nodes(void) { struct radar_queue *rq; int total_hooking_nodes = 0; rq = radar_q; list_for(rq) { if (!rq->node) continue; if (rq->node->flags & MAP_HNODE) total_hooking_nodes++; } return total_hooking_nodes; } /* * final_radar_queue * * analyses the received ECHO_REPLY pkt and write the * average rtt of each found node in the radar_queue. */ void final_radar_queue(void) { struct radar_queue *rq; int e; struct timeval sum; u_int f_rtt; setzero(&sum, sizeof(struct timeval)); rq = radar_q; list_for(rq) { if (!rq->node) continue; /* Sum the rtt of all the received pongs */ for (e = 0; e < rq->pongs; e++) timeradd(&rq->rtt[e], &sum, &sum); /* Add penality rtt for each pong lost */ for (; e < MAX_RADAR_SCANS; e++) timeradd(&rq->rtt[e - rq->pongs], &sum, &sum); f_rtt = MILLISEC(sum) / MAX_RADAR_SCANS; MILLISEC_TO_TV(f_rtt, rq->final_rtt); } my_echo_id = 0; } /* * radar_remove_old_rnodes * * It removes all the old rnodes ^_- It store in rnode_delete[level] the number * of deleted rnodes. This function is used by radar_update_map */ int radar_remove_old_rnodes(char *rnode_deleted) { map_node *node, *root_node, *broot_node; map_gnode *gnode; map_bnode *bnode; ext_rnode *e_rnode = 0; ext_rnode_cache *erc; struct qspn_buffer *qb; struct rnode_list *rnl; int i, e, node_pos, bm, rnode_pos, bnode_rnode_pos, root_node_pos; int broot_node_pos; int level, blevel, external_node, total_levels, first_level; void *void_map, *void_gnode; if (!me.cur_node->links) return 0; for (i = 0; i < me.cur_node->links; i++) { node = (map_node *) me.cur_node->r_node[i].r_node; if (!(node->flags & MAP_VOID)) /* The rnode is not really dead! */ continue; if (node->flags & MAP_ERNODE) { e_rnode = (ext_rnode *) node; external_node = 1; total_levels = e_rnode->quadg.levels; first_level = 1; quadg_setflags(&e_rnode->quadg, MAP_VOID); } else { external_node = 0; total_levels = 1; first_level = 0; } for (level = first_level; level < total_levels; level++) { qspn_set_map_vars(level, 0, &root_node, &root_node_pos, 0); blevel = level - 1; /* delete the rnode from the rnode_list */ rnl = rnl_find_node(rlist, node); rnl_del(&rlist, &rlist_counter, rnl, 1); /* * Just delete it from all the maps. */ if (!level && !external_node) { void_map = me.int_map; node_pos = pos_from_node(node, me.int_map); rnode_pos = i; debug(DBG_NORMAL, "radar: The node %d is dead", node_pos); /* delete it from the int_map and update the gcount */ map_node_del(node); qspn_dec_gcount((int *) qspn_gnode_count, level + 1, 1); /* delete the route */ rt_update_node(0, node, 0, 0, 0, level); send_qspn_now[level] = 1; } else { void_map = me.ext_map; gnode = e_rnode->quadg.gnode[_EL(level)]; /** delete the direct route to the ext_node */ if (level == 1) rt_update_node(&e_rnode->quadg.ipstart[0], e_rnode, 0, 0, 0, /*level=0 */ 0); /**/ void_gnode = (void *) gnode; if (!void_gnode) continue; node_pos = pos_from_gnode(gnode, me.ext_map[_EL(level)]); rnode_pos = g_rnode_find((map_gnode *) root_node, gnode); debug(DBG_NORMAL, "The ext_node (gid %d, lvl %d) is" " dead", e_rnode->quadg.gid[level], level); /* bnode_map update */ for (e = 0; blevel >= 0; blevel--) { qspn_set_map_vars(blevel, 0, &broot_node, &broot_node_pos, 0); bm = map_find_bnode(me.bnode_map[blevel], me.bmap_nodes[blevel], broot_node_pos); if (bm == -1) continue; bnode = &me.bnode_map[blevel][bm]; bnode_rnode_pos = rnode_find(bnode, (map_node *) e_rnode-> quadg.gnode[_EL(level)]); if (bnode_rnode_pos != -1) rnode_del(bnode, bnode_rnode_pos); if (!bnode->links) { me.bnode_map[blevel] = map_bnode_del(me.bnode_map[blevel], &me.bmap_nodes[blevel], bnode); broot_node->flags &= ~MAP_BNODE; } else e = 1; } if (!e) /* We are no more a bnode */ me.cur_node->flags &= ~MAP_BNODE; /* If we were the only bnode which bordered on * `gnode', delete it from the map */ if (map_find_bnode_rnode (me.bnode_map[level - 1], me.bmap_nodes[level - 1], gnode) == -1) { qspn_dec_gcount((int *) qspn_gnode_count, level + 1, gnode->gcount); gmap_node_del(gnode); gnode_dec_seeds(&me.cur_quadg, level); /* update the seeds */ } /* Delete the entries from the routing table */ rt_update_node(0, 0, &e_rnode->quadg, 0, 0, level); send_qspn_now[level] = 1; } if (rnode_pos >= 0 && root_node->links > 0) rnode_del(root_node, rnode_pos); if (!root_node->links) { /* We are alone in the dark. Sigh. */ qspn_time_reset(level, level, FAMILY_LVLS); } else if (!external_node) erc_update_rnodepos(me.cur_erc, root_node, rnode_pos); /* Now we delete it from the qspn_buffer */ if (qspn_b[level]) { qb = qspn_b[level]; qb = qspn_b_find_rnode(qb, node); if (qb) qspn_b[level] = list_del(qspn_b[level], qb); } SET_BIT(rnode_deleted, level); } /* * Kick out the external_node from the root_node and destroy it * from the ext_rnode_cache */ if (external_node) { /* external rnode cache update */ erc = erc_find(me.cur_erc, e_rnode); if (erc) e_rnode_del(&me.cur_erc, &me.cur_erc_counter, erc); rnode_del(me.cur_node, i); } /* If the rnode we deleted from the root_node was swapped with * the last rnodes, we have to inspect again the same * root_node->r_node[ `i' ] rnode, because now it is another * rnode */ if (i != (me.cur_node->links + 1) - 1) i--; } if (!me.cur_node->links) { /* - Diary - * Tue Mar 14 07:29:58 CET 2006 * Damn! All my rnodes died, I am the last survivor in this * great lone land... I have to reset my memory... farewell! */ qspn_reset_counters(FAMILY_LVLS); } return 0; } /* * radar_update_bmap * * updates the bnode map of the given `level' the root_node bnode in the bmap * will also point to the gnode of level `gnode_level'+1 that is * `rq'->quadg.gnode[_EL(gnode_level+1)]. */ void radar_update_bmap(struct radar_queue *rq, int level, int gnode_level) { map_gnode *gnode; map_node *root_node; map_rnode *rnode, rn; int bm, rnode_pos, root_node_pos; void *void_map; if (level == me.cur_quadg.levels - 1) return; qspn_set_map_vars(level, 0, &root_node, &root_node_pos, 0); void_map = me.ext_map; gnode = rq->quadg.gnode[_EL(gnode_level + 1)]; bm = map_find_bnode(me.bnode_map[level], me.bmap_nodes[level], root_node_pos); if (bm == -1) { bm = map_add_bnode(&me.bnode_map[level], &me.bmap_nodes[level], root_node_pos, 0); rnode_pos = -1; } else rnode_pos = rnode_find(&me.bnode_map[level][bm], &gnode->g); if (rnode_pos == -1) { setzero(&rn, sizeof(map_rnode)); rn.r_node = (int *) &gnode->g; rnode_add(&me.bnode_map[level][bm], &rn); rnode_pos = 0; } rnode = &me.bnode_map[level][bm].r_node[rnode_pos]; rnode->trtt = MILLISEC(rq->final_rtt); } /* * radar_update_map * * it updates the int_map and the ext_map if any bnodes are found. * Note that the rnodes in the map are held in a different way. First of all the qspn * is not applied to them (we already know how to reach them ;) and they have only * one rnode... ME. So me.cur_node->r_node[x].r_node->r_node[0] == me.cur_node. * Gotcha? */ void radar_update_map(void) { struct qspn_buffer *qb; struct radar_queue *rq; ext_rnode_cache *erc; map_gnode *gnode = 0; map_node *node, *root_node; map_rnode rnn, *new_root_rnode; ext_rnode *e_rnode; int i, diff, rnode_pos; u_char rnode_added[MAX_LEVELS / 8], rnode_deleted[MAX_LEVELS / 8]; int level, external_node, total_levels, root_node_pos, node_update; void *void_map; const char *ntop; char updated_rnodes, routes_update, devs_update; updated_rnodes = routes_update = devs_update = 0; setzero(rnode_added, sizeof(rnode_added)); setzero(rnode_deleted, sizeof(rnode_deleted)); /** * Let's consider all our rnodes void, in this way we'll know what * rnodes will remain void after the update. */ for (i = 0; i < me.cur_node->links; i++) { node = (map_node *) me.cur_node->r_node[i].r_node; node->flags |= MAP_VOID | MAP_UPDATE; } /**/ rq = radar_q; list_for(rq) { if (!rq->node) continue; if (!(me.cur_node->flags & MAP_HNODE) && (rq->flags & MAP_HNODE)) continue; /* * We need to know if it is a node which is not in the gnode * where we are (external_rnode). */ if ((int) rq->node == RADQ_EXT_RNODE) { external_node = 1; total_levels = rq->quadg.levels; } else { external_node = 0; total_levels = 1; } for (level = total_levels - 1; level >= 0; level--) { qspn_set_map_vars(level, 0, &root_node, &root_node_pos, 0); node_update = devs_update = 0; if (!level) { void_map = me.int_map; node = rq->node; } else { /* Skip the levels where the ext_rnode belongs * to our same gids */ if (!quadg_gids_cmp(rq->quadg, me.cur_quadg, level)) continue; /* Update only the gnodes which belongs to * our same gid of the upper level, because * we don't keep the internal info of the * extern gnodes. */ if ((level < rq->quadg.levels - 1) && quadg_gids_cmp(rq->quadg, me.cur_quadg, level + 1)) { rq->quadg.gnode[_EL(level)] = 0; continue; } /* Ehi, we are a bnode */ root_node->flags |= MAP_BNODE; me.cur_node->flags |= MAP_BNODE; void_map = me.ext_map; gnode = rq->quadg.gnode[_EL(level)]; node = &gnode->g; } if (external_node && !level && me.cur_erc_counter) { erc = e_rnode_find(me.cur_erc, &rq->quadg, 0); if (!erc) rnode_pos = -1; else { rnode_pos = erc->rnode_pos; node = (map_node *) erc->e; } } else rnode_pos = rnode_find(root_node, node); if (rnode_pos == -1) { /* W00t, we've found a new rnode! */ node_update = 1; rnode_pos = root_node->links; ntop = inet_to_str(rq->quadg.ipstart[level]); if (server_opt.dbg_lvl || !level) loginfo ("Radar: New node found: %s, ext: %d, level: %d", ntop, external_node, level); if (external_node && !level) { /* * If this node we are processing is external, at level 0, * in the root_node's rnodes we add a rnode which point * to a ext_rnode struct. */ setzero(&rnn, sizeof(map_rnode)); e_rnode = xzalloc(sizeof(ext_rnode)); memcpy(&e_rnode->quadg, &rq->quadg, sizeof(quadro_group)); e_rnode->node.flags = MAP_BNODE | MAP_GNODE | MAP_RNODE | MAP_ERNODE; rnn.r_node = (int *) e_rnode; node = rq->node = &e_rnode->node; new_root_rnode = &rnn; /* Update the external_rnode_cache list */ e_rnode_add(&me.cur_erc, e_rnode, rnode_pos, &me.cur_erc_counter); } else { /*We purge all the node's rnodes. */ rnode_destroy(node); /* * This node has only one rnode, * and that is the root_node. */ setzero(&rnn, sizeof(map_rnode)); rnn.r_node = (int *) root_node; rnode_add(node, &rnn); /* It is a border node */ if (level) node->flags |= MAP_BNODE | MAP_GNODE; node->flags |= MAP_RNODE; /* * Fill the rnode to be added in the * root_node. */ setzero(&rnn, sizeof(map_rnode)); rnn.r_node = (int *) node; new_root_rnode = &rnn; } /* * The new node is added in the root_node's * rnodes. */ rnode_add(root_node, new_root_rnode); /* Update the qspn_buffer */ if (!external_node || level) { qb = xzalloc(sizeof(struct qspn_buffer)); qb->rnode = node; qspn_b[level] = list_add(qspn_b[level], qb); send_qspn_now[level] = 1; } /* If the new rnode wasn't present in the map, * then it is also a new node in the map, so * update the seeds counter too */ if (!level && !external_node && (node->flags & MAP_VOID)) { gnode_inc_seeds(&me.cur_quadg, level); qspn_inc_gcount(qspn_gnode_count, level + 1, 1); } SET_BIT(rnode_added, level); } else { /* * Nah, We have the node in the map. Let's see if * its rtt is changed */ if (!send_qspn_now[level] && node->links) { diff = abs(root_node->r_node[rnode_pos].trtt - MILLISEC(rq->final_rtt)); if (diff >= RTT_DELTA) { node_update = 1; send_qspn_now[level] = 1; debug(DBG_NOISE, "node %s rtt changed, diff: %d", inet_to_str(rq->ip), diff); } } } /* Restore the flags */ if (level) gnode->flags &= ~GMAP_VOID; node->flags &= ~MAP_VOID & ~MAP_UPDATE & ~QSPN_OLD; /* * Update the devices list of the rnode */ if (!level) { devs_update = rnl_update_devs(&rlist, &rlist_counter, node, rq->dev, rq->dev_n); if (devs_update) routes_update++; } /* Nothing is really changed */ if (!node_update) continue; /* Update the rtt */ root_node->r_node[rnode_pos].trtt = MILLISEC(rq->final_rtt); /* Bnode map stuff */ if (external_node && level) { /* * All the root_node bnodes which are in the * bmaps of level smaller than `level' points to * the same gnode which is rq->quadg.gnode[_EL(level-1+1)]. * This is because the inferior levels cannot * have knowledge about the bordering gnode * which is in an upper level, but it's necessary that * they know which who the root_node borders on, * so the get_route algorithm can descend to * the inferior levels and it will still know * what is the border node which is linked * to the target gnode. */ for (i = 0; i < level; i++) radar_update_bmap(rq, i, level - 1); send_qspn_now[level - 1] = 1; } if (node_update || devs_update) node->flags |= MAP_UPDATE; } /*for(level=0, ...) */ updated_rnodes++; } /*list_for(rq) */ /* Burn the deads */ if (updated_rnodes < me.cur_node->links) radar_remove_old_rnodes((char *) rnode_deleted); /* <> */ if (!is_bufzero(rnode_added, sizeof(rnode_added)) || !is_bufzero(rnode_deleted, sizeof(rnode_deleted))) { /*** * qsort the rnodes of me.cur_node and me.cur_quadg comparing * their trtt */ rnode_trtt_order(me.cur_node); for (i = 1; i < me.cur_quadg.levels; i++) if (TEST_BIT(rnode_added, i) || TEST_BIT(rnode_deleted, i)) rnode_trtt_order(&me.cur_quadg.gnode[_EL(i)]->g); /**/ /* adjust the rnode_pos variables in the ext_rnode_cache list */ erc_reorder_rnodepos(&me.cur_erc, &me.cur_erc_counter, me.cur_node); } /* Give a refresh to the kernel */ if ((!is_bufzero(rnode_added, sizeof(rnode_added)) || routes_update) && !(me.cur_node->flags & MAP_HNODE)) rt_rnodes_update(1); } /* * add_radar_q * * It returns the radar_q struct which handles the pkt.from node. * If the node is not present in the radar_q, it is added, and the * relative struct will be returned. */ struct radar_queue * add_radar_q(PACKET pkt) { map_node *rnode; quadro_group quadg; struct radar_queue *rq; u_int ret = 0; int dev_pos; if (me.cur_node->flags & MAP_HNODE) { /* * We are hooking, we haven't yet an int_map, an ext_map, * a stable ip, so we create fake nodes that will be delete after * the hook. */ if (!(rq = find_ip_radar_q(&pkt.from))) { map_rnode rnn; rnode = xmalloc(sizeof(map_node)); setzero(rnode, sizeof(map_node)); setzero(&rnn, sizeof(map_rnode)); rnn.r_node = (int *) me.cur_node; rnode_add(rnode, &rnn); } else rnode = rq->node; } iptoquadg(pkt.from, me.ext_map, &quadg, QUADG_GID | QUADG_GNODE | QUADG_IPSTART); if (!(me.cur_node->flags & MAP_HNODE)) { iptomap((u_int) me.int_map, pkt.from, me.cur_quadg.ipstart[1], &rnode); ret = quadg_gids_cmp(me.cur_quadg, quadg, 1); } if (!ret) rq = find_node_radar_q(rnode); else rq = find_ip_radar_q(&pkt.from); if (!rq) { /* * If pkt.from isn't already in the queue, add it. */ rq = xzalloc(sizeof(struct radar_queue)); if (ret) rq->node = (map_node *) RADQ_EXT_RNODE; else { rq->node = rnode; /* This pkt has been sent from another hooking * node, let's remember this. */ if (pkt.hdr.flags & HOOK_PKT) rq->node->flags |= MAP_HNODE; } if (pkt.hdr.flags & HOOK_PKT) rq->flags |= MAP_HNODE; inet_copy(&rq->ip, &pkt.from); memcpy(&rq->quadg, &quadg, sizeof(quadro_group)); rq->dev[0] = pkt.dev; rq->dev_n++; clist_add(&radar_q, &radar_q_counter, rq); } else { /* * Check if the input device is in the rq->dev array, * if not add it. */ if (rq->dev_n < MAX_INTERFACES) { dev_pos = FIND_PTR(pkt.dev, rq->dev, rq->dev_n); if (dev_pos < 0) rq->dev[rq->dev_n++] = pkt.dev; } } return rq; } /* * radar_exec_reply * * It reads the received ECHO_REPLY pkt and updates the radar * queue, storing the calculated rtt and the other infos relative to the sender * node. */ int radar_exec_reply(PACKET pkt) { struct timeval t; struct radar_queue *rq; u_int rtt_ms = 0; int dev_pos; gettimeofday(&t, 0); /* * Get the radar_queue struct relative to pkt.from */ rq = add_radar_q(pkt); dev_pos = ifs_get_pos(me.cur_ifs, me.cur_ifs_n, pkt.dev); if (dev_pos < 0) debug(DBG_NORMAL, "The 0x%x ECHO_REPLY pkt was received by a non " "existent interface", pkt.hdr.id); if (me.cur_node->flags & MAP_HNODE) { if (pkt.hdr.flags & HOOK_PKT) { u_char scanning; memcpy(&scanning, pkt.msg, sizeof(u_char)); /* * If the pkt.from node has finished his scan, and we * never received one of its ECHO_ME pkts, and we are * still scanning, set the hook_retry. */ if (!scanning && !rq->pings && (radar_scan_mutex || radar_scans[dev_pos] <= MAX_RADAR_SCANS)) { hook_retry = 1; } } } if (rq->pongs < radar_scans[dev_pos]) { timersub(&t, &scan_start, &rq->rtt[(int) rq->pongs]); /* * Now we divide the rtt, because (t - scan_start) is the time * the pkt used to reach B from A and to return to A from B */ rtt_ms = MILLISEC(rq->rtt[(int) rq->pongs]) / 2; MILLISEC_TO_TV(rtt_ms, rq->rtt[(int) rq->pongs]); rq->pongs++; } return 0; } /* * radar_recv_reply * * It handles the ECHO_REPLY pkts */ int radar_recv_reply(PACKET pkt) { if (!my_echo_id || !radar_scan_mutex || !total_radar_scans) return -1; if (pkt.hdr.id != my_echo_id) { debug(DBG_NORMAL, "I received an ECHO_REPLY with id: 0x%x, but " "my current ECHO_ME is 0x%x", pkt.hdr.id, my_echo_id); return -1; } /* * If the `alwd_rnodes_counter' counter isn't zero, verify that * `pkt.from' is an allowed rnode, otherwise drop this pkt */ if (alwd_rnodes_counter && !is_rnode_allowed(pkt.from, alwd_rnodes)) { debug(DBG_INSANE, "Filtering 0x%x ECHO_REPLY", pkt.hdr.id); return -1; } /* * If the rnode is in restricted mode and we are not, drop the pkt. * If we are in restricted mode and the rnode isn't, drop the pkt */ if ((pkt.hdr.flags & RESTRICTED_PKT && !restricted_mode) || (!(pkt.hdr.flags & RESTRICTED_PKT) && restricted_mode)) return -1; return radar_exec_reply(pkt); } /* * radar_qspn_send_t * * This function is used only by radar_scan(). * It just call the qspn_send() function. We use a thread * because the qspn_send() may sleep, and we don't want to halt the * radar_scan(). */ void * radar_qspn_send_t(void *level) { int *p; u_char i; p = (int *) level; i = (u_char) * p; xfree(p); qspn_send(i); return NULL; } /* * radar_scan * * It starts the scan of the local area. * * It sends MAX_RADAR_SCANS packets in broadcast then it waits MAX_RADAR_WAIT * and in the while the echo replies are gathered. After MAX_RADAR_WAIT it * stops to receive echo replies and it does a statistical analysis of the * gathered echo replies, it updates the r_nodes in the map and sends a qspn * round if something is changed in the map and if the `activate_qspn' argument * is non zero. * * It returns 1 if another radar_scan is in progress, -1 if something went * wrong, 0 on success. */ int radar_scan(int activate_qspn) { pthread_t thread; PACKET pkt; int i, d, *p; ssize_t err; u_char echo_scan; /* We are already doing a radar scan, that's not good */ if (radar_scan_mutex) return 1; radar_scan_mutex = 1; /* * We create the PACKET */ setzero(&pkt, sizeof(PACKET)); inet_setip_bcast(&pkt.to, my_family); my_echo_id = rand(); gettimeofday(&scan_start, 0); /* * Send a bouquet of ECHO_ME pkts */ if (me.cur_node->flags & MAP_HNODE) { pkt.hdr.sz = sizeof(u_char); pkt.hdr.flags |= HOOK_PKT | BCAST_PKT; pkt.msg = xmalloc(pkt.hdr.sz); debug(DBG_INSANE, "Radar scan 0x%x activated", my_echo_id); } else total_radars++; if (restricted_mode) pkt.hdr.flags |= RESTRICTED_PKT; /* Loop through the me.cur_ifs array, sending the bouquet using all the * interfaces we have */ for (d = 0; d < me.cur_ifs_n; d++) { pkt_add_dev(&pkt, &me.cur_ifs[d], 1); pkt.sk = 0; /* Create a new socket */ /* Send MAX_RADAR_SCANS# packets using me.cur_ifs[d] as * outgoing interface */ for (i = 0, echo_scan = 0; i < MAX_RADAR_SCANS; i++, echo_scan++) { if (me.cur_node->flags & MAP_HNODE) memcpy(pkt.msg, &echo_scan, sizeof(u_char)); err = send_rq(&pkt, 0, ECHO_ME, my_echo_id, 0, 0, 0); if (err < 0) { if (errno == ENODEV) { /* * The me.cur_ifs[d] device doesn't * exist anymore. Delete it. */ fatal("The device \"%s\" has been removed", me.cur_ifs[d].dev_name); ifs_del(me.cur_ifs, &me.cur_ifs_n, d); d--; } else error(ERROR_MSG "Error while sending the" " scan 0x%x... skipping", ERROR_FUNC, my_echo_id); break; } radar_scans[d]++; total_radar_scans++; } if (!radar_scans[d]) error("radar_scan(): The scan 0x%x on the %s interface failed." " Not a single scan was sent", my_echo_id, pkt.dev->dev_name); if (pkt.sk > 0) inet_close(&pkt.sk); } pkt_free(&pkt, 1); if (!total_radar_scans) { error("radar_scan(): The scan 0x%x failed. It wasn't possible " "to send a single scan", my_echo_id); return -1; } xtimer(max_radar_wait, max_radar_wait << 1, &radar_wait_counter); final_radar_queue(); radar_update_map(); if (activate_qspn) for (i = 0; i < me.cur_quadg.levels; i++) if (send_qspn_now[i]) { p = xmalloc(sizeof(int)); *p = i; /* We start a new qspn_round in the `i'-th level */ pthread_create(&thread, &radar_qspn_send_t_attr, radar_qspn_send_t, (void *) p); } if (!(me.cur_node->flags & MAP_HNODE)) reset_radar(); radar_scan_mutex = 0; return 0; } /* * radard * * It sends back to rpkt.from the ECHO_REPLY pkt in reply to the ECHO_ME * pkt received. */ int radard(PACKET rpkt) { PACKET pkt; struct radar_queue *rq; ssize_t err; const char *ntop = 0; int dev_pos; u_char echo_scans_count; if (alwd_rnodes_counter && !is_rnode_allowed(rpkt.from, alwd_rnodes)) { debug(DBG_INSANE, "Filtering 0x%x ECHO_ME", rpkt.hdr.id); return -1; } if ((rpkt.hdr.flags & RESTRICTED_PKT && !restricted_mode) || (!(rpkt.hdr.flags & RESTRICTED_PKT) && restricted_mode)) return -1; dev_pos = ifs_get_pos(me.cur_ifs, me.cur_ifs_n, rpkt.dev); if (dev_pos < 0) debug(DBG_NORMAL, "The 0x%x ECHO_ME pkt was received by a non " "existent interface", rpkt.hdr.id); /* If we are hooking we reply only to others hooking nodes */ if (me.cur_node->flags & MAP_HNODE) { if (rpkt.hdr.flags & HOOK_PKT) { memcpy(&echo_scans_count, rpkt.msg, sizeof(u_char)); /* * So, we are hooking, but we haven't yet started the * first scan or we have done less scans than rpkt.from, * this means that this node, who is hooking * too and sent us this rpkt, has started the hook * before us. If we are in a black zone, this flag * will be used to decide which of the hooking nodes * have to create the new gnode: if it is set we'll wait, * the other hooking node will create the gnode, then we * restart the hook. Clear? */ if (!radar_scan_mutex || echo_scans_count >= radar_scans[dev_pos]) hook_retry = 1; } else { /*debug(DBG_NOISE, "ECHO_ME pkt dropped: We are hooking"); */ return 0; } } /* We create the ECHO_REPLY pkt */ setzero(&pkt, sizeof(PACKET)); pkt_addto(&pkt, &rpkt.from); pkt_addsk(&pkt, rpkt.from.family, rpkt.sk, SKT_UDP); if (me.cur_node->flags & MAP_HNODE) { /* * We attach in the ECHO_REPLY a flag that indicates if we have * finished our radar_scan or not. This is usefull if we already * sent all the ECHO_ME pkts of our radar scan and while we are * waiting the MAX_RADAR_WAIT another node start the hooking: * with this flag it can know if we came before him. */ u_char scanning = 1; pkt.hdr.sz = sizeof(u_char); pkt.hdr.flags |= HOOK_PKT; pkt.msg = xmalloc(pkt.hdr.sz); if (radar_scans[dev_pos] == MAX_RADAR_SCANS) scanning = 0; memcpy(pkt.msg, &scanning, sizeof(u_char)); /* * W Poetry Palazzolo, the enlightening holy garden. * Sat Mar 12 20:41:36 CET 2005 */ } if (restricted_mode) pkt.hdr.flags |= RESTRICTED_PKT; /* We send it */ err = send_rq(&pkt, 0, ECHO_REPLY, rpkt.hdr.id, 0, 0, 0); pkt_free(&pkt, 0); if (err < 0) { error("radard(): Cannot send back the ECHO_REPLY to %s.", ntop); return -1; } /* * Ok, we have sent the reply, now we can update the radar_queue with * calm. */ if (radar_q) { rq = add_radar_q(rpkt); rq->pings++; #ifdef DEBUG if (server_opt.dbg_lvl && rq->pings == 1 && me.cur_node->flags & MAP_HNODE) { ntop = inet_to_str(pkt.to); debug(DBG_INSANE, "%s(0x%x) to %s", rq_to_str(ECHO_REPLY), rpkt.hdr.id, ntop); } #endif } return 0; } /* * refresh_hook_root_node * * At hooking the radar_scan doesn't have an int_map, so * all the nodes it found are stored in fake nodes. When we finish the hook, * instead, we have an int_map, so we convert all this fake nodes into real * nodes. To do this we modify each rq->node of the radar_queue and recall the * radar_update_map() func. * rnode_list and qspn_b are also updated. * Note: the me.cur_node must be deleted prior the call of this function. */ int refresh_hook_root_node(void) { struct radar_queue *rq; map_node *rnode; int ret; rq = radar_q; list_for(rq) { ret = iptomap((u_int) me.int_map, rq->ip, me.cur_quadg.ipstart[1], &rnode); if (ret) rq->node = (map_node *) RADQ_EXT_RNODE; else rq->node = rnode; } radar_update_map(); /* * Remove all the rnode_list structs which refers to the fake * rnodes. */ rnl_del_dead_rnode(&rlist, &rlist_counter, me.cur_node); /* Do the same for the qspn_b buffer */ qspn_b_del_all_dead_rnodes(); return 0; } /* * radar_daemon * * keeps the radar up until the end of the universe. */ void * radar_daemon(void *null) { /* If `radar_daemon_ctl' is set to 0 the radar_daemon will stop. * It will restart when it becomes again 1 */ radar_daemon_ctl = 1; debug(DBG_NORMAL, "Radar daemon up & running"); for (;;) { while (!radar_daemon_ctl) sleep(1); radar_scan(1); } } /* * radar_wait_new_scan * * It sleeps until a new radar scan is sent */ void radar_wait_new_scan(void) { int old_echo_id, old_radar_wait_counter; old_echo_id = my_echo_id; old_radar_wait_counter = radar_wait_counter; for (; old_echo_id == my_echo_id;) { usleep(505050); /* If the radar_wait_counter doesn't change, that means that * the radar isn't active */ if (radar_wait_counter == old_radar_wait_counter) break; } } /*EoW*/