#include "all.h"
#include "../ip/ip.h"
#define dprint(...) if(cons.flags&ralloc.flag)print(__VA_ARGS__)
enum
{
Version= 1,
/*
* Packet types.
*/
Request= 1,
Response= 2,
Traceon= 3,
Traceoff= 4,
Infinity= 16, /* infinite hop count */
Maxpacket= 488, /* largest packet body */
};
/*
* network info
*/
typedef struct Rip Rip;
struct Rip
{
uchar family[2];
uchar port[2];
uchar addr[Pasize];
uchar pad[8];
uchar metric[4];
};
typedef struct Ripmsg Ripmsg;
struct Ripmsg
{
uchar type;
uchar vers;
uchar pad[2];
Rip rip[1]; /* the rest of the packet consists of routes */
};
enum
{
Maxroutes= (Maxpacket-4)/sizeof(Ripmsg),
};
/*
* internal route info
*/
enum
{
Nroute = 2*1024,
Nhash = 256, /* routing hash buckets */
Nifc = 16,
};
typedef struct Route Route;
struct Route
{
Route *next;
uchar dest[Pasize];
uchar mask[Pasize];
uchar gate[Pasize];
int metric;
int inuse;
Timet time;
};
struct
{
Lock;
Route route[Nroute];
Route *hash[Nhash];
int nroute;
ulong flag;
int dorip;
} ralloc;
uchar classmask[4][4] =
{
0xff, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xff, 0x00,
};
#define CLASS(p) ((*(uchar*)(p))>>6)
static void considerroute(Route*);
static void deleteroute(Route*);
static void printroute(Route*);
static void printroutes(void);
static void installroute(Route*);
static void getmask(uchar*, uchar*);
static void maskip(uchar*, uchar*, uchar*);
static int equivip(uchar*, uchar*);
static void cmd_route(int, char*[]);
static ulong rhash(uchar*);
void
iprouteinit(void)
{
cmd_install("route", "subcommand -- ip routes", cmd_route);
ralloc.flag = flag_install("route", "-- verbose");
if(!conf.ripoff)
ralloc.dorip = 1;
}
static void
cmd_route(int argc, char *argv[])
{
Route r;
if(argc < 2) {
usage:
print("route add dest gate [mask] -- add a route\n");
print("route delete dest -- remote a route\n");
print("route print [dest] -- print routes\n");
print("route ripon -- listen to RIP packets\n");
print("route ripoff -- ignore RIP packets\n");
return;
}
if(strcmp(argv[1], "ripoff") == 0)
ralloc.dorip = 0;
else if(strcmp(argv[1], "ripon") == 0)
ralloc.dorip = 1;
else if(strcmp(argv[1], "add") == 0) {
switch(argc){
default:
goto usage;
case 4:
memmove(r.mask, classmask[CLASS(r.dest)], Pasize);
break;
case 5:
if(chartoip(r.mask, argv[4]))
goto usage;
break;
}
if(chartoip(r.dest, argv[2]) || chartoip(r.gate, argv[3]))
goto usage;
r.metric = 0; /* rip can't nuke these */
deleteroute(&r);
considerroute(&r);
} else if(strcmp(argv[1], "delete") == 0) {
if(argc != 3 || chartoip(r.dest, argv[2]))
goto usage;
deleteroute(&r);
} else if(strcmp(argv[1], "print") == 0) {
if(argc == 3) {
if(chartoip(r.dest, argv[2]))
goto usage;
printroute(&r);
} else
printroutes();
}
}
/*
* consider installing a route. Do so only if it is better than what
* we have.
*/
static void
considerroute(Route *r)
{
ulong h, i;
ulong m, nm;
Route *hp, **l;
r->next = 0;
r->time = time();
r->inuse = 1;
lock(&ralloc);
h = rhash(r->dest);
for(hp = ralloc.hash[h]; hp; hp = hp->next) {
if(equivip(hp->dest, r->dest) && equivip(hp->mask, r->mask)) {
/*
* found a match, replace if better (or much newer)
*/
if(r->metric < hp->metric || time()-hp->time > 10*60) {
memmove(hp->gate, r->gate, Pasize);
hp->metric = r->metric;
dprint("route: replacement %I & %I -> %I (%d)\n",
hp->dest, hp->mask, hp->dest, hp->metric);
}
if(equivip(r->gate, hp->gate))
hp->time = time();
goto done;
}
}
/*
* no match, look for space
*/
for(hp = ralloc.route; hp < &ralloc.route[Nroute]; hp++)
if(hp->inuse == 0)
break;
if(hp == &ralloc.route[Nroute])
hp = 0;
/*
* look for an old entry
*/
for(i = 0; hp == 0 && i < Nhash; i++) {
l = &ralloc.hash[i];
for(hp = *l; hp; hp = *l) {
if(time() - hp->time > 10*60 && hp->metric > 0){
*l = hp->next;
break;
}
l = &hp->next;
}
}
if(hp == 0) {
print("no more routes");
goto done;
}
memmove(hp, r, sizeof(Route));
/*
* insert largest mask first
*/
m = nhgetl(hp->mask);
for(l = &ralloc.hash[h]; *l; l = &(*l)->next){
nm = nhgetl((*l)->mask);
if(nm < m)
break;
}
hp->next = *l;
*l = hp;
dprint("route: new %I & %I -> %I (%d)\n", hp->dest, hp->mask,
hp->dest, hp->metric);
done:
unlock(&ralloc);
}
static void
deleteroute(Route *r)
{
int h;
Route *hp, **l;
lock(&ralloc);
for(h = 0; h < Nhash; h++) {
l = &ralloc.hash[h];
for(hp = *l; hp; hp = *l){
if(equivip(r->dest, hp->dest)) {
*l = hp->next;
hp->next = 0;
hp->inuse = 0;
break;
}
l = &hp->next;
}
}
unlock(&ralloc);
}
static void
printroutes(void)
{
Ifc *i;
int h;
Route *hp;
uchar mask[Pasize];
lock(&ralloc);
for(h = 0; h < Nhash; h++)
for(hp = ralloc.hash[h]; hp; hp = hp->next)
print("%I & %I -> %I\n", hp->dest, hp->mask, hp->gate);
unlock(&ralloc);
print("\nifc's\n");
for(i = enets; i; i = i->next) {
hnputl(mask, i->mask);
print("addr %I mask %I defgate %I\n", i->ipa, mask, i->netgate);
}
}
static void
printroute(Route *r)
{
int h;
Route *hp;
uchar net[Pasize];
h = rhash(r->dest);
for(hp = ralloc.hash[h]; hp; hp = hp->next){
maskip(r->dest, hp->mask, net);
if(equivip(hp->dest, net)){
print("%I & %I -> %I\n", hp->dest, hp->mask, hp->gate);
return;
}
}
print("default * -> %I\n", enets[0].netgate);
}
void
iproute(uchar *to, uchar *dst, uchar *def)
{
int h;
Route *hp;
uchar net[Pasize];
h = rhash(dst);
for(hp = ralloc.hash[h]; hp; hp = hp->next) {
maskip(dst, hp->mask, net);
if(equivip(hp->dest, net)) {
def = hp->gate;
break;
}
}
memmove(to, def, Pasize);
}
void
riprecv(Msgbuf *mb, Ifc*)
{
int n;
Rip *r;
Ripmsg *m;
Udppkt *uh;
Route route;
if(ralloc.dorip == 0)
goto drop;
uh = (Udppkt*)mb->data;
m = (Ripmsg*)(mb->data + Ensize + Ipsize + Udpsize);
if(m->type != Response || m->vers != Version)
goto drop;
n = nhgets(uh->udplen);
n -= Udpsize;
n = n/sizeof(Rip);
dprint("%d routes from %I\n", n, uh->src);
memmove(route.gate, uh->src, Pasize);
for(r = m->rip; r < &m->rip[n]; r++){
getmask(route.mask, r->addr);
maskip(r->addr, route.mask, route.dest);
route.metric = nhgetl(r->metric) + 1;
if(route.metric < 1)
continue;
considerroute(&route);
}
drop:
mbfree(mb);
}
/*
* route's hashed by net, not subnet
*/
static ulong
rhash(uchar *d)
{
ulong h;
uchar net[Pasize];
maskip(d, classmask[CLASS(d)], net);
h = net[0] + net[1] + net[2];
return h % Nhash;
}
/*
* figure out what mask to use, if we have a direct connected network
* with the same class net use its subnet mask.
*/
static void
getmask(uchar *mask, uchar *dest)
{
Ifc *i;
long ip;
ip = nhgetl(dest);
for(i = enets; i; i = i->next)
if((i->ipaddr & i->cmask) == (ip & i->cmask)) {
hnputl(mask, i->mask);
return;
}
memmove(mask, classmask[CLASS(dest)], Pasize);
}
static void
maskip(uchar *a, uchar *m, uchar *n)
{
int i;
for(i = 0; i < 4; i++)
n[i] = a[i] & m[i];
}
static int
equivip(uchar *a, uchar *b)
{
int i;
for(i = 0; i < 4; i++)
if(a[i] != b[i])
return 0;
return 1;
}
long
ipclassmask(uchar *ip)
{
return nhgetl(classmask[CLASS(ip)]);
}
|