xref: /plan9-contrib/sys/src/9/ip/gre.c (revision 61d44851dbae9c6db4696bac4b180d884ecea735)
10774058cSDavid du Colombier /*
20774058cSDavid du Colombier  * Generic Routing Encapsulation over IPv4, rfc1702
30774058cSDavid du Colombier  */
47dd7cddfSDavid du Colombier #include "u.h"
57dd7cddfSDavid du Colombier #include "../port/lib.h"
67dd7cddfSDavid du Colombier #include "mem.h"
77dd7cddfSDavid du Colombier #include "dat.h"
87dd7cddfSDavid du Colombier #include "fns.h"
97dd7cddfSDavid du Colombier #include "../port/error.h"
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier #include "ip.h"
127dd7cddfSDavid du Colombier 
137ec5746aSDavid du Colombier enum {
147dd7cddfSDavid du Colombier 	GRE_IPONLY	= 12,		/* size of ip header */
157dd7cddfSDavid du Colombier 	GRE_IPPLUSGRE	= 12,		/* minimum size of GRE header */
167dd7cddfSDavid du Colombier 	IP_GREPROTO	= 47,
177dd7cddfSDavid du Colombier 
187dd7cddfSDavid du Colombier 	GRErxms		= 200,
197dd7cddfSDavid du Colombier 	GREtickms	= 100,
207dd7cddfSDavid du Colombier 	GREmaxxmit	= 10,
217ec5746aSDavid du Colombier 
227ec5746aSDavid du Colombier 	K		= 1024,
237ec5746aSDavid du Colombier 	GREqlen		= 256 * K,
247ec5746aSDavid du Colombier 
257ec5746aSDavid du Colombier 	GRE_cksum	= 0x8000,
267ec5746aSDavid du Colombier 	GRE_routing	= 0x4000,
277ec5746aSDavid du Colombier 	GRE_key		= 0x2000,
287ec5746aSDavid du Colombier 	GRE_seq		= 0x1000,
297ec5746aSDavid du Colombier 
307ec5746aSDavid du Colombier 	Nring		= 1 << 10,	/* power of two, please */
317ec5746aSDavid du Colombier 	Ringmask	= Nring - 1,
327ec5746aSDavid du Colombier 
337ec5746aSDavid du Colombier 	GREctlraw	= 0,
347ec5746aSDavid du Colombier 	GREctlcooked,
357ec5746aSDavid du Colombier 	GREctlretunnel,
367ec5746aSDavid du Colombier 	GREctlreport,
377ec5746aSDavid du Colombier 	GREctldlsuspend,
387ec5746aSDavid du Colombier 	GREctlulsuspend,
397ec5746aSDavid du Colombier 	GREctldlresume,
407ec5746aSDavid du Colombier 	GREctlulresume,
417ec5746aSDavid du Colombier 	GREctlforward,
427ec5746aSDavid du Colombier 	GREctlulkey,
437ec5746aSDavid du Colombier 	Ncmds,
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier 
467ec5746aSDavid du Colombier typedef struct GREhdr GREhdr;
477ec5746aSDavid du Colombier struct GREhdr{
487dd7cddfSDavid du Colombier 	/* ip header */
497dd7cddfSDavid du Colombier 	uchar	vihl;		/* Version and header length */
507dd7cddfSDavid du Colombier 	uchar	tos;		/* Type of service */
517dd7cddfSDavid du Colombier 	uchar	len[2];		/* packet length (including headers) */
527dd7cddfSDavid du Colombier 	uchar	id[2];		/* Identification */
537dd7cddfSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
547ec5746aSDavid du Colombier 	uchar	ttl;
557dd7cddfSDavid du Colombier 	uchar	proto;		/* Protocol */
567dd7cddfSDavid du Colombier 	uchar	cksum[2];	/* checksum */
577dd7cddfSDavid du Colombier 	uchar	src[4];		/* Ip source */
587dd7cddfSDavid du Colombier 	uchar	dst[4];		/* Ip destination */
597dd7cddfSDavid du Colombier 
607dd7cddfSDavid du Colombier 	/* gre header */
617dd7cddfSDavid du Colombier 	uchar	flags[2];
627dd7cddfSDavid du Colombier 	uchar	eproto[2];	/* encapsulation protocol */
637ec5746aSDavid du Colombier };
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier typedef struct GREpriv GREpriv;
667ec5746aSDavid du Colombier struct GREpriv{
677dd7cddfSDavid du Colombier 	/* non-MIB stats */
687dd7cddfSDavid du Colombier 	ulong	lenerr;			/* short packet */
697dd7cddfSDavid du Colombier };
707dd7cddfSDavid du Colombier 
717ec5746aSDavid du Colombier typedef struct Bring	Bring;
727ec5746aSDavid du Colombier struct Bring{
737ec5746aSDavid du Colombier 	Block	*ring[Nring];
747ec5746aSDavid du Colombier 	long	produced;
757ec5746aSDavid du Colombier 	long	consumed;
767ec5746aSDavid du Colombier };
777ec5746aSDavid du Colombier 
787ec5746aSDavid du Colombier typedef struct GREconv	GREconv;
797ec5746aSDavid du Colombier struct GREconv{
807ec5746aSDavid du Colombier 	int	raw;
817ec5746aSDavid du Colombier 
827ec5746aSDavid du Colombier 	/* Retunnelling information.  v4 only */
837ec5746aSDavid du Colombier 	uchar	north[4];			/* HA */
847ec5746aSDavid du Colombier 	uchar	south[4];			/* Base station */
857ec5746aSDavid du Colombier 	uchar	hoa[4];				/* Home address */
867ec5746aSDavid du Colombier 	uchar	coa[4];				/* Careof address */
877ec5746aSDavid du Colombier 	ulong	seq;				/* Current sequence # */
887ec5746aSDavid du Colombier 	int	dlsusp;				/* Downlink suspended? */
897ec5746aSDavid du Colombier 	int	ulsusp;				/* Uplink suspended? */
907ec5746aSDavid du Colombier 	ulong	ulkey;				/* GRE key */
917ec5746aSDavid du Colombier 
927ec5746aSDavid du Colombier 	QLock	lock;				/* Lock for rings */
937ec5746aSDavid du Colombier 	Bring	dlpending;			/* Ring of pending packets */
947ec5746aSDavid du Colombier 	Bring	dlbuffered;			/* Received while suspended */
957ec5746aSDavid du Colombier 	Bring	ulbuffered;			/* Received while suspended */
967ec5746aSDavid du Colombier };
977ec5746aSDavid du Colombier 
987ec5746aSDavid du Colombier typedef struct Metablock Metablock;
997ec5746aSDavid du Colombier struct Metablock{
1007ec5746aSDavid du Colombier 	uchar	*rp;
1017ec5746aSDavid du Colombier 	ulong	seq;
1027ec5746aSDavid du Colombier };
1037ec5746aSDavid du Colombier 
1047ec5746aSDavid du Colombier static char *grectlcooked(Conv *, int, char **);
1057ec5746aSDavid du Colombier static char *grectldlresume(Conv *, int, char **);
1067ec5746aSDavid du Colombier static char *grectldlsuspend(Conv *, int, char **);
1077ec5746aSDavid du Colombier static char *grectlforward(Conv *, int, char **);
1087ec5746aSDavid du Colombier static char *grectlraw(Conv *, int, char **);
1097ec5746aSDavid du Colombier static char *grectlreport(Conv *, int, char **);
1107ec5746aSDavid du Colombier static char *grectlretunnel(Conv *, int, char **);
1117ec5746aSDavid du Colombier static char *grectlulkey(Conv *, int, char **);
1127ec5746aSDavid du Colombier static char *grectlulresume(Conv *, int, char **);
1137ec5746aSDavid du Colombier static char *grectlulsuspend(Conv *, int, char **);
1147ec5746aSDavid du Colombier 
1157ec5746aSDavid du Colombier static struct{
1167ec5746aSDavid du Colombier 	char	*cmd;
1177ec5746aSDavid du Colombier 	int	argc;
1187ec5746aSDavid du Colombier 	char	*(*f)(Conv *, int, char **);
1197ec5746aSDavid du Colombier } grectls[Ncmds] = {
1207ec5746aSDavid du Colombier [GREctlraw]	=	{	"raw",		1,	grectlraw,	},
1217ec5746aSDavid du Colombier [GREctlcooked]	=	{	"cooked",	1,	grectlcooked,	},
1227ec5746aSDavid du Colombier [GREctlretunnel]=	{	"retunnel",	5,	grectlretunnel,	},
1237ec5746aSDavid du Colombier [GREctlreport]	=	{	"report",	2,	grectlreport,	},
1247ec5746aSDavid du Colombier [GREctldlsuspend]=	{	"dlsuspend",	1,	grectldlsuspend,},
1257ec5746aSDavid du Colombier [GREctlulsuspend]=	{	"ulsuspend",	1,	grectlulsuspend,},
1267ec5746aSDavid du Colombier [GREctldlresume]=	{	"dlresume",	1,	grectldlresume,	},
1277ec5746aSDavid du Colombier [GREctlulresume]=	{	"ulresume",	1,	grectlulresume,	},
1287ec5746aSDavid du Colombier [GREctlforward]	=	{	"forward",	2,	grectlforward,	},
1297ec5746aSDavid du Colombier [GREctlulkey]	=	{	"ulkey",	2,	grectlulkey,	},
1307ec5746aSDavid du Colombier };
1317ec5746aSDavid du Colombier 
1327ec5746aSDavid du Colombier static uchar nulladdr[4];
1337ec5746aSDavid du Colombier static char *sessend = "session end";
1347ec5746aSDavid du Colombier 
135e6c6b7f8SDavid du Colombier static void grekick(void *x, Block *bp);
1367ec5746aSDavid du Colombier static char *gresetup(Conv *, char *, char *, char *);
1377ec5746aSDavid du Colombier 
1387ec5746aSDavid du Colombier ulong grepdin, grepdout, grebdin, grebdout;
1397ec5746aSDavid du Colombier ulong grepuin, grepuout, grebuin, grebuout;
1407ec5746aSDavid du Colombier 
1417ec5746aSDavid du Colombier static Block *
getring(Bring * r)1427ec5746aSDavid du Colombier getring(Bring *r)
1437ec5746aSDavid du Colombier {
1447ec5746aSDavid du Colombier 	Block *bp;
1457ec5746aSDavid du Colombier 
1467ec5746aSDavid du Colombier 	if(r->consumed == r->produced)
1477ec5746aSDavid du Colombier 		return nil;
1487ec5746aSDavid du Colombier 
1497ec5746aSDavid du Colombier 	bp = r->ring[r->consumed & Ringmask];
1507ec5746aSDavid du Colombier 	r->ring[r->consumed & Ringmask] = nil;
1517ec5746aSDavid du Colombier 	r->consumed++;
1527ec5746aSDavid du Colombier 	return bp;
1537ec5746aSDavid du Colombier }
1547ec5746aSDavid du Colombier 
1557ec5746aSDavid du Colombier static void
addring(Bring * r,Block * bp)1567ec5746aSDavid du Colombier addring(Bring *r, Block *bp)
1577ec5746aSDavid du Colombier {
1587ec5746aSDavid du Colombier 	Block *tbp;
1597ec5746aSDavid du Colombier 
1607ec5746aSDavid du Colombier 	if(r->produced - r->consumed > Ringmask){
1617ec5746aSDavid du Colombier 		/* Full! */
1627ec5746aSDavid du Colombier 		tbp = r->ring[r->produced & Ringmask];
1637ec5746aSDavid du Colombier 		assert(tbp);
1647ec5746aSDavid du Colombier 		freeb(tbp);
1657ec5746aSDavid du Colombier 		r->consumed++;
1667ec5746aSDavid du Colombier 	}
1677ec5746aSDavid du Colombier 	r->ring[r->produced & Ringmask] = bp;
1687ec5746aSDavid du Colombier 	r->produced++;
1697ec5746aSDavid du Colombier }
1703ff48bf5SDavid du Colombier 
1717dd7cddfSDavid du Colombier static char *
greconnect(Conv * c,char ** argv,int argc)1727dd7cddfSDavid du Colombier greconnect(Conv *c, char **argv, int argc)
1737dd7cddfSDavid du Colombier {
1747dd7cddfSDavid du Colombier 	Proto *p;
1757dd7cddfSDavid du Colombier 	char *err;
1767dd7cddfSDavid du Colombier 	Conv *tc, **cp, **ecp;
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier 	err = Fsstdconnect(c, argv, argc);
1797dd7cddfSDavid du Colombier 	if(err != nil)
1807dd7cddfSDavid du Colombier 		return err;
1817dd7cddfSDavid du Colombier 
1827dd7cddfSDavid du Colombier 	/* make sure noone's already connected to this other sys */
1837dd7cddfSDavid du Colombier 	p = c->p;
1847dd7cddfSDavid du Colombier 	qlock(p);
1857dd7cddfSDavid du Colombier 	ecp = &p->conv[p->nc];
1867dd7cddfSDavid du Colombier 	for(cp = p->conv; cp < ecp; cp++){
1877dd7cddfSDavid du Colombier 		tc = *cp;
1887dd7cddfSDavid du Colombier 		if(tc == nil)
1897dd7cddfSDavid du Colombier 			break;
1907dd7cddfSDavid du Colombier 		if(tc == c)
1917dd7cddfSDavid du Colombier 			continue;
1927dd7cddfSDavid du Colombier 		if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
1937dd7cddfSDavid du Colombier 			err = "already connected to that addr/proto";
1947dd7cddfSDavid du Colombier 			ipmove(c->laddr, IPnoaddr);
1957dd7cddfSDavid du Colombier 			ipmove(c->raddr, IPnoaddr);
1967dd7cddfSDavid du Colombier 			break;
1977dd7cddfSDavid du Colombier 		}
1987dd7cddfSDavid du Colombier 	}
1997dd7cddfSDavid du Colombier 	qunlock(p);
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier 	if(err != nil)
2027dd7cddfSDavid du Colombier 		return err;
2037dd7cddfSDavid du Colombier 	Fsconnected(c, nil);
2047dd7cddfSDavid du Colombier 
2057dd7cddfSDavid du Colombier 	return nil;
2067dd7cddfSDavid du Colombier }
2077dd7cddfSDavid du Colombier 
2083ff48bf5SDavid du Colombier static void
grecreate(Conv * c)2093ff48bf5SDavid du Colombier grecreate(Conv *c)
2103ff48bf5SDavid du Colombier {
2117ec5746aSDavid du Colombier 	c->rq = qopen(GREqlen, Qmsg, 0, c);
212e6c6b7f8SDavid du Colombier 	c->wq = qbypass(grekick, c);
2133ff48bf5SDavid du Colombier }
2143ff48bf5SDavid du Colombier 
2157dd7cddfSDavid du Colombier static int
grestate(Conv * c,char * state,int n)2167dd7cddfSDavid du Colombier grestate(Conv *c, char *state, int n)
2177dd7cddfSDavid du Colombier {
2187ec5746aSDavid du Colombier 	GREconv *grec;
2197ec5746aSDavid du Colombier 	char *ep, *p;
2207ec5746aSDavid du Colombier 
2217ec5746aSDavid du Colombier 	grec = c->ptcl;
2227ec5746aSDavid du Colombier 	p    = state;
2237ec5746aSDavid du Colombier 	ep   = p + n;
2247ec5746aSDavid du Colombier 	p    = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
2257ec5746aSDavid du Colombier 	 "pending %uld  %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
2267ec5746aSDavid du Colombier 			c->inuse? "Open ": "Closed ",
2277ec5746aSDavid du Colombier 			grec->raw? "raw ": "",
2287ec5746aSDavid du Colombier 			grec->dlsusp? "DL suspended ": "",
2297ec5746aSDavid du Colombier 			grec->ulsusp? "UL suspended ": "",
2307ec5746aSDavid du Colombier 			grec->hoa, grec->north, grec->south, grec->seq,
2317ec5746aSDavid du Colombier 			grec->dlpending.consumed, grec->dlpending.produced,
2327ec5746aSDavid du Colombier 			grec->dlbuffered.consumed, grec->dlbuffered.produced,
2337ec5746aSDavid du Colombier 			grec->ulbuffered.consumed, grec->ulbuffered.produced,
2347ec5746aSDavid du Colombier 			grec->ulkey);
2357ec5746aSDavid du Colombier 	return p - state;
2367dd7cddfSDavid du Colombier }
2377dd7cddfSDavid du Colombier 
2387dd7cddfSDavid du Colombier static char*
greannounce(Conv *,char **,int)2397dd7cddfSDavid du Colombier greannounce(Conv*, char**, int)
2407dd7cddfSDavid du Colombier {
2417ec5746aSDavid du Colombier 	return "gre does not support announce";
2427dd7cddfSDavid du Colombier }
2437dd7cddfSDavid du Colombier 
2447dd7cddfSDavid du Colombier static void
greclose(Conv * c)2457dd7cddfSDavid du Colombier greclose(Conv *c)
2467dd7cddfSDavid du Colombier {
2477ec5746aSDavid du Colombier 	GREconv *grec;
2487ec5746aSDavid du Colombier 	Block *bp;
2497ec5746aSDavid du Colombier 
2507ec5746aSDavid du Colombier 	grec = c->ptcl;
2517ec5746aSDavid du Colombier 
2527ec5746aSDavid du Colombier 	/* Make sure we don't forward any more packets */
2537ec5746aSDavid du Colombier 	memset(grec->hoa, 0, sizeof grec->hoa);
2547ec5746aSDavid du Colombier 	memset(grec->north, 0, sizeof grec->north);
2557ec5746aSDavid du Colombier 	memset(grec->south, 0, sizeof grec->south);
2567ec5746aSDavid du Colombier 
2577ec5746aSDavid du Colombier 	qlock(&grec->lock);
2587ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlpending)) != nil)
2597ec5746aSDavid du Colombier 		freeb(bp);
2607ec5746aSDavid du Colombier 
2617ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil)
2627ec5746aSDavid du Colombier 		freeb(bp);
2637ec5746aSDavid du Colombier 
2647ec5746aSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil)
2657ec5746aSDavid du Colombier 		freeb(bp);
2667ec5746aSDavid du Colombier 
2677ec5746aSDavid du Colombier 	grec->dlpending.produced = grec->dlpending.consumed = 0;
2687ec5746aSDavid du Colombier 	grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
2697ec5746aSDavid du Colombier 	grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
2707ec5746aSDavid du Colombier 	qunlock(&grec->lock);
2717ec5746aSDavid du Colombier 
2727ec5746aSDavid du Colombier 	grec->raw = 0;
2737ec5746aSDavid du Colombier 	grec->seq = 0;
2747ec5746aSDavid du Colombier 	grec->dlsusp = grec->ulsusp = 1;
2757ec5746aSDavid du Colombier 
2767ec5746aSDavid du Colombier 	qhangup(c->rq, sessend);
2777ec5746aSDavid du Colombier 	qhangup(c->wq, sessend);
2787ec5746aSDavid du Colombier 	qhangup(c->eq, sessend);
2797dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
2807dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
2817ec5746aSDavid du Colombier 	c->lport = c->rport = 0;
2827dd7cddfSDavid du Colombier }
2837dd7cddfSDavid du Colombier 
2847dd7cddfSDavid du Colombier static void
grekick(void * x,Block * bp)285e6c6b7f8SDavid du Colombier grekick(void *x, Block *bp)
2867dd7cddfSDavid du Colombier {
2877ec5746aSDavid du Colombier 	Conv *c;
2887ec5746aSDavid du Colombier 	GREconv *grec;
2897ec5746aSDavid du Colombier 	GREhdr *gre;
2907dd7cddfSDavid du Colombier 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier 	if(bp == nil)
2937dd7cddfSDavid du Colombier 		return;
2947dd7cddfSDavid du Colombier 
2957ec5746aSDavid du Colombier 	c    = x;
2967ec5746aSDavid du Colombier 	grec = c->ptcl;
2977ec5746aSDavid du Colombier 
2987dd7cddfSDavid du Colombier 	/* Make space to fit ip header (gre header already there) */
2997dd7cddfSDavid du Colombier 	bp = padblock(bp, GRE_IPONLY);
3007dd7cddfSDavid du Colombier 	if(bp == nil)
3017dd7cddfSDavid du Colombier 		return;
3027dd7cddfSDavid du Colombier 
3037dd7cddfSDavid du Colombier 	/* make sure the message has a GRE header */
3047dd7cddfSDavid du Colombier 	bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
3057dd7cddfSDavid du Colombier 	if(bp == nil)
3067dd7cddfSDavid du Colombier 		return;
3077dd7cddfSDavid du Colombier 
3087ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
3097ec5746aSDavid du Colombier 	gre->vihl = IP_VER4;
3107dd7cddfSDavid du Colombier 
3117ec5746aSDavid du Colombier 	if(grec->raw == 0){
3127ec5746aSDavid du Colombier 		v4tov6(raddr, gre->dst);
3137dd7cddfSDavid du Colombier 		if(ipcmp(raddr, v4prefix) == 0)
3147ec5746aSDavid du Colombier 			memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
3157ec5746aSDavid du Colombier 		v4tov6(laddr, gre->src);
3167dd7cddfSDavid du Colombier 		if(ipcmp(laddr, v4prefix) == 0){
3177dd7cddfSDavid du Colombier 			if(ipcmp(c->laddr, IPnoaddr) == 0)
3187ec5746aSDavid du Colombier 				/* pick interface closest to dest */
3197ec5746aSDavid du Colombier 				findlocalip(c->p->f, c->laddr, raddr);
3207ec5746aSDavid du Colombier 			memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
3217dd7cddfSDavid du Colombier 		}
3227ec5746aSDavid du Colombier 		hnputs(gre->eproto, c->rport);
3235d82c6aeSDavid du Colombier 	}
3247dd7cddfSDavid du Colombier 
3257ec5746aSDavid du Colombier 	gre->proto = IP_GREPROTO;
3267ec5746aSDavid du Colombier 	gre->frag[0] = gre->frag[1] = 0;
3277dd7cddfSDavid du Colombier 
3287ec5746aSDavid du Colombier 	grepdout++;
3297ec5746aSDavid du Colombier 	grebdout += BLEN(bp);
330a6a9e072SDavid du Colombier 	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
3317dd7cddfSDavid du Colombier }
3327dd7cddfSDavid du Colombier 
3337dd7cddfSDavid du Colombier static void
gredownlink(Conv * c,Block * bp)3347ec5746aSDavid du Colombier gredownlink(Conv *c, Block *bp)
3357dd7cddfSDavid du Colombier {
3367ec5746aSDavid du Colombier 	Metablock *m;
3377ec5746aSDavid du Colombier 	GREconv *grec;
3387ec5746aSDavid du Colombier 	GREhdr *gre;
3397ec5746aSDavid du Colombier 	int hdrlen, suspended, extra;
3407ec5746aSDavid du Colombier 	ushort flags;
3417ec5746aSDavid du Colombier 	ulong seq;
3427dd7cddfSDavid du Colombier 
3437ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
3447ec5746aSDavid du Colombier 	if(gre->ttl == 1){
3457ec5746aSDavid du Colombier 		freeb(bp);
3467dd7cddfSDavid du Colombier 		return;
3477dd7cddfSDavid du Colombier 	}
3487dd7cddfSDavid du Colombier 
3497ec5746aSDavid du Colombier 	/*
3507ec5746aSDavid du Colombier 	 * We've received a packet with a GRE header and we need to
3517ec5746aSDavid du Colombier 	 * re-adjust the packet header to strip all unwanted parts
3527ec5746aSDavid du Colombier 	 * but leave room for only a sequence number.
3537ec5746aSDavid du Colombier 	 */
3547ec5746aSDavid du Colombier 	grec   = c->ptcl;
3557ec5746aSDavid du Colombier 	flags  = nhgets(gre->flags);
3567ec5746aSDavid du Colombier 	hdrlen = 0;
3577ec5746aSDavid du Colombier 	if(flags & GRE_cksum)
3587ec5746aSDavid du Colombier 		hdrlen += 2;
3597ec5746aSDavid du Colombier 	if(flags & GRE_routing){
3607ec5746aSDavid du Colombier 		print("%V routing info present.  Discarding packet", gre->src);
3617ec5746aSDavid du Colombier 		freeb(bp);
3627ec5746aSDavid du Colombier 		return;
3637ec5746aSDavid du Colombier 	}
3647ec5746aSDavid du Colombier 	if(flags & (GRE_cksum|GRE_routing))
3657ec5746aSDavid du Colombier 		hdrlen += 2;			/* Offset field */
3667ec5746aSDavid du Colombier 	if(flags & GRE_key)
3677ec5746aSDavid du Colombier 		hdrlen += 4;
3687ec5746aSDavid du Colombier 	if(flags & GRE_seq)
3697ec5746aSDavid du Colombier 		hdrlen += 4;
3707ec5746aSDavid du Colombier 
3717ec5746aSDavid du Colombier 	/*
3727ec5746aSDavid du Colombier 	 * The outgoing packet only has the sequence number set.  Make room
3737ec5746aSDavid du Colombier 	 * for the sequence number.
3747ec5746aSDavid du Colombier 	 */
3757ec5746aSDavid du Colombier 	if(hdrlen != sizeof(ulong)){
3767ec5746aSDavid du Colombier 		extra = hdrlen - sizeof(ulong);
3777ec5746aSDavid du Colombier 		if(extra < 0 && bp->rp - bp->base < -extra){
3787ec5746aSDavid du Colombier 			print("gredownlink: cannot add sequence number\n");
3797ec5746aSDavid du Colombier 			freeb(bp);
3807ec5746aSDavid du Colombier 			return;
3817ec5746aSDavid du Colombier 		}
3827ec5746aSDavid du Colombier 		memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
3837ec5746aSDavid du Colombier 		bp->rp += extra;
3847ec5746aSDavid du Colombier 		assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
3857ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
3867ec5746aSDavid du Colombier 	}
3877ec5746aSDavid du Colombier 	seq = grec->seq++;
3887ec5746aSDavid du Colombier 	hnputs(gre->flags, GRE_seq);
3897ec5746aSDavid du Colombier 	hnputl(bp->rp + sizeof(GREhdr), seq);
3907ec5746aSDavid du Colombier 
3917ec5746aSDavid du Colombier 	/*
3927ec5746aSDavid du Colombier 	 * Keep rp and seq at the base.  ipoput4 consumes rp for
3937ec5746aSDavid du Colombier 	 * refragmentation.
3947ec5746aSDavid du Colombier 	 */
3957ec5746aSDavid du Colombier 	assert(bp->rp - bp->base >= sizeof(Metablock));
3967ec5746aSDavid du Colombier 	m = (Metablock *)bp->base;
3977ec5746aSDavid du Colombier 	m->rp  = bp->rp;
3987ec5746aSDavid du Colombier 	m->seq = seq;
3997ec5746aSDavid du Colombier 
4007ec5746aSDavid du Colombier 	/*
4017ec5746aSDavid du Colombier 	 * Here we make a decision what we're doing with the packet.  We're
4027ec5746aSDavid du Colombier 	 * doing this w/o holding a lock which means that later on in the
4037ec5746aSDavid du Colombier 	 * process we may discover we've done the wrong thing.  I don't want
4047ec5746aSDavid du Colombier 	 * to call ipoput with the lock held.
4057ec5746aSDavid du Colombier 	 */
4067ec5746aSDavid du Colombier restart:
4077ec5746aSDavid du Colombier 	suspended = grec->dlsusp;
4087ec5746aSDavid du Colombier 	if(suspended){
4097ec5746aSDavid du Colombier 		if(!canqlock(&grec->lock)){
4107ec5746aSDavid du Colombier 			/*
4117ec5746aSDavid du Colombier 			 * just give up.  too bad, we lose a packet.  this
4127ec5746aSDavid du Colombier 			 * is just too hard and my brain already hurts.
4137ec5746aSDavid du Colombier 			 */
4147ec5746aSDavid du Colombier 			freeb(bp);
4157ec5746aSDavid du Colombier 			return;
4167ec5746aSDavid du Colombier 		}
4177ec5746aSDavid du Colombier 
4187ec5746aSDavid du Colombier 		if(!grec->dlsusp){
4197ec5746aSDavid du Colombier 			/*
4207ec5746aSDavid du Colombier 			 * suspend race.  We though we were suspended, but
4217ec5746aSDavid du Colombier 			 * we really weren't.
4227ec5746aSDavid du Colombier 			 */
4237ec5746aSDavid du Colombier 			qunlock(&grec->lock);
4247ec5746aSDavid du Colombier 			goto restart;
4257ec5746aSDavid du Colombier 		}
4267ec5746aSDavid du Colombier 
4277ec5746aSDavid du Colombier 		/* Undo the incorrect ref count addition */
4287ec5746aSDavid du Colombier 		addring(&grec->dlbuffered, bp);
4297ec5746aSDavid du Colombier 		qunlock(&grec->lock);
4307ec5746aSDavid du Colombier 		return;
4317ec5746aSDavid du Colombier 	}
4327ec5746aSDavid du Colombier 
4337ec5746aSDavid du Colombier 	/*
4347ec5746aSDavid du Colombier 	 * When we get here, we're not suspended.  Proceed to send the
4357ec5746aSDavid du Colombier 	 * packet.
4367ec5746aSDavid du Colombier 	 */
4377ec5746aSDavid du Colombier 	memmove(gre->src, grec->coa, sizeof gre->dst);
4387ec5746aSDavid du Colombier 	memmove(gre->dst, grec->south, sizeof gre->dst);
4397ec5746aSDavid du Colombier 
4407ec5746aSDavid du Colombier 	/*
4417ec5746aSDavid du Colombier 	 * Make sure the packet does not go away.
4427ec5746aSDavid du Colombier 	 */
443*61d44851SDavid du Colombier 	ainc(&bp->ref);
4447ec5746aSDavid du Colombier 	assert(bp->ref == 2);
4457ec5746aSDavid du Colombier 
4467ec5746aSDavid du Colombier 	ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
4477ec5746aSDavid du Colombier 	grepdout++;
4487ec5746aSDavid du Colombier 	grebdout += BLEN(bp);
4497ec5746aSDavid du Colombier 
4507ec5746aSDavid du Colombier 	/*
4517ec5746aSDavid du Colombier 	 * Now make sure we didn't do the wrong thing.
4527ec5746aSDavid du Colombier 	 */
4537ec5746aSDavid du Colombier 	if(!canqlock(&grec->lock)){
4547ec5746aSDavid du Colombier 		freeb(bp);		/* The packet just goes away */
4557ec5746aSDavid du Colombier 		return;
4567ec5746aSDavid du Colombier 	}
4577ec5746aSDavid du Colombier 
4587ec5746aSDavid du Colombier 	/* We did the right thing */
4597ec5746aSDavid du Colombier 	addring(&grec->dlpending, bp);
4607ec5746aSDavid du Colombier 	qunlock(&grec->lock);
4617ec5746aSDavid du Colombier }
4627ec5746aSDavid du Colombier 
4637ec5746aSDavid du Colombier static void
greuplink(Conv * c,Block * bp)4647ec5746aSDavid du Colombier greuplink(Conv *c, Block *bp)
4657ec5746aSDavid du Colombier {
4667ec5746aSDavid du Colombier 	GREconv *grec;
4677ec5746aSDavid du Colombier 	GREhdr *gre;
4687ec5746aSDavid du Colombier 	ushort flags;
4697ec5746aSDavid du Colombier 
4707ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
4717ec5746aSDavid du Colombier 	if(gre->ttl == 1)
4727ec5746aSDavid du Colombier 		return;
4737ec5746aSDavid du Colombier 
4747ec5746aSDavid du Colombier 	grec = c->ptcl;
4757ec5746aSDavid du Colombier 	memmove(gre->src, grec->coa, sizeof gre->src);
4767ec5746aSDavid du Colombier 	memmove(gre->dst, grec->north, sizeof gre->dst);
4777ec5746aSDavid du Colombier 
4787ec5746aSDavid du Colombier 	/*
4797ec5746aSDavid du Colombier 	 * Add a key, if needed.
4807ec5746aSDavid du Colombier 	 */
4817ec5746aSDavid du Colombier 	if(grec->ulkey){
4827ec5746aSDavid du Colombier 		flags = nhgets(gre->flags);
4837ec5746aSDavid du Colombier 		if(flags & (GRE_cksum|GRE_routing)){
4847ec5746aSDavid du Colombier 			print("%V routing info present.  Discarding packet\n",
4857ec5746aSDavid du Colombier 				gre->src);
4867ec5746aSDavid du Colombier 			freeb(bp);
4877ec5746aSDavid du Colombier 			return;
4887ec5746aSDavid du Colombier 		}
4897ec5746aSDavid du Colombier 
4907ec5746aSDavid du Colombier 		if((flags & GRE_key) == 0){
4917ec5746aSDavid du Colombier 			/* Make room for the key */
4927ec5746aSDavid du Colombier 			if(bp->rp - bp->base < sizeof(ulong)){
4937ec5746aSDavid du Colombier 				print("%V can't add key\n", gre->src);
4947ec5746aSDavid du Colombier 				freeb(bp);
4957ec5746aSDavid du Colombier 				return;
4967ec5746aSDavid du Colombier 			}
4977ec5746aSDavid du Colombier 
4987ec5746aSDavid du Colombier 			bp->rp -= 4;
4997ec5746aSDavid du Colombier 			memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
5007ec5746aSDavid du Colombier 
5017ec5746aSDavid du Colombier 			gre = (GREhdr *)bp->rp;
5027ec5746aSDavid du Colombier 			hnputs(gre->flags, flags | GRE_key);
5037ec5746aSDavid du Colombier 		}
5047ec5746aSDavid du Colombier 
5057ec5746aSDavid du Colombier 		/* Add the key */
5067ec5746aSDavid du Colombier 		hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
5077ec5746aSDavid du Colombier 	}
5087ec5746aSDavid du Colombier 
5097ec5746aSDavid du Colombier 	if(!canqlock(&grec->lock)){
5107ec5746aSDavid du Colombier 		freeb(bp);
5117ec5746aSDavid du Colombier 		return;
5127ec5746aSDavid du Colombier 	}
5137ec5746aSDavid du Colombier 
5147ec5746aSDavid du Colombier 	if(grec->ulsusp)
5157ec5746aSDavid du Colombier 		addring(&grec->ulbuffered, bp);
5167ec5746aSDavid du Colombier 	else{
5177ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
5187ec5746aSDavid du Colombier 		grepuout++;
5197ec5746aSDavid du Colombier 		grebuout += BLEN(bp);
5207ec5746aSDavid du Colombier 	}
5217ec5746aSDavid du Colombier 	qunlock(&grec->lock);
5227ec5746aSDavid du Colombier }
5237ec5746aSDavid du Colombier 
5247ec5746aSDavid du Colombier static void
greiput(Proto * proto,Ipifc *,Block * bp)5257ec5746aSDavid du Colombier greiput(Proto *proto, Ipifc *, Block *bp)
5267ec5746aSDavid du Colombier {
5277ec5746aSDavid du Colombier 	int len, hdrlen;
5287ec5746aSDavid du Colombier 	ushort eproto, flags;
5297ec5746aSDavid du Colombier 	uchar raddr[IPaddrlen];
5307ec5746aSDavid du Colombier 	Conv *c, **p;
5317ec5746aSDavid du Colombier 	GREconv *grec;
5327ec5746aSDavid du Colombier 	GREhdr *gre;
5337ec5746aSDavid du Colombier 	GREpriv *gpriv;
5347ec5746aSDavid du Colombier 	Ip4hdr *ip;
5357ec5746aSDavid du Colombier 
5367ec5746aSDavid du Colombier 	/*
5377ec5746aSDavid du Colombier 	 * We don't want to deal with block lists.  Ever.  The problem is
5387ec5746aSDavid du Colombier 	 * that when the block is forwarded, devether.c puts the block into
5397ec5746aSDavid du Colombier 	 * a queue that also uses ->next.  Just do not use ->next here!
5407ec5746aSDavid du Colombier 	 */
5417ec5746aSDavid du Colombier 	if(bp->next){
5427ec5746aSDavid du Colombier 		len = blocklen(bp);
5437ec5746aSDavid du Colombier 		bp  = pullupblock(bp, len);
5447ec5746aSDavid du Colombier 		assert(BLEN(bp) == len && bp->next == nil);
5457ec5746aSDavid du Colombier 	}
5467ec5746aSDavid du Colombier 
5477ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
5487ec5746aSDavid du Colombier 	if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
5497ec5746aSDavid du Colombier 		freeb(bp);
5507ec5746aSDavid du Colombier 		return;
5517ec5746aSDavid du Colombier 	}
5527ec5746aSDavid du Colombier 
5537ec5746aSDavid du Colombier 	v4tov6(raddr, gre->src);
5547ec5746aSDavid du Colombier 	eproto = nhgets(gre->eproto);
5557ec5746aSDavid du Colombier 	flags  = nhgets(gre->flags);
5567ec5746aSDavid du Colombier 	hdrlen = sizeof(GREhdr);
5577ec5746aSDavid du Colombier 
5587ec5746aSDavid du Colombier 	if(flags & GRE_cksum)
5597ec5746aSDavid du Colombier 		hdrlen += 2;
5607ec5746aSDavid du Colombier 	if(flags & GRE_routing){
5617ec5746aSDavid du Colombier 		print("%I routing info present.  Discarding packet\n", raddr);
5627ec5746aSDavid du Colombier 		freeb(bp);
5637ec5746aSDavid du Colombier 		return;
5647ec5746aSDavid du Colombier 	}
5657ec5746aSDavid du Colombier 	if(flags & (GRE_cksum|GRE_routing))
5667ec5746aSDavid du Colombier 		hdrlen += 2;			/* Offset field */
5677ec5746aSDavid du Colombier 	if(flags & GRE_key)
5687ec5746aSDavid du Colombier 		hdrlen += 4;
5697ec5746aSDavid du Colombier 	if(flags & GRE_seq)
5707ec5746aSDavid du Colombier 		hdrlen += 4;
5717ec5746aSDavid du Colombier 
5727ec5746aSDavid du Colombier 	if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
5737ec5746aSDavid du Colombier 		print("greretunnel: packet too short (s=%V d=%V)\n",
5747ec5746aSDavid du Colombier 			gre->src, gre->dst);
5757ec5746aSDavid du Colombier 		freeb(bp);
5767ec5746aSDavid du Colombier 		return;
5777ec5746aSDavid du Colombier 	}
5787ec5746aSDavid du Colombier 	ip = (Ip4hdr *)(bp->rp + hdrlen);
5797ec5746aSDavid du Colombier 
5807ec5746aSDavid du Colombier 	qlock(proto);
5817ec5746aSDavid du Colombier 	/*
5827ec5746aSDavid du Colombier 	 * Look for a conversation structure for this port and address, or
5837ec5746aSDavid du Colombier 	 * match the retunnel part, or match on the raw flag.
5847ec5746aSDavid du Colombier 	 */
5857ec5746aSDavid du Colombier 	for(p = proto->conv; *p; p++) {
5867ec5746aSDavid du Colombier 		c = *p;
5877ec5746aSDavid du Colombier 
5887ec5746aSDavid du Colombier 		if(c->inuse == 0)
5897ec5746aSDavid du Colombier 			continue;
5907ec5746aSDavid du Colombier 
5917ec5746aSDavid du Colombier 		/*
5927ec5746aSDavid du Colombier 		 * Do not stop this session - blocking here
5937ec5746aSDavid du Colombier 		 * implies that etherread is blocked.
5947ec5746aSDavid du Colombier 		 */
5957ec5746aSDavid du Colombier 		grec = c->ptcl;
5967ec5746aSDavid du Colombier 		if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
5977ec5746aSDavid du Colombier 			grepdin++;
5987ec5746aSDavid du Colombier 			grebdin += BLEN(bp);
5997ec5746aSDavid du Colombier 			gredownlink(c, bp);
6007ec5746aSDavid du Colombier 			qunlock(proto);
6017ec5746aSDavid du Colombier 			return;
6027ec5746aSDavid du Colombier 		}
6037ec5746aSDavid du Colombier 
6047ec5746aSDavid du Colombier 		if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
6057ec5746aSDavid du Colombier 			grepuin++;
6067ec5746aSDavid du Colombier 			grebuin += BLEN(bp);
6077ec5746aSDavid du Colombier 			greuplink(c, bp);
6087ec5746aSDavid du Colombier 			qunlock(proto);
6097ec5746aSDavid du Colombier 			return;
6107ec5746aSDavid du Colombier 		}
6117ec5746aSDavid du Colombier 	}
6127ec5746aSDavid du Colombier 
6137ec5746aSDavid du Colombier 	/*
6147ec5746aSDavid du Colombier 	 * when we get here, none of the forwarding tunnels matched.  now
6157ec5746aSDavid du Colombier 	 * try to match on raw and conversational sessions.
6167ec5746aSDavid du Colombier 	 */
6177ec5746aSDavid du Colombier 	for(c = nil, p = proto->conv; *p; p++) {
6187ec5746aSDavid du Colombier 		c = *p;
6197ec5746aSDavid du Colombier 
6207ec5746aSDavid du Colombier 		if(c->inuse == 0)
6217ec5746aSDavid du Colombier 			continue;
6227ec5746aSDavid du Colombier 
6237ec5746aSDavid du Colombier 		/*
6247ec5746aSDavid du Colombier 		 * Do not stop this session - blocking here
6257ec5746aSDavid du Colombier 		 * implies that etherread is blocked.
6267ec5746aSDavid du Colombier 		 */
6277ec5746aSDavid du Colombier 		grec = c->ptcl;
6287ec5746aSDavid du Colombier 		if(c->rport == eproto &&
6297ec5746aSDavid du Colombier 		    (grec->raw || ipcmp(c->raddr, raddr) == 0))
6307ec5746aSDavid du Colombier 			break;
6317ec5746aSDavid du Colombier 	}
6327ec5746aSDavid du Colombier 
6337ec5746aSDavid du Colombier 	qunlock(proto);
6347ec5746aSDavid du Colombier 
6357ec5746aSDavid du Colombier 	if(*p == nil){
6367ec5746aSDavid du Colombier 		freeb(bp);
6377ec5746aSDavid du Colombier 		return;
6387ec5746aSDavid du Colombier 	}
6397dd7cddfSDavid du Colombier 
6407dd7cddfSDavid du Colombier 	/*
6417dd7cddfSDavid du Colombier 	 * Trim the packet down to data size
6427dd7cddfSDavid du Colombier 	 */
6437ec5746aSDavid du Colombier 	len = nhgets(gre->len) - GRE_IPONLY;
6447dd7cddfSDavid du Colombier 	if(len < GRE_IPPLUSGRE){
6457ec5746aSDavid du Colombier 		freeb(bp);
6467dd7cddfSDavid du Colombier 		return;
6477dd7cddfSDavid du Colombier 	}
6487ec5746aSDavid du Colombier 
6497dd7cddfSDavid du Colombier 	bp = trimblock(bp, GRE_IPONLY, len);
6507dd7cddfSDavid du Colombier 	if(bp == nil){
6517ec5746aSDavid du Colombier 		gpriv = proto->priv;
6527dd7cddfSDavid du Colombier 		gpriv->lenerr++;
6537dd7cddfSDavid du Colombier 		return;
6547dd7cddfSDavid du Colombier 	}
6557dd7cddfSDavid du Colombier 
6567dd7cddfSDavid du Colombier 	/*
6577dd7cddfSDavid du Colombier 	 *  Can't delimit packet so pull it all into one block.
6587dd7cddfSDavid du Colombier 	 */
6597ec5746aSDavid du Colombier 	if(qlen(c->rq) > GREqlen)
6607ec5746aSDavid du Colombier 		freeb(bp);
6617dd7cddfSDavid du Colombier 	else{
6627dd7cddfSDavid du Colombier 		bp = concatblock(bp);
6637dd7cddfSDavid du Colombier 		if(bp == 0)
6647dd7cddfSDavid du Colombier 			panic("greiput");
6657dd7cddfSDavid du Colombier 		qpass(c->rq, bp);
6667dd7cddfSDavid du Colombier 	}
6677dd7cddfSDavid du Colombier }
6687dd7cddfSDavid du Colombier 
6697dd7cddfSDavid du Colombier int
grestats(Proto * gre,char * buf,int len)6707dd7cddfSDavid du Colombier grestats(Proto *gre, char *buf, int len)
6717dd7cddfSDavid du Colombier {
6727dd7cddfSDavid du Colombier 	GREpriv *gpriv;
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier 	gpriv = gre->priv;
6757ec5746aSDavid du Colombier 	return snprint(buf, len,
6767ec5746aSDavid du Colombier 		"gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
6777ec5746aSDavid du Colombier 		grepdin, grepdout, grepuin, grepuout,
6787ec5746aSDavid du Colombier 		grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
6797ec5746aSDavid du Colombier }
6807dd7cddfSDavid du Colombier 
6817ec5746aSDavid du Colombier static char *
grectlraw(Conv * c,int,char **)6827ec5746aSDavid du Colombier grectlraw(Conv *c, int, char **)
6837ec5746aSDavid du Colombier {
6847ec5746aSDavid du Colombier 	GREconv *grec;
6857ec5746aSDavid du Colombier 
6867ec5746aSDavid du Colombier 	grec = c->ptcl;
6877ec5746aSDavid du Colombier 	grec->raw = 1;
6887ec5746aSDavid du Colombier 	return nil;
6897ec5746aSDavid du Colombier }
6907ec5746aSDavid du Colombier 
6917ec5746aSDavid du Colombier static char *
grectlcooked(Conv * c,int,char **)6927ec5746aSDavid du Colombier grectlcooked(Conv *c, int, char **)
6937ec5746aSDavid du Colombier {
6947ec5746aSDavid du Colombier 	GREconv *grec;
6957ec5746aSDavid du Colombier 
6967ec5746aSDavid du Colombier 	grec = c->ptcl;
6977ec5746aSDavid du Colombier 	grec->raw = 0;
6987ec5746aSDavid du Colombier 	return nil;
6997ec5746aSDavid du Colombier }
7007ec5746aSDavid du Colombier 
7017ec5746aSDavid du Colombier static char *
grectlretunnel(Conv * c,int,char ** argv)7027ec5746aSDavid du Colombier grectlretunnel(Conv *c, int, char **argv)
7037ec5746aSDavid du Colombier {
7047ec5746aSDavid du Colombier 	GREconv *grec;
7057ec5746aSDavid du Colombier 	uchar ipaddr[4];
7067ec5746aSDavid du Colombier 
7077ec5746aSDavid du Colombier 	grec = c->ptcl;
7087ec5746aSDavid du Colombier 	if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
7097ec5746aSDavid du Colombier 		return "tunnel already set up";
7107ec5746aSDavid du Colombier 
7117ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[1]);
7127ec5746aSDavid du Colombier 	if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
7137ec5746aSDavid du Colombier 		return "bad hoa";
7147ec5746aSDavid du Colombier 	memmove(grec->hoa, ipaddr, sizeof grec->hoa);
7157ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[2]);
7167ec5746aSDavid du Colombier 	memmove(grec->north, ipaddr, sizeof grec->north);
7177ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[3]);
7187ec5746aSDavid du Colombier 	memmove(grec->south, ipaddr, sizeof grec->south);
7197ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[4]);
7207ec5746aSDavid du Colombier 	memmove(grec->coa, ipaddr, sizeof grec->coa);
7217ec5746aSDavid du Colombier 	grec->ulsusp = 1;
7227ec5746aSDavid du Colombier 	grec->dlsusp = 0;
7237ec5746aSDavid du Colombier 
7247ec5746aSDavid du Colombier 	return nil;
7257ec5746aSDavid du Colombier }
7267ec5746aSDavid du Colombier 
7277ec5746aSDavid du Colombier static char *
grectlreport(Conv * c,int,char ** argv)7287ec5746aSDavid du Colombier grectlreport(Conv *c, int, char **argv)
7297ec5746aSDavid du Colombier {
7307ec5746aSDavid du Colombier 	ulong seq;
7317ec5746aSDavid du Colombier 	Block *bp;
7327ec5746aSDavid du Colombier 	Bring *r;
7337ec5746aSDavid du Colombier 	GREconv *grec;
7347ec5746aSDavid du Colombier 	Metablock *m;
7357ec5746aSDavid du Colombier 
7367ec5746aSDavid du Colombier 	grec = c->ptcl;
7377ec5746aSDavid du Colombier 	seq  = strtoul(argv[1], nil, 0);
7387ec5746aSDavid du Colombier 
7397ec5746aSDavid du Colombier 	qlock(&grec->lock);
7407ec5746aSDavid du Colombier 	r = &grec->dlpending;
7417ec5746aSDavid du Colombier 	while(r->produced - r->consumed > 0){
7427ec5746aSDavid du Colombier 		bp = r->ring[r->consumed & Ringmask];
7437ec5746aSDavid du Colombier 
7447ec5746aSDavid du Colombier 		assert(bp && bp->rp - bp->base >= sizeof(Metablock));
7457ec5746aSDavid du Colombier 		m = (Metablock *)bp->base;
7467ec5746aSDavid du Colombier 		if((long)(seq - m->seq) <= 0)
7477ec5746aSDavid du Colombier 			break;
7487ec5746aSDavid du Colombier 
7497ec5746aSDavid du Colombier 		r->ring[r->consumed & Ringmask] = nil;
7507ec5746aSDavid du Colombier 		r->consumed++;
7517ec5746aSDavid du Colombier 
7527ec5746aSDavid du Colombier 		freeb(bp);
7537ec5746aSDavid du Colombier 	}
7547ec5746aSDavid du Colombier 	qunlock(&grec->lock);
7557ec5746aSDavid du Colombier 	return nil;
7567ec5746aSDavid du Colombier }
7577ec5746aSDavid du Colombier 
7587ec5746aSDavid du Colombier static char *
grectldlsuspend(Conv * c,int,char **)7597ec5746aSDavid du Colombier grectldlsuspend(Conv *c, int, char **)
7607ec5746aSDavid du Colombier {
7617ec5746aSDavid du Colombier 	GREconv *grec;
7627ec5746aSDavid du Colombier 
7637ec5746aSDavid du Colombier 	grec = c->ptcl;
7647ec5746aSDavid du Colombier 	if(grec->dlsusp)
7657ec5746aSDavid du Colombier 		return "already suspended";
7667ec5746aSDavid du Colombier 
7677ec5746aSDavid du Colombier 	grec->dlsusp = 1;
7687ec5746aSDavid du Colombier 	return nil;
7697ec5746aSDavid du Colombier }
7707ec5746aSDavid du Colombier 
7717ec5746aSDavid du Colombier static char *
grectlulsuspend(Conv * c,int,char **)7727ec5746aSDavid du Colombier grectlulsuspend(Conv *c, int, char **)
7737ec5746aSDavid du Colombier {
7747ec5746aSDavid du Colombier 	GREconv *grec;
7757ec5746aSDavid du Colombier 
7767ec5746aSDavid du Colombier 	grec = c->ptcl;
7777ec5746aSDavid du Colombier 	if(grec->ulsusp)
7787ec5746aSDavid du Colombier 		return "already suspended";
7797ec5746aSDavid du Colombier 
7807ec5746aSDavid du Colombier 	grec->ulsusp = 1;
7817ec5746aSDavid du Colombier 	return nil;
7827ec5746aSDavid du Colombier }
7837ec5746aSDavid du Colombier 
7847ec5746aSDavid du Colombier static char *
grectldlresume(Conv * c,int,char **)7857ec5746aSDavid du Colombier grectldlresume(Conv *c, int, char **)
7867ec5746aSDavid du Colombier {
7877ec5746aSDavid du Colombier 	GREconv *grec;
7887ec5746aSDavid du Colombier 	GREhdr *gre;
7897ec5746aSDavid du Colombier 	Block *bp;
7907ec5746aSDavid du Colombier 
7917ec5746aSDavid du Colombier 	grec = c->ptcl;
7927ec5746aSDavid du Colombier 
7937ec5746aSDavid du Colombier 	qlock(&grec->lock);
7947ec5746aSDavid du Colombier 	if(!grec->dlsusp){
7957ec5746aSDavid du Colombier 		qunlock(&grec->lock);
7967ec5746aSDavid du Colombier 		return "not suspended";
7977ec5746aSDavid du Colombier 	}
7987ec5746aSDavid du Colombier 
7997ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil){
8007ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8017ec5746aSDavid du Colombier 		qunlock(&grec->lock);
8027ec5746aSDavid du Colombier 
8037ec5746aSDavid du Colombier 		/*
8047ec5746aSDavid du Colombier 		 * Make sure the packet does not go away.
8057ec5746aSDavid du Colombier 		 */
806*61d44851SDavid du Colombier 		ainc(&bp->ref);
8077ec5746aSDavid du Colombier 		assert(bp->ref == 2);
8087ec5746aSDavid du Colombier 
8097ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8107ec5746aSDavid du Colombier 
8117ec5746aSDavid du Colombier 		qlock(&grec->lock);
8127ec5746aSDavid du Colombier 		addring(&grec->dlpending, bp);
8137ec5746aSDavid du Colombier 	}
8147ec5746aSDavid du Colombier 	grec->dlsusp = 0;
8157ec5746aSDavid du Colombier 	qunlock(&grec->lock);
8167ec5746aSDavid du Colombier 	return nil;
8177ec5746aSDavid du Colombier }
8187ec5746aSDavid du Colombier 
8197ec5746aSDavid du Colombier static char *
grectlulresume(Conv * c,int,char **)8207ec5746aSDavid du Colombier grectlulresume(Conv *c, int, char **)
8217ec5746aSDavid du Colombier {
8227ec5746aSDavid du Colombier 	GREconv *grec;
8237ec5746aSDavid du Colombier 	GREhdr *gre;
8247ec5746aSDavid du Colombier 	Block *bp;
8257ec5746aSDavid du Colombier 
8267ec5746aSDavid du Colombier 	grec = c->ptcl;
8277ec5746aSDavid du Colombier 
8287ec5746aSDavid du Colombier 	qlock(&grec->lock);
8297ec5746aSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil){
8307ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8317ec5746aSDavid du Colombier 
8327ec5746aSDavid du Colombier 		qunlock(&grec->lock);
8337ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8347ec5746aSDavid du Colombier 		qlock(&grec->lock);
8357ec5746aSDavid du Colombier 	}
8367ec5746aSDavid du Colombier 	grec->ulsusp = 0;
8377ec5746aSDavid du Colombier 	qunlock(&grec->lock);
8387ec5746aSDavid du Colombier 	return nil;
8397ec5746aSDavid du Colombier }
8407ec5746aSDavid du Colombier 
8417ec5746aSDavid du Colombier static char *
grectlforward(Conv * c,int,char ** argv)8427ec5746aSDavid du Colombier grectlforward(Conv *c, int, char **argv)
8437ec5746aSDavid du Colombier {
8447ec5746aSDavid du Colombier 	int len;
8457ec5746aSDavid du Colombier 	Block *bp, *nbp;
8467ec5746aSDavid du Colombier 	GREconv *grec;
8477ec5746aSDavid du Colombier 	GREhdr *gre;
8487ec5746aSDavid du Colombier 	Metablock *m;
8497ec5746aSDavid du Colombier 
8507ec5746aSDavid du Colombier 	grec = c->ptcl;
8517ec5746aSDavid du Colombier 
8527ec5746aSDavid du Colombier 	v4parseip(grec->south, argv[1]);
8537ec5746aSDavid du Colombier 	memmove(grec->north, grec->south, sizeof grec->north);
8547ec5746aSDavid du Colombier 
8557ec5746aSDavid du Colombier 	qlock(&grec->lock);
8567ec5746aSDavid du Colombier 	if(!grec->dlsusp){
8577ec5746aSDavid du Colombier 		qunlock(&grec->lock);
8587ec5746aSDavid du Colombier 		return "not suspended";
8597ec5746aSDavid du Colombier 	}
8607ec5746aSDavid du Colombier 	grec->dlsusp = 0;
8617ec5746aSDavid du Colombier 	grec->ulsusp = 0;
8627ec5746aSDavid du Colombier 
8637ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlpending)) != nil){
8647ec5746aSDavid du Colombier 
8657ec5746aSDavid du Colombier 		assert(bp->rp - bp->base >= sizeof(Metablock));
8667ec5746aSDavid du Colombier 		m = (Metablock *)bp->base;
8677ec5746aSDavid du Colombier 		assert(m->rp >= bp->base && m->rp < bp->lim);
8687ec5746aSDavid du Colombier 
8697ec5746aSDavid du Colombier 		/*
8707ec5746aSDavid du Colombier 		 * If the packet is still held inside the IP transmit
8717ec5746aSDavid du Colombier 		 * system, make a copy of the packet first.
8727ec5746aSDavid du Colombier 		 */
8737ec5746aSDavid du Colombier 		if(bp->ref > 1){
8747ec5746aSDavid du Colombier 			len = bp->wp - m->rp;
8757ec5746aSDavid du Colombier 			nbp = allocb(len);
8767ec5746aSDavid du Colombier 			memmove(nbp->wp, m->rp, len);
8777ec5746aSDavid du Colombier 			nbp->wp += len;
8787ec5746aSDavid du Colombier 			freeb(bp);
8797ec5746aSDavid du Colombier 			bp  = nbp;
8807ec5746aSDavid du Colombier 		}
8817ec5746aSDavid du Colombier 		else{
8827ec5746aSDavid du Colombier 			/* Patch up rp */
8837ec5746aSDavid du Colombier 			bp->rp = m->rp;
8847ec5746aSDavid du Colombier 		}
8857ec5746aSDavid du Colombier 
8867ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8877ec5746aSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
8887ec5746aSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
8897ec5746aSDavid du Colombier 
8907ec5746aSDavid du Colombier 		qunlock(&grec->lock);
8917ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8927ec5746aSDavid du Colombier 		qlock(&grec->lock);
8937ec5746aSDavid du Colombier 	}
8947ec5746aSDavid du Colombier 
8957ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil){
8967ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8977ec5746aSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
8987ec5746aSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
8997ec5746aSDavid du Colombier 
9007ec5746aSDavid du Colombier 		qunlock(&grec->lock);
9017ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9027ec5746aSDavid du Colombier 		qlock(&grec->lock);
9037ec5746aSDavid du Colombier 	}
9047ec5746aSDavid du Colombier 
9057ec5746aSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil){
9067ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
9077ec5746aSDavid du Colombier 
9087ec5746aSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
9097ec5746aSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
9107ec5746aSDavid du Colombier 
9117ec5746aSDavid du Colombier 		qunlock(&grec->lock);
9127ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9137ec5746aSDavid du Colombier 		qlock(&grec->lock);
9147ec5746aSDavid du Colombier 	}
9157ec5746aSDavid du Colombier 	qunlock(&grec->lock);
9167ec5746aSDavid du Colombier 	return nil;
9177ec5746aSDavid du Colombier }
9187ec5746aSDavid du Colombier 
9197ec5746aSDavid du Colombier static char *
grectlulkey(Conv * c,int,char ** argv)9207ec5746aSDavid du Colombier grectlulkey(Conv *c, int, char **argv)
9217ec5746aSDavid du Colombier {
9227ec5746aSDavid du Colombier 	GREconv *grec;
9237ec5746aSDavid du Colombier 
9247ec5746aSDavid du Colombier 	grec = c->ptcl;
9257ec5746aSDavid du Colombier 	grec->ulkey = strtoul(argv[1], nil, 0);
9267ec5746aSDavid du Colombier 	return nil;
9277dd7cddfSDavid du Colombier }
9287dd7cddfSDavid du Colombier 
9295d82c6aeSDavid du Colombier char *
grectl(Conv * c,char ** f,int n)9305d82c6aeSDavid du Colombier grectl(Conv *c, char **f, int n)
9315d82c6aeSDavid du Colombier {
9327ec5746aSDavid du Colombier 	int i;
9335d82c6aeSDavid du Colombier 
9347ec5746aSDavid du Colombier 	if(n < 1)
9357ec5746aSDavid du Colombier 		return "too few arguments";
9367ec5746aSDavid du Colombier 
9377ec5746aSDavid du Colombier 	for(i = 0; i < Ncmds; i++)
9387ec5746aSDavid du Colombier 		if(strcmp(f[0], grectls[i].cmd) == 0)
9397ec5746aSDavid du Colombier 			break;
9407ec5746aSDavid du Colombier 
9417ec5746aSDavid du Colombier 	if(i == Ncmds)
9427ec5746aSDavid du Colombier 		return "no such command";
9437ec5746aSDavid du Colombier 	if(grectls[i].argc != 0 && grectls[i].argc != n)
9447ec5746aSDavid du Colombier 		return "incorrect number of arguments";
9457ec5746aSDavid du Colombier 
9467ec5746aSDavid du Colombier 	return grectls[i].f(c, n, f);
9475d82c6aeSDavid du Colombier }
9485d82c6aeSDavid du Colombier 
9497dd7cddfSDavid du Colombier void
greinit(Fs * fs)9507dd7cddfSDavid du Colombier greinit(Fs *fs)
9517dd7cddfSDavid du Colombier {
9527dd7cddfSDavid du Colombier 	Proto *gre;
9537dd7cddfSDavid du Colombier 
9547dd7cddfSDavid du Colombier 	gre = smalloc(sizeof(Proto));
9557dd7cddfSDavid du Colombier 	gre->priv = smalloc(sizeof(GREpriv));
9567dd7cddfSDavid du Colombier 	gre->name = "gre";
9577dd7cddfSDavid du Colombier 	gre->connect = greconnect;
9587dd7cddfSDavid du Colombier 	gre->announce = greannounce;
9597dd7cddfSDavid du Colombier 	gre->state = grestate;
9607dd7cddfSDavid du Colombier 	gre->create = grecreate;
9617dd7cddfSDavid du Colombier 	gre->close = greclose;
9627dd7cddfSDavid du Colombier 	gre->rcv = greiput;
9635d82c6aeSDavid du Colombier 	gre->ctl = grectl;
9647dd7cddfSDavid du Colombier 	gre->advise = nil;
9657dd7cddfSDavid du Colombier 	gre->stats = grestats;
9667dd7cddfSDavid du Colombier 	gre->ipproto = IP_GREPROTO;
9677dd7cddfSDavid du Colombier 	gre->nc = 64;
9687ec5746aSDavid du Colombier 	gre->ptclsize = sizeof(GREconv);
9697dd7cddfSDavid du Colombier 
9707dd7cddfSDavid du Colombier 	Fsproto(fs, gre);
9717dd7cddfSDavid du Colombier }
972