netsukuku/src/hook.c

1648 lines
42 KiB
C

/* 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.
*
* --
*
* hook.c:
* This is code which handles the hooking of a new node in netsukuku, or the
* creation of a new gnode.
*/
#include "includes.h"
#include "common.h"
#include "libnetlink.h"
#include "ll_map.h"
#include "inet.h"
#include "if.h"
#include "krnl_route.h"
#include "iptunnel.h"
#include "endianness.h"
#include "bmap.h"
#include "route.h"
#include "request.h"
#include "pkts.h"
#include "tracer.h"
#include "qspn.h"
#include "hook.h"
#include "rehook.h"
#include "radar.h"
#include "netsukuku.h"
#include "common.h"
int free_the_tmp_cur_node;
int we_are_rehooking; /* 1 if it is true */
void hook_reset(void);
/*
* hook_fill_rq
*
* It's just a wrapper to rnl_fill_rq().
*/
int hook_fill_rq(map_node *dst_rnode, PACKET *pkt, u_char rq)
{
if(rnl_fill_rq(dst_rnode, pkt) < 0)
return -1;
if(server_opt.dbg_lvl) {
const char *ntop;
ntop=inet_to_str(pkt->to);
debug(DBG_INSANE, "Quest %s to %s", rq_to_str(rq), ntop);
}
return 0;
}
/*
* verify_free_nodes_hdr: verifies the validity of the `fn_hdr'
* free_nodes_hdr. `to' is the ip of the node which sent the
* put_free_nodes reply.
* If the header is valid 0 is returned.
*/
int verify_free_nodes_hdr(inet_prefix *to, struct free_nodes_hdr *fn_hdr)
{
quadro_group qg_a, qg_b;
inet_prefix ipstart;
if(fn_hdr->max_levels > FAMILY_LVLS || !fn_hdr->level)
return 1;
if(fn_hdr->level >= fn_hdr->max_levels)
return 1;
/* If fn_hdr->ipstart != `to' there is an error */
inet_setip(&ipstart, (u_int *)fn_hdr->ipstart, my_family);
iptoquadg(ipstart, me.ext_map, &qg_a, QUADG_GID);
iptoquadg(*to, me.ext_map, &qg_b, QUADG_GID);
if(quadg_gids_cmp(qg_a, qg_b, fn_hdr->level))
return 1;
if(fn_hdr->nodes <= 0 || fn_hdr->nodes == (MAXGROUPNODE-1))
return 1;
return 0;
}
/*\
* * * put/get free_nodes * *
\*/
/*
* get_free_nodes
*
* It send the GET_FREE_NODES request, used to retrieve the free_nodes pkt
* (see hook.h), to rnode `dst_rnode'.
* `fn_hdr' is the header of the received free_nodes packet.
* `nodes' must be an u_char array with at least MAXGROUPNODES members. All the
* members that go from `free_nodes[0]' to `free_nodes[fn_hdr.nodes]' will be
* filled with the gids of the received free nodes.
* -1 is returned if `to' said its quadro_group is full or if a generic error
* occurred. In this case it is advised to ask to get the free_nodes list from
* another rnode.
* If -2 is returned, the whole Netsukuku net is full, so desist to retry, or
* drop down your neighbors.
*/
int get_free_nodes(map_node *dst_rnode,
struct free_nodes_hdr *fn_hdr, u_char *nodes)
{
PACKET pkt, rpkt;
ssize_t err;
int ret=0, e, i;
char *buf=0;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
hook_fill_rq(dst_rnode, &pkt, GET_FREE_NODES) < 0 && _return (-1);
pkt_addtimeout(&pkt, HOOK_RQ_TIMEOUT, 1, 0);
err=rnl_send_rq(dst_rnode, &pkt, 0, GET_FREE_NODES, 0, PUT_FREE_NODES,
1, &rpkt);
if(err < 0) {
if(rpkt.hdr.sz && (u_char)(*rpkt.msg) == E_NTK_FULL)
ERROR_FINISH(ret, -2, finish);
ERROR_FINISH(ret, -1, finish);
}
ints_network_to_host(rpkt.msg, free_nodes_hdr_iinfo);
memcpy(fn_hdr, rpkt.msg, sizeof(struct free_nodes_hdr));
if(verify_free_nodes_hdr(&pkt.to, fn_hdr)) {
error("Malformed PUT_FREE_NODES request hdr from %s",
inet_to_str(pkt.to));
ERROR_FINISH(ret, -1, finish);
}
fn_hdr->nodes++;
buf=rpkt.msg+sizeof(struct free_nodes_hdr);
for(i=0, e=0; i<MAXGROUPNODE; i++) {
if(TEST_BIT(buf, i)) {
nodes[e]=i;
e++;
}
}
debug(DBG_NORMAL, "Received %d free %s", fn_hdr->nodes,
fn_hdr->level == 1 ? "nodes" : "gnodes");
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt,0);
return ret;
}
/*
* put_free_nodes: It sends a free_nodes pkt to rq_pkt.from. To see what's a
* free_nodes pkt go in hook.h.
*/
int put_free_nodes(PACKET rq_pkt)
{
struct fn_pkt {
struct free_nodes_hdr fn_hdr;
u_char free_nodes[MAXGROUPNODE>>3];
}_PACKED_ fn_pkt;
PACKET pkt;
int ret=0, i, e=0, links;
ssize_t err, pkt_sz;
u_char level, err_reply;
const char *ntop;
char *p=0;
ntop=inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addport(&pkt, ntk_tcp_port);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_add_dev(&pkt, rq_pkt.dev, 1);
pkt_addcompress(&pkt);
/* We search in each level a gnode which is not full. */
for(level=1, e=0; level < me.cur_quadg.levels; level++) {
if(!(me.cur_quadg.gnode[_EL(level)]->flags & GMAP_FULL)) {
e=1;
break;
}
}
if(!e) {
if(me.ext_map[_EL(me.cur_quadg.levels)][0].flags & GMAP_FULL)
/* <<Netsukuku is completely full, sry>> */
err_reply=E_NTK_FULL;
else
/* Our Quadro Group is full, bye */
err_reply=E_QGROUP_FULL;
pkt_fill_hdr(&pkt.hdr, HOOK_PKT, rq_pkt.hdr.id, PUT_FREE_NODES, 0);
err=pkt_err(pkt, err_reply, 1);
goto finish;
}
/* Ok, we've found one, so let's roll the pkt */
setzero(&fn_pkt, sizeof(fn_pkt));
/*
* Fill the reply packet
*/
fn_pkt.fn_hdr.max_levels=me.cur_quadg.levels;
inet_copy_ipdata(fn_pkt.fn_hdr.ipstart, &me.cur_quadg.ipstart[level]);
fn_pkt.fn_hdr.level=level;
fn_pkt.fn_hdr.gid=me.cur_quadg.gid[level];
/*
* Update the hook_join_rate and stores give the stores in
* `fn_pkt.fn_hdr.join_rate' the join_rate destined to `rq_pkt.from'
*/
links=me.cur_node->links-rnodes_rehooked-1;
if(hook_join_rate >= links && links > 0)
fn_pkt.fn_hdr.join_rate = hook_join_rate/links;
else if(hook_join_rate > 0)
fn_pkt.fn_hdr.join_rate = 1;
else
fn_pkt.fn_hdr.join_rate = 0;
hook_join_rate -= fn_pkt.fn_hdr.join_rate;
hook_join_rate = hook_join_rate < 0 ? 0 : hook_join_rate;
rnodes_rehooked++;
/*
* Creates the list of the free nodes, which belongs to the gnode. If
* the gnode level is 1 it scans the int_map to find all the MAP_VOID
* nodes, otherwise it scans the gnode map at level-1 searching for
* GMAP_VOID gnodes.
*/
e=0;
if(level == 1) {
for(i=0; i<MAXGROUPNODE; i++)
if(me.int_map[i].flags & MAP_VOID) {
SET_BIT(fn_pkt.free_nodes, i);
e++;
}
} else {
for(i=0; i<MAXGROUPNODE; i++)
if(me.ext_map[_EL(level-1)][i].flags & GMAP_VOID ||
me.ext_map[_EL(level-1)][i].g.flags & MAP_VOID) {
SET_BIT(fn_pkt.free_nodes, i);
e++;
}
}
fn_pkt.fn_hdr.nodes=(u_char)e-1;
/* Go pkt, go! Follow your instinct */
pkt_sz=FREE_NODES_SZ((fn_pkt.fn_hdr.nodes+1));
pkt_fill_hdr(&pkt.hdr, HOOK_PKT, rq_pkt.hdr.id, PUT_FREE_NODES, pkt_sz);
pkt.msg=xzalloc(pkt_sz);
p=pkt.msg;
memcpy(p, &fn_pkt, sizeof(fn_pkt));
ints_host_to_network(p, free_nodes_hdr_iinfo);
debug(DBG_INSANE, "Reply %s to %s", re_to_str(pkt.hdr.op), ntop);
err=pkt_send(&pkt);
finish:
if(err < 0) {
error("put_free_nodes(): Cannot send the PUT_FREE_NODES reply to %s.", ntop);
ret=-1;
}
pkt_free(&pkt, 0);
return ret;
}
/*\
* * * put/get qspn_round * *
\*/
/*
* get_qspn_round: It send the GET_QSPN_ROUND request, used to retrieve the
* qspn ids and and qspn times. (see hook.h).
*/
int get_qspn_round(map_node *dst_rnode, struct timeval to_rtt,
struct timeval *qtime, int *qspn_id, int *qspn_gcount)
{
PACKET pkt, rpkt;
struct timeval cur_t;
ssize_t err;
int ret=0, level;
char *buf=0;
u_char max_levels;
int_info qr_pkt_iinfo;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
hook_fill_rq(dst_rnode, &pkt, GET_QSPN_ROUND) < 0 && _return (-1);
pkt_addtimeout(&pkt, HOOK_RQ_TIMEOUT, 1, 0);
err=rnl_send_rq(dst_rnode, &pkt, 0, GET_QSPN_ROUND, 0, PUT_QSPN_ROUND,
1, &rpkt);
if(err < 0)
ERROR_FINISH(ret, -1, finish);
buf=rpkt.msg;
bufget(&max_levels, sizeof(u_char));
if(QSPN_ROUND_PKT_SZ(max_levels) != rpkt.hdr.sz ||
max_levels > FAMILY_LVLS) {
error("Malformed PUT_QSPN_ROUND request hdr from %s",
inet_to_str(pkt.to));
ERROR_FINISH(ret, -1, finish);
}
/* Convert the pkt from network to host order */
int_info_copy(&qr_pkt_iinfo, &qspn_round_pkt_iinfo);
qr_pkt_iinfo.int_offset[1] = me.cur_quadg.levels*sizeof(int)+sizeof(char);
qr_pkt_iinfo.int_offset[2] = qr_pkt_iinfo.int_offset[1] + sizeof(struct timeval)*max_levels;
qr_pkt_iinfo.int_nmemb[0] = max_levels;
qr_pkt_iinfo.int_nmemb[1] = max_levels*2;
ints_network_to_host(rpkt.msg, qr_pkt_iinfo);
/* Restoring the qspn_id and the qspn_round time */
bufget(qspn_id, max_levels*sizeof(int));
bufget(qtime, max_levels*sizeof(struct timeval));
gettimeofday(&cur_t, 0);
for(level=0; level < max_levels; level++) {
timeradd(&to_rtt, &qtime[level], &qtime[level]);
timersub(&cur_t, &qtime[level], &qtime[level]);
}
/* Extracting the qspn_gnode_count */
bufget(qspn_gcount, sizeof(u_int)*GCOUNT_LEVELS);
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt,0);
return ret;
}
/*
* put_qspn_round
*
* It sends the current qspn times and ids to rq_pkt.from.
*/
int put_qspn_round(PACKET rq_pkt)
{
/*
* We cannot use this elegant struct because gcc is bugged, -_.
* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=27945
*
* We have to wait some years, then when gcc4 will be obsolete (and
* the bug will be solved), we'll activate it.
*
* struct qspn_round_pkt {
* u_char max_levels;
* int32_t qspn_id[me.cur_quadg.levels];
* struct timeval _PACKED_ qtime[me.cur_quadg.levels];
* u_int gcount[GCOUNT_LEVELS];
* }_PACKED_ qr_pkt;
*/
char qr_pkt[QSPN_ROUND_PKT_SZ(me.cur_quadg.levels)];
int_info qr_pkt_iinfo;
PACKET pkt;
struct timeval cur_t, *tptr;
int ret=0;
ssize_t err, pkt_sz;
u_char level;
const char *ntop;
u_char max_levels;
char *buf=0;
ntop=inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addport(&pkt, ntk_tcp_port);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_add_dev(&pkt, rq_pkt.dev, 1);
pkt_addcompress(&pkt);
/* We fill the qspn_id and the qspn round time */
buf=qr_pkt;
max_levels=me.cur_quadg.levels;
bufput(&max_levels, sizeof(u_char));
bufput(me.cur_qspn_id, sizeof(int)*max_levels);
gettimeofday(&cur_t, 0);
for(level=0; level < max_levels; level++) {
update_qspn_time(level, 0);
tptr=(struct timeval *)buf;
timersub(&cur_t, &me.cur_qspn_time[level], tptr);
buf+=sizeof(struct timeval);
}
/* copy in the pkt the qspn_gnode_count */
bufput(qspn_gnode_count, sizeof(qspn_gnode_count));
/* Convert the pkt from host to network order */
int_info_copy(&qr_pkt_iinfo, &qspn_round_pkt_iinfo);
qr_pkt_iinfo.int_offset[1] = me.cur_quadg.levels*sizeof(int)+sizeof(char);
qr_pkt_iinfo.int_offset[2] = qr_pkt_iinfo.int_offset[1] +
sizeof(struct timeval)*max_levels;
qr_pkt_iinfo.int_nmemb[0] = me.cur_quadg.levels;
qr_pkt_iinfo.int_nmemb[1] = me.cur_quadg.levels*2;
ints_host_to_network(qr_pkt, qr_pkt_iinfo);
/* fill the pkt header */
pkt_sz=sizeof(qr_pkt);
pkt_fill_hdr(&pkt.hdr, HOOK_PKT, rq_pkt.hdr.id, PUT_QSPN_ROUND, pkt_sz);
pkt.msg=xzalloc(pkt_sz);
/* Go pkt, go! Follow your instinct */
debug(DBG_INSANE, "Reply %s to %s", re_to_str(pkt.hdr.op), ntop);
memcpy(pkt.msg, &qr_pkt, sizeof(qr_pkt));
err=pkt_send(&pkt);
if(err < 0) {
error("put_qspn_round(): Cannot send the PUT_QSPN_ROUND reply to %s.", ntop);
ret=-1;
}
pkt_free(&pkt, 0);
return ret;
}
/*\
* * * put/get ext_map * *
\*/
int put_ext_map(PACKET rq_pkt)
{
PACKET pkt;
const char *ntop;
int ret=0;
ssize_t err;
size_t pkt_sz=0;
ntop=inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_addcompress(&pkt);
pkt.msg=pack_extmap(me.ext_map, MAXGROUPNODE, &me.cur_quadg, &pkt_sz);
pkt.hdr.sz=pkt_sz;
debug(DBG_INSANE, "Reply %s to %s", re_to_str(PUT_EXT_MAP), ntop);
err=send_rq(&pkt, 0, PUT_EXT_MAP, rq_pkt.hdr.id, 0, 0, 0);
if(err < 0) {
error("put_ext_maps(): Cannot send the PUT_EXT_MAP reply to %s.", ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
* get_ext_map: It sends the GET_EXT_MAP request to retrieve the
* dst_node's ext_map.
*/
map_gnode **get_ext_map(map_node *dst_rnode, quadro_group *new_quadg)
{
PACKET pkt, rpkt;
char *pack;
int err;
map_gnode **ext_map=0, **ret=0;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
hook_fill_rq(dst_rnode, &pkt, GET_EXT_MAP) < 0 && _return (0);
pkt_addtimeout(&pkt, HOOK_RQ_TIMEOUT, 1, 0);
err=rnl_send_rq(dst_rnode, &pkt, 0, GET_EXT_MAP, 0, PUT_EXT_MAP, 1,
&rpkt);
if(err < 0) {
ret=0;
goto finish;
}
pack=rpkt.msg;
ret=ext_map=unpack_extmap(pack, new_quadg);
if(!ext_map)
error("get_ext_map: Malformed ext_map. Cannot unpack the ext_map.");
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt, 0);
return ret;
}
/*\
* * * put/get int_map * *
\*/
int put_int_map(PACKET rq_pkt)
{
PACKET pkt;
map_node *map=me.int_map;
const char *ntop;
int ret=0;
ssize_t err;
size_t pkt_sz=0;
ntop=inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_add_dev(&pkt, rq_pkt.dev, 1);
pkt_addcompress(&pkt);
pkt.msg=pack_map(map, 0, MAXGROUPNODE, me.cur_node, &pkt_sz);
pkt.hdr.sz=pkt_sz;
debug(DBG_INSANE, "Reply %s to %s", re_to_str(PUT_INT_MAP), ntop);
err=send_rq(&pkt, 0, PUT_INT_MAP, rq_pkt.hdr.id, 0, 0, 0);
if(err < 0) {
error("put_int_map(): Cannot send the PUT_INT_MAP reply to %s.", ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
* get_int_map: It sends the GET_INT_MAP request to retrieve the
* dst_node's int_map.
*/
map_node *get_int_map(map_node *dst_rnode, map_node **new_root)
{
PACKET pkt, rpkt;
map_node *int_map, *ret=0;
int err;
char *pack;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
hook_fill_rq(dst_rnode, &pkt, GET_INT_MAP) < 0 && _return (0);
pkt_addtimeout(&pkt, HOOK_RQ_TIMEOUT, 1, 0);
err=rnl_send_rq(dst_rnode, &pkt, 0, GET_INT_MAP, 0, PUT_INT_MAP, 1,
&rpkt);
if(err < 0) {
ret=0;
goto finish;
}
pack=rpkt.msg;
ret=int_map=unpack_map(pack, 0, new_root, MAXGROUPNODE,
MAXRNODEBLOCK_PACK_SZ);
if(!int_map)
error("get_int_map(): Malformed int_map. Cannot load it");
/*Finished, yeah*/
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt, 0);
return ret;
}
/*\
* * * put/get bnode_map * *
\*/
int put_bnode_map(PACKET rq_pkt)
{
PACKET pkt;
map_bnode **bmaps=me.bnode_map;
const char *ntop;
int ret=0;
ssize_t err;
size_t pack_sz=0;
ntop=inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_add_dev(&pkt, rq_pkt.dev, 1);
pkt_addcompress(&pkt);
pkt.msg=pack_all_bmaps(bmaps, me.bmap_nodes, me.ext_map, me.cur_quadg, &pack_sz);
pkt.hdr.sz=pack_sz;
debug(DBG_INSANE, "Reply %s to %s", re_to_str(PUT_BNODE_MAP), ntop);
err=send_rq(&pkt, 0, PUT_BNODE_MAP, rq_pkt.hdr.id, 0, 0, 0);
if(err < 0) {
error("put_bnode_maps(): Cannot send the PUT_BNODE_MAP reply to %s.", ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
* get_bnode_map: It sends the GET_BNODE_MAP request to retrieve the
* dst_node's bnode_map.
*/
map_bnode **get_bnode_map(map_node *dst_rnode, u_int **bmap_nodes)
{
PACKET pkt, rpkt;
int err;
map_bnode **bnode_map, **ret=0;
char *pack;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
hook_fill_rq(dst_rnode, &pkt, GET_BNODE_MAP) < 0 && _return (0);
pkt_addtimeout(&pkt, HOOK_RQ_TIMEOUT, 1, 0);
err=rnl_send_rq(dst_rnode, &pkt, 0, GET_BNODE_MAP, 0, PUT_BNODE_MAP,
1, &rpkt);
if(err < 0) {
ret=0;
goto finish;
}
/* Extracting the map... */
pack=rpkt.msg;
ret=bnode_map=unpack_all_bmaps(pack, FAMILY_LVLS, me.ext_map, bmap_nodes,
MAXGROUPNODE, MAXBNODE_RNODEBLOCK);
if(!bnode_map)
error("get_bnode_map(): Malformed bnode_map. Cannot load it");
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt, 0);
return ret;
}
/*\
* * * put/get internet gateways list * *
\*/
int put_internet_gws(PACKET rq_pkt)
{
PACKET pkt;
const char *ntop;
int ret=0;
ssize_t err;
size_t pack_sz=0;
ntop=inet_to_str(rq_pkt.from);
setzero(&pkt, sizeof(PACKET));
pkt_addto(&pkt, &rq_pkt.from);
pkt_addsk(&pkt, my_family, rq_pkt.sk, rq_pkt.sk_type);
pkt_add_dev(&pkt, rq_pkt.dev, 1);
pkt_addcompress(&pkt);
pkt.msg=pack_igws(me.igws, me.igws_counter, me.cur_quadg.levels,
(int*)&pack_sz);
pkt.hdr.sz=pack_sz;
debug(DBG_INSANE, "Reply %s to %s", re_to_str(PUT_INTERNET_GWS), ntop);
err=send_rq(&pkt, 0, PUT_INTERNET_GWS, rq_pkt.hdr.id, 0, 0, 0);
if(err < 0) {
error("put_internet_gws(): Cannot send the PUT_INTERNET_GWS "
"reply to %s.", ntop);
ERROR_FINISH(ret, -1, finish);
}
finish:
pkt_free(&pkt, 0);
return ret;
}
/*
* get_internet_gws: It sends the GET_INTERNET_GWS request to retrieve the
* Internet Gateways list from `to'.
*/
inet_gw **get_internet_gws(map_node *dst_rnode, int **igws_counter)
{
PACKET pkt, rpkt;
int err, ret=0;
inet_gw **igws=0;
char *pack;
setzero(&pkt, sizeof(PACKET));
setzero(&rpkt, sizeof(PACKET));
hook_fill_rq(dst_rnode, &pkt, GET_INTERNET_GWS) < 0 && _return (0);
pkt_addtimeout(&pkt, HOOK_RQ_TIMEOUT, 1, 0);
err=rnl_send_rq(dst_rnode, &pkt, 0, GET_INTERNET_GWS, 0,
PUT_INTERNET_GWS, 1, &rpkt);
if(err < 0)
ERROR_FINISH(ret, 0, finish);
/* Extracting the list... */
pack=rpkt.msg;
ret=unpack_igws(pack, rpkt.hdr.sz, me.int_map, me.ext_map, FAMILY_LVLS,
&igws, igws_counter);
if(ret < 0) {
error("get_internet_gws(): Malformed internet_gws. Cannot load it");
igws=0;
}
finish:
pkt_free(&pkt, 0);
pkt_free(&rpkt, 0);
return igws;
}
/*
* hook_set_all_ips
*
* Sets the same `ip' to all the devices.
*/
void hook_set_all_ips(inet_prefix ip, interface *ifs, int ifs_n)
{
const char *ntop;
ntop=inet_to_str(ip);
loginfo("Setting the %s ip to all the interfaces", ntop);
if(my_family == AF_INET) {
/* Down & Up: reset the configurations of all the interfaces */
set_all_ifs(ifs, ifs_n, set_dev_down);
set_all_ifs(ifs, ifs_n, set_dev_up);
} else {
ip_addr_flush_all_ifs(ifs, ifs_n, my_family, RT_SCOPE_UNIVERSE);
ip_addr_flush_all_ifs(ifs, ifs_n, my_family, RT_SCOPE_SITE);
}
if(set_all_dev_ip(ip, ifs, ifs_n) < 0)
fatal("Cannot set the %s ip to all the interfaces", ntop);
if(restricted_mode && (server_opt.use_shared_inet ||
server_opt.share_internet)) {
set_dev_down(DEFAULT_TUNL_IF);
set_dev_up(DEFAULT_TUNL_IF);
if(set_dev_ip(ip, DEFAULT_TUNL_IF) < 0)
fatal("Cannot assign an IP to the default tunnel");
}
}
/*
* create_gnodes
*
* This function is used to create a new gnode (or more) when we are the first
* node in the area or when all the other gnodes are full.
* Our ip will be set to `ip'. If `ip' is NULL, a random ip is chosen.
* create_gnodes() sets also all the vital variables for the new gnode/gnodes
* like me.cur_quadg, me.cur_ip, etc...
* `final_level' is the highest level where we create the gnode, all the other
* gnodes we create are in the sub-levels of `final_level'.
*/
int create_gnodes(inet_prefix *ip, int final_level)
{
int i;
if(!ip) {
random_ip(0, 0, 0, FAMILY_LVLS, me.ext_map, 0, &me.cur_ip,
my_family);
} else
inet_copy(&me.cur_ip, ip);
if(restricted_mode)
inet_setip_localaddr(&me.cur_ip, my_family, restricted_class);
if(!final_level)
final_level=FAMILY_LVLS;
/*
* We remove all the traces of the old gnodes in the ext_map to add the
* new ones.
*/
if(!(me.cur_node->flags & MAP_HNODE))
for(i=1; i<final_level; i++) {
me.cur_quadg.gnode[_EL(i)]->flags &= ~GMAP_ME;
me.cur_quadg.gnode[_EL(i)]->g.flags &= ~MAP_ME & ~MAP_GNODE;
}
/* Now, we update the ext_map with the new gnodes */
me.cur_quadg.levels=FAMILY_LVLS;
reset_extmap(me.ext_map, me.cur_quadg.levels, 0);
iptoquadg(me.cur_ip, me.ext_map, &me.cur_quadg, QUADG_GID|QUADG_GNODE|QUADG_IPSTART);
/* Set the new flags */
for(i=1; i<final_level; i++) {
me.cur_quadg.gnode[_EL(i)]->flags &= ~GMAP_VOID;
me.cur_quadg.gnode[_EL(i)]->flags |= GMAP_ME;
me.cur_quadg.gnode[_EL(i)]->g.flags&=~ MAP_VOID;
me.cur_quadg.gnode[_EL(i)]->g.flags |= MAP_ME | MAP_GNODE;
/* Increment the gnode seeds counter */
gnode_inc_seeds(&me.cur_quadg, i);
}
/* Reset the `qspn_gnode_count' counter */
qspn_reset_gcount(qspn_gnode_count, final_level, 1);
/* Tidying up the internal map */
if(free_the_tmp_cur_node) {
xfree(me.cur_node);
free_the_tmp_cur_node=0;
}
reset_int_map(me.int_map, 0);
me.cur_node = &me.int_map[me.cur_quadg.gid[0]];
me.cur_node->flags &= ~MAP_VOID;
me.cur_node->flags |= MAP_ME;
return 0;
}
/*
* create_new_qgroup
*
* this is just a wrapper to create_gnodes(). It creates completely
* random gnodes in all the levels.
*/
void create_new_qgroup(int hook_level)
{
const char *ntop;
if(we_are_rehooking)
create_gnodes(&rk_gnode_ip, hook_level+1);
else
create_gnodes(0, FAMILY_LVLS);
ntop=inet_to_str(me.cur_ip);
hook_set_all_ips(me.cur_ip, me.cur_ifs, me.cur_ifs_n);
loginfo("Now we are in a brand new gnode. The ip %s is now"
" used.", ntop);
}
/*
* update_join_rate
*
* it updates the `hook_join_rate' according to `gnode_count', which has the
* gnode count of `hook_gnode' and to `fn_hdr', which is the free_nodes_hdr
* received from `hook_gnode'.
* `old_gcount' is the gnode_count we had before the start of the rehook.
* If a new_gnode has to be created 1 is returned, (and hook_join_rate will be
* 0), otherwise 0 is the returned value.
* If we aren't rehooking or if it isn't necessary to consider the join_rate,
* -1 is returned.
*/
int update_join_rate(map_gnode *hook_gnode, int hook_level,
u_int *old_gcount, u_int *gnode_count,
struct free_nodes_hdr *fn_hdr)
{
u_int free_nodes, total_bnodes;
int new_gnode=0, i;
if(!hook_level || !hook_gnode || !we_are_rehooking)
return -1;
/*
* `free_nodes' is the number of VOID nodes present at the
* `hook_level' in `hook_gnode', and it is the difference
* between the maximum number of nodes in that level and the
* actual number of nodes in it (gnode_count).
*/
free_nodes = NODES_PER_LEVEL(hook_level) - gnode_count[_EL(hook_level)];
if(old_gcount[_EL(hook_level)] <= free_nodes)
return -1;
debug(DBG_SOFT, "update_join_rate: free_nodes %d, fn_hdr->join_rate %d",
free_nodes, fn_hdr->join_rate);
/* There aren't free nodes in `hook_gnode', so skip this function */
if(free_nodes <= 0) {
new_gnode=1;
goto finish;
}
if(map_find_bnode_rnode(me.bnode_map[hook_level-1], me.bmap_nodes[hook_level-1],
hook_gnode) >= 0) {
/* We border on `hook_gnode' so we initialize
* `hook_join_rate' for this new re-hook session,
* later we'll hook at `hook_gnode'. */
hook_join_rate = free_nodes;
for(i=hook_level-1; i >= 0; i--) {
/* `total_bnodes' = how many bnodes border to
* `hook_gnode' in level `i'th */
total_bnodes = map_count_bnode_rnode(me.bnode_map[i], me.bmap_nodes[i],
hook_gnode);
total_bnodes = total_bnodes ? total_bnodes : 1;
hook_join_rate /= total_bnodes;
}
new_gnode=0;
} else if(fn_hdr->join_rate) {
/* The join_rate we got from our rnode is > 0, so
* we hook at `hook_gnode'. */
hook_join_rate = fn_hdr->join_rate;
new_gnode=0;
} else
new_gnode=1;
if(!new_gnode) {
/* Don't count us in the join_rate, 'cause we are rehooking */
hook_join_rate--;
hook_join_rate = hook_join_rate < 0 ? 0 : hook_join_rate;
}
debug(DBG_NOISE, "update_join_rate: new join_rate %u, new_gnode %d",
hook_join_rate, new_gnode);
finish:
return new_gnode;
}
/*
* hook_init
*
* inits the hook.c code. Call this function only once, at the start of the
* daemon.
*/
int hook_init(void)
{
/* register the hook's ops in the pkt_op_table */
add_pkt_op(GET_FREE_NODES, SKT_TCP, ntk_tcp_port, put_free_nodes);
add_pkt_op(PUT_FREE_NODES, SKT_TCP, ntk_tcp_port, 0);
add_pkt_op(GET_QSPN_ROUND, SKT_TCP, ntk_tcp_port, put_qspn_round);
add_pkt_op(PUT_QSPN_ROUND, SKT_TCP, ntk_tcp_port, 0);
add_pkt_op(GET_INT_MAP, SKT_TCP, ntk_tcp_port, put_int_map);
add_pkt_op(PUT_INT_MAP, SKT_TCP, ntk_tcp_port, 0);
add_pkt_op(GET_EXT_MAP, SKT_TCP, ntk_tcp_port, put_ext_map);
add_pkt_op(PUT_EXT_MAP, SKT_TCP, ntk_tcp_port, 0);
add_pkt_op(GET_BNODE_MAP, SKT_TCP, ntk_tcp_port, put_bnode_map);
add_pkt_op(PUT_BNODE_MAP, SKT_TCP, ntk_tcp_port, 0);
add_pkt_op(GET_INTERNET_GWS, SKT_TCP, ntk_tcp_port, put_internet_gws);
add_pkt_op(PUT_INTERNET_GWS, SKT_TCP, ntk_tcp_port, 0);
total_hooks=0;
we_are_rehooking=0;
free_the_tmp_cur_node=0;
hook_reset();
debug(DBG_NORMAL, "Activating ip_forward and disabling rp_filter");
route_ip_forward(my_family, 1);
route_rp_filter_all_dev(my_family, me.cur_ifs, me.cur_ifs_n, 0);
if(restricted_mode && (server_opt.share_internet ||
server_opt.share_internet))
route_rp_filter(my_family, DEFAULT_TUNL_IF, 0);
return 0;
}
/*
* hook_reset: resets all the variables needed to hook. This function is
* called at the beginning of netsukuku_hook().
*/
void hook_reset(void)
{
u_int idata[MAX_IP_INT];
/* We use a fake root_node for a while */
if(free_the_tmp_cur_node)
xfree(me.cur_node);
free_the_tmp_cur_node=1;
me.cur_node=xzalloc(sizeof(map_node));
me.cur_node->flags|=MAP_HNODE;
rnodes_rehooked=hook_join_rate=0;
/*
* Do not reply to any request while we are hooking, except the radar
* ECHO_ME, ECHO_REPLY, and all the replies.
*/
op_filter_reset(OP_FILTER_DROP);
op_filter_reset_re(OP_FILTER_ALLOW);
op_filter_clr(ECHO_ME);
op_filter_clr(ECHO_REPLY);
/*
* We set the dev ip to HOOKING_IP+random_number to begin our
* transaction.
*/
setzero(idata, MAX_IP_SZ);
if(my_family==AF_INET) {
idata[0]=restricted_class == RESTRICTED_10 ? HOOKING_IP_10 : HOOKING_IP_172;
} else
idata[0]=HOOKING_IPV6;
if(my_family == AF_INET6)
idata[0]+=rand_range(0, MAXGROUPNODE-2);
else
idata[0]+=rand_range(0, MAXGROUPNODE-2);
inet_setip_raw(&me.cur_ip, idata, my_family);
iptoquadg(me.cur_ip, me.ext_map, &me.cur_quadg,
QUADG_GID|QUADG_GNODE|QUADG_IPSTART);
hook_set_all_ips(me.cur_ip, me.cur_ifs, me.cur_ifs_n);
}
/*
* hook_first_radar_scan: launches the first scan to know what rnodes we have
* around us.
* If a new gnode has to be created, 1 is returned.
*/
int hook_first_radar_scan(map_gnode *hook_gnode, int hook_level, quadro_group *old_quadg)
{
int total_hooking_nodes, i;
/*
* If we are rehooking to `hook_gnode' tell the radar to ignore all
* the other rnodes, which don't belong to it.
*/
if(hook_gnode && we_are_rehooking) {
int gid[NMEMB(old_quadg->gid)];
memcpy(gid, old_quadg->gid, sizeof(old_quadg->gid));
gid[hook_level]=pos_from_gnode(hook_gnode, me.ext_map[_EL(hook_level)]);
new_rnode_allowed(&alwd_rnodes, &alwd_rnodes_counter,
gid, hook_level, FAMILY_LVLS);
}
/*
* If we are in restricted mode, ignore the restricted nodes which
* belong to our opposite restricted class. So, if we are 10.0.0.1
* ignore 172.x.x.x, and viceversa. This happens only in ipv4.
*/
if(restricted_mode && my_family == AF_INET) {
int gid[IPV4_LEVELS]={0,0,0,0};
if(restricted_class == RESTRICTED_10)
gid[3]=10;
else
gid[3]=172;
new_rnode_allowed(&alwd_rnodes, &alwd_rnodes_counter,
gid, 3, FAMILY_LVLS);
}
/*
* We do our first scans to know what we've around us. The rnodes are
* kept in me.cur_node->r_nodes.
* The fastest one is in me.cur_node->r_nodes[0].
*
* If after MAX_FIRST_RADAR_SCANS# tries we haven't found any rnodes
* we start as a new gnode.
*/
for(i=0; i<MAX_FIRST_RADAR_SCANS; i++) {
me.cur_node->flags|=MAP_HNODE;
loginfo("Launching radar_scan %d of %d", i+1, MAX_FIRST_RADAR_SCANS);
if(radar_scan(0))
fatal("%s:%d: Scan of the area failed. Cannot continue.",
ERROR_POS);
total_hooking_nodes=count_hooking_nodes();
if(!me.cur_node->links ||
( me.cur_node->links==total_hooking_nodes
&& !hook_retry )) {
/*
* If we have 0 nodes around us, we are alone, so we create a
* new gnode.
* If all the nodes around us are hooking and we started hooking
* before them, we create the new gnode.
*/
if(!me.cur_node->links) {
/*
* We haven't found any rnodes. Let's retry the
* radar_scan if i+1<MAX_FIRST_RADAR_SCANS
*/
if(i+1 < MAX_FIRST_RADAR_SCANS)
goto hook_retry_scan;
loginfo("No nodes found! This is a black zone. "
"Creating a new_gnode.");
} else
loginfo("There are %d nodes around, which are hooking"
" like us, but we came first so we have "
"to create the new gnode",
total_hooking_nodes);
create_new_qgroup(hook_level);
return 1;
} else if(hook_retry) {
/*
* There are only hooking nodes, but we started the hooking
* after them, so we wait until some of them create the new
* gnode.
*/
loginfo("I've seen %d hooking nodes around us, and one of them "
"is becoming a new gnode.\n"
" We wait, then we'll restart the hook.",
total_hooking_nodes);
usleep(rand_range(0, 1024)); /* ++entropy, thx to katolaz :) */
sleep(MAX_RADAR_WAIT);
i--;
} else
break;
hook_retry_scan:
reset_radar();
rnode_destroy(me.cur_node);
setzero(me.cur_node, sizeof(map_node));
me.cur_node->flags|=MAP_HNODE;
qspn_b_del_all_dead_rnodes();
}
if(me.cur_node->links < 1) {
loginfo("We have %d nodes around us. (%d are hooking)",
me.cur_node->links, total_hooking_nodes);
}
else if(me.cur_node->links == 1) {
loginfo("We have %d node around us. (%d are hooking)",
me.cur_node->links, total_hooking_nodes);
}
return 0;
}
/*
* hook_get_free_nodes
*
* gets the free_nodes list and the qson_round info from our nearest rnode.
* In `fn_hdr', `fnodes', `gnode_ipstart' and `new_gcount' there will be stored the relative
* value.
* If a new gnode has to be created 1 is returned.
*/
int hook_get_free_nodes(int hook_level, struct free_nodes_hdr *fn_hdr,
u_char *fnodes, inet_prefix *gnode_ipstart, u_int *new_gcount,
struct rnode_list **ret_rnl)
{
struct radar_queue *rq=0;
struct rnode_list *rnl=rlist;
int e=0, err;
/*
* Now we choose the nearest rnode we found and we send it the
* GET_FREE_NODES request.
*/
list_for(rnl) {
if(rnl->node->flags & MAP_HNODE)
continue;
err=get_free_nodes(rnl->node, fn_hdr, fnodes);
if(err == -2)
fatal("Netsukuku is full! Bring down some nodes and retry");
else if(err == -1)
continue;
/* Extract the ipstart of the gnode */
inet_setip(gnode_ipstart, (u_int *)fn_hdr->ipstart, my_family);
/* Get the qspn round info */
rq=find_node_radar_q(rnl->node);
if(!get_qspn_round(rnl->node, rq->final_rtt, me.cur_qspn_time,
me.cur_qspn_id,
(int*)new_gcount)) {
e=1;
break;
}
}
*ret_rnl=rnl;
if(!e) {
loginfo("It seems all the quadro_groups in this area are full "
"or are not cooperating.\n "
"We are going to create a new gnode");
create_new_qgroup(hook_level);
return 1;
}
return 0;
}
/*
* hook_choose_new_ip
*
* after reading the received `fn_hdr', it decides our new IP and if we have to
* create a new gnode it returns 1.
*/
int hook_choose_new_ip(map_gnode *hook_gnode, int hook_level,
struct free_nodes_hdr *fn_hdr, u_char *fnodes,
inet_prefix *gnode_ipstart)
{
int new_gnode, e;
/*
* Let's see if we can re-hook at `hook_gnode' or if we have
* to create a new gnode, in other words: update the join_rate.
*/
new_gnode=update_join_rate(hook_gnode, hook_level, qspn_old_gcount,
qspn_gnode_count, fn_hdr);
if(new_gnode > 0) {
/*
* The `hook_gnode' gnode cannot take all the nodes of our
* gnode so we just give up and create a new gnode, which has
* a gid based on the hash of our current gid.
*/
inet_copy(&me.cur_ip, &rk_gnode_ip);
debug(DBG_NORMAL, "rehook_create_gnode: %s is our new ip",
inet_to_str(me.cur_ip));
} else {
/*
* We are hooking fine,
* let's choose a random ip using the free nodes list we received.
*/
e=rand_range(0, fn_hdr->nodes-1);
if(fn_hdr->level == 1) {
new_gnode=0;
postoip(fnodes[e], *gnode_ipstart, &me.cur_ip);
} else {
new_gnode=1;
for(;;) {
random_ip(gnode_ipstart, fn_hdr->level, fn_hdr->gid,
FAMILY_LVLS, me.ext_map, 0,
&me.cur_ip, my_family);
if(!inet_validate_ip(me.cur_ip))
break;
}
}
}
if(restricted_mode)
inet_setip_localaddr(&me.cur_ip, my_family, restricted_class);
hook_set_all_ips(me.cur_ip, me.cur_ifs, me.cur_ifs_n);
/*
* Close all the rnl->tcp_sk sockets, 'cause we've changed IP and they
* aren't valid anymore
*/
rnl_close_all_sk(rlist);
return new_gnode;
}
/*
* hook_get_ext_map
*
* gets the external map from the rnodes who sent us the free_nodes list.
* `rq' points to that rnode.
* `old_ext_map' is the currently used ext_map; it will be merged with the
* new received map.
*
* If a new gnode has been created, 1 is returned.
*/
int hook_get_ext_map(int hook_level, int new_gnode,
struct rnode_list *rnl, struct free_nodes_hdr *fn_hdr,
map_gnode **old_ext_map, quadro_group *old_quadg)
{
map_gnode **new_ext_map;
/*
* Fetch the ext_map from the node who gave us the free nodes list.
*/
if(!(new_ext_map=get_ext_map(rnl->node, &me.cur_quadg)))
fatal("None of the rnodes in this area gave me the extern map");
me.ext_map=new_ext_map;
if(we_are_rehooking && hook_level) {
int gcount, old_gid;
/*
* Since we are rehooking, our gnode will change and it will be
* dismantled, our old gcount has to be decremented from our
* old ext_map and the one we receive.
*/
gcount = new_ext_map[_EL(hook_level)][old_quadg->gid[hook_level]].gcount;
qspn_dec_gcount((int*)qspn_gnode_count, hook_level+1, gcount);
old_gid=old_quadg->gid[hook_level];
new_ext_map[_EL(hook_level)][old_gid].gcount=0;
old_ext_map[_EL(hook_level)][old_gid].gcount=0;
/*
* We can also delete our old gid, 'cause it doesn't exist
* anymore
*/
gmap_node_del(&new_ext_map[_EL(hook_level)][old_gid]);
gmap_node_del(&old_ext_map[_EL(hook_level)][old_gid]);
}
/* If we have to create new gnodes, let's do it. */
if(new_gnode) {
me.ext_map = old_ext_map;
old_ext_map = new_ext_map;
memcpy(old_quadg, &me.cur_quadg, sizeof(quadro_group));
/* Create a new gnode. After this we have a new ip,
* ext_map and quadro_group */
create_gnodes(&me.cur_ip, we_are_rehooking ? hook_level : fn_hdr->level);
/* Merge the received ext_map with our new empty ext_map */
merge_ext_maps(me.ext_map, new_ext_map, me.cur_quadg, *old_quadg);
free_extmap(old_ext_map, FAMILY_LVLS, 0);
return 1;
}
free_extmap(old_ext_map, FAMILY_LVLS, 0);
return 0;
}
/*
* hook_get_int_map
*
* fetch the internal map from a rnode which belongs to our same gnode.
*/
void hook_get_int_map(void)
{
struct radar_queue *rq=radar_q;
map_node **merg_map, *new_root;
int imaps=0, i;
/*
* We want a new shiny traslucent internal map
*/
reset_int_map(me.int_map, 0);
iptoquadg(me.cur_ip, me.ext_map, &me.cur_quadg,
QUADG_GID|QUADG_GNODE|QUADG_IPSTART);
/* Increment the gnode seeds counter of level one, since
* we are new in that gnode */
gnode_inc_seeds(&me.cur_quadg, 0);
/*
* Fetch the int_map from each rnode and merge them into a
* single, big, shiny map.
*/
imaps=0;
rq=radar_q;
merg_map=xzalloc(me.cur_node->links*sizeof(map_node *));
for(i=0; i<me.cur_node->links; i++) {
rq=find_node_radar_q((map_node *)me.cur_node->r_node[i].r_node);
if(rq->node->flags & MAP_HNODE)
continue;
if(quadg_gids_cmp(rq->quadg, me.cur_quadg, 1))
/* This node isn't part of our gnode, let's skip it */
continue;
if((merg_map[imaps]=get_int_map(rq->node, &new_root))) {
merge_maps(me.int_map, merg_map[imaps], me.cur_node, new_root);
imaps++;
}
}
if(!imaps)
fatal("None of the rnodes in this area gave me the int_map");
for(i=0; i<imaps; i++)
free_map(merg_map[i], 0);
xfree(merg_map);
}
void hook_get_bnode_map(void)
{
struct radar_queue *rq=radar_q;
map_bnode **old_bnode_map;
u_int *old_bnodes;
int e, i;
/*
* Let's get the bnode map. Fast, fast, quick quick!
*/
e=0;
for(i=0; i<me.cur_node->links; i++) {
rq=find_node_radar_q((map_node *)me.cur_node->r_node[i].r_node);
if(rq->node->flags & MAP_HNODE)
continue;
if(quadg_gids_cmp(rq->quadg, me.cur_quadg, 1))
/* This node isn't part of our gnode, let's skip it */
continue;
old_bnode_map=me.bnode_map;
old_bnodes=me.bmap_nodes;
me.bnode_map=get_bnode_map(rq->node, &me.bmap_nodes);
if(me.bnode_map) {
bmap_levels_free(old_bnode_map, old_bnodes);
e=1;
break;
} else {
me.bnode_map=old_bnode_map;
me.bmap_nodes=old_bnodes;
}
}
if(!e)
loginfo("None of the rnodes in this area gave me the bnode map.");
}
void hook_get_igw(void)
{
struct radar_queue *rq=radar_q;
inet_gw **old_igws;
int *old_igws_counter;
int e, i;
/*
* Let's get the Internet Gateway list
*/
e=0;
for(i=0; i<me.cur_node->links; i++) {
rq=find_node_radar_q((map_node *)me.cur_node->r_node[i].r_node);
if(rq->node->flags & MAP_HNODE)
continue;
if(quadg_gids_cmp(rq->quadg, me.cur_quadg, 1))
/* This node isn't part of our gnode, let's skip it */
continue;
old_igws=me.igws;
old_igws_counter=me.igws_counter;
me.igws=get_internet_gws(rq->node, &me.igws_counter);
if(me.igws) {
free_igws(old_igws, old_igws_counter, FAMILY_LVLS);
e=1;
break;
} else {
me.igws=old_igws;
me.igws_counter=old_igws_counter;
}
}
if(!e) {
loginfo("None gave me the Internet Gateway list");
reset_igws(me.igws, me.igws_counter, FAMILY_LVLS);
}
}
/*
* hook_finish: final part of the netsukuku_hook process
*/
void hook_finish(int new_gnode, struct free_nodes_hdr *fn_hdr)
{
int tracer_levels, i;
/*
* We must reset the radar_queue because the first radar_scan, used while hooking,
* has to keep the list of the rnodes' "inet_prefix ip". In this way we know
* the rnodes' ips even if we haven't an int_map yet.
*/
reset_radar();
/* Clear the allowed_rnode llist */
reset_rnode_allowed(&alwd_rnodes, &alwd_rnodes_counter);
/* We have finished the hook */
me.cur_node->flags&=~MAP_HNODE;
/* Disable the filter */
op_filter_reset(OP_FILTER_ALLOW);
if(new_gnode) {
if(!me.cur_node->links)
/*
* We are a node lost in the desert, so we don't send
* anything because nobody is listening
*/
tracer_levels=0;
else
/*
* We are a new gnode, so we send the tracer in all higher
* levels
*/
tracer_levels=fn_hdr->level;
} else {
/*
* We are just a normal node inside a gnode, let's notice only
* the other nodes in this gnode.
*/
tracer_levels=2;
}
/*
* Initialize me.my_igws
*/
if(server_opt.share_internet) {
free_my_igws(&me.my_igws);
init_my_igws(me.igws, me.igws_counter, &me.my_igws, me.my_bandwidth,
me.cur_node, &me.cur_quadg);
}
loginfo("Starting the second radar scan before sending our"
" first tracer_pkt");
if(radar_scan(0))
fatal("%s:%d: Scan of the area failed. Cannot continue.",
ERROR_POS);
/*
* Now we send a simple tracer_pkt in all the level we have to. This pkt
* is just to say <<Hey there, I'm here, alive>>, thus the other nodes
* of the gnode will have the basic routes to reach us.
* Note that this is done only at the first time we hook.
*/
if(!we_are_rehooking) {
usleep(rand_range(0, 999999));
tracer_pkt_start_mutex=0;
for(i=1; i<tracer_levels; i++)
tracer_pkt_start(i-1);
}
/* Let's fill the krnl routing table */
loginfo("Filling the kernel routing table");
rt_full_update(0);
if(restricted_mode && (server_opt.use_shared_inet ||
server_opt.share_internet))
igw_replace_def_igws(me.igws, me.igws_counter,
me.my_igws, me.cur_quadg.levels, my_family);
/* (Re)Hook completed */
loginfo("%sook completed", we_are_rehooking ? "Reh":"H");
we_are_rehooking=0;
}
/*
* netsukuku_hook: hooks/rehooks at an existing gnode or creates a new one.
* `hook_level' specifies at what level we are hooking, generally it is 0.
* If `hook_gnode' is not null, netsukuku_hook will try to hook only to the
* rnodes which belongs to the `hook_gnode' at `hook_level' level.
*/
int netsukuku_hook(map_gnode *hook_gnode, int hook_level)
{
struct rnode_list *rnl=rlist;
struct free_nodes_hdr fn_hdr;
inet_prefix gnode_ipstart, old_ip;
quadro_group old_quadg;
int ret=0, new_gnode=0;
u_char fnodes[MAXGROUPNODE];
/* Save our current IP before resetting */
inet_copy(&old_ip, &me.cur_ip);
memcpy(&old_quadg, &me.cur_quadg, sizeof(quadro_group));
/* Reset the hook */
if(total_hooks) {
hook_reset();
we_are_rehooking=1;
}
total_hooks++;
/*
* * The beginning * *
*/
loginfo("The %s begins. Starting to scan the area",
we_are_rehooking ? "rehook" : "hook");
new_gnode=hook_first_radar_scan(hook_gnode, hook_level, &old_quadg);
if(new_gnode)
goto finish;
/*
* Get the free nodes list
*/
qspn_backup_gcount(qspn_old_gcount, (int*)qspn_gnode_count);
new_gnode=hook_get_free_nodes(hook_level, &fn_hdr, fnodes,
&gnode_ipstart, qspn_gnode_count, &rnl);
if(new_gnode)
goto finish;
/*
* Choose a new IP
*/
new_gnode=hook_choose_new_ip(hook_gnode, hook_level, &fn_hdr, fnodes,
&gnode_ipstart);
/*
* Get the external map
*/
new_gnode=hook_get_ext_map(hook_level, new_gnode, rnl, &fn_hdr,
me.ext_map, &old_quadg);
if(new_gnode)
goto finish;
/*
* Get the internal map
*/
hook_get_int_map();
/*
* Fetch the bnode map
*/
hook_get_bnode_map();
/*
* If we are in restricted mode, get the Internet Gateways
*/
if(restricted_mode && (server_opt.use_shared_inet ||
server_opt.share_internet))
hook_get_igw();
/*
* And that's all, clean the mess
*/
if(free_the_tmp_cur_node) {
xfree(me.cur_node);
free_the_tmp_cur_node=0;
}
me.cur_node = &me.int_map[me.cur_quadg.gid[0]];
map_node_del(me.cur_node);
me.cur_node->flags &= ~MAP_VOID;
me.cur_node->flags |= MAP_ME;
/* We need a fresh me.cur_node */
refresh_hook_root_node();
finish:
hook_finish(new_gnode, &fn_hdr);
return ret;
}
/*
* And this is the end my dear.
*/