![cinap_lenrek](/assets/img/avatar_default.png)
This avoids ipconfig having to explicitely specify the tag when we want to set route type, as the tag can be provided implicitely thru the "tag" command.
1166 lines
21 KiB
C
1166 lines
21 KiB
C
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
|
|
#include "ip.h"
|
|
|
|
static void walkadd(Fs*, Route**, Route*);
|
|
static void addnode(Fs*, Route**, Route*);
|
|
static void calcd(Route*);
|
|
|
|
/* these are used for all instances of IP */
|
|
static Route* v4freelist;
|
|
static Route* v6freelist;
|
|
static RWlock routelock;
|
|
static ulong v4routegeneration, v6routegeneration;
|
|
|
|
static void
|
|
freeroute(Route *r)
|
|
{
|
|
Route **l;
|
|
|
|
r->ref = 0;
|
|
r->left = nil;
|
|
r->right = nil;
|
|
if(r->type & Rv4)
|
|
l = &v4freelist;
|
|
else
|
|
l = &v6freelist;
|
|
r->mid = *l;
|
|
*l = r;
|
|
}
|
|
|
|
static Route*
|
|
allocroute(int type)
|
|
{
|
|
Route *r, **l;
|
|
int n;
|
|
|
|
if(type & Rv4){
|
|
n = sizeof(RouteTree) + sizeof(V4route);
|
|
l = &v4freelist;
|
|
} else {
|
|
n = sizeof(RouteTree) + sizeof(V6route);
|
|
l = &v6freelist;
|
|
}
|
|
|
|
if((r = *l) != nil)
|
|
*l = r->mid;
|
|
else
|
|
r = smalloc(n);
|
|
memset(r, 0, n);
|
|
r->type = type;
|
|
r->ifc = nil;
|
|
r->ref = 1;
|
|
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
addqueue(Route **q, Route *r)
|
|
{
|
|
Route *l;
|
|
|
|
if(r == nil)
|
|
return;
|
|
|
|
l = allocroute(r->type);
|
|
l->left = r;
|
|
l->mid = *q;
|
|
*q = l;
|
|
}
|
|
|
|
/*
|
|
* compare 2 v6 addresses
|
|
*/
|
|
static int
|
|
lcmp(ulong *a, ulong *b)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < IPllen; i++){
|
|
if(a[i] > b[i])
|
|
return 1;
|
|
if(a[i] < b[i])
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* compare 2 v4 or v6 ranges
|
|
*/
|
|
enum
|
|
{
|
|
Rpreceeds, /* a left of b */
|
|
Rfollows, /* a right of b */
|
|
Requals, /* a equals b */
|
|
Rcontains, /* a contians b */
|
|
Roverlaps, /* a overlaps b */
|
|
};
|
|
|
|
static int
|
|
rangecompare(Route *a, Route *b)
|
|
{
|
|
if(a->type & Rv4){
|
|
if(a->v4.endaddress < b->v4.address)
|
|
return Rpreceeds;
|
|
if(a->v4.address > b->v4.endaddress)
|
|
return Rfollows;
|
|
if(a->v4.address <= b->v4.address
|
|
&& a->v4.endaddress >= b->v4.endaddress){
|
|
if(a->v4.address == b->v4.address
|
|
&& a->v4.endaddress == b->v4.endaddress){
|
|
if(a->v4.source <= b->v4.source
|
|
&& a->v4.endsource >= b->v4.endsource){
|
|
if(a->v4.source == b->v4.source
|
|
&& a->v4.endsource == b->v4.endsource)
|
|
return Requals;
|
|
return Rcontains;
|
|
}
|
|
return Roverlaps;
|
|
}
|
|
return Rcontains;
|
|
}
|
|
return Roverlaps;
|
|
}
|
|
|
|
if(lcmp(a->v6.endaddress, b->v6.address) < 0)
|
|
return Rpreceeds;
|
|
if(lcmp(a->v6.address, b->v6.endaddress) > 0)
|
|
return Rfollows;
|
|
if(lcmp(a->v6.address, b->v6.address) <= 0
|
|
&& lcmp(a->v6.endaddress, b->v6.endaddress) >= 0){
|
|
if(lcmp(a->v6.address, b->v6.address) == 0
|
|
&& lcmp(a->v6.endaddress, b->v6.endaddress) == 0){
|
|
if(lcmp(a->v6.source, b->v6.source) <= 0
|
|
&& lcmp(a->v6.endsource, b->v6.endsource) >= 0){
|
|
if(lcmp(a->v6.source, b->v6.source) == 0
|
|
&& lcmp(a->v6.endsource, b->v6.endsource) == 0)
|
|
return Requals;
|
|
return Rcontains;
|
|
}
|
|
return Roverlaps;
|
|
}
|
|
return Rcontains;
|
|
}
|
|
return Roverlaps;
|
|
}
|
|
|
|
/* return 1 if a matches b, otherwise 0 */
|
|
static int
|
|
matchroute(Route *a, Route *b)
|
|
{
|
|
if(a == b)
|
|
return 1;
|
|
|
|
if((a->type^b->type) & (Rifc|Runi|Rmulti|Rbcast))
|
|
return 0;
|
|
|
|
if(a->type & Rv4){
|
|
if(memcmp(a->v4.gate, IPnoaddr+IPv4off, IPv4addrlen) != 0
|
|
&& memcmp(a->v4.gate, b->v4.gate, IPv4addrlen) != 0)
|
|
return 0;
|
|
} else {
|
|
if(ipcmp(a->v6.gate, IPnoaddr) != 0
|
|
&& ipcmp(a->v6.gate, b->v6.gate) != 0)
|
|
return 0;
|
|
}
|
|
|
|
if(a->ifc != nil && b->ifc != nil && (a->ifc != b->ifc || a->ifcid != b->ifcid))
|
|
return 0;
|
|
|
|
if(*a->tag != 0 && strncmp(a->tag, b->tag, sizeof(a->tag)) != 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
copygate(Route *old, Route *new)
|
|
{
|
|
old->type = new->type;
|
|
old->ifc = new->ifc;
|
|
old->ifcid = new->ifcid;
|
|
if(new->type & Rv4)
|
|
memmove(old->v4.gate, new->v4.gate, IPv4addrlen);
|
|
else
|
|
ipmove(old->v6.gate, new->v6.gate);
|
|
strncpy(old->tag, new->tag, sizeof(new->tag));
|
|
}
|
|
|
|
/*
|
|
* walk down a tree adding nodes back in
|
|
*/
|
|
static void
|
|
walkadd(Fs *f, Route **root, Route *p)
|
|
{
|
|
Route *l, *r;
|
|
|
|
l = p->left;
|
|
r = p->right;
|
|
p->left = nil;
|
|
p->right = nil;
|
|
addnode(f, root, p);
|
|
if(l != nil)
|
|
walkadd(f, root, l);
|
|
if(r != nil)
|
|
walkadd(f, root, r);
|
|
}
|
|
|
|
/*
|
|
* calculate depth
|
|
*/
|
|
static void
|
|
calcd(Route *p)
|
|
{
|
|
Route *q;
|
|
int d;
|
|
|
|
if(p != nil) {
|
|
d = 0;
|
|
q = p->left;
|
|
if(q != nil)
|
|
d = q->depth;
|
|
q = p->right;
|
|
if(q != nil && q->depth > d)
|
|
d = q->depth;
|
|
q = p->mid;
|
|
if(q != nil && q->depth > d)
|
|
d = q->depth;
|
|
p->depth = d+1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* balance the tree at the current node
|
|
*/
|
|
static void
|
|
balancetree(Route **cur)
|
|
{
|
|
Route *p, *l, *r;
|
|
int dl, dr;
|
|
|
|
/*
|
|
* if left and right are
|
|
* too out of balance,
|
|
* rotate tree node
|
|
*/
|
|
p = *cur;
|
|
dl = 0; if((l = p->left) != nil) dl = l->depth;
|
|
dr = 0; if((r = p->right) != nil) dr = r->depth;
|
|
|
|
if(dl > dr+1) {
|
|
p->left = l->right;
|
|
l->right = p;
|
|
*cur = l;
|
|
calcd(p);
|
|
calcd(l);
|
|
} else
|
|
if(dr > dl+1) {
|
|
p->right = r->left;
|
|
r->left = p;
|
|
*cur = r;
|
|
calcd(p);
|
|
calcd(r);
|
|
} else
|
|
calcd(p);
|
|
}
|
|
|
|
/*
|
|
* add a new node to the tree
|
|
*/
|
|
static void
|
|
addnode(Fs *f, Route **cur, Route *new)
|
|
{
|
|
Route *p;
|
|
|
|
p = *cur;
|
|
if(p == nil) {
|
|
*cur = new;
|
|
new->depth = 1;
|
|
return;
|
|
}
|
|
|
|
switch(rangecompare(new, p)){
|
|
case Rpreceeds:
|
|
addnode(f, &p->left, new);
|
|
break;
|
|
case Rfollows:
|
|
addnode(f, &p->right, new);
|
|
break;
|
|
case Rcontains:
|
|
/*
|
|
* if new node is superset
|
|
* of tree node,
|
|
* replace tree node and
|
|
* queue tree node to be
|
|
* merged into root.
|
|
*/
|
|
*cur = new;
|
|
new->depth = 1;
|
|
addqueue(&f->queue, p);
|
|
break;
|
|
case Requals:
|
|
/*
|
|
* supercede the old entry if the old one isn't
|
|
* a local interface.
|
|
*/
|
|
if((p->type & Rifc) == 0)
|
|
copygate(p, new);
|
|
else if(new->type & Rifc)
|
|
p->ref++;
|
|
freeroute(new);
|
|
break;
|
|
case Roverlaps:
|
|
addnode(f, &p->mid, new);
|
|
break;
|
|
}
|
|
|
|
balancetree(cur);
|
|
}
|
|
|
|
/*
|
|
* find node matching r
|
|
*/
|
|
static Route**
|
|
looknode(Route **cur, Route *r)
|
|
{
|
|
Route *p;
|
|
|
|
for(;;){
|
|
p = *cur;
|
|
if(p == nil)
|
|
return nil;
|
|
switch(rangecompare(r, p)){
|
|
case Rcontains:
|
|
return nil;
|
|
case Rpreceeds:
|
|
cur = &p->left;
|
|
break;
|
|
case Rfollows:
|
|
cur = &p->right;
|
|
break;
|
|
case Roverlaps:
|
|
cur = &p->mid;
|
|
break;
|
|
case Requals:
|
|
if((p->type & Rifc) == 0 && !matchroute(r, p))
|
|
return nil;
|
|
return cur;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Route*
|
|
looknodetag(Route *r, char *tag)
|
|
{
|
|
Route *x;
|
|
|
|
if(r == nil)
|
|
return nil;
|
|
|
|
if((x = looknodetag(r->mid, tag)) != nil)
|
|
return x;
|
|
if((x = looknodetag(r->left, tag)) != nil)
|
|
return x;
|
|
if((x = looknodetag(r->right, tag)) != nil)
|
|
return x;
|
|
|
|
if((r->type & Rifc) == 0){
|
|
if(tag == nil || strncmp(tag, r->tag, sizeof(r->tag)) == 0)
|
|
return r;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
#define V4H(a) ((a&0x07ffffff)>>(32-Lroot-5))
|
|
#define V6H(a) (((a)[0]&0x80000000)>>(32-Lroot) | ((a)[(IPllen/2)-1]&(0xffff>>(16-Lroot))))
|
|
|
|
static void
|
|
routeadd(Fs *f, Route *r)
|
|
{
|
|
Route **h, **e, *p, *q;
|
|
|
|
if(r->type & Rv4){
|
|
h = &f->v4root[V4H(r->v4.address)];
|
|
e = &f->v4root[V4H(r->v4.endaddress)];
|
|
} else {
|
|
h = &f->v6root[V6H(r->v6.address)];
|
|
e = &f->v6root[V6H(r->v6.endaddress)];
|
|
}
|
|
|
|
for(; h <= e; h++) {
|
|
p = allocroute(r->type);
|
|
|
|
p->ifc = r->ifc;
|
|
p->ifcid = r->ifcid;
|
|
|
|
if(r->type & Rv4)
|
|
memmove(&p->v4, &r->v4, sizeof(r->v4));
|
|
else
|
|
memmove(&p->v6, &r->v6, sizeof(r->v6));
|
|
|
|
memmove(p->tag, r->tag, sizeof(r->tag));
|
|
|
|
addnode(f, h, p);
|
|
while((p = f->queue) != nil) {
|
|
f->queue = p->mid;
|
|
q = p->left;
|
|
freeroute(p);
|
|
walkadd(f, h, q);
|
|
}
|
|
}
|
|
|
|
if(r->type & Rv4)
|
|
v4routegeneration++;
|
|
else
|
|
v6routegeneration++;
|
|
}
|
|
|
|
static void
|
|
routerem(Fs *f, Route *r)
|
|
{
|
|
Route **h, **e, **l, *p, *q;
|
|
|
|
if(r->type & Rv4){
|
|
h = &f->v4root[V4H(r->v4.address)];
|
|
e = &f->v4root[V4H(r->v4.endaddress)];
|
|
} else {
|
|
h = &f->v6root[V6H(r->v6.address)];
|
|
e = &f->v6root[V6H(r->v6.endaddress)];
|
|
}
|
|
|
|
for(; h <= e; h++) {
|
|
if((l = looknode(h, r)) == nil)
|
|
continue;
|
|
p = *l;
|
|
if(--(p->ref) != 0)
|
|
continue;
|
|
*l = nil;
|
|
addqueue(&f->queue, p->left);
|
|
addqueue(&f->queue, p->mid);
|
|
addqueue(&f->queue, p->right);
|
|
freeroute(p);
|
|
|
|
while((p = f->queue) != nil) {
|
|
f->queue = p->mid;
|
|
q = p->left;
|
|
freeroute(p);
|
|
walkadd(f, h, q);
|
|
}
|
|
}
|
|
|
|
if(r->type & Rv4)
|
|
v4routegeneration++;
|
|
else
|
|
v6routegeneration++;
|
|
}
|
|
|
|
static Route
|
|
mkroute(uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag)
|
|
{
|
|
ulong x, y;
|
|
Route r;
|
|
int h;
|
|
|
|
memset(&r, 0, sizeof(r));
|
|
|
|
r.type = type;
|
|
|
|
if(type & Rv4){
|
|
x = nhgetl(a+IPv4off);
|
|
y = nhgetl(mask+IPv4off);
|
|
r.v4.address = x & y;
|
|
r.v4.endaddress = x | ~y;
|
|
|
|
x = nhgetl(s+IPv4off);
|
|
y = nhgetl(smask+IPv4off);
|
|
if(y != 0)
|
|
r.type |= Rsrc;
|
|
r.v4.source = x & y;
|
|
r.v4.endsource = x | ~y;
|
|
|
|
memmove(r.v4.gate, gate+IPv4off, IPv4addrlen);
|
|
} else {
|
|
for(h = 0; h < IPllen; h++){
|
|
x = nhgetl(a+4*h);
|
|
y = nhgetl(mask+4*h);
|
|
r.v6.address[h] = x & y;
|
|
r.v6.endaddress[h] = x | ~y;
|
|
|
|
x = nhgetl(s+4*h);
|
|
y = nhgetl(smask+4*h);
|
|
if(y != 0)
|
|
r.type |= Rsrc;
|
|
r.v6.source[h] = x & y;
|
|
r.v6.endsource[h] = x | ~y;
|
|
}
|
|
|
|
memmove(r.v6.gate, gate, IPaddrlen);
|
|
}
|
|
|
|
if(ifc != nil){
|
|
r.ifc = ifc;
|
|
r.ifcid = ifc->ifcid;
|
|
}
|
|
|
|
if(tag != nil)
|
|
strncpy(r.tag, tag, sizeof(r.tag));
|
|
|
|
return r;
|
|
}
|
|
|
|
void
|
|
addroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag)
|
|
{
|
|
Route r = mkroute(a, mask, s, smask, gate, type, ifc, tag);
|
|
wlock(&routelock);
|
|
routeadd(f, &r);
|
|
wunlock(&routelock);
|
|
}
|
|
|
|
void
|
|
remroute(Fs *f, uchar *a, uchar *mask, uchar *s, uchar *smask, uchar *gate, int type, Ipifc *ifc, char *tag)
|
|
{
|
|
Route r = mkroute(a, mask, s, smask, gate, type, ifc, tag);
|
|
wlock(&routelock);
|
|
routerem(f, &r);
|
|
wunlock(&routelock);
|
|
}
|
|
|
|
/* get the outgoing interface for route r */
|
|
static Ipifc*
|
|
routefindipifc(Route *r, Fs *f)
|
|
{
|
|
uchar local[IPaddrlen], gate[IPaddrlen];
|
|
Ipifc *ifc;
|
|
int i;
|
|
|
|
ifc = r->ifc;
|
|
if(ifc != nil && ifc->ifcid == r->ifcid)
|
|
return ifc;
|
|
|
|
if(r->type & Rsrc) {
|
|
if(r->type & Rv4) {
|
|
hnputl(local+IPv4off, r->v4.source);
|
|
memmove(local, v4prefix, IPv4off);
|
|
} else {
|
|
for(i = 0; i < IPllen; i++)
|
|
hnputl(local+4*i, r->v6.source[i]);
|
|
}
|
|
} else {
|
|
ipmove(local, IPnoaddr);
|
|
}
|
|
|
|
if(r->type & Rifc) {
|
|
if(r->type & Rv4) {
|
|
hnputl(gate+IPv4off, r->v4.address);
|
|
memmove(gate, v4prefix, IPv4off);
|
|
} else {
|
|
for(i = 0; i < IPllen; i++)
|
|
hnputl(gate+4*i, r->v6.address[i]);
|
|
}
|
|
} else {
|
|
if(r->type & Rv4)
|
|
v4tov6(gate, r->v4.gate);
|
|
else
|
|
ipmove(gate, r->v6.gate);
|
|
}
|
|
|
|
if((ifc = findipifc(f, local, gate, r->type)) == nil)
|
|
return nil;
|
|
|
|
r->ifc = ifc;
|
|
r->ifcid = ifc->ifcid;
|
|
return ifc;
|
|
}
|
|
|
|
/*
|
|
* v4lookup, v6lookup:
|
|
* lookup a route to destination address a from source address s
|
|
* and return the route. returns nil if no route was found.
|
|
* an optional Routehint can be passed in rh to cache the lookup.
|
|
*
|
|
* for v4lookup, addresses are in 4 byte format.
|
|
*/
|
|
Route*
|
|
v4lookup(Fs *f, uchar *a, uchar *s, Routehint *rh)
|
|
{
|
|
ulong la, ls;
|
|
Route *p, *q;
|
|
Ipifc *ifc;
|
|
|
|
if(rh != nil
|
|
&& rh->rgen == v4routegeneration
|
|
&& (q = rh->r) != nil
|
|
&& (ifc = q->ifc) != nil
|
|
&& q->ifcid == ifc->ifcid
|
|
&& q->ref > 0)
|
|
return q;
|
|
|
|
q = nil;
|
|
la = nhgetl(a);
|
|
ls = nhgetl(s);
|
|
|
|
rlock(&routelock);
|
|
for(p = f->v4root[V4H(la)]; p != nil;){
|
|
if(la < p->v4.address){
|
|
p = p->left;
|
|
continue;
|
|
}
|
|
if(la > p->v4.endaddress){
|
|
p = p->right;
|
|
continue;
|
|
}
|
|
if(p->type & Rsrc){
|
|
if(ls < p->v4.source){
|
|
p = p->mid;
|
|
continue;
|
|
}
|
|
if(ls > p->v4.endsource){
|
|
p = p->mid;
|
|
continue;
|
|
}
|
|
}
|
|
q = p;
|
|
p = p->mid;
|
|
}
|
|
if(q != nil){
|
|
if(routefindipifc(q, f) == nil)
|
|
q = nil;
|
|
else if(rh != nil){
|
|
rh->r = q;
|
|
rh->rgen = v4routegeneration;
|
|
}
|
|
}
|
|
runlock(&routelock);
|
|
return q;
|
|
}
|
|
|
|
Route*
|
|
v6lookup(Fs *f, uchar *a, uchar *s, Routehint *rh)
|
|
{
|
|
ulong la[IPllen], ls[IPllen];
|
|
ulong x, y;
|
|
Route *p, *q;
|
|
Ipifc *ifc;
|
|
int h;
|
|
|
|
if(isv4(s)){
|
|
if(isv4(a))
|
|
return v4lookup(f, a+IPv4off, s+IPv4off, rh);
|
|
return nil;
|
|
}
|
|
|
|
if(rh != nil
|
|
&& rh->rgen == v6routegeneration
|
|
&& (q = rh->r) != nil
|
|
&& (ifc = q->ifc) != nil
|
|
&& q->ifcid == ifc->ifcid
|
|
&& q->ref > 0)
|
|
return q;
|
|
|
|
q = nil;
|
|
for(h = 0; h < IPllen; h++){
|
|
la[h] = nhgetl(a+4*h);
|
|
ls[h] = nhgetl(s+4*h);
|
|
}
|
|
|
|
rlock(&routelock);
|
|
for(p = f->v6root[V6H(la)]; p != nil;){
|
|
for(h = 0; h < IPllen; h++){
|
|
x = la[h];
|
|
y = p->v6.address[h];
|
|
if(x == y)
|
|
continue;
|
|
if(x < y){
|
|
p = p->left;
|
|
goto next;
|
|
}
|
|
break;
|
|
}
|
|
for(h = 0; h < IPllen; h++){
|
|
x = la[h];
|
|
y = p->v6.endaddress[h];
|
|
if(x == y)
|
|
continue;
|
|
if(x > y){
|
|
p = p->right;
|
|
goto next;
|
|
}
|
|
break;
|
|
}
|
|
if(p->type & Rsrc){
|
|
for(h = 0; h < IPllen; h++){
|
|
x = ls[h];
|
|
y = p->v6.source[h];
|
|
if(x == y)
|
|
continue;
|
|
if(x < y){
|
|
p = p->mid;
|
|
goto next;
|
|
}
|
|
break;
|
|
}
|
|
for(h = 0; h < IPllen; h++){
|
|
x = ls[h];
|
|
y = p->v6.endsource[h];
|
|
if(x == y)
|
|
continue;
|
|
if(x > y){
|
|
p = p->mid;
|
|
goto next;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
q = p;
|
|
p = p->mid;
|
|
next: ;
|
|
}
|
|
if(q != nil){
|
|
if(routefindipifc(q, f) == nil)
|
|
q = nil;
|
|
else if(rh != nil){
|
|
rh->r = q;
|
|
rh->rgen = v6routegeneration;
|
|
}
|
|
}
|
|
runlock(&routelock);
|
|
return q;
|
|
}
|
|
|
|
/*
|
|
* v4source, v6source:
|
|
* lookup a route to destination address a and also find
|
|
* a suitable source address s on the outgoing interface.
|
|
* return the route on success or nil when no route
|
|
* was found.
|
|
*
|
|
* for v4source, addresses are in 4 byte format.
|
|
*/
|
|
Route*
|
|
v4source(Fs *f, uchar *a, uchar *s)
|
|
{
|
|
uchar src[IPv4addrlen];
|
|
int splen;
|
|
ulong x, la;
|
|
Route *p, *q;
|
|
Ipifc *ifc;
|
|
|
|
q = nil;
|
|
la = nhgetl(a);
|
|
rlock(&routelock);
|
|
for(p = f->v4root[V4H(la)]; p != nil;){
|
|
if(la < p->v4.address){
|
|
p = p->left;
|
|
continue;
|
|
}
|
|
if(la > p->v4.endaddress){
|
|
p = p->right;
|
|
continue;
|
|
}
|
|
splen = 0;
|
|
if(p->type & Rsrc){
|
|
/* calculate local prefix length for source specific routes */
|
|
for(x = ~(p->v4.endsource ^ p->v4.source); x & 0x80000000UL; x <<= 1)
|
|
splen++;
|
|
hnputl(src, p->v4.source);
|
|
}
|
|
if((ifc = routefindipifc(p, f)) == nil
|
|
|| !ipv4local(ifc, src, splen, (p->type & (Rifc|Rbcast|Rmulti|Rv4))==Rv4? p->v4.gate: a)){
|
|
p = p->mid;
|
|
continue;
|
|
}
|
|
memmove(s, src, IPv4addrlen);
|
|
q = p;
|
|
p = p->mid;
|
|
}
|
|
runlock(&routelock);
|
|
return q;
|
|
}
|
|
|
|
Route*
|
|
v6source(Fs *f, uchar *a, uchar *s)
|
|
{
|
|
uchar src[IPaddrlen];
|
|
int splen, h;
|
|
ulong x, y, la[IPllen];
|
|
Route *p, *q;
|
|
Ipifc *ifc;
|
|
|
|
q = nil;
|
|
for(h = 0; h < IPllen; h++)
|
|
la[h] = nhgetl(a+4*h);
|
|
rlock(&routelock);
|
|
for(p = f->v6root[V6H(la)]; p != nil;){
|
|
for(h = 0; h < IPllen; h++){
|
|
x = la[h];
|
|
y = p->v6.address[h];
|
|
if(x == y)
|
|
continue;
|
|
if(x < y){
|
|
p = p->left;
|
|
goto next;
|
|
}
|
|
break;
|
|
}
|
|
for(h = 0; h < IPllen; h++){
|
|
x = la[h];
|
|
y = p->v6.endaddress[h];
|
|
if(x == y)
|
|
continue;
|
|
if(x > y){
|
|
p = p->right;
|
|
goto next;
|
|
}
|
|
break;
|
|
}
|
|
splen = 0;
|
|
if(p->type & Rsrc){
|
|
/* calculate local prefix length for source specific routes */
|
|
for(h = 0; h < IPllen; h++){
|
|
hnputl(src+4*h, p->v6.source[h]);
|
|
if((x = ~(p->v6.endsource[h] ^ p->v6.source[h])) != ~0UL){
|
|
for(; x & 0x80000000UL; x <<= 1)
|
|
splen++;
|
|
break;
|
|
}
|
|
splen += 32;
|
|
}
|
|
}
|
|
if((ifc = routefindipifc(p, f)) == nil
|
|
|| !ipv6local(ifc, src, splen, a)){
|
|
p = p->mid;
|
|
continue;
|
|
}
|
|
ipmove(s, src);
|
|
q = p;
|
|
p = p->mid;
|
|
next: ;
|
|
}
|
|
runlock(&routelock);
|
|
return q;
|
|
}
|
|
|
|
static int
|
|
parseroutetype(char *p)
|
|
{
|
|
int type = 0;
|
|
switch(*p++){
|
|
default: return -1;
|
|
case '4': type |= Rv4;
|
|
case '6': break;
|
|
}
|
|
for(;;) switch(*p++){
|
|
default:
|
|
return -1;
|
|
case 'i':
|
|
if(((type ^= Rifc) & Rifc) != Rifc) return -1;
|
|
break;
|
|
case 'u':
|
|
if(((type ^= Runi) & (Runi|Rbcast|Rmulti)) != Runi) return -1;
|
|
break;
|
|
case 'b':
|
|
if(((type ^= Rbcast) & (Runi|Rbcast|Rmulti)) != Rbcast) return -1;
|
|
break;
|
|
case 'm':
|
|
if(((type ^= Rmulti) & (Runi|Rbcast|Rmulti)) != Rmulti) return -1;
|
|
break;
|
|
case 'p':
|
|
if(((type ^= Rptpt) & Rptpt) != Rptpt) return -1;
|
|
break;
|
|
case 't':
|
|
if(((type ^= Rtrans) & Rtrans) != Rtrans) return -1;
|
|
break;
|
|
case '\0':
|
|
return type;
|
|
}
|
|
}
|
|
|
|
void
|
|
routetype(int type, char p[8])
|
|
{
|
|
if(type & Rv4)
|
|
*p++ = '4';
|
|
else
|
|
*p++ = '6';
|
|
|
|
if(type & Rifc)
|
|
*p++ = 'i';
|
|
|
|
if(type & Runi)
|
|
*p++ = 'u';
|
|
else if(type & Rbcast)
|
|
*p++ = 'b';
|
|
else if(type & Rmulti)
|
|
*p++ = 'm';
|
|
|
|
if(type & Rptpt)
|
|
*p++ = 'p';
|
|
|
|
if(type & Rtrans)
|
|
*p++ = 't';
|
|
|
|
*p = 0;
|
|
}
|
|
|
|
static void
|
|
convroute(Route *r, uchar *addr, uchar *mask, uchar *src, uchar *smask, uchar *gate)
|
|
{
|
|
int i;
|
|
|
|
if(r->type & Rv4){
|
|
memmove(addr, v4prefix, IPv4off);
|
|
hnputl(addr+IPv4off, r->v4.address);
|
|
|
|
memset(mask, 0xff, IPv4off);
|
|
hnputl(mask+IPv4off, ~(r->v4.endaddress ^ r->v4.address));
|
|
|
|
memmove(src, v4prefix, IPv4off);
|
|
hnputl(src+IPv4off, r->v4.source);
|
|
|
|
memset(smask, 0xff, IPv4off);
|
|
hnputl(smask+IPv4off, ~(r->v4.endsource ^ r->v4.source));
|
|
|
|
memmove(gate, v4prefix, IPv4off);
|
|
memmove(gate+IPv4off, r->v4.gate, IPv4addrlen);
|
|
} else {
|
|
for(i = 0; i < IPllen; i++){
|
|
hnputl(addr + 4*i, r->v6.address[i]);
|
|
hnputl(mask + 4*i, ~(r->v6.endaddress[i] ^ r->v6.address[i]));
|
|
hnputl(src + 4*i, r->v6.source[i]);
|
|
hnputl(smask + 4*i, ~(r->v6.endsource[i] ^ r->v6.source[i]));
|
|
}
|
|
memmove(gate, r->v6.gate, IPaddrlen);
|
|
}
|
|
}
|
|
|
|
static char*
|
|
seprintroute(char *p, char *e, Route *r)
|
|
{
|
|
uchar addr[IPaddrlen], mask[IPaddrlen], src[IPaddrlen], smask[IPaddrlen], gate[IPaddrlen];
|
|
char type[8], ifbuf[4], *iname;
|
|
|
|
convroute(r, addr, mask, src, smask, gate);
|
|
routetype(r->type, type);
|
|
if(r->ifc != nil && r->ifcid == r->ifc->ifcid)
|
|
snprint(iname = ifbuf, sizeof ifbuf, "%d", r->ifc->conv->x);
|
|
else
|
|
iname = "-";
|
|
return seprint(p, e, "%-15I %-4M %-15I %-4s %4.4s %3s %-15I %-4M\n",
|
|
addr, mask, gate, type, r->tag, iname, src, smask);
|
|
}
|
|
|
|
typedef struct Routewalk Routewalk;
|
|
struct Routewalk
|
|
{
|
|
int o;
|
|
int h;
|
|
char* p;
|
|
char* e;
|
|
};
|
|
|
|
static int
|
|
rr1(Routewalk *rw, Route *r)
|
|
{
|
|
int n = seprintroute(rw->p, rw->e, r) - rw->p;
|
|
if(rw->o < 0){
|
|
if(n > -rw->o){
|
|
memmove(rw->p, rw->p - rw->o, n + rw->o);
|
|
rw->p += n + rw->o;
|
|
}
|
|
rw->o += n;
|
|
} else
|
|
rw->p += n;
|
|
return rw->p < rw->e;
|
|
}
|
|
|
|
static int
|
|
rr(Route *r, Routewalk *rw)
|
|
{
|
|
int h;
|
|
|
|
if(r == nil)
|
|
return 1;
|
|
if(rr(r->left, rw) == 0)
|
|
return 0;
|
|
if(r->type & Rv4)
|
|
h = V4H(r->v4.address);
|
|
else
|
|
h = V6H(r->v6.address);
|
|
if(h == rw->h){
|
|
if(rr1(rw, r) == 0)
|
|
return 0;
|
|
}
|
|
if(rr(r->mid, rw) == 0)
|
|
return 0;
|
|
return rr(r->right, rw);
|
|
}
|
|
|
|
long
|
|
routeread(Fs *f, char *p, ulong offset, int n)
|
|
{
|
|
Routewalk rw[1];
|
|
|
|
rw->p = p;
|
|
rw->e = p+n;
|
|
rw->o = -offset;
|
|
if(rw->o > 0)
|
|
return 0;
|
|
|
|
rlock(&routelock);
|
|
if(rw->p < rw->e) {
|
|
for(rw->h = 0; rw->h < nelem(f->v4root); rw->h++)
|
|
if(rr(f->v4root[rw->h], rw) == 0)
|
|
break;
|
|
}
|
|
if(rw->p < rw->e) {
|
|
for(rw->h = 0; rw->h < nelem(f->v6root); rw->h++)
|
|
if(rr(f->v6root[rw->h], rw) == 0)
|
|
break;
|
|
}
|
|
runlock(&routelock);
|
|
|
|
return rw->p - p;
|
|
}
|
|
|
|
/*
|
|
* 4 add addr mask gate
|
|
* 5 add addr mask gate ifc
|
|
* 6 add addr mask gate src smask
|
|
* 7 add addr mask gate ifc src smask
|
|
* 8 add addr mask gate type ifc src smask
|
|
* 9 add addr mask gate type tag ifc src smask
|
|
* 3 remove addr mask
|
|
* 4 remove addr mask gate
|
|
* 5 remove addr mask src smask
|
|
* 6 remove addr mask gate src smask
|
|
* 7 remove addr mask gate ifc src smask
|
|
* 8 remove addr mask gate type ifc src smask
|
|
* 9 remove addr mask gate type tag ifc src smask
|
|
*/
|
|
static Route
|
|
parseroute(Fs *f, char **argv, int argc)
|
|
{
|
|
uchar addr[IPaddrlen], mask[IPaddrlen];
|
|
uchar src[IPaddrlen], smask[IPaddrlen];
|
|
uchar gate[IPaddrlen];
|
|
Ipifc *ifc;
|
|
char *tag;
|
|
int type;
|
|
|
|
type = 0;
|
|
tag = nil;
|
|
ifc = nil;
|
|
ipmove(gate, IPnoaddr);
|
|
ipmove(src, IPnoaddr);
|
|
ipmove(smask, IPnoaddr);
|
|
|
|
if(argc < 3)
|
|
error(Ebadctl);
|
|
|
|
if(parseipandmask(addr, mask, argv[1], argv[2]) == -1)
|
|
error(Ebadip);
|
|
|
|
if(strcmp(argv[0], "add") == 0 || (argc > 3 && argc != 5)){
|
|
if(argc < 4)
|
|
error(Ebadctl);
|
|
if(parseip(gate, argv[3]) == -1)
|
|
error(Ebadip);
|
|
}
|
|
|
|
if(argc > 4 && (strcmp(argv[0], "add") != 0 || argc != 5)){
|
|
if(parseipandmask(src, smask, argv[argc-2], argv[argc-1]) == -1)
|
|
error(Ebadip);
|
|
}
|
|
|
|
if(argc == 5 && strcmp(argv[0], "add") == 0)
|
|
ifc = findipifcstr(f, argv[4]);
|
|
if(argc > 6)
|
|
ifc = findipifcstr(f, argv[argc-3]);
|
|
|
|
if(argc > 7 && (type = parseroutetype(argv[4])) < 0)
|
|
error(Ebadctl);
|
|
if(isv4(addr))
|
|
type |= Rv4;
|
|
|
|
if(argc > 8)
|
|
tag = argv[5];
|
|
if(argc > 9)
|
|
error(Ebadctl);
|
|
|
|
if(type & Rv4){
|
|
if(ipcmp(smask, IPnoaddr) != 0 && !isv4(src))
|
|
error(Ebadip);
|
|
if(ipcmp(gate, IPnoaddr) != 0 && !isv4(gate))
|
|
error(Ebadip);
|
|
} else {
|
|
if(isv4(addr))
|
|
error(Ebadip);
|
|
}
|
|
|
|
return mkroute(addr, mask, src, smask, gate, type, ifc, tag);
|
|
}
|
|
|
|
long
|
|
routewrite(Fs *f, Chan *c, char *p, int n)
|
|
{
|
|
Cmdbuf *cb;
|
|
IPaux *a;
|
|
Route *x, r;
|
|
|
|
cb = parsecmd(p, n);
|
|
if(waserror()){
|
|
free(cb);
|
|
nexterror();
|
|
}
|
|
if(cb->nf < 1)
|
|
error("short control request");
|
|
if(strcmp(cb->f[0], "flush") == 0){
|
|
char *tag = cb->nf < 2 ? nil : cb->f[1];
|
|
int h;
|
|
|
|
wlock(&routelock);
|
|
for(h = 0; h < nelem(f->v4root); h++)
|
|
while((x = looknodetag(f->v4root[h], tag)) != nil){
|
|
memmove(&r, x, sizeof(RouteTree) + sizeof(V4route));
|
|
routerem(f, &r);
|
|
}
|
|
for(h = 0; h < nelem(f->v6root); h++)
|
|
while((x = looknodetag(f->v6root[h], tag)) != nil){
|
|
memmove(&r, x, sizeof(RouteTree) + sizeof(V6route));
|
|
routerem(f, &r);
|
|
}
|
|
wunlock(&routelock);
|
|
} else if(strcmp(cb->f[0], "add") == 0 || strcmp(cb->f[0], "remove") == 0){
|
|
r = parseroute(f, cb->f, cb->nf);
|
|
if(*r.tag == 0){
|
|
a = c->aux;
|
|
strncpy(r.tag, a->tag, sizeof(r.tag));
|
|
}
|
|
wlock(&routelock);
|
|
if(strcmp(cb->f[0], "add") == 0)
|
|
routeadd(f, &r);
|
|
else
|
|
routerem(f, &r);
|
|
wunlock(&routelock);
|
|
} else if(strcmp(cb->f[0], "tag") == 0) {
|
|
if(cb->nf < 2)
|
|
error(Ebadarg);
|
|
a = c->aux;
|
|
c->aux = newipaux(a->owner, cb->f[1]);
|
|
free(a);
|
|
} else
|
|
error(Ebadctl);
|
|
|
|
poperror();
|
|
free(cb);
|
|
return n;
|
|
}
|