netsukuku/src/rehook.c

423 lines
11 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)
2013-09-16 09:53:25 +00:00
{
total_rehooks = 0;
last_instance_rehook = 0;
rehook_mutex = 0;
2013-09-16 09:53:25 +00:00
pthread_attr_init(&new_rehook_thread_attr);
pthread_attr_setdetachstate(&new_rehook_thread_attr,
PTHREAD_CREATE_DETACHED);
2013-09-16 09:53:25 +00:00
}
/*
* 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)
2013-09-16 09:53:25 +00:00
{
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);
2013-09-16 09:53:25 +00:00
/* 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)
2013-09-16 09:53:25 +00:00
{
/* TODO ^_^ */
2013-09-16 09:53:25 +00:00
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)
2013-09-16 09:53:25 +00:00
{
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)) {
2013-09-16 09:53:25 +00:00
/*
* REHOOK_INSTANCE_TIME expired: we cannot take anymore rehooks
* in this instance.
*/
if (sec_elapsed > REHOOK_WAIT_TIME(level))
2013-09-16 09:53:25 +00:00
/* REHOOK_WAIT_TIME expired: a new instance begins */
total_rehooks = 0;
2013-09-16 09:53:25 +00:00
else
return -1;
}
if (total_rehooks > REHOOK_PER_INSTANCE)
2013-09-16 09:53:25 +00:00
/* Too many rehooks in this instance */
return -1;
if (!total_rehooks)
last_instance_rehook = cur_t;
2013-09-16 09:53:25 +00:00
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)
2013-09-16 09:53:25 +00:00
{
ext_rnode_cache *erc;
int gid_a[MAX_LEVELS], gid_b[MAX_LEVELS];
int e = 0, i, retries;
2013-09-16 09:53:25 +00:00
debug(DBG_NOISE,
"wait_new_rnode: waiting the %d rnode %d lvl appearance",
rargv->gid, rargv->level);
2013-09-16 09:53:25 +00:00
memcpy(&gid_a, me.cur_quadg.gid, sizeof(me.cur_quadg.gid));
gid_a[rargv->level] = rargv->gid;
2013-09-16 09:53:25 +00:00
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;
2013-09-16 09:53:25 +00:00
list_for(erc) {
if (!gids_cmp(erc->e->quadg.gid, gid_a, rargv->level,
me.cur_quadg.levels)) {
e = 1;
2013-09-16 09:53:25 +00:00
break;
}
if (!gids_cmp(erc->e->quadg.gid, gid_b, rargv->level,
me.cur_quadg.levels)) {
e = 1;
2013-09-16 09:53:25 +00:00
break;
}
}
if (e) {
2013-09-16 09:53:25 +00:00
debug(DBG_NOISE, "wait_new_rnode: %d rnode %d "
"lvl found", rargv->gid, rargv->level);
2013-09-16 09:53:25 +00:00
return;
}
radar_wait_new_scan();
}
debug(DBG_NORMAL,
"wait_new_rnode: not found! Anyway, trying to rehook");
2013-09-16 09:53:25 +00:00
}
/*
* 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)
2013-09-16 09:53:25 +00:00
{
struct rehook_argv *rargv = (struct rehook_argv *) r;
2013-09-16 09:53:25 +00:00
ext_rnode_cache *erc;
map_node *root_node;
map_gnode *gnode;
int i;
2013-09-16 09:53:25 +00:00
/*
* 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))
2013-09-16 09:53:25 +00:00
/* 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])
2013-09-16 09:53:25 +00:00
usleep(505050);
#endif
2013-09-16 09:53:25 +00:00
/* Wait the radar_daemon, we need it up & running */
while (!radar_daemon_ctl)
2013-09-16 09:53:25 +00:00
usleep(505050);
if (rargv->gid != me.cur_quadg.gid[rargv->level])
2013-09-16 09:53:25 +00:00
wait_new_rnode(rargv);
/*
* Rehook now
*/
rehook(rargv->gnode, rargv->level);
if (rargv->level) {
2013-09-16 09:53:25 +00:00
/* 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)
2013-09-16 09:53:25 +00:00
continue;
if (erc->e->quadg.gnode[_EL(rargv->level)])
erc->e->quadg.gnode[_EL(rargv->level)]->flags |=
GMAP_HGNODE;
2013-09-16 09:53:25 +00:00
}
/* Mark also rargv->gnode */
rargv->gnode->flags |= GMAP_HGNODE;
2013-09-16 09:53:25 +00:00
/* 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;
2013-09-16 09:53:25 +00:00
}
}
finish:
2013-09-16 09:53:25 +00:00
xfree(rargv);
rehook_mutex = 0;
2013-09-16 09:53:25 +00:00
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)
2013-09-16 09:53:25 +00:00
{
struct rehook_argv *rargv;
pthread_t thread;
if (restricted_mode && level == me.cur_quadg.levels - 1 &&
gid != me.cur_quadg.gid[level])
2013-09-16 09:53:25 +00:00
/* 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])
2013-09-16 09:53:25 +00:00
/* 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)])
2013-09-16 09:53:25 +00:00
/* 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])
2013-09-16 09:53:25 +00:00
/* 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)
2013-09-16 09:53:25 +00:00
/* If `gnode' has our same gid and it is our rnode,
* it's alright. */
return;
}
2013-09-16 09:53:25 +00:00
if (gid == me.cur_quadg.gid[level]) {
2013-09-16 09:53:25 +00:00
/*
* 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)
2013-09-16 09:53:25 +00:00
level++;
gid = me.cur_quadg.gid[level];
2013-09-16 09:53:25 +00:00
gnode = me.cur_quadg.gnode[_EL(level)];
} else if (level && gnode->flags & GMAP_HGNODE)
2013-09-16 09:53:25 +00:00
/* `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);
2013-09-16 09:53:25 +00:00
/*
* Update the rehook time and let's see if we can take this new rehook
*/
if (update_rehook_time(level)) {
2013-09-16 09:53:25 +00:00
debug(DBG_SOFT, "new_rehook: we have to wait before accepting "
"another rehook");
2013-09-16 09:53:25 +00:00
return;
}
if (rehook_mutex)
2013-09-16 09:53:25 +00:00
return;
rehook_mutex = 1;
2013-09-16 09:53:25 +00:00
rargv = xmalloc(sizeof(struct rehook_argv));
rargv->gid = gid;
rargv->gnode = gnode;
rargv->level = level;
2013-09-16 09:53:25 +00:00
rargv->gnode_count = gnode_count;
pthread_create(&thread, &new_rehook_thread_attr, new_rehook_thread,
(void *) rargv);
2013-09-16 09:53:25 +00:00
}
/*
* 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)
2013-09-16 09:53:25 +00:00
{
int ret = 0;
2013-09-16 09:53:25 +00:00
/* Stop the radar_daemon */
radar_daemon_ctl = 0;
2013-09-16 09:53:25 +00:00
/* 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;
2013-09-16 09:53:25 +00:00
/*
* 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) {
2013-09-16 09:53:25 +00:00
/*
* 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();
2013-09-16 09:53:25 +00:00
free_my_igws(&me.my_igws);
}
/* Andna reset */
if (!server_opt.disable_andna) {
2013-09-16 09:53:25 +00:00
andna_cache_destroy();
counter_c_destroy();
rh_cache_flush();
}
2013-09-16 09:53:25 +00:00
/* Clear the uptime */
me.uptime = time(0);
2013-09-16 09:53:25 +00:00
/*
* * * REHOOK! * * *
*/
netsukuku_hook(hook_gnode, hook_level);
/* Restart the radar daemon */
radar_daemon_ctl = 1;
2013-09-16 09:53:25 +00:00
if (!server_opt.disable_andna) {
2013-09-16 09:53:25 +00:00
/* Rehook in ANDNA and update our hostnames */
andna_hook(0);
andna_update_hnames(0);
}
return ret;
}