netsukuku/src/rehook.c

413 lines
10 KiB
C
Raw Normal View History

2013-09-16 09:53:25 +00:00
/* This file is part of Netsukuku
* (c) Copyright 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
*
* 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.
*
* --
* rehook.c:
* This code manages the rehook of gnodes, the challenges that must be solved
* and generated in order to prove the number of nodes present in a gnode.
*/
#include "includes.h"
#include "common.h"
#include "hash.h"
#include "llist.c"
#include "libnetlink.h"
#include "ll_map.h"
#include "inet.h"
#include "if.h"
#include "krnl_route.h"
#include "endianness.h"
#include "bmap.h"
#include "route.h"
#include "iptunnel.h"
#include "request.h"
#include "pkts.h"
#include "tracer.h"
#include "qspn.h"
#include "andna.h"
#include "hook.h"
#include "rehook.h"
#include "radar.h"
#include "netsukuku.h"
#include "common.h"
/*
* rehook_argv: argv for the new_rehook_thread thread
*/
struct rehook_argv {
int gid;
map_gnode *gnode;
int level;
int gnode_count;
};
pthread_attr_t new_rehook_thread_attr;
void rehook_init(void)
{
total_rehooks=0;
last_instance_rehook=0;
rehook_mutex=0;
pthread_attr_init(&new_rehook_thread_attr);
pthread_attr_setdetachstate(&new_rehook_thread_attr, PTHREAD_CREATE_DETACHED);
}
/*
* rehook_compute_new_gnode: computes the IP which shall be used to create a
* new gnode if the we cannot rehook to any gnode.
* The computed ip is stored in `new_ip'.
* `old_ip' is the IP we used before the rehook was launched.
*/
void rehook_compute_new_gnode(inet_prefix *old_ip, inet_prefix *new_ip,
int hook_level)
{
quadro_group qg;
int hash_gid;
iptoquadg(*old_ip, me.ext_map, &qg, QUADG_GID);
/*
* Hash our gids starting from the `hook_level' level,
* then xor the bytes of the hash merging them in a single byte.
*/
hash_gid=fnv_32_buf(&qg.gid[hook_level],
(FAMILY_LVLS-hook_level),
FNV1_32_INIT);
qg.gid[hook_level]=xor_int(hash_gid);
/* Be sure to choose VOID gnodes */
void_gids(&qg, hook_level, me.ext_map, me.int_map);
/* Save the new ip in `new_ip' */
gidtoipstart(qg.gid, FAMILY_LVLS, FAMILY_LVLS, my_family, new_ip);
}
int send_challenge(int gnode, int level, int gnode_count)
{
/* TODO ^_^ */
return 0;
}
/*
* update_rehook_time: updates the rehook_time counter. If the limits are
* reached -1 is returned and nothing is changed, otherwise 0 is the returned
* value. (See rehook.h for more info on the limits).
*/
int update_rehook_time(int level)
{
time_t cur_t, sec_elapsed;
cur_t=time(0);
sec_elapsed=(cur_t - last_instance_rehook);
if(total_rehooks && sec_elapsed > REHOOK_INSTANCE_TIME(level)) {
/*
* REHOOK_INSTANCE_TIME expired: we cannot take anymore rehooks
* in this instance.
*/
if(sec_elapsed > REHOOK_WAIT_TIME(level))
/* REHOOK_WAIT_TIME expired: a new instance begins */
total_rehooks=0;
else
return -1;
}
if(total_rehooks > REHOOK_PER_INSTANCE)
/* Too many rehooks in this instance */
return -1;
if(!total_rehooks)
last_instance_rehook=cur_t;
total_rehooks++;
return 0;
}
/*
* wait_new_rnode: it waits until we have a rnode, which belongs to
* `rargv->gnode' or to `rk_gnode_ip'.
*/
void wait_new_rnode(struct rehook_argv *rargv)
{
ext_rnode_cache *erc;
int gid_a[MAX_LEVELS], gid_b[MAX_LEVELS];
int e=0, i, retries;
debug(DBG_NOISE, "wait_new_rnode: waiting the %d rnode %d lvl appearance",
rargv->gid, rargv->level);
memcpy(&gid_a, me.cur_quadg.gid, sizeof(me.cur_quadg.gid));
gid_a[rargv->level]=rargv->gid;
iptogids(&rk_gnode_ip, gid_b, me.cur_quadg.levels);
retries = QSPN_WAIT_ROUND_LVL(rargv->level)/MAX_RADAR_WAIT + 1;
for(i=0; i<retries; i++) {
e=0;
erc=me.cur_erc;
list_for(erc) {
if(!gids_cmp(erc->e->quadg.gid, gid_a, rargv->level,
me.cur_quadg.levels)) {
e=1;
break;
}
if(!gids_cmp(erc->e->quadg.gid, gid_b, rargv->level,
me.cur_quadg.levels)) {
e=1;
break;
}
}
if(e) {
debug(DBG_NOISE, "wait_new_rnode: %d rnode %d "
"lvl found", rargv->gid, rargv->level);
return;
}
radar_wait_new_scan();
}
debug(DBG_NORMAL, "wait_new_rnode: not found! Anyway, trying to rehook");
}
/*
* new_rehook_thread: a thread for each rehook() is necessary because the
* rehook has to run without stopping the calling thread.
*/
void *new_rehook_thread(void *r)
{
struct rehook_argv *rargv=(struct rehook_argv *)r;
ext_rnode_cache *erc;
map_node *root_node;
map_gnode *gnode;
int i;
/*
* Send a new challenge if `CHALLENGE_THRESHOLD' was exceeded
*/
if(rargv->level && rargv->gnode_count >= CHALLENGE_THRESHOLD)
if(send_challenge(rargv->gid, rargv->level,
rargv->gnode_count))
/* Challenge failed, do not rehook */
goto finish;
/* Store in `rk_gnode_ip' our new gnode ip to be used when the rehook
* fails, just in case */
rehook_compute_new_gnode(&me.cur_ip, &rk_gnode_ip, rargv->level);
#if 0
/* Before rehooking, at least one qspn_round has to be completed */
while(!me.cur_qspn_id[rargv->level])
usleep(505050);
#endif
/* Wait the radar_daemon, we need it up & running */
while(!radar_daemon_ctl)
usleep(505050);
if(rargv->gid != me.cur_quadg.gid[rargv->level])
wait_new_rnode(rargv);
/*
* Rehook now
*/
rehook(rargv->gnode, rargv->level);
if(rargv->level) {
/* Mark all the gnodes we border on as HOOKED, in this way
* we won't try to rehook each time */
erc=me.cur_erc;
list_for(erc) {
if(!erc->e)
continue;
if(erc->e->quadg.gnode[_EL(rargv->level)])
erc->e->quadg.gnode[_EL(rargv->level)]->flags|=GMAP_HGNODE;
}
/* Mark also rargv->gnode */
rargv->gnode->flags|=GMAP_HGNODE;
/* Mark all the gnodes which are rnodes of our gnode of the
* `rargv->level' level. */
root_node=&me.cur_quadg.gnode[_EL(rargv->level)]->g;
for(i=0; i<root_node->links; i++) {
gnode=(map_gnode *)root_node->r_node[i].r_node;
gnode->g.flags|=GMAP_HGNODE;
}
}
finish:
xfree(rargv);
rehook_mutex=0;
return 0;
}
/*
* new_rehook: takes in exam the `gnode' composed by `gnode_count'# nodes, which
* is at level `level' and which has a gnode id equal to `gid'.
* When `level' is 0, `gnode' is a node and gnode_count isn't considered.
*/
void new_rehook(map_gnode *gnode, int gid, int level, int gnode_count)
{
struct rehook_argv *rargv;
pthread_t thread;
if(restricted_mode && level == me.cur_quadg.levels-1 &&
gid != me.cur_quadg.gid[level])
/* We are in restricted mode. The `gnode' is too restricted.
* Our restricted class isn't the same of `gnode', therefore
* do nothing. The restricted class are immutable. */
return;
if(!level && gid != me.cur_quadg.gid[level])
/* We rehook at level 0 only if we have the same gid of
* another node, so in this case we don't have to rehook */
return;
else if(level) {
if(gnode_count < qspn_gnode_count[_EL(level)])
/* We have more nodes, we don't have to rehook! */
return;
else if(gnode_count == qspn_gnode_count[_EL(level)] &&
gid < me.cur_quadg.gid[level])
/* We have the same number of nodes, but `gid' is
* smaller than our gnode id, so it must rehook,
* not us */
return;
else if(gnode_count == qspn_gnode_count[_EL(level)] &&
gid == me.cur_quadg.gid[level] &&
gnode->g.flags & MAP_RNODE)
/* If `gnode' has our same gid and it is our rnode,
* it's alright. */
return;
}
if(gid == me.cur_quadg.gid[level]) {
/*
* There is a (g)node which has our same gid, hence we rehook
* to our gnode of the higher level (hopefully we get a new
* gid).
*/
if(level+1 < me.cur_quadg.levels)
level++;
gid = me.cur_quadg.gid[level];
gnode = me.cur_quadg.gnode[_EL(level)];
} else if(level && gnode->flags & GMAP_HGNODE)
/* `gnode' is marked as HOOKED, return. */
return;
debug(DBG_NORMAL, "new_rehook: me.gid %d, gnode %d, level %d, gnode_count %d, "
"qspn_gcount %d, our rnode: %d",
me.cur_quadg.gid[level], gid, level,
gnode_count, qspn_gnode_count[_EL(level)],
gnode->g.flags & MAP_RNODE);
/*
* Update the rehook time and let's see if we can take this new rehook
*/
if(update_rehook_time(level)) {
debug(DBG_SOFT, "new_rehook: we have to wait before accepting "
"another rehook");
return;
}
if(rehook_mutex)
return;
rehook_mutex=1;
rargv = xmalloc(sizeof(struct rehook_argv));
rargv->gid = gid;
rargv->gnode = gnode;
rargv->level = level;
rargv->gnode_count = gnode_count;
pthread_create(&thread, &new_rehook_thread_attr, new_rehook_thread,
(void *)rargv);
}
/*
* rehook: resets all the global variables set during the last hook/rehook,
* and launches the netsukuku_hook() again. All the previous map will be lost
* if not saved, the IP will also change.
* During the rehook, the radar_daemon and andna_maintain_hnames_active() are
* stopped.
* After the rehook, the andna_hook will be launched and the stopped daemon
* reactivated.
*/
int rehook(map_gnode *hook_gnode, int hook_level)
{
int ret=0;
/* Stop the radar_daemon */
radar_daemon_ctl=0;
/* Wait the end of the current radar */
radar_wait_new_scan();
/* Mark ourself as hooking, this will stop
* andna_maintain_hnames_active() daemon too. */
me.cur_node->flags|=MAP_HNODE;
/*
* Reset the rnode list and external rnode list
*/
rnl_reset(&rlist, &rlist_counter);
e_rnode_free(&me.cur_erc, &me.cur_erc_counter);
if(restricted_mode) {
/*
* Delete all the tunnels, and reset all the structs used by
* igs.c
*/
del_all_tunnel_ifs(0, 0, 0, NTK_TUNL_PREFIX);
reset_igw_nexthop(multigw_nh);
reset_igws(me.igws, me.igws_counter, me.cur_quadg.levels);
reset_igw_rules();
free_my_igws(&me.my_igws);
}
/* Andna reset */
if(!server_opt.disable_andna) {
andna_cache_destroy();
counter_c_destroy();
rh_cache_flush();
}
/* Clear the uptime */
me.uptime=time(0);
/*
* * * REHOOK! * * *
*/
netsukuku_hook(hook_gnode, hook_level);
/* Restart the radar daemon */
radar_daemon_ctl=1;
if(!server_opt.disable_andna) {
/* Rehook in ANDNA and update our hostnames */
andna_hook(0);
andna_update_hnames(0);
}
return ret;
}