xref: /plan9-contrib/sys/src/9k/ip/gre.c (revision cbdb37039db65a840f47b4b0a939d24658a6524a)
19ef1f84bSDavid du Colombier /*
29ef1f84bSDavid du Colombier  * Generic Routing Encapsulation over IPv4, rfc1702
39ef1f84bSDavid du Colombier  */
49ef1f84bSDavid du Colombier #include "u.h"
59ef1f84bSDavid du Colombier #include "../port/lib.h"
69ef1f84bSDavid du Colombier #include "mem.h"
79ef1f84bSDavid du Colombier #include "dat.h"
89ef1f84bSDavid du Colombier #include "fns.h"
99ef1f84bSDavid du Colombier #include "../port/error.h"
109ef1f84bSDavid du Colombier 
119ef1f84bSDavid du Colombier #include "ip.h"
129ef1f84bSDavid du Colombier 
139ef1f84bSDavid du Colombier enum {
149ef1f84bSDavid du Colombier 	GRE_IPONLY	= 12,		/* size of ip header */
159ef1f84bSDavid du Colombier 	GRE_IPPLUSGRE	= 12,		/* minimum size of GRE header */
169ef1f84bSDavid du Colombier 	IP_GREPROTO	= 47,
179ef1f84bSDavid du Colombier 
189ef1f84bSDavid du Colombier 	GRErxms		= 200,
199ef1f84bSDavid du Colombier 	GREtickms	= 100,
209ef1f84bSDavid du Colombier 	GREmaxxmit	= 10,
219ef1f84bSDavid du Colombier 
229ef1f84bSDavid du Colombier 	K		= 1024,
239ef1f84bSDavid du Colombier 	GREqlen		= 256 * K,
249ef1f84bSDavid du Colombier 
259ef1f84bSDavid du Colombier 	GRE_cksum	= 0x8000,
269ef1f84bSDavid du Colombier 	GRE_routing	= 0x4000,
279ef1f84bSDavid du Colombier 	GRE_key		= 0x2000,
289ef1f84bSDavid du Colombier 	GRE_seq		= 0x1000,
299ef1f84bSDavid du Colombier 
309ef1f84bSDavid du Colombier 	Nring		= 1 << 10,	/* power of two, please */
319ef1f84bSDavid du Colombier 	Ringmask	= Nring - 1,
329ef1f84bSDavid du Colombier 
339ef1f84bSDavid du Colombier 	GREctlraw	= 0,
349ef1f84bSDavid du Colombier 	GREctlcooked,
359ef1f84bSDavid du Colombier 	GREctlretunnel,
369ef1f84bSDavid du Colombier 	GREctlreport,
379ef1f84bSDavid du Colombier 	GREctldlsuspend,
389ef1f84bSDavid du Colombier 	GREctlulsuspend,
399ef1f84bSDavid du Colombier 	GREctldlresume,
409ef1f84bSDavid du Colombier 	GREctlulresume,
419ef1f84bSDavid du Colombier 	GREctlforward,
429ef1f84bSDavid du Colombier 	GREctlulkey,
439ef1f84bSDavid du Colombier 	Ncmds,
449ef1f84bSDavid du Colombier };
459ef1f84bSDavid du Colombier 
469ef1f84bSDavid du Colombier typedef struct GREhdr GREhdr;
479ef1f84bSDavid du Colombier struct GREhdr{
489ef1f84bSDavid du Colombier 	/* ip header */
499ef1f84bSDavid du Colombier 	uchar	vihl;		/* Version and header length */
509ef1f84bSDavid du Colombier 	uchar	tos;		/* Type of service */
519ef1f84bSDavid du Colombier 	uchar	len[2];		/* packet length (including headers) */
529ef1f84bSDavid du Colombier 	uchar	id[2];		/* Identification */
539ef1f84bSDavid du Colombier 	uchar	frag[2];	/* Fragment information */
549ef1f84bSDavid du Colombier 	uchar	ttl;
559ef1f84bSDavid du Colombier 	uchar	proto;		/* Protocol */
569ef1f84bSDavid du Colombier 	uchar	cksum[2];	/* checksum */
579ef1f84bSDavid du Colombier 	uchar	src[4];		/* Ip source */
589ef1f84bSDavid du Colombier 	uchar	dst[4];		/* Ip destination */
599ef1f84bSDavid du Colombier 
609ef1f84bSDavid du Colombier 	/* gre header */
619ef1f84bSDavid du Colombier 	uchar	flags[2];
629ef1f84bSDavid du Colombier 	uchar	eproto[2];	/* encapsulation protocol */
639ef1f84bSDavid du Colombier };
649ef1f84bSDavid du Colombier 
659ef1f84bSDavid du Colombier typedef struct GREpriv GREpriv;
669ef1f84bSDavid du Colombier struct GREpriv{
679ef1f84bSDavid du Colombier 	/* non-MIB stats */
689ef1f84bSDavid du Colombier 	ulong	lenerr;			/* short packet */
699ef1f84bSDavid du Colombier };
709ef1f84bSDavid du Colombier 
719ef1f84bSDavid du Colombier typedef struct Bring	Bring;
729ef1f84bSDavid du Colombier struct Bring{
739ef1f84bSDavid du Colombier 	Block	*ring[Nring];
749ef1f84bSDavid du Colombier 	long	produced;
759ef1f84bSDavid du Colombier 	long	consumed;
769ef1f84bSDavid du Colombier };
779ef1f84bSDavid du Colombier 
789ef1f84bSDavid du Colombier typedef struct GREconv	GREconv;
799ef1f84bSDavid du Colombier struct GREconv{
809ef1f84bSDavid du Colombier 	int	raw;
819ef1f84bSDavid du Colombier 
829ef1f84bSDavid du Colombier 	/* Retunnelling information.  v4 only */
839ef1f84bSDavid du Colombier 	uchar	north[4];			/* HA */
849ef1f84bSDavid du Colombier 	uchar	south[4];			/* Base station */
859ef1f84bSDavid du Colombier 	uchar	hoa[4];				/* Home address */
869ef1f84bSDavid du Colombier 	uchar	coa[4];				/* Careof address */
879ef1f84bSDavid du Colombier 	ulong	seq;				/* Current sequence # */
889ef1f84bSDavid du Colombier 	int	dlsusp;				/* Downlink suspended? */
899ef1f84bSDavid du Colombier 	int	ulsusp;				/* Uplink suspended? */
909ef1f84bSDavid du Colombier 	ulong	ulkey;				/* GRE key */
919ef1f84bSDavid du Colombier 
929ef1f84bSDavid du Colombier 	QLock	lock;				/* Lock for rings */
939ef1f84bSDavid du Colombier 	Bring	dlpending;			/* Ring of pending packets */
949ef1f84bSDavid du Colombier 	Bring	dlbuffered;			/* Received while suspended */
959ef1f84bSDavid du Colombier 	Bring	ulbuffered;			/* Received while suspended */
969ef1f84bSDavid du Colombier };
979ef1f84bSDavid du Colombier 
989ef1f84bSDavid du Colombier typedef struct Metablock Metablock;
999ef1f84bSDavid du Colombier struct Metablock{
1009ef1f84bSDavid du Colombier 	uchar	*rp;
1019ef1f84bSDavid du Colombier 	ulong	seq;
1029ef1f84bSDavid du Colombier };
1039ef1f84bSDavid du Colombier 
1049ef1f84bSDavid du Colombier static char *grectlcooked(Conv *, int, char **);
1059ef1f84bSDavid du Colombier static char *grectldlresume(Conv *, int, char **);
1069ef1f84bSDavid du Colombier static char *grectldlsuspend(Conv *, int, char **);
1079ef1f84bSDavid du Colombier static char *grectlforward(Conv *, int, char **);
1089ef1f84bSDavid du Colombier static char *grectlraw(Conv *, int, char **);
1099ef1f84bSDavid du Colombier static char *grectlreport(Conv *, int, char **);
1109ef1f84bSDavid du Colombier static char *grectlretunnel(Conv *, int, char **);
1119ef1f84bSDavid du Colombier static char *grectlulkey(Conv *, int, char **);
1129ef1f84bSDavid du Colombier static char *grectlulresume(Conv *, int, char **);
1139ef1f84bSDavid du Colombier static char *grectlulsuspend(Conv *, int, char **);
1149ef1f84bSDavid du Colombier 
1159ef1f84bSDavid du Colombier static struct{
1169ef1f84bSDavid du Colombier 	char	*cmd;
1179ef1f84bSDavid du Colombier 	int	argc;
1189ef1f84bSDavid du Colombier 	char	*(*f)(Conv *, int, char **);
1199ef1f84bSDavid du Colombier } grectls[Ncmds] = {
1209ef1f84bSDavid du Colombier [GREctlraw]	=	{	"raw",		1,	grectlraw,	},
1219ef1f84bSDavid du Colombier [GREctlcooked]	=	{	"cooked",	1,	grectlcooked,	},
1229ef1f84bSDavid du Colombier [GREctlretunnel]=	{	"retunnel",	5,	grectlretunnel,	},
1239ef1f84bSDavid du Colombier [GREctlreport]	=	{	"report",	2,	grectlreport,	},
1249ef1f84bSDavid du Colombier [GREctldlsuspend]=	{	"dlsuspend",	1,	grectldlsuspend,},
1259ef1f84bSDavid du Colombier [GREctlulsuspend]=	{	"ulsuspend",	1,	grectlulsuspend,},
1269ef1f84bSDavid du Colombier [GREctldlresume]=	{	"dlresume",	1,	grectldlresume,	},
1279ef1f84bSDavid du Colombier [GREctlulresume]=	{	"ulresume",	1,	grectlulresume,	},
1289ef1f84bSDavid du Colombier [GREctlforward]	=	{	"forward",	2,	grectlforward,	},
1299ef1f84bSDavid du Colombier [GREctlulkey]	=	{	"ulkey",	2,	grectlulkey,	},
1309ef1f84bSDavid du Colombier };
1319ef1f84bSDavid du Colombier 
1329ef1f84bSDavid du Colombier static uchar nulladdr[4];
1339ef1f84bSDavid du Colombier static char *sessend = "session end";
1349ef1f84bSDavid du Colombier 
1359ef1f84bSDavid du Colombier static void grekick(void *x, Block *bp);
1369ef1f84bSDavid du Colombier static char *gresetup(Conv *, char *, char *, char *);
1379ef1f84bSDavid du Colombier 
1389ef1f84bSDavid du Colombier ulong grepdin, grepdout, grebdin, grebdout;
1399ef1f84bSDavid du Colombier ulong grepuin, grepuout, grebuin, grebuout;
1409ef1f84bSDavid du Colombier 
1419ef1f84bSDavid du Colombier static Block *
getring(Bring * r)1429ef1f84bSDavid du Colombier getring(Bring *r)
1439ef1f84bSDavid du Colombier {
1449ef1f84bSDavid du Colombier 	Block *bp;
1459ef1f84bSDavid du Colombier 
1469ef1f84bSDavid du Colombier 	if(r->consumed == r->produced)
1479ef1f84bSDavid du Colombier 		return nil;
1489ef1f84bSDavid du Colombier 
1499ef1f84bSDavid du Colombier 	bp = r->ring[r->consumed & Ringmask];
1509ef1f84bSDavid du Colombier 	r->ring[r->consumed & Ringmask] = nil;
1519ef1f84bSDavid du Colombier 	r->consumed++;
1529ef1f84bSDavid du Colombier 	return bp;
1539ef1f84bSDavid du Colombier }
1549ef1f84bSDavid du Colombier 
1559ef1f84bSDavid du Colombier static void
addring(Bring * r,Block * bp)1569ef1f84bSDavid du Colombier addring(Bring *r, Block *bp)
1579ef1f84bSDavid du Colombier {
1589ef1f84bSDavid du Colombier 	Block *tbp;
1599ef1f84bSDavid du Colombier 
1609ef1f84bSDavid du Colombier 	if(r->produced - r->consumed > Ringmask){
1619ef1f84bSDavid du Colombier 		/* Full! */
1629ef1f84bSDavid du Colombier 		tbp = r->ring[r->produced & Ringmask];
1639ef1f84bSDavid du Colombier 		assert(tbp);
1649ef1f84bSDavid du Colombier 		freeb(tbp);
1659ef1f84bSDavid du Colombier 		r->consumed++;
1669ef1f84bSDavid du Colombier 	}
1679ef1f84bSDavid du Colombier 	r->ring[r->produced & Ringmask] = bp;
1689ef1f84bSDavid du Colombier 	r->produced++;
1699ef1f84bSDavid du Colombier }
1709ef1f84bSDavid du Colombier 
1719ef1f84bSDavid du Colombier static char *
greconnect(Conv * c,char ** argv,int argc)1729ef1f84bSDavid du Colombier greconnect(Conv *c, char **argv, int argc)
1739ef1f84bSDavid du Colombier {
1749ef1f84bSDavid du Colombier 	Proto *p;
1759ef1f84bSDavid du Colombier 	char *err;
1769ef1f84bSDavid du Colombier 	Conv *tc, **cp, **ecp;
1779ef1f84bSDavid du Colombier 
1789ef1f84bSDavid du Colombier 	err = Fsstdconnect(c, argv, argc);
1799ef1f84bSDavid du Colombier 	if(err != nil)
1809ef1f84bSDavid du Colombier 		return err;
1819ef1f84bSDavid du Colombier 
1829ef1f84bSDavid du Colombier 	/* make sure noone's already connected to this other sys */
1839ef1f84bSDavid du Colombier 	p = c->p;
1849ef1f84bSDavid du Colombier 	qlock(p);
1859ef1f84bSDavid du Colombier 	ecp = &p->conv[p->nc];
1869ef1f84bSDavid du Colombier 	for(cp = p->conv; cp < ecp; cp++){
1879ef1f84bSDavid du Colombier 		tc = *cp;
1889ef1f84bSDavid du Colombier 		if(tc == nil)
1899ef1f84bSDavid du Colombier 			break;
1909ef1f84bSDavid du Colombier 		if(tc == c)
1919ef1f84bSDavid du Colombier 			continue;
1929ef1f84bSDavid du Colombier 		if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
1939ef1f84bSDavid du Colombier 			err = "already connected to that addr/proto";
1949ef1f84bSDavid du Colombier 			ipmove(c->laddr, IPnoaddr);
1959ef1f84bSDavid du Colombier 			ipmove(c->raddr, IPnoaddr);
1969ef1f84bSDavid du Colombier 			break;
1979ef1f84bSDavid du Colombier 		}
1989ef1f84bSDavid du Colombier 	}
1999ef1f84bSDavid du Colombier 	qunlock(p);
2009ef1f84bSDavid du Colombier 
2019ef1f84bSDavid du Colombier 	if(err != nil)
2029ef1f84bSDavid du Colombier 		return err;
2039ef1f84bSDavid du Colombier 	Fsconnected(c, nil);
2049ef1f84bSDavid du Colombier 
2059ef1f84bSDavid du Colombier 	return nil;
2069ef1f84bSDavid du Colombier }
2079ef1f84bSDavid du Colombier 
2089ef1f84bSDavid du Colombier static void
grecreate(Conv * c)2099ef1f84bSDavid du Colombier grecreate(Conv *c)
2109ef1f84bSDavid du Colombier {
2119ef1f84bSDavid du Colombier 	c->rq = qopen(GREqlen, Qmsg, 0, c);
2129ef1f84bSDavid du Colombier 	c->wq = qbypass(grekick, c);
2139ef1f84bSDavid du Colombier }
2149ef1f84bSDavid du Colombier 
2159ef1f84bSDavid du Colombier static int
grestate(Conv * c,char * state,int n)2169ef1f84bSDavid du Colombier grestate(Conv *c, char *state, int n)
2179ef1f84bSDavid du Colombier {
2189ef1f84bSDavid du Colombier 	GREconv *grec;
2199ef1f84bSDavid du Colombier 	char *ep, *p;
2209ef1f84bSDavid du Colombier 
2219ef1f84bSDavid du Colombier 	grec = c->ptcl;
2229ef1f84bSDavid du Colombier 	p    = state;
2239ef1f84bSDavid du Colombier 	ep   = p + n;
2249ef1f84bSDavid du Colombier 	p    = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
2259ef1f84bSDavid du Colombier 	 "pending %uld  %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
2269ef1f84bSDavid du Colombier 			c->inuse? "Open ": "Closed ",
2279ef1f84bSDavid du Colombier 			grec->raw? "raw ": "",
2289ef1f84bSDavid du Colombier 			grec->dlsusp? "DL suspended ": "",
2299ef1f84bSDavid du Colombier 			grec->ulsusp? "UL suspended ": "",
2309ef1f84bSDavid du Colombier 			grec->hoa, grec->north, grec->south, grec->seq,
2319ef1f84bSDavid du Colombier 			grec->dlpending.consumed, grec->dlpending.produced,
2329ef1f84bSDavid du Colombier 			grec->dlbuffered.consumed, grec->dlbuffered.produced,
2339ef1f84bSDavid du Colombier 			grec->ulbuffered.consumed, grec->ulbuffered.produced,
2349ef1f84bSDavid du Colombier 			grec->ulkey);
2359ef1f84bSDavid du Colombier 	return p - state;
2369ef1f84bSDavid du Colombier }
2379ef1f84bSDavid du Colombier 
2389ef1f84bSDavid du Colombier static char*
greannounce(Conv *,char **,int)2399ef1f84bSDavid du Colombier greannounce(Conv*, char**, int)
2409ef1f84bSDavid du Colombier {
2419ef1f84bSDavid du Colombier 	return "gre does not support announce";
2429ef1f84bSDavid du Colombier }
2439ef1f84bSDavid du Colombier 
2449ef1f84bSDavid du Colombier static void
greclose(Conv * c)2459ef1f84bSDavid du Colombier greclose(Conv *c)
2469ef1f84bSDavid du Colombier {
2479ef1f84bSDavid du Colombier 	GREconv *grec;
2489ef1f84bSDavid du Colombier 	Block *bp;
2499ef1f84bSDavid du Colombier 
2509ef1f84bSDavid du Colombier 	grec = c->ptcl;
2519ef1f84bSDavid du Colombier 
2529ef1f84bSDavid du Colombier 	/* Make sure we don't forward any more packets */
2539ef1f84bSDavid du Colombier 	memset(grec->hoa, 0, sizeof grec->hoa);
2549ef1f84bSDavid du Colombier 	memset(grec->north, 0, sizeof grec->north);
2559ef1f84bSDavid du Colombier 	memset(grec->south, 0, sizeof grec->south);
2569ef1f84bSDavid du Colombier 
2579ef1f84bSDavid du Colombier 	qlock(&grec->lock);
2589ef1f84bSDavid du Colombier 	while((bp = getring(&grec->dlpending)) != nil)
2599ef1f84bSDavid du Colombier 		freeb(bp);
2609ef1f84bSDavid du Colombier 
2619ef1f84bSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil)
2629ef1f84bSDavid du Colombier 		freeb(bp);
2639ef1f84bSDavid du Colombier 
2649ef1f84bSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil)
2659ef1f84bSDavid du Colombier 		freeb(bp);
2669ef1f84bSDavid du Colombier 
2679ef1f84bSDavid du Colombier 	grec->dlpending.produced = grec->dlpending.consumed = 0;
2689ef1f84bSDavid du Colombier 	grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
2699ef1f84bSDavid du Colombier 	grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
2709ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
2719ef1f84bSDavid du Colombier 
2729ef1f84bSDavid du Colombier 	grec->raw = 0;
2739ef1f84bSDavid du Colombier 	grec->seq = 0;
2749ef1f84bSDavid du Colombier 	grec->dlsusp = grec->ulsusp = 1;
2759ef1f84bSDavid du Colombier 
2769ef1f84bSDavid du Colombier 	qhangup(c->rq, sessend);
2779ef1f84bSDavid du Colombier 	qhangup(c->wq, sessend);
2789ef1f84bSDavid du Colombier 	qhangup(c->eq, sessend);
2799ef1f84bSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
2809ef1f84bSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
2819ef1f84bSDavid du Colombier 	c->lport = c->rport = 0;
2829ef1f84bSDavid du Colombier }
2839ef1f84bSDavid du Colombier 
2849ef1f84bSDavid du Colombier static void
grekick(void * x,Block * bp)2859ef1f84bSDavid du Colombier grekick(void *x, Block *bp)
2869ef1f84bSDavid du Colombier {
2879ef1f84bSDavid du Colombier 	Conv *c;
2889ef1f84bSDavid du Colombier 	GREconv *grec;
2899ef1f84bSDavid du Colombier 	GREhdr *gre;
2909ef1f84bSDavid du Colombier 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
2919ef1f84bSDavid du Colombier 
2929ef1f84bSDavid du Colombier 	if(bp == nil)
2939ef1f84bSDavid du Colombier 		return;
2949ef1f84bSDavid du Colombier 
2959ef1f84bSDavid du Colombier 	c    = x;
2969ef1f84bSDavid du Colombier 	grec = c->ptcl;
2979ef1f84bSDavid du Colombier 
2989ef1f84bSDavid du Colombier 	/* Make space to fit ip header (gre header already there) */
2999ef1f84bSDavid du Colombier 	bp = padblock(bp, GRE_IPONLY);
3009ef1f84bSDavid du Colombier 	if(bp == nil)
3019ef1f84bSDavid du Colombier 		return;
3029ef1f84bSDavid du Colombier 
3039ef1f84bSDavid du Colombier 	/* make sure the message has a GRE header */
3049ef1f84bSDavid du Colombier 	bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
3059ef1f84bSDavid du Colombier 	if(bp == nil)
3069ef1f84bSDavid du Colombier 		return;
3079ef1f84bSDavid du Colombier 
3089ef1f84bSDavid du Colombier 	gre = (GREhdr *)bp->rp;
3099ef1f84bSDavid du Colombier 	gre->vihl = IP_VER4;
3109ef1f84bSDavid du Colombier 
3119ef1f84bSDavid du Colombier 	if(grec->raw == 0){
3129ef1f84bSDavid du Colombier 		v4tov6(raddr, gre->dst);
3139ef1f84bSDavid du Colombier 		if(ipcmp(raddr, v4prefix) == 0)
3149ef1f84bSDavid du Colombier 			memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
3159ef1f84bSDavid du Colombier 		v4tov6(laddr, gre->src);
3169ef1f84bSDavid du Colombier 		if(ipcmp(laddr, v4prefix) == 0){
3179ef1f84bSDavid du Colombier 			if(ipcmp(c->laddr, IPnoaddr) == 0)
3189ef1f84bSDavid du Colombier 				/* pick interface closest to dest */
3199ef1f84bSDavid du Colombier 				findlocalip(c->p->f, c->laddr, raddr);
3209ef1f84bSDavid du Colombier 			memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
3219ef1f84bSDavid du Colombier 		}
3229ef1f84bSDavid du Colombier 		hnputs(gre->eproto, c->rport);
3239ef1f84bSDavid du Colombier 	}
3249ef1f84bSDavid du Colombier 
3259ef1f84bSDavid du Colombier 	gre->proto = IP_GREPROTO;
3269ef1f84bSDavid du Colombier 	gre->frag[0] = gre->frag[1] = 0;
3279ef1f84bSDavid du Colombier 
3289ef1f84bSDavid du Colombier 	grepdout++;
3299ef1f84bSDavid du Colombier 	grebdout += BLEN(bp);
3309ef1f84bSDavid du Colombier 	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
3319ef1f84bSDavid du Colombier }
3329ef1f84bSDavid du Colombier 
3339ef1f84bSDavid du Colombier static void
gredownlink(Conv * c,Block * bp)3349ef1f84bSDavid du Colombier gredownlink(Conv *c, Block *bp)
3359ef1f84bSDavid du Colombier {
3369ef1f84bSDavid du Colombier 	Metablock *m;
3379ef1f84bSDavid du Colombier 	GREconv *grec;
3389ef1f84bSDavid du Colombier 	GREhdr *gre;
3399ef1f84bSDavid du Colombier 	int hdrlen, suspended, extra;
3409ef1f84bSDavid du Colombier 	ushort flags;
3419ef1f84bSDavid du Colombier 	ulong seq;
3429ef1f84bSDavid du Colombier 
3439ef1f84bSDavid du Colombier 	gre = (GREhdr *)bp->rp;
3449ef1f84bSDavid du Colombier 	if(gre->ttl == 1){
3459ef1f84bSDavid du Colombier 		freeb(bp);
3469ef1f84bSDavid du Colombier 		return;
3479ef1f84bSDavid du Colombier 	}
3489ef1f84bSDavid du Colombier 
3499ef1f84bSDavid du Colombier 	/*
3509ef1f84bSDavid du Colombier 	 * We've received a packet with a GRE header and we need to
3519ef1f84bSDavid du Colombier 	 * re-adjust the packet header to strip all unwanted parts
3529ef1f84bSDavid du Colombier 	 * but leave room for only a sequence number.
3539ef1f84bSDavid du Colombier 	 */
3549ef1f84bSDavid du Colombier 	grec   = c->ptcl;
3559ef1f84bSDavid du Colombier 	flags  = nhgets(gre->flags);
3569ef1f84bSDavid du Colombier 	hdrlen = 0;
3579ef1f84bSDavid du Colombier 	if(flags & GRE_cksum)
3589ef1f84bSDavid du Colombier 		hdrlen += 2;
3599ef1f84bSDavid du Colombier 	if(flags & GRE_routing){
3609ef1f84bSDavid du Colombier 		print("%V routing info present.  Discarding packet", gre->src);
3619ef1f84bSDavid du Colombier 		freeb(bp);
3629ef1f84bSDavid du Colombier 		return;
3639ef1f84bSDavid du Colombier 	}
3649ef1f84bSDavid du Colombier 	if(flags & (GRE_cksum|GRE_routing))
3659ef1f84bSDavid du Colombier 		hdrlen += 2;			/* Offset field */
3669ef1f84bSDavid du Colombier 	if(flags & GRE_key)
3679ef1f84bSDavid du Colombier 		hdrlen += 4;
3689ef1f84bSDavid du Colombier 	if(flags & GRE_seq)
3699ef1f84bSDavid du Colombier 		hdrlen += 4;
3709ef1f84bSDavid du Colombier 
3719ef1f84bSDavid du Colombier 	/*
3729ef1f84bSDavid du Colombier 	 * The outgoing packet only has the sequence number set.  Make room
3739ef1f84bSDavid du Colombier 	 * for the sequence number.
3749ef1f84bSDavid du Colombier 	 */
3759ef1f84bSDavid du Colombier 	if(hdrlen != sizeof(ulong)){
3769ef1f84bSDavid du Colombier 		extra = hdrlen - sizeof(ulong);
3779ef1f84bSDavid du Colombier 		if(extra < 0 && bp->rp - bp->base < -extra){
3789ef1f84bSDavid du Colombier 			print("gredownlink: cannot add sequence number\n");
3799ef1f84bSDavid du Colombier 			freeb(bp);
3809ef1f84bSDavid du Colombier 			return;
3819ef1f84bSDavid du Colombier 		}
3829ef1f84bSDavid du Colombier 		memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
3839ef1f84bSDavid du Colombier 		bp->rp += extra;
3849ef1f84bSDavid du Colombier 		assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
3859ef1f84bSDavid du Colombier 		gre = (GREhdr *)bp->rp;
3869ef1f84bSDavid du Colombier 	}
3879ef1f84bSDavid du Colombier 	seq = grec->seq++;
3889ef1f84bSDavid du Colombier 	hnputs(gre->flags, GRE_seq);
3899ef1f84bSDavid du Colombier 	hnputl(bp->rp + sizeof(GREhdr), seq);
3909ef1f84bSDavid du Colombier 
3919ef1f84bSDavid du Colombier 	/*
3929ef1f84bSDavid du Colombier 	 * Keep rp and seq at the base.  ipoput4 consumes rp for
3939ef1f84bSDavid du Colombier 	 * refragmentation.
3949ef1f84bSDavid du Colombier 	 */
3959ef1f84bSDavid du Colombier 	assert(bp->rp - bp->base >= sizeof(Metablock));
3969ef1f84bSDavid du Colombier 	m = (Metablock *)bp->base;
3979ef1f84bSDavid du Colombier 	m->rp  = bp->rp;
3989ef1f84bSDavid du Colombier 	m->seq = seq;
3999ef1f84bSDavid du Colombier 
4009ef1f84bSDavid du Colombier 	/*
4019ef1f84bSDavid du Colombier 	 * Here we make a decision what we're doing with the packet.  We're
4029ef1f84bSDavid du Colombier 	 * doing this w/o holding a lock which means that later on in the
4039ef1f84bSDavid du Colombier 	 * process we may discover we've done the wrong thing.  I don't want
4049ef1f84bSDavid du Colombier 	 * to call ipoput with the lock held.
4059ef1f84bSDavid du Colombier 	 */
4069ef1f84bSDavid du Colombier restart:
4079ef1f84bSDavid du Colombier 	suspended = grec->dlsusp;
4089ef1f84bSDavid du Colombier 	if(suspended){
4099ef1f84bSDavid du Colombier 		if(!canqlock(&grec->lock)){
4109ef1f84bSDavid du Colombier 			/*
4119ef1f84bSDavid du Colombier 			 * just give up.  too bad, we lose a packet.  this
4129ef1f84bSDavid du Colombier 			 * is just too hard and my brain already hurts.
4139ef1f84bSDavid du Colombier 			 */
4149ef1f84bSDavid du Colombier 			freeb(bp);
4159ef1f84bSDavid du Colombier 			return;
4169ef1f84bSDavid du Colombier 		}
4179ef1f84bSDavid du Colombier 
4189ef1f84bSDavid du Colombier 		if(!grec->dlsusp){
4199ef1f84bSDavid du Colombier 			/*
4209ef1f84bSDavid du Colombier 			 * suspend race.  We though we were suspended, but
4219ef1f84bSDavid du Colombier 			 * we really weren't.
4229ef1f84bSDavid du Colombier 			 */
4239ef1f84bSDavid du Colombier 			qunlock(&grec->lock);
4249ef1f84bSDavid du Colombier 			goto restart;
4259ef1f84bSDavid du Colombier 		}
4269ef1f84bSDavid du Colombier 
4279ef1f84bSDavid du Colombier 		/* Undo the incorrect ref count addition */
4289ef1f84bSDavid du Colombier 		addring(&grec->dlbuffered, bp);
4299ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
4309ef1f84bSDavid du Colombier 		return;
4319ef1f84bSDavid du Colombier 	}
4329ef1f84bSDavid du Colombier 
4339ef1f84bSDavid du Colombier 	/*
4349ef1f84bSDavid du Colombier 	 * When we get here, we're not suspended.  Proceed to send the
4359ef1f84bSDavid du Colombier 	 * packet.
4369ef1f84bSDavid du Colombier 	 */
4379ef1f84bSDavid du Colombier 	memmove(gre->src, grec->coa, sizeof gre->dst);
4389ef1f84bSDavid du Colombier 	memmove(gre->dst, grec->south, sizeof gre->dst);
4399ef1f84bSDavid du Colombier 
4409ef1f84bSDavid du Colombier 	/*
4419ef1f84bSDavid du Colombier 	 * Make sure the packet does not go away.
4429ef1f84bSDavid du Colombier 	 */
443*cbdb3703SDavid du Colombier 	ainc(&bp->ref);
4449ef1f84bSDavid du Colombier 	assert(bp->ref == 2);
4459ef1f84bSDavid du Colombier 
4469ef1f84bSDavid du Colombier 	ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
4479ef1f84bSDavid du Colombier 	grepdout++;
4489ef1f84bSDavid du Colombier 	grebdout += BLEN(bp);
4499ef1f84bSDavid du Colombier 
4509ef1f84bSDavid du Colombier 	/*
4519ef1f84bSDavid du Colombier 	 * Now make sure we didn't do the wrong thing.
4529ef1f84bSDavid du Colombier 	 */
4539ef1f84bSDavid du Colombier 	if(!canqlock(&grec->lock)){
4549ef1f84bSDavid du Colombier 		freeb(bp);		/* The packet just goes away */
4559ef1f84bSDavid du Colombier 		return;
4569ef1f84bSDavid du Colombier 	}
4579ef1f84bSDavid du Colombier 
4589ef1f84bSDavid du Colombier 	/* We did the right thing */
4599ef1f84bSDavid du Colombier 	addring(&grec->dlpending, bp);
4609ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
4619ef1f84bSDavid du Colombier }
4629ef1f84bSDavid du Colombier 
4639ef1f84bSDavid du Colombier static void
greuplink(Conv * c,Block * bp)4649ef1f84bSDavid du Colombier greuplink(Conv *c, Block *bp)
4659ef1f84bSDavid du Colombier {
4669ef1f84bSDavid du Colombier 	GREconv *grec;
4679ef1f84bSDavid du Colombier 	GREhdr *gre;
4689ef1f84bSDavid du Colombier 	ushort flags;
4699ef1f84bSDavid du Colombier 
4709ef1f84bSDavid du Colombier 	gre = (GREhdr *)bp->rp;
4719ef1f84bSDavid du Colombier 	if(gre->ttl == 1)
4729ef1f84bSDavid du Colombier 		return;
4739ef1f84bSDavid du Colombier 
4749ef1f84bSDavid du Colombier 	grec = c->ptcl;
4759ef1f84bSDavid du Colombier 	memmove(gre->src, grec->coa, sizeof gre->src);
4769ef1f84bSDavid du Colombier 	memmove(gre->dst, grec->north, sizeof gre->dst);
4779ef1f84bSDavid du Colombier 
4789ef1f84bSDavid du Colombier 	/*
4799ef1f84bSDavid du Colombier 	 * Add a key, if needed.
4809ef1f84bSDavid du Colombier 	 */
4819ef1f84bSDavid du Colombier 	if(grec->ulkey){
4829ef1f84bSDavid du Colombier 		flags = nhgets(gre->flags);
4839ef1f84bSDavid du Colombier 		if(flags & (GRE_cksum|GRE_routing)){
4849ef1f84bSDavid du Colombier 			print("%V routing info present.  Discarding packet\n",
4859ef1f84bSDavid du Colombier 				gre->src);
4869ef1f84bSDavid du Colombier 			freeb(bp);
4879ef1f84bSDavid du Colombier 			return;
4889ef1f84bSDavid du Colombier 		}
4899ef1f84bSDavid du Colombier 
4909ef1f84bSDavid du Colombier 		if((flags & GRE_key) == 0){
4919ef1f84bSDavid du Colombier 			/* Make room for the key */
4929ef1f84bSDavid du Colombier 			if(bp->rp - bp->base < sizeof(ulong)){
4939ef1f84bSDavid du Colombier 				print("%V can't add key\n", gre->src);
4949ef1f84bSDavid du Colombier 				freeb(bp);
4959ef1f84bSDavid du Colombier 				return;
4969ef1f84bSDavid du Colombier 			}
4979ef1f84bSDavid du Colombier 
4989ef1f84bSDavid du Colombier 			bp->rp -= 4;
4999ef1f84bSDavid du Colombier 			memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
5009ef1f84bSDavid du Colombier 
5019ef1f84bSDavid du Colombier 			gre = (GREhdr *)bp->rp;
5029ef1f84bSDavid du Colombier 			hnputs(gre->flags, flags | GRE_key);
5039ef1f84bSDavid du Colombier 		}
5049ef1f84bSDavid du Colombier 
5059ef1f84bSDavid du Colombier 		/* Add the key */
5069ef1f84bSDavid du Colombier 		hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
5079ef1f84bSDavid du Colombier 	}
5089ef1f84bSDavid du Colombier 
5099ef1f84bSDavid du Colombier 	if(!canqlock(&grec->lock)){
5109ef1f84bSDavid du Colombier 		freeb(bp);
5119ef1f84bSDavid du Colombier 		return;
5129ef1f84bSDavid du Colombier 	}
5139ef1f84bSDavid du Colombier 
5149ef1f84bSDavid du Colombier 	if(grec->ulsusp)
5159ef1f84bSDavid du Colombier 		addring(&grec->ulbuffered, bp);
5169ef1f84bSDavid du Colombier 	else{
5179ef1f84bSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
5189ef1f84bSDavid du Colombier 		grepuout++;
5199ef1f84bSDavid du Colombier 		grebuout += BLEN(bp);
5209ef1f84bSDavid du Colombier 	}
5219ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
5229ef1f84bSDavid du Colombier }
5239ef1f84bSDavid du Colombier 
5249ef1f84bSDavid du Colombier static void
greiput(Proto * proto,Ipifc *,Block * bp)5259ef1f84bSDavid du Colombier greiput(Proto *proto, Ipifc *, Block *bp)
5269ef1f84bSDavid du Colombier {
5279ef1f84bSDavid du Colombier 	int len, hdrlen;
5289ef1f84bSDavid du Colombier 	ushort eproto, flags;
5299ef1f84bSDavid du Colombier 	uchar raddr[IPaddrlen];
5309ef1f84bSDavid du Colombier 	Conv *c, **p;
5319ef1f84bSDavid du Colombier 	GREconv *grec;
5329ef1f84bSDavid du Colombier 	GREhdr *gre;
5339ef1f84bSDavid du Colombier 	GREpriv *gpriv;
5349ef1f84bSDavid du Colombier 	Ip4hdr *ip;
5359ef1f84bSDavid du Colombier 
5369ef1f84bSDavid du Colombier 	/*
5379ef1f84bSDavid du Colombier 	 * We don't want to deal with block lists.  Ever.  The problem is
5389ef1f84bSDavid du Colombier 	 * that when the block is forwarded, devether.c puts the block into
5399ef1f84bSDavid du Colombier 	 * a queue that also uses ->next.  Just do not use ->next here!
5409ef1f84bSDavid du Colombier 	 */
5419ef1f84bSDavid du Colombier 	if(bp->next){
5429ef1f84bSDavid du Colombier 		len = blocklen(bp);
5439ef1f84bSDavid du Colombier 		bp  = pullupblock(bp, len);
5449ef1f84bSDavid du Colombier 		assert(BLEN(bp) == len && bp->next == nil);
5459ef1f84bSDavid du Colombier 	}
5469ef1f84bSDavid du Colombier 
5479ef1f84bSDavid du Colombier 	gre = (GREhdr *)bp->rp;
5489ef1f84bSDavid du Colombier 	if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
5499ef1f84bSDavid du Colombier 		freeb(bp);
5509ef1f84bSDavid du Colombier 		return;
5519ef1f84bSDavid du Colombier 	}
5529ef1f84bSDavid du Colombier 
5539ef1f84bSDavid du Colombier 	v4tov6(raddr, gre->src);
5549ef1f84bSDavid du Colombier 	eproto = nhgets(gre->eproto);
5559ef1f84bSDavid du Colombier 	flags  = nhgets(gre->flags);
5569ef1f84bSDavid du Colombier 	hdrlen = sizeof(GREhdr);
5579ef1f84bSDavid du Colombier 
5589ef1f84bSDavid du Colombier 	if(flags & GRE_cksum)
5599ef1f84bSDavid du Colombier 		hdrlen += 2;
5609ef1f84bSDavid du Colombier 	if(flags & GRE_routing){
5619ef1f84bSDavid du Colombier 		print("%I routing info present.  Discarding packet\n", raddr);
5629ef1f84bSDavid du Colombier 		freeb(bp);
5639ef1f84bSDavid du Colombier 		return;
5649ef1f84bSDavid du Colombier 	}
5659ef1f84bSDavid du Colombier 	if(flags & (GRE_cksum|GRE_routing))
5669ef1f84bSDavid du Colombier 		hdrlen += 2;			/* Offset field */
5679ef1f84bSDavid du Colombier 	if(flags & GRE_key)
5689ef1f84bSDavid du Colombier 		hdrlen += 4;
5699ef1f84bSDavid du Colombier 	if(flags & GRE_seq)
5709ef1f84bSDavid du Colombier 		hdrlen += 4;
5719ef1f84bSDavid du Colombier 
5729ef1f84bSDavid du Colombier 	if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
5739ef1f84bSDavid du Colombier 		print("greretunnel: packet too short (s=%V d=%V)\n",
5749ef1f84bSDavid du Colombier 			gre->src, gre->dst);
5759ef1f84bSDavid du Colombier 		freeb(bp);
5769ef1f84bSDavid du Colombier 		return;
5779ef1f84bSDavid du Colombier 	}
5789ef1f84bSDavid du Colombier 	ip = (Ip4hdr *)(bp->rp + hdrlen);
5799ef1f84bSDavid du Colombier 
5809ef1f84bSDavid du Colombier 	qlock(proto);
5819ef1f84bSDavid du Colombier 	/*
5829ef1f84bSDavid du Colombier 	 * Look for a conversation structure for this port and address, or
5839ef1f84bSDavid du Colombier 	 * match the retunnel part, or match on the raw flag.
5849ef1f84bSDavid du Colombier 	 */
5859ef1f84bSDavid du Colombier 	for(p = proto->conv; *p; p++) {
5869ef1f84bSDavid du Colombier 		c = *p;
5879ef1f84bSDavid du Colombier 
5889ef1f84bSDavid du Colombier 		if(c->inuse == 0)
5899ef1f84bSDavid du Colombier 			continue;
5909ef1f84bSDavid du Colombier 
5919ef1f84bSDavid du Colombier 		/*
5929ef1f84bSDavid du Colombier 		 * Do not stop this session - blocking here
5939ef1f84bSDavid du Colombier 		 * implies that etherread is blocked.
5949ef1f84bSDavid du Colombier 		 */
5959ef1f84bSDavid du Colombier 		grec = c->ptcl;
5969ef1f84bSDavid du Colombier 		if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
5979ef1f84bSDavid du Colombier 			grepdin++;
5989ef1f84bSDavid du Colombier 			grebdin += BLEN(bp);
5999ef1f84bSDavid du Colombier 			gredownlink(c, bp);
6009ef1f84bSDavid du Colombier 			qunlock(proto);
6019ef1f84bSDavid du Colombier 			return;
6029ef1f84bSDavid du Colombier 		}
6039ef1f84bSDavid du Colombier 
6049ef1f84bSDavid du Colombier 		if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
6059ef1f84bSDavid du Colombier 			grepuin++;
6069ef1f84bSDavid du Colombier 			grebuin += BLEN(bp);
6079ef1f84bSDavid du Colombier 			greuplink(c, bp);
6089ef1f84bSDavid du Colombier 			qunlock(proto);
6099ef1f84bSDavid du Colombier 			return;
6109ef1f84bSDavid du Colombier 		}
6119ef1f84bSDavid du Colombier 	}
6129ef1f84bSDavid du Colombier 
6139ef1f84bSDavid du Colombier 	/*
6149ef1f84bSDavid du Colombier 	 * when we get here, none of the forwarding tunnels matched.  now
6159ef1f84bSDavid du Colombier 	 * try to match on raw and conversational sessions.
6169ef1f84bSDavid du Colombier 	 */
6179ef1f84bSDavid du Colombier 	for(c = nil, p = proto->conv; *p; p++) {
6189ef1f84bSDavid du Colombier 		c = *p;
6199ef1f84bSDavid du Colombier 
6209ef1f84bSDavid du Colombier 		if(c->inuse == 0)
6219ef1f84bSDavid du Colombier 			continue;
6229ef1f84bSDavid du Colombier 
6239ef1f84bSDavid du Colombier 		/*
6249ef1f84bSDavid du Colombier 		 * Do not stop this session - blocking here
6259ef1f84bSDavid du Colombier 		 * implies that etherread is blocked.
6269ef1f84bSDavid du Colombier 		 */
6279ef1f84bSDavid du Colombier 		grec = c->ptcl;
6289ef1f84bSDavid du Colombier 		if(c->rport == eproto &&
6299ef1f84bSDavid du Colombier 		    (grec->raw || ipcmp(c->raddr, raddr) == 0))
6309ef1f84bSDavid du Colombier 			break;
6319ef1f84bSDavid du Colombier 	}
6329ef1f84bSDavid du Colombier 
6339ef1f84bSDavid du Colombier 	qunlock(proto);
6349ef1f84bSDavid du Colombier 
6359ef1f84bSDavid du Colombier 	if(*p == nil){
6369ef1f84bSDavid du Colombier 		freeb(bp);
6379ef1f84bSDavid du Colombier 		return;
6389ef1f84bSDavid du Colombier 	}
6399ef1f84bSDavid du Colombier 
6409ef1f84bSDavid du Colombier 	/*
6419ef1f84bSDavid du Colombier 	 * Trim the packet down to data size
6429ef1f84bSDavid du Colombier 	 */
6439ef1f84bSDavid du Colombier 	len = nhgets(gre->len) - GRE_IPONLY;
6449ef1f84bSDavid du Colombier 	if(len < GRE_IPPLUSGRE){
6459ef1f84bSDavid du Colombier 		freeb(bp);
6469ef1f84bSDavid du Colombier 		return;
6479ef1f84bSDavid du Colombier 	}
6489ef1f84bSDavid du Colombier 
6499ef1f84bSDavid du Colombier 	bp = trimblock(bp, GRE_IPONLY, len);
6509ef1f84bSDavid du Colombier 	if(bp == nil){
6519ef1f84bSDavid du Colombier 		gpriv = proto->priv;
6529ef1f84bSDavid du Colombier 		gpriv->lenerr++;
6539ef1f84bSDavid du Colombier 		return;
6549ef1f84bSDavid du Colombier 	}
6559ef1f84bSDavid du Colombier 
6569ef1f84bSDavid du Colombier 	/*
6579ef1f84bSDavid du Colombier 	 *  Can't delimit packet so pull it all into one block.
6589ef1f84bSDavid du Colombier 	 */
6599ef1f84bSDavid du Colombier 	if(qlen(c->rq) > GREqlen)
6609ef1f84bSDavid du Colombier 		freeb(bp);
6619ef1f84bSDavid du Colombier 	else{
6629ef1f84bSDavid du Colombier 		bp = concatblock(bp);
6639ef1f84bSDavid du Colombier 		if(bp == 0)
6649ef1f84bSDavid du Colombier 			panic("greiput");
6659ef1f84bSDavid du Colombier 		qpass(c->rq, bp);
6669ef1f84bSDavid du Colombier 	}
6679ef1f84bSDavid du Colombier }
6689ef1f84bSDavid du Colombier 
6699ef1f84bSDavid du Colombier int
grestats(Proto * gre,char * buf,int len)6709ef1f84bSDavid du Colombier grestats(Proto *gre, char *buf, int len)
6719ef1f84bSDavid du Colombier {
6729ef1f84bSDavid du Colombier 	GREpriv *gpriv;
6739ef1f84bSDavid du Colombier 
6749ef1f84bSDavid du Colombier 	gpriv = gre->priv;
6759ef1f84bSDavid du Colombier 	return snprint(buf, len,
6769ef1f84bSDavid du Colombier 		"gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
6779ef1f84bSDavid du Colombier 		grepdin, grepdout, grepuin, grepuout,
6789ef1f84bSDavid du Colombier 		grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
6799ef1f84bSDavid du Colombier }
6809ef1f84bSDavid du Colombier 
6819ef1f84bSDavid du Colombier static char *
grectlraw(Conv * c,int,char **)6829ef1f84bSDavid du Colombier grectlraw(Conv *c, int, char **)
6839ef1f84bSDavid du Colombier {
6849ef1f84bSDavid du Colombier 	GREconv *grec;
6859ef1f84bSDavid du Colombier 
6869ef1f84bSDavid du Colombier 	grec = c->ptcl;
6879ef1f84bSDavid du Colombier 	grec->raw = 1;
6889ef1f84bSDavid du Colombier 	return nil;
6899ef1f84bSDavid du Colombier }
6909ef1f84bSDavid du Colombier 
6919ef1f84bSDavid du Colombier static char *
grectlcooked(Conv * c,int,char **)6929ef1f84bSDavid du Colombier grectlcooked(Conv *c, int, char **)
6939ef1f84bSDavid du Colombier {
6949ef1f84bSDavid du Colombier 	GREconv *grec;
6959ef1f84bSDavid du Colombier 
6969ef1f84bSDavid du Colombier 	grec = c->ptcl;
6979ef1f84bSDavid du Colombier 	grec->raw = 0;
6989ef1f84bSDavid du Colombier 	return nil;
6999ef1f84bSDavid du Colombier }
7009ef1f84bSDavid du Colombier 
7019ef1f84bSDavid du Colombier static char *
grectlretunnel(Conv * c,int,char ** argv)7029ef1f84bSDavid du Colombier grectlretunnel(Conv *c, int, char **argv)
7039ef1f84bSDavid du Colombier {
7049ef1f84bSDavid du Colombier 	GREconv *grec;
7059ef1f84bSDavid du Colombier 	uchar ipaddr[4];
7069ef1f84bSDavid du Colombier 
7079ef1f84bSDavid du Colombier 	grec = c->ptcl;
7089ef1f84bSDavid du Colombier 	if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
7099ef1f84bSDavid du Colombier 		return "tunnel already set up";
7109ef1f84bSDavid du Colombier 
7119ef1f84bSDavid du Colombier 	v4parseip(ipaddr, argv[1]);
7129ef1f84bSDavid du Colombier 	if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
7139ef1f84bSDavid du Colombier 		return "bad hoa";
7149ef1f84bSDavid du Colombier 	memmove(grec->hoa, ipaddr, sizeof grec->hoa);
7159ef1f84bSDavid du Colombier 	v4parseip(ipaddr, argv[2]);
7169ef1f84bSDavid du Colombier 	memmove(grec->north, ipaddr, sizeof grec->north);
7179ef1f84bSDavid du Colombier 	v4parseip(ipaddr, argv[3]);
7189ef1f84bSDavid du Colombier 	memmove(grec->south, ipaddr, sizeof grec->south);
7199ef1f84bSDavid du Colombier 	v4parseip(ipaddr, argv[4]);
7209ef1f84bSDavid du Colombier 	memmove(grec->coa, ipaddr, sizeof grec->coa);
7219ef1f84bSDavid du Colombier 	grec->ulsusp = 1;
7229ef1f84bSDavid du Colombier 	grec->dlsusp = 0;
7239ef1f84bSDavid du Colombier 
7249ef1f84bSDavid du Colombier 	return nil;
7259ef1f84bSDavid du Colombier }
7269ef1f84bSDavid du Colombier 
7279ef1f84bSDavid du Colombier static char *
grectlreport(Conv * c,int,char ** argv)7289ef1f84bSDavid du Colombier grectlreport(Conv *c, int, char **argv)
7299ef1f84bSDavid du Colombier {
7309ef1f84bSDavid du Colombier 	ulong seq;
7319ef1f84bSDavid du Colombier 	Block *bp;
7329ef1f84bSDavid du Colombier 	Bring *r;
7339ef1f84bSDavid du Colombier 	GREconv *grec;
7349ef1f84bSDavid du Colombier 	Metablock *m;
7359ef1f84bSDavid du Colombier 
7369ef1f84bSDavid du Colombier 	grec = c->ptcl;
7379ef1f84bSDavid du Colombier 	seq  = strtoul(argv[1], nil, 0);
7389ef1f84bSDavid du Colombier 
7399ef1f84bSDavid du Colombier 	qlock(&grec->lock);
7409ef1f84bSDavid du Colombier 	r = &grec->dlpending;
7419ef1f84bSDavid du Colombier 	while(r->produced - r->consumed > 0){
7429ef1f84bSDavid du Colombier 		bp = r->ring[r->consumed & Ringmask];
7439ef1f84bSDavid du Colombier 
7449ef1f84bSDavid du Colombier 		assert(bp && bp->rp - bp->base >= sizeof(Metablock));
7459ef1f84bSDavid du Colombier 		m = (Metablock *)bp->base;
7469ef1f84bSDavid du Colombier 		if((long)(seq - m->seq) <= 0)
7479ef1f84bSDavid du Colombier 			break;
7489ef1f84bSDavid du Colombier 
7499ef1f84bSDavid du Colombier 		r->ring[r->consumed & Ringmask] = nil;
7509ef1f84bSDavid du Colombier 		r->consumed++;
7519ef1f84bSDavid du Colombier 
7529ef1f84bSDavid du Colombier 		freeb(bp);
7539ef1f84bSDavid du Colombier 	}
7549ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
7559ef1f84bSDavid du Colombier 	return nil;
7569ef1f84bSDavid du Colombier }
7579ef1f84bSDavid du Colombier 
7589ef1f84bSDavid du Colombier static char *
grectldlsuspend(Conv * c,int,char **)7599ef1f84bSDavid du Colombier grectldlsuspend(Conv *c, int, char **)
7609ef1f84bSDavid du Colombier {
7619ef1f84bSDavid du Colombier 	GREconv *grec;
7629ef1f84bSDavid du Colombier 
7639ef1f84bSDavid du Colombier 	grec = c->ptcl;
7649ef1f84bSDavid du Colombier 	if(grec->dlsusp)
7659ef1f84bSDavid du Colombier 		return "already suspended";
7669ef1f84bSDavid du Colombier 
7679ef1f84bSDavid du Colombier 	grec->dlsusp = 1;
7689ef1f84bSDavid du Colombier 	return nil;
7699ef1f84bSDavid du Colombier }
7709ef1f84bSDavid du Colombier 
7719ef1f84bSDavid du Colombier static char *
grectlulsuspend(Conv * c,int,char **)7729ef1f84bSDavid du Colombier grectlulsuspend(Conv *c, int, char **)
7739ef1f84bSDavid du Colombier {
7749ef1f84bSDavid du Colombier 	GREconv *grec;
7759ef1f84bSDavid du Colombier 
7769ef1f84bSDavid du Colombier 	grec = c->ptcl;
7779ef1f84bSDavid du Colombier 	if(grec->ulsusp)
7789ef1f84bSDavid du Colombier 		return "already suspended";
7799ef1f84bSDavid du Colombier 
7809ef1f84bSDavid du Colombier 	grec->ulsusp = 1;
7819ef1f84bSDavid du Colombier 	return nil;
7829ef1f84bSDavid du Colombier }
7839ef1f84bSDavid du Colombier 
7849ef1f84bSDavid du Colombier static char *
grectldlresume(Conv * c,int,char **)7859ef1f84bSDavid du Colombier grectldlresume(Conv *c, int, char **)
7869ef1f84bSDavid du Colombier {
7879ef1f84bSDavid du Colombier 	GREconv *grec;
7889ef1f84bSDavid du Colombier 	GREhdr *gre;
7899ef1f84bSDavid du Colombier 	Block *bp;
7909ef1f84bSDavid du Colombier 
7919ef1f84bSDavid du Colombier 	grec = c->ptcl;
7929ef1f84bSDavid du Colombier 
7939ef1f84bSDavid du Colombier 	qlock(&grec->lock);
7949ef1f84bSDavid du Colombier 	if(!grec->dlsusp){
7959ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
7969ef1f84bSDavid du Colombier 		return "not suspended";
7979ef1f84bSDavid du Colombier 	}
7989ef1f84bSDavid du Colombier 
7999ef1f84bSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil){
8009ef1f84bSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8019ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
8029ef1f84bSDavid du Colombier 
8039ef1f84bSDavid du Colombier 		/*
8049ef1f84bSDavid du Colombier 		 * Make sure the packet does not go away.
8059ef1f84bSDavid du Colombier 		 */
806*cbdb3703SDavid du Colombier 		ainc(&bp->ref);
8079ef1f84bSDavid du Colombier 		assert(bp->ref == 2);
8089ef1f84bSDavid du Colombier 
8099ef1f84bSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8109ef1f84bSDavid du Colombier 
8119ef1f84bSDavid du Colombier 		qlock(&grec->lock);
8129ef1f84bSDavid du Colombier 		addring(&grec->dlpending, bp);
8139ef1f84bSDavid du Colombier 	}
8149ef1f84bSDavid du Colombier 	grec->dlsusp = 0;
8159ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
8169ef1f84bSDavid du Colombier 	return nil;
8179ef1f84bSDavid du Colombier }
8189ef1f84bSDavid du Colombier 
8199ef1f84bSDavid du Colombier static char *
grectlulresume(Conv * c,int,char **)8209ef1f84bSDavid du Colombier grectlulresume(Conv *c, int, char **)
8219ef1f84bSDavid du Colombier {
8229ef1f84bSDavid du Colombier 	GREconv *grec;
8239ef1f84bSDavid du Colombier 	GREhdr *gre;
8249ef1f84bSDavid du Colombier 	Block *bp;
8259ef1f84bSDavid du Colombier 
8269ef1f84bSDavid du Colombier 	grec = c->ptcl;
8279ef1f84bSDavid du Colombier 
8289ef1f84bSDavid du Colombier 	qlock(&grec->lock);
8299ef1f84bSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil){
8309ef1f84bSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8319ef1f84bSDavid du Colombier 
8329ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
8339ef1f84bSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8349ef1f84bSDavid du Colombier 		qlock(&grec->lock);
8359ef1f84bSDavid du Colombier 	}
8369ef1f84bSDavid du Colombier 	grec->ulsusp = 0;
8379ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
8389ef1f84bSDavid du Colombier 	return nil;
8399ef1f84bSDavid du Colombier }
8409ef1f84bSDavid du Colombier 
8419ef1f84bSDavid du Colombier static char *
grectlforward(Conv * c,int,char ** argv)8429ef1f84bSDavid du Colombier grectlforward(Conv *c, int, char **argv)
8439ef1f84bSDavid du Colombier {
8449ef1f84bSDavid du Colombier 	int len;
8459ef1f84bSDavid du Colombier 	Block *bp, *nbp;
8469ef1f84bSDavid du Colombier 	GREconv *grec;
8479ef1f84bSDavid du Colombier 	GREhdr *gre;
8489ef1f84bSDavid du Colombier 	Metablock *m;
8499ef1f84bSDavid du Colombier 
8509ef1f84bSDavid du Colombier 	grec = c->ptcl;
8519ef1f84bSDavid du Colombier 
8529ef1f84bSDavid du Colombier 	v4parseip(grec->south, argv[1]);
8539ef1f84bSDavid du Colombier 	memmove(grec->north, grec->south, sizeof grec->north);
8549ef1f84bSDavid du Colombier 
8559ef1f84bSDavid du Colombier 	qlock(&grec->lock);
8569ef1f84bSDavid du Colombier 	if(!grec->dlsusp){
8579ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
8589ef1f84bSDavid du Colombier 		return "not suspended";
8599ef1f84bSDavid du Colombier 	}
8609ef1f84bSDavid du Colombier 	grec->dlsusp = 0;
8619ef1f84bSDavid du Colombier 	grec->ulsusp = 0;
8629ef1f84bSDavid du Colombier 
8639ef1f84bSDavid du Colombier 	while((bp = getring(&grec->dlpending)) != nil){
8649ef1f84bSDavid du Colombier 
8659ef1f84bSDavid du Colombier 		assert(bp->rp - bp->base >= sizeof(Metablock));
8669ef1f84bSDavid du Colombier 		m = (Metablock *)bp->base;
8679ef1f84bSDavid du Colombier 		assert(m->rp >= bp->base && m->rp < bp->lim);
8689ef1f84bSDavid du Colombier 
8699ef1f84bSDavid du Colombier 		/*
8709ef1f84bSDavid du Colombier 		 * If the packet is still held inside the IP transmit
8719ef1f84bSDavid du Colombier 		 * system, make a copy of the packet first.
8729ef1f84bSDavid du Colombier 		 */
8739ef1f84bSDavid du Colombier 		if(bp->ref > 1){
8749ef1f84bSDavid du Colombier 			len = bp->wp - m->rp;
8759ef1f84bSDavid du Colombier 			nbp = allocb(len);
8769ef1f84bSDavid du Colombier 			memmove(nbp->wp, m->rp, len);
8779ef1f84bSDavid du Colombier 			nbp->wp += len;
8789ef1f84bSDavid du Colombier 			freeb(bp);
8799ef1f84bSDavid du Colombier 			bp  = nbp;
8809ef1f84bSDavid du Colombier 		}
8819ef1f84bSDavid du Colombier 		else{
8829ef1f84bSDavid du Colombier 			/* Patch up rp */
8839ef1f84bSDavid du Colombier 			bp->rp = m->rp;
8849ef1f84bSDavid du Colombier 		}
8859ef1f84bSDavid du Colombier 
8869ef1f84bSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8879ef1f84bSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
8889ef1f84bSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
8899ef1f84bSDavid du Colombier 
8909ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
8919ef1f84bSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8929ef1f84bSDavid du Colombier 		qlock(&grec->lock);
8939ef1f84bSDavid du Colombier 	}
8949ef1f84bSDavid du Colombier 
8959ef1f84bSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil){
8969ef1f84bSDavid du Colombier 		gre = (GREhdr *)bp->rp;
8979ef1f84bSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
8989ef1f84bSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
8999ef1f84bSDavid du Colombier 
9009ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
9019ef1f84bSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9029ef1f84bSDavid du Colombier 		qlock(&grec->lock);
9039ef1f84bSDavid du Colombier 	}
9049ef1f84bSDavid du Colombier 
9059ef1f84bSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil){
9069ef1f84bSDavid du Colombier 		gre = (GREhdr *)bp->rp;
9079ef1f84bSDavid du Colombier 
9089ef1f84bSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
9099ef1f84bSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
9109ef1f84bSDavid du Colombier 
9119ef1f84bSDavid du Colombier 		qunlock(&grec->lock);
9129ef1f84bSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9139ef1f84bSDavid du Colombier 		qlock(&grec->lock);
9149ef1f84bSDavid du Colombier 	}
9159ef1f84bSDavid du Colombier 	qunlock(&grec->lock);
9169ef1f84bSDavid du Colombier 	return nil;
9179ef1f84bSDavid du Colombier }
9189ef1f84bSDavid du Colombier 
9199ef1f84bSDavid du Colombier static char *
grectlulkey(Conv * c,int,char ** argv)9209ef1f84bSDavid du Colombier grectlulkey(Conv *c, int, char **argv)
9219ef1f84bSDavid du Colombier {
9229ef1f84bSDavid du Colombier 	GREconv *grec;
9239ef1f84bSDavid du Colombier 
9249ef1f84bSDavid du Colombier 	grec = c->ptcl;
9259ef1f84bSDavid du Colombier 	grec->ulkey = strtoul(argv[1], nil, 0);
9269ef1f84bSDavid du Colombier 	return nil;
9279ef1f84bSDavid du Colombier }
9289ef1f84bSDavid du Colombier 
9299ef1f84bSDavid du Colombier char *
grectl(Conv * c,char ** f,int n)9309ef1f84bSDavid du Colombier grectl(Conv *c, char **f, int n)
9319ef1f84bSDavid du Colombier {
9329ef1f84bSDavid du Colombier 	int i;
9339ef1f84bSDavid du Colombier 
9349ef1f84bSDavid du Colombier 	if(n < 1)
9359ef1f84bSDavid du Colombier 		return "too few arguments";
9369ef1f84bSDavid du Colombier 
9379ef1f84bSDavid du Colombier 	for(i = 0; i < Ncmds; i++)
9389ef1f84bSDavid du Colombier 		if(strcmp(f[0], grectls[i].cmd) == 0)
9399ef1f84bSDavid du Colombier 			break;
9409ef1f84bSDavid du Colombier 
9419ef1f84bSDavid du Colombier 	if(i == Ncmds)
9429ef1f84bSDavid du Colombier 		return "no such command";
9439ef1f84bSDavid du Colombier 	if(grectls[i].argc != 0 && grectls[i].argc != n)
9449ef1f84bSDavid du Colombier 		return "incorrect number of arguments";
9459ef1f84bSDavid du Colombier 
9469ef1f84bSDavid du Colombier 	return grectls[i].f(c, n, f);
9479ef1f84bSDavid du Colombier }
9489ef1f84bSDavid du Colombier 
9499ef1f84bSDavid du Colombier void
greinit(Fs * fs)9509ef1f84bSDavid du Colombier greinit(Fs *fs)
9519ef1f84bSDavid du Colombier {
9529ef1f84bSDavid du Colombier 	Proto *gre;
9539ef1f84bSDavid du Colombier 
9549ef1f84bSDavid du Colombier 	gre = smalloc(sizeof(Proto));
9559ef1f84bSDavid du Colombier 	gre->priv = smalloc(sizeof(GREpriv));
9569ef1f84bSDavid du Colombier 	gre->name = "gre";
9579ef1f84bSDavid du Colombier 	gre->connect = greconnect;
9589ef1f84bSDavid du Colombier 	gre->announce = greannounce;
9599ef1f84bSDavid du Colombier 	gre->state = grestate;
9609ef1f84bSDavid du Colombier 	gre->create = grecreate;
9619ef1f84bSDavid du Colombier 	gre->close = greclose;
9629ef1f84bSDavid du Colombier 	gre->rcv = greiput;
9639ef1f84bSDavid du Colombier 	gre->ctl = grectl;
9649ef1f84bSDavid du Colombier 	gre->advise = nil;
9659ef1f84bSDavid du Colombier 	gre->stats = grestats;
9669ef1f84bSDavid du Colombier 	gre->ipproto = IP_GREPROTO;
9679ef1f84bSDavid du Colombier 	gre->nc = 64;
9689ef1f84bSDavid du Colombier 	gre->ptclsize = sizeof(GREconv);
9699ef1f84bSDavid du Colombier 
9709ef1f84bSDavid du Colombier 	Fsproto(fs, gre);
9719ef1f84bSDavid du Colombier }
972