| #include <vfs.h> | 
 | #include <kfs.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 |  | 
 | static void walkadd(struct fs *, struct route **, struct route *); | 
 | static void addnode(struct fs *, struct route **, struct route *); | 
 | static void calcd(struct route *); | 
 |  | 
 | /* these are used for all instances of IP */ | 
 | static struct route *v4freelist; | 
 | static struct route *v6freelist; | 
 | static rwlock_t routelock; | 
 | static uint32_t v4routegeneration, v6routegeneration; | 
 |  | 
 | static void freeroute(struct route *r) | 
 | { | 
 | 	struct route **l; | 
 |  | 
 | 	r->routeTree.left = NULL; | 
 | 	r->routeTree.right = NULL; | 
 | 	if (r->routeTree.type & Rv4) | 
 | 		l = &v4freelist; | 
 | 	else | 
 | 		l = &v6freelist; | 
 | 	r->routeTree.mid = *l; | 
 | 	*l = r; | 
 | } | 
 |  | 
 | static struct route *allocroute(int type) | 
 | { | 
 | 	struct route *r; | 
 | 	int n; | 
 | 	struct route **l; | 
 |  | 
 | 	if (type & Rv4) { | 
 | 		n = sizeof(struct routeTree) + sizeof(struct V4route); | 
 | 		l = &v4freelist; | 
 | 	} else { | 
 | 		n = sizeof(struct routeTree) + sizeof(struct V6route); | 
 | 		l = &v6freelist; | 
 | 	} | 
 |  | 
 | 	r = *l; | 
 | 	if (r != NULL) { | 
 | 		*l = r->routeTree.mid; | 
 | 	} else { | 
 | 		r = kzmalloc(n, 0); | 
 | 		if (r == NULL) | 
 | 			panic("out of routing nodes"); | 
 | 	} | 
 | 	memset(r, 0, n); | 
 | 	r->routeTree.type = type; | 
 | 	r->routeTree.ifc = NULL; | 
 | 	kref_init(&r->routeTree.ref, fake_release, 1); | 
 |  | 
 | 	return r; | 
 | } | 
 |  | 
 | static void addqueue(struct route **q, struct route *r) | 
 | { | 
 | 	struct route *l; | 
 |  | 
 | 	if (r == NULL) | 
 | 		return; | 
 |  | 
 | 	l = allocroute(r->routeTree.type); | 
 | 	l->routeTree.mid = *q; | 
 | 	*q = l; | 
 | 	l->routeTree.left = r; | 
 | } | 
 |  | 
 | /* | 
 |  *   compare 2 v6 addresses | 
 |  */ | 
 | static int lcmp(uint32_t * a, uint32_t * 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, | 
 | 	Rfollows, | 
 | 	Requals, | 
 | 	Rcontains, | 
 | 	Rcontained, | 
 | }; | 
 |  | 
 | static int rangecompare(struct route *a, struct route *b) | 
 | { | 
 | 	if (a->routeTree.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) | 
 | 				return Requals; | 
 | 			return Rcontains; | 
 | 		} | 
 | 		return Rcontained; | 
 | 	} | 
 |  | 
 | 	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) | 
 | 			return Requals; | 
 | 		return Rcontains; | 
 | 	} | 
 |  | 
 | 	return Rcontained; | 
 | } | 
 |  | 
 | static void copygate(struct route *old, struct route *new) | 
 | { | 
 | 	if (new->routeTree.type & Rv4) | 
 | 		memmove(old->v4.gate, new->v4.gate, IPv4addrlen); | 
 | 	else | 
 | 		memmove(old->v6.gate, new->v6.gate, IPaddrlen); | 
 | } | 
 |  | 
 | /* | 
 |  *  walk down a tree adding nodes back in | 
 |  */ | 
 | static void walkadd(struct fs *f, struct route **root, struct route *p) | 
 | { | 
 | 	struct route *l, *r; | 
 |  | 
 | 	l = p->routeTree.left; | 
 | 	r = p->routeTree.right; | 
 | 	p->routeTree.left = 0; | 
 | 	p->routeTree.right = 0; | 
 | 	addnode(f, root, p); | 
 | 	if (l) | 
 | 		walkadd(f, root, l); | 
 | 	if (r) | 
 | 		walkadd(f, root, r); | 
 | } | 
 |  | 
 | /* | 
 |  *  calculate depth | 
 |  */ | 
 | static void calcd(struct route *p) | 
 | { | 
 | 	struct route *q; | 
 | 	int d; | 
 |  | 
 | 	if (p) { | 
 | 		d = 0; | 
 | 		q = p->routeTree.left; | 
 | 		if (q) | 
 | 			d = q->routeTree.depth; | 
 | 		q = p->routeTree.right; | 
 | 		if (q && q->routeTree.depth > d) | 
 | 			d = q->routeTree.depth; | 
 | 		q = p->routeTree.mid; | 
 | 		if (q && q->routeTree.depth > d) | 
 | 			d = q->routeTree.depth; | 
 | 		p->routeTree.depth = d + 1; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  balance the tree at the current node | 
 |  */ | 
 | static void balancetree(struct route **cur) | 
 | { | 
 | 	struct 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->routeTree.left)) | 
 | 		dl = l->routeTree.depth; | 
 | 	dr = 0; | 
 | 	if ((r = p->routeTree.right)) | 
 | 		dr = r->routeTree.depth; | 
 |  | 
 | 	if (dl > dr + 1) { | 
 | 		p->routeTree.left = l->routeTree.right; | 
 | 		l->routeTree.right = p; | 
 | 		*cur = l; | 
 | 		calcd(p); | 
 | 		calcd(l); | 
 | 	} else if (dr > dl + 1) { | 
 | 		p->routeTree.right = r->routeTree.left; | 
 | 		r->routeTree.left = p; | 
 | 		*cur = r; | 
 | 		calcd(p); | 
 | 		calcd(r); | 
 | 	} else | 
 | 		calcd(p); | 
 | } | 
 |  | 
 | /* | 
 |  *  add a new node to the tree | 
 |  */ | 
 | static void addnode(struct fs *f, struct route **cur, struct route *new) | 
 | { | 
 | 	struct route *p; | 
 |  | 
 | 	p = *cur; | 
 | 	if (p == 0) { | 
 | 		*cur = new; | 
 | 		new->routeTree.depth = 1; | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	switch (rangecompare(new, p)) { | 
 | 		case Rpreceeds: | 
 | 			addnode(f, &p->routeTree.left, new); | 
 | 			break; | 
 | 		case Rfollows: | 
 | 			addnode(f, &p->routeTree.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->routeTree.depth = 1; | 
 | 			addqueue(&f->queue, p); | 
 | 			break; | 
 | 		case Requals: | 
 | 			/* | 
 | 			 *  supercede the old entry if the old one isn't | 
 | 			 *  a local interface. | 
 | 			 */ | 
 | 			if ((p->routeTree.type & Rifc) == 0) { | 
 | 				p->routeTree.type = new->routeTree.type; | 
 | 				p->routeTree.ifcid = -1; | 
 | 				copygate(p, new); | 
 | 			} else if (new->routeTree.type & Rifc) | 
 | 				kref_get(&p->routeTree.ref, 1); | 
 | 			freeroute(new); | 
 | 			break; | 
 | 		case Rcontained: | 
 | 			addnode(f, &p->routeTree.mid, new); | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	balancetree(cur); | 
 | } | 
 |  | 
 | #define	V4H(a)	((a&0x07ffffff)>>(32-Lroot-5)) | 
 |  | 
 | void | 
 | v4addroute(struct fs *f, char *tag, uint8_t * a, uint8_t * mask, | 
 | 		   uint8_t * gate, int type) | 
 | { | 
 | 	struct route *p; | 
 | 	uint32_t sa; | 
 | 	uint32_t m; | 
 | 	uint32_t ea; | 
 | 	int h, eh; | 
 |  | 
 | 	m = nhgetl(mask); | 
 | 	sa = nhgetl(a) & m; | 
 | 	ea = sa | ~m; | 
 |  | 
 | 	eh = V4H(ea); | 
 | 	for (h = V4H(sa); h <= eh; h++) { | 
 | 		p = allocroute(Rv4 | type); | 
 | 		p->v4.address = sa; | 
 | 		p->v4.endaddress = ea; | 
 | 		memmove(p->v4.gate, gate, sizeof(p->v4.gate)); | 
 | 		memmove(p->routeTree.tag, tag, sizeof(p->routeTree.tag)); | 
 |  | 
 | 		wlock(&routelock); | 
 | 		addnode(f, &f->v4root[h], p); | 
 | 		while ((p = f->queue)) { | 
 | 			f->queue = p->routeTree.mid; | 
 | 			walkadd(f, &f->v4root[h], p->routeTree.left); | 
 | 			freeroute(p); | 
 | 		} | 
 | 		wunlock(&routelock); | 
 | 	} | 
 | 	v4routegeneration++; | 
 |  | 
 | 	ipifcaddroute(f, Rv4, a, mask, gate, type); | 
 | } | 
 |  | 
 | #define	V6H(a)	(((a)[IPllen-1] & 0x07ffffff)>>(32-Lroot-5)) | 
 | #define ISDFLT(a, mask, tag) ((ipcmp((a),v6Unspecified)==0) && (ipcmp((mask),v6Unspecified)==0) && (strcmp((tag), "ra")!=0)) | 
 |  | 
 | void | 
 | v6addroute(struct fs *f, char *tag, uint8_t * a, uint8_t * mask, | 
 | 		   uint8_t * gate, int type) | 
 | { | 
 | 	struct route *p; | 
 | 	uint32_t sa[IPllen], ea[IPllen]; | 
 | 	uint32_t x, y; | 
 | 	int h, eh; | 
 |  | 
 | 	/* | 
 | 	   if(ISDFLT(a, mask, tag)) | 
 | 	   f->v6p->cdrouter = -1; | 
 | 	 */ | 
 |  | 
 | 	for (h = 0; h < IPllen; h++) { | 
 | 		x = nhgetl(a + 4 * h); | 
 | 		y = nhgetl(mask + 4 * h); | 
 | 		sa[h] = x & y; | 
 | 		ea[h] = x | ~y; | 
 | 	} | 
 |  | 
 | 	eh = V6H(ea); | 
 | 	for (h = V6H(sa); h <= eh; h++) { | 
 | 		p = allocroute(type); | 
 | 		memmove(p->v6.address, sa, IPaddrlen); | 
 | 		memmove(p->v6.endaddress, ea, IPaddrlen); | 
 | 		memmove(p->v6.gate, gate, IPaddrlen); | 
 | 		memmove(p->routeTree.tag, tag, sizeof(p->routeTree.tag)); | 
 |  | 
 | 		wlock(&routelock); | 
 | 		addnode(f, &f->v6root[h], p); | 
 | 		while ((p = f->queue)) { | 
 | 			f->queue = p->routeTree.mid; | 
 | 			walkadd(f, &f->v6root[h], p->routeTree.left); | 
 | 			freeroute(p); | 
 | 		} | 
 | 		wunlock(&routelock); | 
 | 	} | 
 | 	v6routegeneration++; | 
 |  | 
 | 	ipifcaddroute(f, 0, a, mask, gate, type); | 
 | } | 
 |  | 
 | struct route **looknode(struct route **cur, struct route *r) | 
 | { | 
 | 	struct route *p; | 
 |  | 
 | 	for (;;) { | 
 | 		p = *cur; | 
 | 		if (p == 0) | 
 | 			return 0; | 
 |  | 
 | 		switch (rangecompare(r, p)) { | 
 | 			case Rcontains: | 
 | 				return 0; | 
 | 			case Rpreceeds: | 
 | 				cur = &p->routeTree.left; | 
 | 				break; | 
 | 			case Rfollows: | 
 | 				cur = &p->routeTree.right; | 
 | 				break; | 
 | 			case Rcontained: | 
 | 				cur = &p->routeTree.mid; | 
 | 				break; | 
 | 			case Requals: | 
 | 				return cur; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void v4delroute(struct fs *f, uint8_t * a, uint8_t * mask, int dolock) | 
 | { | 
 | 	struct route **r, *p; | 
 | 	struct route rt; | 
 | 	int h, eh; | 
 | 	uint32_t m; | 
 |  | 
 | 	m = nhgetl(mask); | 
 | 	rt.v4.address = nhgetl(a) & m; | 
 | 	rt.v4.endaddress = rt.v4.address | ~m; | 
 | 	rt.routeTree.type = Rv4; | 
 |  | 
 | 	eh = V4H(rt.v4.endaddress); | 
 | 	for (h = V4H(rt.v4.address); h <= eh; h++) { | 
 | 		if (dolock) | 
 | 			wlock(&routelock); | 
 | 		r = looknode(&f->v4root[h], &rt); | 
 | 		if (r) { | 
 | 			p = *r; | 
 | 			/* TODO: bad usage of kref (use a release).  I didn't change this | 
 | 			 * one, since it looks like the if code is when we want to release. | 
 | 			 * */ | 
 | 			if (kref_put(&p->routeTree.ref)) { | 
 | 				*r = 0; | 
 | 				addqueue(&f->queue, p->routeTree.left); | 
 | 				addqueue(&f->queue, p->routeTree.mid); | 
 | 				addqueue(&f->queue, p->routeTree.right); | 
 | 				freeroute(p); | 
 | 				while ((p = f->queue)) { | 
 | 					f->queue = p->routeTree.mid; | 
 | 					walkadd(f, &f->v4root[h], p->routeTree.left); | 
 | 					freeroute(p); | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		if (dolock) | 
 | 			wunlock(&routelock); | 
 | 	} | 
 | 	v4routegeneration++; | 
 |  | 
 | 	ipifcremroute(f, Rv4, a, mask); | 
 | } | 
 |  | 
 | void v6delroute(struct fs *f, uint8_t * a, uint8_t * mask, int dolock) | 
 | { | 
 | 	struct route **r, *p; | 
 | 	struct route rt; | 
 | 	int h, eh; | 
 | 	uint32_t x, y; | 
 |  | 
 | 	for (h = 0; h < IPllen; h++) { | 
 | 		x = nhgetl(a + 4 * h); | 
 | 		y = nhgetl(mask + 4 * h); | 
 | 		rt.v6.address[h] = x & y; | 
 | 		rt.v6.endaddress[h] = x | ~y; | 
 | 	} | 
 | 	rt.routeTree.type = 0; | 
 |  | 
 | 	eh = V6H(rt.v6.endaddress); | 
 | 	for (h = V6H(rt.v6.address); h <= eh; h++) { | 
 | 		if (dolock) | 
 | 			wlock(&routelock); | 
 | 		r = looknode(&f->v6root[h], &rt); | 
 | 		if (r) { | 
 | 			p = *r; | 
 | 			/* TODO: bad usage of kref (use a release).  I didn't change this | 
 | 			 * one, since it looks like the if code is when we want to release. | 
 | 			 * TODO: better code reuse btw v4 and v6... */ | 
 | 			if (kref_put(&p->routeTree.ref)) { | 
 | 				*r = 0; | 
 | 				addqueue(&f->queue, p->routeTree.left); | 
 | 				addqueue(&f->queue, p->routeTree.mid); | 
 | 				addqueue(&f->queue, p->routeTree.right); | 
 | 				freeroute(p); | 
 | 				while ((p = f->queue)) { | 
 | 					f->queue = p->routeTree.mid; | 
 | 					walkadd(f, &f->v6root[h], p->routeTree.left); | 
 | 					freeroute(p); | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 		if (dolock) | 
 | 			wunlock(&routelock); | 
 | 	} | 
 | 	v6routegeneration++; | 
 |  | 
 | 	ipifcremroute(f, 0, a, mask); | 
 | } | 
 |  | 
 | struct route *v4lookup(struct fs *f, uint8_t * a, struct conv *c) | 
 | { | 
 | 	struct route *p, *q; | 
 | 	uint32_t la; | 
 | 	uint8_t gate[IPaddrlen]; | 
 | 	struct ipifc *ifc; | 
 |  | 
 | 	if (c != NULL && c->r != NULL && c->r->routeTree.ifc != NULL | 
 | 		&& c->rgen == v4routegeneration) | 
 | 		return c->r; | 
 |  | 
 | 	la = nhgetl(a); | 
 | 	q = NULL; | 
 | 	for (p = f->v4root[V4H(la)]; p;) | 
 | 		if (la >= p->v4.address) { | 
 | 			if (la <= p->v4.endaddress) { | 
 | 				q = p; | 
 | 				p = p->routeTree.mid; | 
 | 			} else | 
 | 				p = p->routeTree.right; | 
 | 		} else | 
 | 			p = p->routeTree.left; | 
 |  | 
 | 	if (q | 
 | 		&& (q->routeTree.ifc == NULL | 
 | 			|| q->routeTree.ifcid != q->routeTree.ifc->ifcid)) { | 
 | 		if (q->routeTree.type & Rifc) { | 
 | 			hnputl(gate + IPv4off, q->v4.address); | 
 | 			memmove(gate, v4prefix, IPv4off); | 
 | 		} else | 
 | 			v4tov6(gate, q->v4.gate); | 
 | 		ifc = findipifc(f, gate, q->routeTree.type); | 
 | 		if (ifc == NULL) | 
 | 			return NULL; | 
 | 		q->routeTree.ifc = ifc; | 
 | 		q->routeTree.ifcid = ifc->ifcid; | 
 | 	} | 
 |  | 
 | 	if (c != NULL) { | 
 | 		c->r = q; | 
 | 		c->rgen = v4routegeneration; | 
 | 	} | 
 |  | 
 | 	return q; | 
 | } | 
 |  | 
 | struct route *v6lookup(struct fs *f, uint8_t * a, struct conv *c) | 
 | { | 
 | 	struct route *p, *q; | 
 | 	uint32_t la[IPllen]; | 
 | 	int h; | 
 | 	uint32_t x, y; | 
 | 	uint8_t gate[IPaddrlen]; | 
 | 	struct ipifc *ifc; | 
 |  | 
 | 	if (memcmp(a, v4prefix, IPv4off) == 0) { | 
 | 		q = v4lookup(f, a + IPv4off, c); | 
 | 		if (q != NULL) | 
 | 			return q; | 
 | 	} | 
 |  | 
 | 	if (c != NULL && c->r != NULL && c->r->routeTree.ifc != NULL | 
 | 		&& c->rgen == v6routegeneration) | 
 | 		return c->r; | 
 |  | 
 | 	for (h = 0; h < IPllen; h++) | 
 | 		la[h] = nhgetl(a + 4 * h); | 
 |  | 
 | 	q = 0; | 
 | 	for (p = f->v6root[V6H(la)]; p;) { | 
 | 		for (h = 0; h < IPllen; h++) { | 
 | 			x = la[h]; | 
 | 			y = p->v6.address[h]; | 
 | 			if (x == y) | 
 | 				continue; | 
 | 			if (x < y) { | 
 | 				p = p->routeTree.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->routeTree.right; | 
 | 				goto next; | 
 | 			} | 
 | 			break; | 
 | 		} | 
 | 		q = p; | 
 | 		p = p->routeTree.mid; | 
 | next:	; | 
 | 	} | 
 |  | 
 | 	if (q | 
 | 		&& (q->routeTree.ifc == NULL | 
 | 			|| q->routeTree.ifcid != q->routeTree.ifc->ifcid)) { | 
 | 		if (q->routeTree.type & Rifc) { | 
 | 			for (h = 0; h < IPllen; h++) | 
 | 				hnputl(gate + 4 * h, q->v6.address[h]); | 
 | 			ifc = findipifc(f, gate, q->routeTree.type); | 
 | 		} else | 
 | 			ifc = findipifc(f, q->v6.gate, q->routeTree.type); | 
 | 		if (ifc == NULL) | 
 | 			return NULL; | 
 | 		q->routeTree.ifc = ifc; | 
 | 		q->routeTree.ifcid = ifc->ifcid; | 
 | 	} | 
 | 	if (c != NULL) { | 
 | 		c->r = q; | 
 | 		c->rgen = v6routegeneration; | 
 | 	} | 
 |  | 
 | 	return q; | 
 | } | 
 |  | 
 | void routetype(int type, char *p) | 
 | { | 
 | 	memset(p, ' ', 4); | 
 | 	p[4] = 0; | 
 | 	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'; | 
 | } | 
 |  | 
 | static char *rformat = "%-15I %-4M %-15I %4.4s %4.4s %3s\n"; | 
 |  | 
 | void | 
 | convroute(struct route *r, uint8_t * addr, uint8_t * mask, | 
 | 		  uint8_t * gate, char *t, int *nifc) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if (r->routeTree.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(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])); | 
 | 		} | 
 | 		memmove(gate, r->v6.gate, IPaddrlen); | 
 | 	} | 
 |  | 
 | 	routetype(r->routeTree.type, t); | 
 |  | 
 | 	if (r->routeTree.ifc) | 
 | 		*nifc = r->routeTree.ifc->conv->x; | 
 | 	else | 
 | 		*nifc = -1; | 
 | } | 
 |  | 
 | /* | 
 |  *  this code is not in rr to reduce stack size | 
 |  */ | 
 | static void sprintroute(struct route *r, struct routewalk *rw) | 
 | { | 
 | 	int nifc, n; | 
 | 	char t[5], *iname, ifbuf[5]; | 
 | 	uint8_t addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen]; | 
 | 	char *p; | 
 | 	int i; | 
 |  | 
 | 	convroute(r, addr, mask, gate, t, &nifc); | 
 | 	iname = "-"; | 
 | 	if (nifc != -1) { | 
 | 		iname = ifbuf; | 
 | 		snprintf(ifbuf, sizeof ifbuf, "%d", nifc); | 
 | 	} | 
 |  | 
 | 	p = seprintf(rw->p, rw->e, rformat, addr, mask, gate, t, r->routeTree.tag, | 
 | 				 iname); | 
 | 	if (rw->o < 0) { | 
 | 		n = p - rw->p; | 
 | 		if (n > -rw->o) { | 
 | 			memmove(rw->p, rw->p - rw->o, n + rw->o); | 
 | 			rw->p = p + rw->o; | 
 | 		} | 
 | 		rw->o += n; | 
 | 	} else | 
 | 		rw->p = p; | 
 | } | 
 |  | 
 | /* | 
 |  *  recurse descending tree, applying the function in Routewalk | 
 |  */ | 
 | static int rr(struct route *r, struct routewalk *rw) | 
 | { | 
 | 	int h; | 
 |  | 
 | 	if (rw->e <= rw->p) | 
 | 		return 0; | 
 | 	if (r == NULL) | 
 | 		return 1; | 
 |  | 
 | 	if (rr(r->routeTree.left, rw) == 0) | 
 | 		return 0; | 
 |  | 
 | 	if (r->routeTree.type & Rv4) | 
 | 		h = V4H(r->v4.address); | 
 | 	else | 
 | 		h = V6H(r->v6.address); | 
 |  | 
 | 	if (h == rw->h) | 
 | 		rw->walk(r, rw); | 
 |  | 
 | 	if (rr(r->routeTree.mid, rw) == 0) | 
 | 		return 0; | 
 |  | 
 | 	return rr(r->routeTree.right, rw); | 
 | } | 
 |  | 
 | void ipwalkroutes(struct fs *f, struct routewalk *rw) | 
 | { | 
 | 	rlock(&routelock); | 
 | 	if (rw->e > rw->p) { | 
 | 		for (rw->h = 0; rw->h < ARRAY_SIZE(f->v4root); rw->h++) | 
 | 			if (rr(f->v4root[rw->h], rw) == 0) | 
 | 				break; | 
 | 	} | 
 | 	if (rw->e > rw->p) { | 
 | 		for (rw->h = 0; rw->h < ARRAY_SIZE(f->v6root); rw->h++) | 
 | 			if (rr(f->v6root[rw->h], rw) == 0) | 
 | 				break; | 
 | 	} | 
 | 	runlock(&routelock); | 
 | } | 
 |  | 
 | long routeread(struct fs *f, char *p, uint32_t offset, int n) | 
 | { | 
 | 	struct routewalk rw; | 
 |  | 
 | 	rw.p = p; | 
 | 	rw.e = p + n; | 
 | 	rw.o = -offset; | 
 | 	rw.walk = sprintroute; | 
 |  | 
 | 	ipwalkroutes(f, &rw); | 
 |  | 
 | 	return rw.p - p; | 
 | } | 
 |  | 
 | /* | 
 |  *  this code is not in routeflush to reduce stack size | 
 |  */ | 
 | void delroute(struct fs *f, struct route *r, int dolock) | 
 | { | 
 | 	uint8_t addr[IPaddrlen]; | 
 | 	uint8_t mask[IPaddrlen]; | 
 | 	uint8_t gate[IPaddrlen]; | 
 | 	char t[5]; | 
 | 	int nifc; | 
 |  | 
 | 	convroute(r, addr, mask, gate, t, &nifc); | 
 | 	if (r->routeTree.type & Rv4) | 
 | 		v4delroute(f, addr + IPv4off, mask + IPv4off, dolock); | 
 | 	else | 
 | 		v6delroute(f, addr, mask, dolock); | 
 | } | 
 |  | 
 | /* | 
 |  *  recurse until one route is deleted | 
 |  *    returns 0 if nothing is deleted, 1 otherwise | 
 |  */ | 
 | int routeflush(struct fs *f, struct route *r, char *tag) | 
 | { | 
 | 	if (r == NULL) | 
 | 		return 0; | 
 | 	if (routeflush(f, r->routeTree.mid, tag)) | 
 | 		return 1; | 
 | 	if (routeflush(f, r->routeTree.left, tag)) | 
 | 		return 1; | 
 | 	if (routeflush(f, r->routeTree.right, tag)) | 
 | 		return 1; | 
 | 	if ((r->routeTree.type & Rifc) == 0) { | 
 | 		if (tag == NULL | 
 | 			|| strncmp(tag, r->routeTree.tag, sizeof(r->routeTree.tag)) == 0) { | 
 | 			delroute(f, r, 0); | 
 | 			return 1; | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | struct route *iproute(struct fs *fs, uint8_t * ip) | 
 | { | 
 | 	if (isv4(ip)) | 
 | 		return v4lookup(fs, ip + IPv4off, NULL); | 
 | 	else | 
 | 		return v6lookup(fs, ip, NULL); | 
 | } | 
 |  | 
 | static void printroute(struct route *r) | 
 | { | 
 | 	int nifc; | 
 | 	char t[5], *iname, ifbuf[5]; | 
 | 	uint8_t addr[IPaddrlen], mask[IPaddrlen], gate[IPaddrlen]; | 
 |  | 
 | 	convroute(r, addr, mask, gate, t, &nifc); | 
 | 	iname = "-"; | 
 | 	if (nifc != -1) { | 
 | 		iname = ifbuf; | 
 | 		snprintf(ifbuf, sizeof ifbuf, "%d", nifc); | 
 | 	} | 
 | 	printd(rformat, addr, mask, gate, t, r->routeTree.tag, iname); | 
 | } | 
 |  | 
 | long routewrite(struct fs *f, struct chan *c, char *p, int n) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	int h, changed; | 
 | 	char *tag; | 
 | 	struct cmdbuf *cb; | 
 | 	uint8_t addr[IPaddrlen]; | 
 | 	uint8_t mask[IPaddrlen]; | 
 | 	uint8_t gate[IPaddrlen]; | 
 | 	struct IPaux *a, *na; | 
 | 	struct route *q; | 
 |  | 
 | 	cb = parsecmd(p, n); | 
 | 	if (waserror()) { | 
 | 		kfree(cb); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	if (strcmp(cb->f[0], "flush") == 0) { | 
 | 		tag = cb->f[1]; | 
 | 		for (h = 0; h < ARRAY_SIZE(f->v4root); h++) | 
 | 			for (changed = 1; changed;) { | 
 | 				wlock(&routelock); | 
 | 				changed = routeflush(f, f->v4root[h], tag); | 
 | 				wunlock(&routelock); | 
 | 			} | 
 | 		for (h = 0; h < ARRAY_SIZE(f->v6root); h++) | 
 | 			for (changed = 1; changed;) { | 
 | 				wlock(&routelock); | 
 | 				changed = routeflush(f, f->v6root[h], tag); | 
 | 				wunlock(&routelock); | 
 | 			} | 
 | 	} else if (strcmp(cb->f[0], "remove") == 0) { | 
 | 		if (cb->nf < 3) | 
 | 			error("%s: remove expects 3 args", Ebadarg); | 
 | 		if (parseip(addr, cb->f[1]) == -1) | 
 | 			error(Ebadip); | 
 | 		parseipmask(mask, cb->f[2]); | 
 | 		if (memcmp(addr, v4prefix, IPv4off) == 0) | 
 | 			v4delroute(f, addr + IPv4off, mask + IPv4off, 1); | 
 | 		else | 
 | 			v6delroute(f, addr, mask, 1); | 
 | 	} else if (strcmp(cb->f[0], "add") == 0) { | 
 | 		if (cb->nf < 4) | 
 | 			error("%s: needs 3 args, not %d", Ebadarg, cb->nf-1); | 
 | 		if (parseip(addr, cb->f[1]) == -1 || parseip(gate, cb->f[3]) == -1) | 
 | 			error(Ebadip); | 
 | 		parseipmask(mask, cb->f[2]); | 
 | 		tag = "none"; | 
 | 		if (c != NULL) { | 
 | 			a = c->aux; | 
 | 			tag = a->tag; | 
 | 		} | 
 | 		if (memcmp(addr, v4prefix, IPv4off) == 0) | 
 | 			v4addroute(f, tag, addr + IPv4off, mask + IPv4off, gate + IPv4off, | 
 | 					   0); | 
 | 		else | 
 | 			v6addroute(f, tag, addr, mask, gate, 0); | 
 | 	} else if (strcmp(cb->f[0], "tag") == 0) { | 
 | 		if (cb->nf < 2) | 
 | 			error(Ebadarg); | 
 |  | 
 | 		a = c->aux; | 
 | 		na = newipaux(a->owner, cb->f[1]); | 
 | 		c->aux = na; | 
 | 		kfree(a); | 
 | 	} else if (strcmp(cb->f[0], "route") == 0) { | 
 | 		if (cb->nf < 2) | 
 | 			error("%s: needs an arg", Ebadarg); | 
 | 		if (parseip(addr, cb->f[1]) == -1) | 
 | 			error(Ebadip); | 
 |  | 
 | 		q = iproute(f, addr); | 
 | 		printd("%I: ", addr); | 
 | 		if (q == NULL) | 
 | 			printk("no route\n"); | 
 | 		else | 
 | 			printroute(q); | 
 | 	} | 
 |  | 
 | 	poperror(); | 
 | 	kfree(cb); | 
 | 	return n; | 
 | } |