plan9fox/sys/src/9/ip/iproute.c
cinap_lenrek ca313087c1 ip(3): use flags instead of tag for 8 column route add/remove
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.
2022-03-14 18:45:27 +00:00

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;
}