mirror of
https://github.com/ChronosX88/netsukuku.git
synced 2024-10-18 17:31:00 +00:00
877 lines
25 KiB
C
877 lines
25 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.
|
|
*
|
|
* --
|
|
* route.c:
|
|
* Routing table management code.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
#include "libnetlink.h"
|
|
#include "inet.h"
|
|
#include "krnl_route.h"
|
|
#include "request.h"
|
|
#include "endianness.h"
|
|
#include "pkts.h"
|
|
#include "bmap.h"
|
|
#include "qspn.h"
|
|
#include "radar.h"
|
|
#include "netsukuku.h"
|
|
#include "route.h"
|
|
|
|
int get_gw_gnode_recurse(map_node *, map_gnode **, map_bnode **, u_int *,
|
|
map_gnode *, map_gnode *, map_node *, u_char, u_char,
|
|
void **, int, int);
|
|
|
|
/*
|
|
* get_gw_bnode_recurse: this function is part of get_gw_gnode_recurse().
|
|
* The explanation of it's way of working is inside the get_gw_gnode()
|
|
* function.
|
|
*/
|
|
int get_gw_bnode_recurse(map_node *int_map, map_gnode **ext_map,
|
|
map_bnode **bnode_map, u_int *bmap_nodes, map_gnode *find_gnode,
|
|
map_gnode *gnode_gw, map_node *node_gw, u_char gnode_level,
|
|
u_char gw_level, void **gateways, int gateways_nmembs, int single_gw)
|
|
{
|
|
map_gnode *gnode=0;
|
|
map_node *node, *root_node;
|
|
ext_rnode_cache *erc;
|
|
int i, bpos;
|
|
|
|
i=gnode_level;
|
|
|
|
if(i == gw_level) {
|
|
/* Gateway found */
|
|
gateways[0]=(void *)node_gw;
|
|
return 0;
|
|
} else if(!i)
|
|
return -1;
|
|
|
|
/* Find the bnode which borders on the `node_gw' gnode */
|
|
bpos=map_find_bnode_rnode(bnode_map[i-1], bmap_nodes[i-1], (void *)node_gw);
|
|
if(bpos == -1) {
|
|
/*debug(DBG_INSANE, "get_gw: l=%d, node_gw=%x not found in bmap lvl %d",
|
|
i, node_gw, i-1);*/
|
|
return -1;
|
|
}
|
|
|
|
if(!(i-1))
|
|
node=node_from_pos(bnode_map[i-1][bpos].bnode_ptr, int_map);
|
|
else {
|
|
gnode=gnode_from_pos(bnode_map[i-1][bpos].bnode_ptr,
|
|
ext_map[_EL(i-1)]);
|
|
node=&gnode->g;
|
|
|
|
/* If we are a bnode and the `gnode', the found bnode, is us,
|
|
* let's check if we have `gnode_gw' in our external rnode
|
|
* cache. If we have, the gw has been found */
|
|
qspn_set_map_vars(i-1, 0, &root_node, 0, 0);
|
|
if(me.cur_node->flags & MAP_BNODE &&
|
|
gnode == (map_gnode *)root_node) {
|
|
/* debug(DBG_INSANE, "get_gw: bmap searching ernode for gnode 0x%x",node_gw); */
|
|
|
|
erc=erc_find_gnode(me.cur_erc, gnode_gw, i);
|
|
if(erc) {
|
|
gateways[0]=(void *)erc->e;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* debug(DBG_INSANE, "get_gw: bmap found = %x", node); */
|
|
|
|
/* Descend in the lower level */
|
|
if((--i) >= gw_level)
|
|
return get_gw_gnode_recurse(int_map, ext_map, bnode_map, bmap_nodes,
|
|
find_gnode, gnode, node, i, gw_level,
|
|
gateways, gateways_nmembs, single_gw);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_gw_gnode_recurse: recursive part of the get_gw_gnode function (see
|
|
* below).
|
|
* `gateways' is the array of pointers. which point to the found gateway
|
|
* nodes.
|
|
* `gateways_nmembs' is the number of members of the `gateways' array.
|
|
*/
|
|
int get_gw_gnode_recurse(map_node *int_map, map_gnode **ext_map,
|
|
map_bnode **bnode_map, u_int *bmap_nodes, map_gnode *find_gnode,
|
|
map_gnode *gnode, map_node *node, u_char gnode_level,
|
|
u_char gw_level, void **gateways, int gateways_nmembs,
|
|
int single_gw)
|
|
{
|
|
map_gnode *gnode_gw=0;
|
|
map_node *node_gw;
|
|
int i, pos, routes, sub_routes, e, ret;
|
|
|
|
/*debug(DBG_INSANE, "get_gw: find_gnode=%x", find_gnode);*/
|
|
i=gnode_level;
|
|
|
|
if(node->flags & MAP_RNODE) {
|
|
/*
|
|
* If `node' is an our rnode, then the gateway to reach it is
|
|
* itself, so we set the `gnode_gw' to `node' in order to find
|
|
* in the lower level a bnode which borders on `node'
|
|
*/
|
|
gnode_gw=(void *)node;
|
|
node_gw=(map_node *)gnode_gw;
|
|
/*debug(DBG_INSANE, "get_gw: l=%d, node & MAP_RNODE. node_gw=node=%x",
|
|
i, node);*/
|
|
} else if (node->flags & MAP_ME) {
|
|
/*
|
|
* If `node' is an our gnode. we reset the gnode_gw to
|
|
* `find_gnode', in this way, in the lower level we'll find a
|
|
* bnode which borders on `find_gnode'.
|
|
*/
|
|
gnode_gw=(void *)find_gnode;
|
|
node_gw=(map_node *)gnode_gw;
|
|
/*debug(DBG_INSANE, "get_gw: l=%d, node & MAP_ME. find_gnode: %x",
|
|
i, find_gnode);*/
|
|
} else {
|
|
|
|
if(!node->links || (i && !gnode))
|
|
/* That's no good */
|
|
return -1;
|
|
|
|
if(single_gw) {
|
|
/* only one route is needed, do not fork */
|
|
routes=1;
|
|
sub_routes=1;
|
|
} else {
|
|
/* `routes': how many different links we must consider */
|
|
routes = sub_gw_links[FAMILY_LVLS - i - 1];
|
|
routes = routes > node->links ? node->links : routes;
|
|
|
|
/* How many routes there are in each of the `routes'# links */
|
|
sub_routes = gateways_nmembs/routes;
|
|
|
|
}
|
|
|
|
int old_pos[routes];
|
|
memset(old_pos, -1, sizeof(int)*routes);
|
|
|
|
ret=0;
|
|
for(e=0; e < routes; e++) {
|
|
|
|
/* Choose a random link, which was not chosen before */
|
|
while(find_int((pos=rand_range(0, node->links-1)), old_pos, routes));
|
|
old_pos[e]=pos;
|
|
|
|
if(!i) {
|
|
node_gw=(void *)node->r_node[pos].r_node;
|
|
} else {
|
|
gnode_gw=(map_gnode *)gnode->g.r_node[pos].r_node;
|
|
node_gw=(void *)gnode_gw;
|
|
}
|
|
|
|
if(node_gw->flags & MAP_RNODE)
|
|
find_gnode=(map_gnode *)node_gw;
|
|
/*debug(DBG_INSANE, "get_gw: l=%d, e %d node_gw=rnode[%d].r_node=%x,"
|
|
" find_gnode=%x", i, e, pos, node_gw, find_gnode);*/
|
|
|
|
ret+=get_gw_bnode_recurse(int_map, ext_map, bnode_map, bmap_nodes,
|
|
find_gnode, gnode_gw, node_gw, i, gw_level,
|
|
&gateways[e*sub_routes], sub_routes, single_gw);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
return get_gw_bnode_recurse(int_map, ext_map, bnode_map, bmap_nodes,
|
|
find_gnode, gnode_gw, node_gw, i, gw_level, gateways,
|
|
gateways_nmembs, single_gw);
|
|
}
|
|
|
|
|
|
/*
|
|
* get_gw_gnode: It finds the MAX_MULTIPATH_ROUTES best gateway present in the
|
|
* map of level `gw_level'. These gateways are the nodes to be used as gateway
|
|
* to reach, from the `gw_level' level, the `find_gnode' gnode at the
|
|
* `gnode_level' level.
|
|
* If not a single gateway is found, NULL is returned, otherwise an array of
|
|
* pointers to the gateway nodes is returned, the array has
|
|
* MAX_MULTIPATH_ROUTES+1 nmembs. Some member of the array can be NULL, ignore
|
|
* them. Remember to xfree the array of pointers!
|
|
* If `single_gw' is not 0, only one gateway will be returned.
|
|
*/
|
|
void **get_gw_gnode(map_node *int_map, map_gnode **ext_map,
|
|
map_bnode **bnode_map, u_int *bmap_nodes,
|
|
map_gnode *find_gnode, u_char gnode_level,
|
|
u_char gw_level, int single_gw)
|
|
{
|
|
map_gnode *gnode;
|
|
map_node *node;
|
|
void **gateways=0;
|
|
int ret;
|
|
|
|
if(!gnode_level || gw_level > gnode_level)
|
|
goto error;
|
|
|
|
gateways=xzalloc(sizeof(void *) * MAX_MULTIPATH_ROUTES+1);
|
|
|
|
/*
|
|
* In order to find the gateway at level `gw_level', which will be
|
|
* used to reach the `find_gnode' gnode at level `gnode_level',
|
|
* firstly we find the gnode, at level `gnode_level' that can be used
|
|
* as a gateway to reach `find_gnode', then we go down of one level
|
|
* and we search the gateway that can be used to reach the border node
|
|
* which borders to the previously found gateway. The same procedure
|
|
* is done until we arrive at the desired `gw_level' level.
|
|
*
|
|
* Sadly this procedure can give us only one route to reach the
|
|
* `find_gnode' gnode, in fact, there can be, at each level, multiple
|
|
* gateways to reach the same gnode (or bnode). For this reason, at
|
|
* each level, we must first fork at each found gateway and then we
|
|
* can descend in the lower level. In this way each forked child will
|
|
* try to find the gateway to reach the bnode which borders on the
|
|
* parent gateway.
|
|
* Each found gateway, which belongs to the `gw_level' is added to the
|
|
* `gateways' array, but here comes another problem: if in each level
|
|
* we find the maximum number of available gateways, there will be a
|
|
* total of MAX_MULTIPATH_ROUTES^(gnode_level-gw_level) routes.
|
|
* We can return only MAX_MULTIPATH_ROUTES routes, so we restrict the
|
|
* number of forks per level. Each level has its forks number, which
|
|
* is already stored in the `sub_gw_links' array. (For more info on
|
|
* that array, read route.h).
|
|
* That's all.
|
|
*
|
|
* To implement all that mess we use three functions, in this way:
|
|
*
|
|
* get_gw_gnode() is only the starting function. It sets
|
|
* gnode=find_gnode and launches get_gw_gnode_recurse(gnode), which
|
|
* finds the gateway to reach the given `gnode'. It then forks for
|
|
* each gateway found and launches get_gw_bnode_recurse(gateway) which
|
|
* searches, in the lower level, the bnode which borders on `gateway'.
|
|
* Then get_gw_bnode_recurse() sets gnode to the found gateway and
|
|
* launches again get_gw_gnode(gnode). The loop continues until the
|
|
* `gw_level' is reached.
|
|
*
|
|
* Wow, that was a long explanation ;)
|
|
*/
|
|
gnode=find_gnode;
|
|
node=&gnode->g;
|
|
|
|
/* The gateway to reach me is myself. */
|
|
if(node->flags & MAP_ME) {
|
|
gateways[0]=(void *)node;
|
|
return gateways;
|
|
}
|
|
|
|
ret=get_gw_gnode_recurse(int_map, ext_map, bnode_map, bmap_nodes,
|
|
find_gnode, gnode, node, gnode_level, gw_level,
|
|
gateways, MAX_MULTIPATH_ROUTES, single_gw);
|
|
|
|
if(ret < 0)
|
|
goto error;
|
|
|
|
return gateways;
|
|
|
|
error:
|
|
if(gateways)
|
|
xfree(gateways);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* get_gw_ips: It's a wrapper to get_gw_gnode() that converts the found
|
|
* gateways to IPs.
|
|
* `gw_ip' is the array of inet_prefix structs where the converted IPs will be
|
|
* stored. It must have MAX_MULTIPATH_ROUTES members.
|
|
* If `single_gw' is not null, only the best gateway will be converted and it
|
|
* is assumed that `gw_ip' has only 1 member.
|
|
* The number of IPs stored in `gw_ip' is returned.
|
|
* The pointer to the gateways node pointers are copied in the `gw_gnodes'
|
|
* array, (if not null), which must have at least MAX_MULTIPATH_ROUTES members.
|
|
* On error -1 is returned.
|
|
*/
|
|
int get_gw_ips(map_node *int_map, map_gnode **ext_map,
|
|
map_bnode **bnode_map, u_int *bmap_nodes,
|
|
quadro_group *cur_quadg,
|
|
map_gnode *find_gnode, u_char gnode_level,
|
|
u_char gw_level, inet_prefix *gw_ip, map_node **gw_nodes,
|
|
int single_gw)
|
|
{
|
|
ext_rnode *e_rnode=0;
|
|
map_node **gw_node=0;
|
|
int i, e, gw_ip_members;
|
|
|
|
gw_ip_members=single_gw ? 1 : MAX_MULTIPATH_ROUTES;
|
|
setzero(gw_ip, sizeof(inet_prefix)*gw_ip_members);
|
|
|
|
gw_node=(map_node **)get_gw_gnode(int_map, ext_map, bnode_map, bmap_nodes,
|
|
find_gnode, gnode_level, gw_level, single_gw);
|
|
|
|
if(!gw_node)
|
|
return -1;
|
|
|
|
for(i=0, e=0; i<MAX_MULTIPATH_ROUTES; i++) {
|
|
|
|
if(!gw_node[i])
|
|
continue;
|
|
|
|
if(gw_node[i]->flags & MAP_ERNODE) {
|
|
e_rnode=(ext_rnode *)gw_node[i];
|
|
inet_copy(&gw_ip[e], &e_rnode->quadg.ipstart[gw_level]);
|
|
} else
|
|
maptoip((u_int)int_map, (u_int)gw_node[i], cur_quadg->ipstart[1],
|
|
&gw_ip[e]);
|
|
|
|
if(gw_nodes)
|
|
gw_nodes[e]=gw_node[i];
|
|
|
|
e++;
|
|
}
|
|
|
|
xfree(gw_node);
|
|
return e ? e : -1;
|
|
}
|
|
|
|
/*
|
|
* find_rnode_dev_and_retry
|
|
*
|
|
* Searches with rnl_get_dev() the rnode_list which points to `node'.
|
|
* If it is not found it waits the next radar_scan. If it is not found again
|
|
* NULL is returned, otherwise the devices list of the related rnode_list
|
|
* struct is returned.
|
|
*/
|
|
interface **find_rnode_dev_and_retry(map_node *node)
|
|
{
|
|
int retries;
|
|
interface **devs=0;
|
|
|
|
for(retries=0; !(devs=rnl_get_dev(rlist, node)) && !retries; retries++)
|
|
radar_wait_new_scan();
|
|
return devs;
|
|
}
|
|
|
|
/*
|
|
* rt_build_nexthop_gw: returns an array of nexthop structs, which has a
|
|
* maximum of `maxhops' members. The nexthop are all gateway which can be used
|
|
* to reach `node' or `gnode'.
|
|
* The array is xmallocateed.
|
|
* On error NULL is returned.
|
|
*/
|
|
struct nexthop *rt_build_nexthop_gw(map_node *node, map_gnode *gnode, int level,
|
|
int maxhops)
|
|
{
|
|
map_node *tmp_node;
|
|
struct nexthop *nh=0;
|
|
interface **devs;
|
|
int n, i, ips, routes;
|
|
|
|
if(!level) {
|
|
nh=xmalloc(sizeof(struct nexthop)*(node->links+1));
|
|
setzero(nh, sizeof(struct nexthop)*(node->links+1));
|
|
|
|
for(i=0, n=0; i<node->links; i++) {
|
|
tmp_node=(map_node *)node->r_node[i].r_node;
|
|
|
|
maptoip((u_int)me.int_map, (u_int)tmp_node,
|
|
me.cur_quadg.ipstart[1], &nh[n].gw);
|
|
inet_htonl(nh[n].gw.data, nh[n].gw.family);
|
|
|
|
if(!(devs=find_rnode_dev_and_retry(tmp_node)))
|
|
continue;
|
|
nh[n].dev=devs[0]->dev_name;
|
|
|
|
nh[n].hops=node->links-i; /* multipath weigth */
|
|
n++;
|
|
|
|
if(maxhops && n >= maxhops)
|
|
break;
|
|
}
|
|
nh[n].dev=0;
|
|
} else if(level) {
|
|
inet_prefix gnode_gws[MAX_MULTIPATH_ROUTES];
|
|
map_node *gw_nodes[MAX_MULTIPATH_ROUTES];
|
|
|
|
routes=get_gw_ips(me.int_map, me.ext_map, me.bnode_map,
|
|
me.bmap_nodes, &me.cur_quadg,
|
|
gnode, level, 0, gnode_gws, gw_nodes, 0);
|
|
if(routes < 0)
|
|
goto finish;
|
|
|
|
nh=xmalloc(sizeof(struct nexthop)*(routes+1));
|
|
setzero(nh, sizeof(struct nexthop)*(routes+1));
|
|
|
|
for(ips=0, n=0; ips < routes; ips++) {
|
|
inet_copy(&nh[n].gw, &gnode_gws[ips]);
|
|
inet_htonl(nh[n].gw.data, nh[n].gw.family);
|
|
|
|
if(!(devs=find_rnode_dev_and_retry(gw_nodes[ips])))
|
|
continue;
|
|
nh[n].dev=devs[0]->dev_name;
|
|
|
|
nh[n].hops=routes-ips; /* multipath weigth */
|
|
n++;
|
|
|
|
if(maxhops && n >= maxhops)
|
|
break;
|
|
}
|
|
|
|
nh[n].dev=0;
|
|
}
|
|
finish:
|
|
return nh;
|
|
}
|
|
|
|
struct nexthop *rt_build_nexthop_voidgw(void *void_gw, interface **oifs)
|
|
{
|
|
map_node *gw_node=0;
|
|
ext_rnode *e_rnode=0;
|
|
struct nexthop *nh;
|
|
int dev_n, i;
|
|
|
|
if(void_gw)
|
|
gw_node=(map_node *)void_gw;
|
|
|
|
if(!oifs && !(oifs=find_rnode_dev_and_retry(gw_node)))
|
|
/* It wasn't found any suitable dev */
|
|
return 0;
|
|
|
|
for(dev_n=0; oifs[dev_n]; dev_n++);
|
|
|
|
nh=xmalloc(sizeof(struct nexthop)*(dev_n+1));
|
|
setzero(nh, sizeof(struct nexthop)*(dev_n+1));
|
|
|
|
if(gw_node->flags & MAP_ERNODE) {
|
|
e_rnode=(ext_rnode *)gw_node;
|
|
inet_copy(&nh[0].gw, &e_rnode->quadg.ipstart[0]);
|
|
} else
|
|
maptoip((u_int)me.int_map, (u_int)gw_node,
|
|
me.cur_quadg.ipstart[1], &nh[0].gw);
|
|
inet_htonl(nh[0].gw.data, nh[0].gw.family);
|
|
nh[0].dev=oifs[0]->dev_name;
|
|
nh[0].hops=1;
|
|
|
|
for(i=1; i<dev_n; i++) {
|
|
memcpy(&nh[i], &nh[0], sizeof(struct nexthop));
|
|
nh[i].dev=oifs[i]->dev_name;
|
|
nh[i].hops=1;
|
|
}
|
|
nh[i].dev=0;
|
|
|
|
return nh;
|
|
}
|
|
|
|
/*
|
|
* rt_update_node
|
|
*
|
|
* It adds/replaces or removes a route from the kernel's
|
|
* table, if the node's flag is found, respectively, to be set to
|
|
* MAP_UPDATE or set to MAP_VOID. When a route is deleted only the destination
|
|
* arguments are required (i.e `void_gw', `oif' are not needed).
|
|
*
|
|
* The destination of the route can be given with `dst_ip', `dst_node' or
|
|
* `dst_quadg'.
|
|
*
|
|
* If `dst_ip' is not null, the given inet_prefix struct is used, it's also
|
|
* used the `dst_node' to retrieve the flags.
|
|
* If the destination of the route is a node which belongs to the level 0, the
|
|
* same node must be passed in the `dst_node' argument.
|
|
*
|
|
* If `level' is > 0 and `dst_quadg' is not null, then it updates the gnode
|
|
* which is inside the `dst_quadg' struct: dst_quadg->gnode[_EL(level)]. The
|
|
* quadro_group struct must be complete and refer to the groups of the
|
|
* given gnode.
|
|
*
|
|
* If `level' is > 0 and `dst_quadg' is null, it's assumed that the gnode is passed
|
|
* in `dst_node' and that the quadro_group for that gnode is me.cur_quadg.
|
|
*
|
|
* If `void_gw' is not null, it is used as the only gw to reach the destination
|
|
* node, otherwise the gw will be calculated.
|
|
* `oifs', if not null, will be used in conjuction with `void_gw' as the output
|
|
* interfaces to be used in the route. `oifs' is an array of pointers of
|
|
* maximum MAX_INTERFACES# members.
|
|
*/
|
|
void rt_update_node(inet_prefix *dst_ip, void *dst_node, quadro_group *dst_quadg,
|
|
void *void_gw, interface **oifs, u_char level)
|
|
{
|
|
map_node *node=0;
|
|
map_gnode *gnode=0;
|
|
struct nexthop *nh=0;
|
|
inet_prefix to;
|
|
int node_pos=0, route_scope=0;
|
|
|
|
#ifdef DEBUG
|
|
#define MAX_GW_IP_STR_SIZE (MAX_MULTIPATH_ROUTES*((INET6_ADDRSTRLEN+1)+IFNAMSIZ)+1)
|
|
int n;
|
|
char *to_ip=0, gw_ip[MAX_GW_IP_STR_SIZE]="";
|
|
#else
|
|
const char *to_ip=0;
|
|
#endif
|
|
|
|
node=(map_node *)dst_node;
|
|
gnode=(map_gnode *)dst_node;
|
|
|
|
/*
|
|
* Deduce the destination's ip
|
|
*/
|
|
if(dst_ip)
|
|
inet_copy(&to, dst_ip);
|
|
else if(level) {
|
|
if(!dst_quadg) {
|
|
dst_quadg=&me.cur_quadg;
|
|
node_pos=pos_from_gnode(gnode, me.ext_map[_EL(level)]);
|
|
} else {
|
|
gnode=dst_quadg->gnode[_EL(level)];
|
|
node_pos=dst_quadg->gid[level];
|
|
}
|
|
node=&gnode->g;
|
|
gnodetoip(dst_quadg, node_pos, level, &to);
|
|
} else {
|
|
node_pos=pos_from_node(node, me.int_map);
|
|
maptoip((u_int)me.int_map, (u_int)node, me.cur_quadg.ipstart[1], &to);
|
|
}
|
|
#ifdef DEBUG
|
|
to_ip=xstrdup(inet_to_str(to));
|
|
#else
|
|
to_ip=inet_to_str(to);
|
|
#endif
|
|
inet_htonl(to.data, to.family);
|
|
|
|
if(node->flags & MAP_VOID)
|
|
/* We have only to delete the route, skip to do_update */
|
|
goto do_update;
|
|
|
|
/*
|
|
* If `node' it's a rnode of level 0, do nothing! It is already
|
|
* directly connected to me. (If void_gw is not null, skip this check).
|
|
*/
|
|
if(node->flags & MAP_RNODE && !level && !void_gw)
|
|
goto finish;
|
|
|
|
/* Dumb you, we don't need the route to reach ourself */
|
|
if(node->flags & MAP_ME)
|
|
goto finish;
|
|
|
|
/*
|
|
* Now, get the gateway to reach the destination.
|
|
*/
|
|
if(void_gw)
|
|
nh=rt_build_nexthop_voidgw(void_gw, oifs);
|
|
else
|
|
nh=rt_build_nexthop_gw(node, gnode, level, MAX_MULTIPATH_ROUTES);
|
|
if(!nh) {
|
|
debug(DBG_NORMAL, "Cannot get the gateway for "
|
|
"the (g)node: %d of level: %d, ip:"
|
|
"%s", node_pos, level, to_ip);
|
|
goto finish;
|
|
}
|
|
|
|
do_update:
|
|
#ifdef DEBUG
|
|
for(n=0; nh && nh[n].dev; n++){
|
|
strcat(gw_ip, inet_to_str(nh[n].gw));
|
|
strcat(gw_ip, ":");
|
|
strcat(gw_ip, nh[n].dev);
|
|
if(nh[n+1].dev)
|
|
strcat(gw_ip, ",");
|
|
}
|
|
if(node->flags & MAP_VOID)
|
|
strcpy(gw_ip, "deleted");
|
|
debug(DBG_INSANE, "rt_update_node: to "PURPLE("%s/%d") " via " RED("%s"),
|
|
to_ip, to.bits, gw_ip);
|
|
|
|
#endif
|
|
if(node->flags & MAP_RNODE && !level)
|
|
/* The dst node is a node directly linked to us */
|
|
route_scope = RT_SCOPE_LINK;
|
|
|
|
if(node->flags & MAP_VOID) {
|
|
/* Ok, let's delete it */
|
|
if(route_del(RTN_UNICAST, 0, 0, &to, 0, 0, 0))
|
|
error("WARNING: Cannot delete the route entry for the"
|
|
"%snode %d lvl %d!", !level ? " " : " g",
|
|
node_pos, level);
|
|
} else if(route_replace(0, route_scope, 0, &to, nh, 0, 0))
|
|
error("WARNING: Cannot update the route entry for the"
|
|
"%snode %d lvl %d",!level ? " " : " g",
|
|
node_pos, level);
|
|
finish:
|
|
#ifdef DEBUG
|
|
if(to_ip)
|
|
xfree(to_ip);
|
|
#endif
|
|
if(nh)
|
|
xfree(nh);
|
|
}
|
|
|
|
/*
|
|
* rt_rnodes_update
|
|
*
|
|
* It updates all the node which are rnodes of the root_node
|
|
* of all the maps. If `check_update_flag' is non zero, the rnode will be
|
|
* updated only if it has the MAP_UPDATE flag set.
|
|
*/
|
|
void rt_rnodes_update(int check_update_flag)
|
|
{
|
|
u_short i, level;
|
|
ext_rnode *e_rnode;
|
|
map_node *root_node, *node, *rnode;
|
|
map_gnode *gnode;
|
|
interface **out_devs;
|
|
|
|
/* Internal map */
|
|
root_node=me.cur_node;
|
|
for(i=0; i < root_node->links; i++) {
|
|
rnode=(map_node *)root_node->r_node[i].r_node;
|
|
out_devs=rnl_get_dev(rlist, rnode);
|
|
|
|
if(check_update_flag && !(rnode->flags & MAP_UPDATE))
|
|
/* nothing to do for this rnode */
|
|
continue;
|
|
|
|
if(rnode->flags & MAP_ERNODE) {
|
|
e_rnode=(ext_rnode *)rnode;
|
|
|
|
rt_update_node(&e_rnode->quadg.ipstart[0], rnode, 0,
|
|
me.cur_node, out_devs, /*level*/0);
|
|
rnode->flags&=~MAP_UPDATE;
|
|
|
|
for(level=1; level < e_rnode->quadg.levels; level++) {
|
|
if(!(gnode = e_rnode->quadg.gnode[_EL(level)]))
|
|
continue;
|
|
|
|
node = &gnode->g;
|
|
rt_update_node(0, 0, &e_rnode->quadg,
|
|
rnode, out_devs, level);
|
|
node->flags&=~MAP_UPDATE;
|
|
}
|
|
} else {
|
|
rt_update_node(0, rnode, 0, me.cur_node, out_devs, /*level*/0);
|
|
rnode->flags&=~MAP_UPDATE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Shall we activate it?
|
|
* route_flush_cache(my_family);
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* rt_full_update
|
|
*
|
|
* It updates _ALL_ the possible routes it can get from _ALL_ the maps.
|
|
* If `check_update_flag' is not 0, it will update only the routes of the
|
|
* nodes with the MAP_UPDATE flag set. Note that the MAP_VOID nodes aren't
|
|
* considered.
|
|
*/
|
|
void rt_full_update(int check_update_flag)
|
|
{
|
|
u_short i, l;
|
|
|
|
/* Update ext_maps */
|
|
for(l=me.cur_quadg.levels-1; l>=1; l--)
|
|
for(i=0; i<MAXGROUPNODE; i++) {
|
|
if(me.ext_map[_EL(l)][i].g.flags & MAP_VOID ||
|
|
me.ext_map[_EL(l)][i].flags & GMAP_VOID ||
|
|
me.ext_map[_EL(l)][i].g.flags & MAP_ME)
|
|
continue;
|
|
|
|
if(check_update_flag &&
|
|
!(me.ext_map[_EL(l)][i].g.flags & MAP_UPDATE))
|
|
continue;
|
|
|
|
rt_update_node(0, &me.ext_map[_EL(l)][i].g, 0, 0, 0, l);
|
|
me.ext_map[_EL(l)][i].g.flags&=~MAP_UPDATE;
|
|
}
|
|
|
|
/* Update int_map */
|
|
for(i=0, l=0; i<MAXGROUPNODE; i++) {
|
|
if(me.int_map[i].flags & MAP_VOID || me.int_map[i].flags & MAP_ME)
|
|
continue;
|
|
|
|
if(check_update_flag && !((me.int_map[i].flags & MAP_UPDATE)))
|
|
continue;
|
|
|
|
rt_update_node(0, &me.int_map[i], 0, 0, 0, l);
|
|
me.int_map[i].flags&=~MAP_UPDATE;
|
|
}
|
|
|
|
route_flush_cache(my_family);
|
|
}
|
|
|
|
/*
|
|
* rt_get_default_gw
|
|
*
|
|
* It stores in `gw' the IP address of the current default gw, and in
|
|
* `dev_name' its utilised net interface. If the default gw doesn't exist
|
|
* `gw' and `dev_name' are set to 0.
|
|
* If an error occurred a number < 0 is returned.
|
|
* `dev_name' must be of IFNAMSIZ# bytes.
|
|
*/
|
|
int rt_get_default_gw(inet_prefix *gw, char *dev_name)
|
|
{
|
|
inet_prefix default_gw;
|
|
|
|
inet_setip_anyaddr(&default_gw, my_family);
|
|
default_gw.len=default_gw.bits=0;
|
|
return route_get_exact_prefix_dst(default_gw, gw, dev_name);
|
|
}
|
|
|
|
int rt_exec_gw(char *dev, inet_prefix to, inet_prefix gw,
|
|
int (*route_function)(ROUTE_CMD_VARS), u_char table)
|
|
{
|
|
struct nexthop nh[2], *neho;
|
|
|
|
if(to.len)
|
|
inet_htonl(to.data, to.family);
|
|
|
|
if(gw.len) {
|
|
setzero(nh, sizeof(struct nexthop)*2);
|
|
inet_copy(&nh[0].gw, &gw);
|
|
inet_htonl(nh[0].gw.data, nh[0].gw.family);
|
|
nh[0].dev=dev;
|
|
nh[1].dev=0;
|
|
neho=nh;
|
|
} else
|
|
neho=0;
|
|
|
|
return route_function(0, 0, 0, &to, neho, dev, table);
|
|
}
|
|
|
|
int rt_add_gw(char *dev, inet_prefix to, inet_prefix gw, u_char table)
|
|
{
|
|
return rt_exec_gw(dev, to, gw, route_add, table);
|
|
}
|
|
|
|
int rt_del_gw(char *dev, inet_prefix to, inet_prefix gw, u_char table)
|
|
{
|
|
return rt_exec_gw(dev, to, gw, route_del, table);
|
|
}
|
|
|
|
int rt_change_gw(char *dev, inet_prefix to, inet_prefix gw, u_char table)
|
|
{
|
|
return rt_exec_gw(dev, to, gw, route_change, table);
|
|
}
|
|
|
|
int rt_replace_gw(char *dev, inet_prefix to, inet_prefix gw, u_char table)
|
|
{
|
|
return rt_exec_gw(dev, to, gw, route_replace, table);
|
|
}
|
|
|
|
int rt_replace_def_gw(char *dev, inet_prefix gw, u_char table)
|
|
{
|
|
inet_prefix to;
|
|
|
|
if(inet_setip_anyaddr(&to, my_family)) {
|
|
error("rt_replace_def_gw(): Cannot use INADRR_ANY for the %d "
|
|
"family", to.family);
|
|
return -1;
|
|
}
|
|
to.len=to.bits=0;
|
|
|
|
return rt_replace_gw(dev, to, gw, table);
|
|
}
|
|
|
|
int rt_delete_def_gw(u_char table)
|
|
{
|
|
inet_prefix to;
|
|
|
|
if(inet_setip_anyaddr(&to, my_family)) {
|
|
error("rt_delete_def_gw(): Cannot use INADRR_ANY for the %d "
|
|
"family", to.family);
|
|
return -1;
|
|
}
|
|
to.len=to.bits=0;
|
|
|
|
return route_del(0, 0, 0, &to, 0, 0, table);
|
|
}
|
|
|
|
/*
|
|
* rt_del_loopback_net:
|
|
* We remove the loopback net, leaving only the 127.0.0.1 ip for loopback.
|
|
* ip route del local 127.0.0.0/8 proto kernel scope host src 127.0.0.1
|
|
* ip route del broadcast 127.255.255.255 proto kernel scope link src 127.0.0.1
|
|
* ip route del broadcast 127.0.0.0 proto kernel scope link src 127.0.0.1
|
|
*/
|
|
int rt_del_loopback_net(void)
|
|
{
|
|
inet_prefix to;
|
|
char lo_dev[]="lo";
|
|
u_int idata[MAX_IP_INT];
|
|
|
|
setzero(idata, MAX_IP_SZ);
|
|
if(my_family!=AF_INET)
|
|
return 0;
|
|
|
|
/*
|
|
* ip route del broadcast 127.0.0.0 proto kernel scope link \
|
|
* src 127.0.0.1
|
|
*/
|
|
idata[0]=LOOPBACK_NET;
|
|
inet_setip(&to, idata, my_family);
|
|
route_del(RTN_BROADCAST, 0, 0, &to, 0, 0, RT_TABLE_LOCAL);
|
|
|
|
/*
|
|
* ip route del local 127.0.0.0/8 proto kernel scope host \
|
|
* src 127.0.0.1
|
|
*/
|
|
to.bits=8;
|
|
route_del(RTN_LOCAL, 0, 0, &to, 0, lo_dev, RT_TABLE_LOCAL);
|
|
|
|
/*
|
|
* ip route del broadcast 127.255.255.255 proto kernel scope link \
|
|
* src 127.0.0.1
|
|
*/
|
|
idata[0]=LOOPBACK_BCAST;
|
|
inet_setip(&to, idata, my_family);
|
|
route_del(RTN_BROADCAST, 0, 0, &to, 0, lo_dev, RT_TABLE_LOCAL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* rt_append_subnet_src:
|
|
* it appends the subnet relative to a `src' IP and its device in the routing
|
|
* table, f.e. when you do "ifconfig eth0 10.2.3.1 up" the kernel
|
|
* automatically adds this route:
|
|
* 10.0.0.0/8 dev eth0 proto kernel scope link src 10.2.3.1
|
|
* In this case `src'="10.2.3.1" and `dev'="eth0"
|
|
*/
|
|
int rt_append_subnet_src(inet_prefix *src, char *dev)
|
|
{
|
|
inet_prefix to, src_htonl;
|
|
|
|
if(src->family == AF_INET6)
|
|
fatal(ERROR_MSG "Family not supported", ERROR_POS);
|
|
|
|
inet_copy(&src_htonl, src);
|
|
inet_htonl(src_htonl.data, src->family);
|
|
|
|
setzero(&to, sizeof(inet_prefix));
|
|
to.family=src->family;
|
|
to.len=src->len;
|
|
if(((NTK_PRIVATE_B(src_htonl.data[0])) ||
|
|
(NTK_PRIVATE_C(src_htonl.data[0])))) {
|
|
to.bits=16;
|
|
to.data[0]=htonl((src->data[0] & 0xffff0000));
|
|
} else {
|
|
to.bits=8;
|
|
to.data[0]=htonl((src->data[0] & 0xff000000));
|
|
}
|
|
|
|
return route_append(0, RT_SCOPE_LINK, &src_htonl, &to, 0, dev, 0);
|
|
}
|