void reset_extmap(map_gnode ** ext_map, u_char levels, int groups) { int i; for (i = 1; i < levels; i++) reset_gmap(ext_map[_EL(i)], groups); gmap_node_del(&ext_map[_EL(levels)][0]); }
/* * gnode_dec_seeds * * the same of gnode_inc_seeds, but it decrements instead. */ void gnode_dec_seeds(quadro_group * qg, int level) { if (level >= qg->levels - 1) return; if (qg->gnode[_EL(level + 1)]->seeds - 1 >= 0) qg->gnode[_EL(level + 1)]->seeds--; qg->gnode[_EL(level + 1)]->flags &= ~GMAP_FULL; }
/* * gnode_inc_seeds * * it increments the seeds counter in the `qg'->gnode[_EL(`level'-1)] gnode, * setting the appropriate flag if the gnodes is full. */ void gnode_inc_seeds(quadro_group * qg, int level) { if (level >= qg->levels - 1) return; if (qg->gnode[_EL(level + 1)]->seeds == MAXGROUPNODE - 1) qg->gnode[_EL(level + 1)]->flags |= GMAP_FULL; else qg->gnode[_EL(level + 1)]->seeds++; }
/* extmap_find_level: It returns the position of the gnode map which contains * the 'gnode`. This position corresponds to the level of that gmap. * The ext_map is given in `ext_map`. `max_level' is the maximum number of level * present in the `ext_map'. * ex: if gnode is in ext_map[i] it will return i; * On failure -1 is returned.*/ int extmap_find_level(map_gnode ** ext_map, map_gnode * gnode, u_char max_level) { int i, a, b, c; for (i = 1; i < max_level; i++) { a = (int) gnode; b = (int) &ext_map[_EL(i)][0]; c = (int) &ext_map[_EL(i)][MAXGROUPNODE - 1]; if (a >= b && a <= c) return i; } return -1; }
/* * init_my_igws * * initialiases the `my_igws' array. This list keeps inet_gw structs which * points to our (g)nodes, for example: * my_igws[0]->node == me.cur_node, * my_igws[1]->node == &me.cur_quadg.gnode[_EL(1)]->g, * ... */ void init_my_igws(inet_gw ** igws, int *igws_counter, inet_gw *** my_new_igws, u_char my_bandwidth, map_node * cur_node, quadro_group * qg) { inet_gw *igw, **my_igws; map_node *node; int i = 0, e, bw_mean; init_igws(&my_igws, 0, qg->levels); for (i = 0; i < qg->levels; i++) { if (!i) { node = cur_node; bw_mean = my_bandwidth; } else { node = &qg->gnode[_EL(i)]->g; bw_mean = e = 0; igw = igws[i - 1]; list_for(igw) { bw_mean += igw->bandwidth; e++; } bw_mean /= e; } igw = igw_add_node(igws, igws_counter, i, qg->gid[i], node, (int *) qg->ipstart[0].data, (u_char) bw_mean); my_igws[i] = igw; } *my_new_igws = my_igws; }
/* * 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); }
void quadg_setflags(quadro_group * qg, char flags) { map_gnode *gnode; int i; for (i = 1; i < qg->levels; i++) if ((gnode = qg->gnode[_EL(i)])) gnode->g.flags |= flags; }
void qspn_set_map_vars(u_char level, map_node **map, map_node **root_node, int *root_node_pos, map_gnode **gmap) { if(!level) { if(map) *map=me.int_map; if(root_node) *root_node=me.cur_node; if(root_node_pos) *root_node_pos=pos_from_node(me.cur_node, me.int_map); } else { if(map) *map=(map_node *)me.ext_map[_EL(level)]; if(gmap) *gmap=me.ext_map[_EL(level)]; if(root_node) *root_node=&me.cur_quadg.gnode[_EL(level)]->g; if(root_node_pos) *root_node_pos=me.cur_quadg.gid[level]; } }
/* * erc_find_gnode; Returns the first ext_rnode_cache having * erc->e->quadg.gnode[_EL( `level' )] == `gnode' */ ext_rnode_cache * erc_find_gnode(ext_rnode_cache * erc, map_gnode * gnode, u_char level) { ext_rnode_cache *p = erc; if (!erc || !level) return 0; list_for(p) { if (!p->e) continue; if (p->e->quadg.gnode[_EL(level)] == gnode) return p; } return 0; }
/* * random_ip * * It generates a new random ip. * If `ipstart' is not NULL the new ip is restricted in the `final_gid' of * `final_level', so it'll be taken inside this range: * A=ipstart + (MAXGROUPNODE^( final_level + 1)) * final_gid; * B=ipstart + (MAXGROUPNODE^( final_level + 1)) * (final_gid + 1); * A <= x <= B * If `ipstart' is NULL a completely random ip is generated. * `total_levels' is the maximum number of levels. * `ext_map' is an external map. * If `only_free_gnode' is not 0, only the available and empty gnode are chosen. * In this case -1 may be returned if there aren't any free gnode to choose. * The new ip is stored in `new_ip' and on success 0 is returned. */ int random_ip(inet_prefix * ipstart, int final_level, int final_gid, int total_levels, map_gnode ** ext_map, int only_free_gnode, inet_prefix * new_ip, int my_family) { int i, e, x, level, levels; int gid[total_levels]; quadro_group qg; setzero(new_ip, sizeof(inet_prefix)); if (!ipstart || final_level == total_levels) { u_int idata[MAX_IP_INT] = { 0, 0, 0, 0 }; for (;;) { /* * Let's choose a completely random ip. */ levels = total_levels; if (my_family == AF_INET) idata[0] = rand(); else { idata[0] = rand(); idata[1] = rand(); idata[2] = rand(); idata[3] = rand(); } inet_setip(new_ip, idata, my_family); if (!inet_validate_ip(*new_ip)) break; } return 0; } /* * We can choose only a random ip which is inside the final_gid. * `final_gid' is a gnode of the `final_level' level. * `final_level' has its `ipstart'; with it we determine * its higher levels. * The work is done in this way: * - ipstart is splitted in gnode_ids and they are placed in qg.gid. * - The final_level gid is set to `final_gid'. * - The gids of levels lower than `final_level' are chosen * randomly. * - The gids of levels higher than `final_level' are set to the * gids of qg.gid[x >= final_level]. * - The ipstart is recomposed from the gids. */ levels = final_level; iptoquadg(*ipstart, ext_map, &qg, QUADG_GID); gid[levels] = final_gid; for (i = final_level + 1; i < total_levels; i++) gid[i] = qg.gid[i]; /* Change the gids if some of them point to full gnodes */ free_gids(&qg, final_level + 1, ext_map, 0); /* * Now we choose random gids for each level so we'll have a random ip * with gidtoipstart(); */ for (level = levels - 1; level >= 0; level--) { gid[level] = rand_range(0, MAXGROUPNODE - 1); if (level && only_free_gnode) { /* * We have to be sure that we're not picking a gnode * already used in the ext_map. Generally when we hook * we have loaded the old ext_map, so skipping the * taken gnodes we increase the possibility to create a * brand new, and not already used, gnode. */ if (!(ext_map[_EL(level)][gid[level]].flags & GMAP_VOID)) { /* Take a random position and loop trough the * map */ i = rand_range(0, MAXGROUPNODE - 1); for (x = 0, e = i; e < MAXGROUPNODE; e++) { if (is_group_invalid(gid, e, level, my_family)) continue; if (ext_map[_EL(level)][e].flags & GMAP_VOID) { gid[level] = e; x = 1; break; } } if (!x) { for (x = 0; i >= 0; i--) { if (is_group_invalid(gid, i, level, my_family)) continue; if (ext_map[_EL(level)][i].flags & GMAP_VOID) { gid[level] = i; x = 1; break; } } } if (!x) /* not a single free gnode was found */ return -1; } } } /* * Ok, we've set the gids of each level so we recompose them in the * new_ip. */ gidtoipstart(gid, total_levels, total_levels, my_family, new_ip); return 0; }
/* * increment_gids * * It increments the members of the `qg'->gid array until all its * gids point to gnodes present in the ext_map, which don't have * a particular gnode->flag or node->flag set. * * In order to verify that a gnode doesn't have the flag set the * `is_gnode_flag_set' function is called, the same is done for the nodes with * the `is_node_flag_set' function. * `is_gnode_flag_set' returns 1 if the flag is set. * * increment_gids() starts from the qg->gid[`level'] member and finishes to * qg->gid[qg->levels-1]. * If all the gids point to gnodes, which have the gnode->flag set, -1 is * returned. * It's assumed that `ext_map' and `int_map' are the maps relative to the * `qg' quadro_group. */ int increment_gids(quadro_group * qg, int level, map_gnode ** ext_map, map_node * int_map, int (*is_gnode_flag_set) (map_gnode * gnode), int (*is_node_flag_set) (map_node * node)) { int g, groups, gid, i, e = 0, family; if (level >= qg->levels) return -1; family = qg->ipstart[level].family; g = level == qg->levels ? 0 : qg->gid[level]; groups = get_groups(qg->levels, level); gid = qg->gid[level]; if ((!level && !is_node_flag_set(&int_map[gid])) || (level && is_gnode_flag_set(&ext_map[_EL(level)][g]))) { /* * find a gid in this `level' which isn't full */ for (i = 0, e = 0; i < groups; i++) { qg->gid[level] = (gid + i) % groups; if (is_group_invalid(qg->gid, qg->gid[level], level, family)) continue; if ((!level && is_node_flag_set(&int_map[qg->gid[level]])) || (level && !is_gnode_flag_set(&ext_map[_EL(level)][qg->gid[level]]))) { e = 1; break; } } /* * not a single free gid was found */ if (!e) { g = level + 1 == qg->levels ? 0 : qg->gid[level + 1]; if ((is_gmap_full_flag_set == is_gnode_flag_set) && !(ext_map[_EL(level + 1)][g].flags & GMAP_FULL)) { /* * There is a logical contradiction here: * we didn't find any free (g)nodes in this * level, but the upper gnode at level+1, * which is the parent of this level, isn't * marked as full! So what's happening here? * Ignore this absurd and mark it as full -_- */ ext_map[_EL(level + 1)][g].flags |= GMAP_FULL; debug(DBG_NORMAL, ERROR_MSG "logical " "contradiction detected", ERROR_POS); } /* * Recurse by leveling up */ if (!increment_gids(qg, level + 1, ext_map, int_map, is_gnode_flag_set, is_node_flag_set)) /* * We changed one of our upper gid, we can * retake the old gid we had at this `level' */ qg->gid[level] = gid; else /* * It's all full! */ return -1; } } return 0; }
/* * 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); /* <<keep your room tidy... order, ORDER>> */ 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); }
/* * 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; }