xref: /plan9/sys/src/9/ip/gre.c (revision 7ec5746a5244cc505568e3d45ab9d5421abbdc7d)
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 
13*7ec5746aSDavid 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,
21*7ec5746aSDavid du Colombier 
22*7ec5746aSDavid du Colombier 	K		= 1024,
23*7ec5746aSDavid du Colombier 	GREqlen		= 256 * K,
24*7ec5746aSDavid du Colombier 
25*7ec5746aSDavid du Colombier 	GRE_cksum	= 0x8000,
26*7ec5746aSDavid du Colombier 	GRE_routing	= 0x4000,
27*7ec5746aSDavid du Colombier 	GRE_key		= 0x2000,
28*7ec5746aSDavid du Colombier 	GRE_seq		= 0x1000,
29*7ec5746aSDavid du Colombier 
30*7ec5746aSDavid du Colombier 	Nring		= 1 << 10,	/* power of two, please */
31*7ec5746aSDavid du Colombier 	Ringmask	= Nring - 1,
32*7ec5746aSDavid du Colombier 
33*7ec5746aSDavid du Colombier 	GREctlraw	= 0,
34*7ec5746aSDavid du Colombier 	GREctlcooked,
35*7ec5746aSDavid du Colombier 	GREctlretunnel,
36*7ec5746aSDavid du Colombier 	GREctlreport,
37*7ec5746aSDavid du Colombier 	GREctldlsuspend,
38*7ec5746aSDavid du Colombier 	GREctlulsuspend,
39*7ec5746aSDavid du Colombier 	GREctldlresume,
40*7ec5746aSDavid du Colombier 	GREctlulresume,
41*7ec5746aSDavid du Colombier 	GREctlforward,
42*7ec5746aSDavid du Colombier 	GREctlulkey,
43*7ec5746aSDavid du Colombier 	Ncmds,
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier 
46*7ec5746aSDavid du Colombier typedef struct GREhdr GREhdr;
47*7ec5746aSDavid 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 */
54*7ec5746aSDavid 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 */
63*7ec5746aSDavid du Colombier };
647dd7cddfSDavid du Colombier 
657dd7cddfSDavid du Colombier typedef struct GREpriv GREpriv;
66*7ec5746aSDavid du Colombier struct GREpriv{
677dd7cddfSDavid du Colombier 	/* non-MIB stats */
687dd7cddfSDavid du Colombier 	ulong	lenerr;			/* short packet */
697dd7cddfSDavid du Colombier };
707dd7cddfSDavid du Colombier 
71*7ec5746aSDavid du Colombier typedef struct Bring	Bring;
72*7ec5746aSDavid du Colombier struct Bring{
73*7ec5746aSDavid du Colombier 	Block	*ring[Nring];
74*7ec5746aSDavid du Colombier 	long	produced;
75*7ec5746aSDavid du Colombier 	long	consumed;
76*7ec5746aSDavid du Colombier };
77*7ec5746aSDavid du Colombier 
78*7ec5746aSDavid du Colombier typedef struct GREconv	GREconv;
79*7ec5746aSDavid du Colombier struct GREconv{
80*7ec5746aSDavid du Colombier 	int	raw;
81*7ec5746aSDavid du Colombier 
82*7ec5746aSDavid du Colombier 	/* Retunnelling information.  v4 only */
83*7ec5746aSDavid du Colombier 	uchar	north[4];			/* HA */
84*7ec5746aSDavid du Colombier 	uchar	south[4];			/* Base station */
85*7ec5746aSDavid du Colombier 	uchar	hoa[4];				/* Home address */
86*7ec5746aSDavid du Colombier 	uchar	coa[4];				/* Careof address */
87*7ec5746aSDavid du Colombier 	ulong	seq;				/* Current sequence # */
88*7ec5746aSDavid du Colombier 	int	dlsusp;				/* Downlink suspended? */
89*7ec5746aSDavid du Colombier 	int	ulsusp;				/* Uplink suspended? */
90*7ec5746aSDavid du Colombier 	ulong	ulkey;				/* GRE key */
91*7ec5746aSDavid du Colombier 
92*7ec5746aSDavid du Colombier 	QLock	lock;				/* Lock for rings */
93*7ec5746aSDavid du Colombier 	Bring	dlpending;			/* Ring of pending packets */
94*7ec5746aSDavid du Colombier 	Bring	dlbuffered;			/* Received while suspended */
95*7ec5746aSDavid du Colombier 	Bring	ulbuffered;			/* Received while suspended */
96*7ec5746aSDavid du Colombier };
97*7ec5746aSDavid du Colombier 
98*7ec5746aSDavid du Colombier typedef struct Metablock Metablock;
99*7ec5746aSDavid du Colombier struct Metablock{
100*7ec5746aSDavid du Colombier 	uchar	*rp;
101*7ec5746aSDavid du Colombier 	ulong	seq;
102*7ec5746aSDavid du Colombier };
103*7ec5746aSDavid du Colombier 
104*7ec5746aSDavid du Colombier static char *grectlcooked(Conv *, int, char **);
105*7ec5746aSDavid du Colombier static char *grectldlresume(Conv *, int, char **);
106*7ec5746aSDavid du Colombier static char *grectldlsuspend(Conv *, int, char **);
107*7ec5746aSDavid du Colombier static char *grectlforward(Conv *, int, char **);
108*7ec5746aSDavid du Colombier static char *grectlraw(Conv *, int, char **);
109*7ec5746aSDavid du Colombier static char *grectlreport(Conv *, int, char **);
110*7ec5746aSDavid du Colombier static char *grectlretunnel(Conv *, int, char **);
111*7ec5746aSDavid du Colombier static char *grectlulkey(Conv *, int, char **);
112*7ec5746aSDavid du Colombier static char *grectlulresume(Conv *, int, char **);
113*7ec5746aSDavid du Colombier static char *grectlulsuspend(Conv *, int, char **);
114*7ec5746aSDavid du Colombier 
115*7ec5746aSDavid du Colombier static struct{
116*7ec5746aSDavid du Colombier 	char	*cmd;
117*7ec5746aSDavid du Colombier 	int	argc;
118*7ec5746aSDavid du Colombier 	char	*(*f)(Conv *, int, char **);
119*7ec5746aSDavid du Colombier } grectls[Ncmds] = {
120*7ec5746aSDavid du Colombier [GREctlraw]	=	{	"raw",		1,	grectlraw,	},
121*7ec5746aSDavid du Colombier [GREctlcooked]	=	{	"cooked",	1,	grectlcooked,	},
122*7ec5746aSDavid du Colombier [GREctlretunnel]=	{	"retunnel",	5,	grectlretunnel,	},
123*7ec5746aSDavid du Colombier [GREctlreport]	=	{	"report",	2,	grectlreport,	},
124*7ec5746aSDavid du Colombier [GREctldlsuspend]=	{	"dlsuspend",	1,	grectldlsuspend,},
125*7ec5746aSDavid du Colombier [GREctlulsuspend]=	{	"ulsuspend",	1,	grectlulsuspend,},
126*7ec5746aSDavid du Colombier [GREctldlresume]=	{	"dlresume",	1,	grectldlresume,	},
127*7ec5746aSDavid du Colombier [GREctlulresume]=	{	"ulresume",	1,	grectlulresume,	},
128*7ec5746aSDavid du Colombier [GREctlforward]	=	{	"forward",	2,	grectlforward,	},
129*7ec5746aSDavid du Colombier [GREctlulkey]	=	{	"ulkey",	2,	grectlulkey,	},
130*7ec5746aSDavid du Colombier };
131*7ec5746aSDavid du Colombier 
132*7ec5746aSDavid du Colombier static uchar nulladdr[4];
133*7ec5746aSDavid du Colombier static char *sessend = "session end";
134*7ec5746aSDavid du Colombier 
135e6c6b7f8SDavid du Colombier static void grekick(void *x, Block *bp);
136*7ec5746aSDavid du Colombier static char *gresetup(Conv *, char *, char *, char *);
137*7ec5746aSDavid du Colombier 
138*7ec5746aSDavid du Colombier ulong grepdin, grepdout, grebdin, grebdout;
139*7ec5746aSDavid du Colombier ulong grepuin, grepuout, grebuin, grebuout;
140*7ec5746aSDavid du Colombier 
141*7ec5746aSDavid du Colombier static Block *
getring(Bring * r)142*7ec5746aSDavid du Colombier getring(Bring *r)
143*7ec5746aSDavid du Colombier {
144*7ec5746aSDavid du Colombier 	Block *bp;
145*7ec5746aSDavid du Colombier 
146*7ec5746aSDavid du Colombier 	if(r->consumed == r->produced)
147*7ec5746aSDavid du Colombier 		return nil;
148*7ec5746aSDavid du Colombier 
149*7ec5746aSDavid du Colombier 	bp = r->ring[r->consumed & Ringmask];
150*7ec5746aSDavid du Colombier 	r->ring[r->consumed & Ringmask] = nil;
151*7ec5746aSDavid du Colombier 	r->consumed++;
152*7ec5746aSDavid du Colombier 	return bp;
153*7ec5746aSDavid du Colombier }
154*7ec5746aSDavid du Colombier 
155*7ec5746aSDavid du Colombier static void
addring(Bring * r,Block * bp)156*7ec5746aSDavid du Colombier addring(Bring *r, Block *bp)
157*7ec5746aSDavid du Colombier {
158*7ec5746aSDavid du Colombier 	Block *tbp;
159*7ec5746aSDavid du Colombier 
160*7ec5746aSDavid du Colombier 	if(r->produced - r->consumed > Ringmask){
161*7ec5746aSDavid du Colombier 		/* Full! */
162*7ec5746aSDavid du Colombier 		tbp = r->ring[r->produced & Ringmask];
163*7ec5746aSDavid du Colombier 		assert(tbp);
164*7ec5746aSDavid du Colombier 		freeb(tbp);
165*7ec5746aSDavid du Colombier 		r->consumed++;
166*7ec5746aSDavid du Colombier 	}
167*7ec5746aSDavid du Colombier 	r->ring[r->produced & Ringmask] = bp;
168*7ec5746aSDavid du Colombier 	r->produced++;
169*7ec5746aSDavid 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 {
211*7ec5746aSDavid 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 {
218*7ec5746aSDavid du Colombier 	GREconv *grec;
219*7ec5746aSDavid du Colombier 	char *ep, *p;
220*7ec5746aSDavid du Colombier 
221*7ec5746aSDavid du Colombier 	grec = c->ptcl;
222*7ec5746aSDavid du Colombier 	p    = state;
223*7ec5746aSDavid du Colombier 	ep   = p + n;
224*7ec5746aSDavid du Colombier 	p    = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
225*7ec5746aSDavid du Colombier 	 "pending %uld  %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
226*7ec5746aSDavid du Colombier 			c->inuse? "Open ": "Closed ",
227*7ec5746aSDavid du Colombier 			grec->raw? "raw ": "",
228*7ec5746aSDavid du Colombier 			grec->dlsusp? "DL suspended ": "",
229*7ec5746aSDavid du Colombier 			grec->ulsusp? "UL suspended ": "",
230*7ec5746aSDavid du Colombier 			grec->hoa, grec->north, grec->south, grec->seq,
231*7ec5746aSDavid du Colombier 			grec->dlpending.consumed, grec->dlpending.produced,
232*7ec5746aSDavid du Colombier 			grec->dlbuffered.consumed, grec->dlbuffered.produced,
233*7ec5746aSDavid du Colombier 			grec->ulbuffered.consumed, grec->ulbuffered.produced,
234*7ec5746aSDavid du Colombier 			grec->ulkey);
235*7ec5746aSDavid 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 {
241*7ec5746aSDavid 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 {
247*7ec5746aSDavid du Colombier 	GREconv *grec;
248*7ec5746aSDavid du Colombier 	Block *bp;
249*7ec5746aSDavid du Colombier 
250*7ec5746aSDavid du Colombier 	grec = c->ptcl;
251*7ec5746aSDavid du Colombier 
252*7ec5746aSDavid du Colombier 	/* Make sure we don't forward any more packets */
253*7ec5746aSDavid du Colombier 	memset(grec->hoa, 0, sizeof grec->hoa);
254*7ec5746aSDavid du Colombier 	memset(grec->north, 0, sizeof grec->north);
255*7ec5746aSDavid du Colombier 	memset(grec->south, 0, sizeof grec->south);
256*7ec5746aSDavid du Colombier 
257*7ec5746aSDavid du Colombier 	qlock(&grec->lock);
258*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlpending)) != nil)
259*7ec5746aSDavid du Colombier 		freeb(bp);
260*7ec5746aSDavid du Colombier 
261*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil)
262*7ec5746aSDavid du Colombier 		freeb(bp);
263*7ec5746aSDavid du Colombier 
264*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil)
265*7ec5746aSDavid du Colombier 		freeb(bp);
266*7ec5746aSDavid du Colombier 
267*7ec5746aSDavid du Colombier 	grec->dlpending.produced = grec->dlpending.consumed = 0;
268*7ec5746aSDavid du Colombier 	grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
269*7ec5746aSDavid du Colombier 	grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
270*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
271*7ec5746aSDavid du Colombier 
272*7ec5746aSDavid du Colombier 	grec->raw = 0;
273*7ec5746aSDavid du Colombier 	grec->seq = 0;
274*7ec5746aSDavid du Colombier 	grec->dlsusp = grec->ulsusp = 1;
275*7ec5746aSDavid du Colombier 
276*7ec5746aSDavid du Colombier 	qhangup(c->rq, sessend);
277*7ec5746aSDavid du Colombier 	qhangup(c->wq, sessend);
278*7ec5746aSDavid du Colombier 	qhangup(c->eq, sessend);
2797dd7cddfSDavid du Colombier 	ipmove(c->laddr, IPnoaddr);
2807dd7cddfSDavid du Colombier 	ipmove(c->raddr, IPnoaddr);
281*7ec5746aSDavid 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 {
287*7ec5746aSDavid du Colombier 	Conv *c;
288*7ec5746aSDavid du Colombier 	GREconv *grec;
289*7ec5746aSDavid 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 
295*7ec5746aSDavid du Colombier 	c    = x;
296*7ec5746aSDavid du Colombier 	grec = c->ptcl;
297*7ec5746aSDavid 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 
308*7ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
309*7ec5746aSDavid du Colombier 	gre->vihl = IP_VER4;
3107dd7cddfSDavid du Colombier 
311*7ec5746aSDavid du Colombier 	if(grec->raw == 0){
312*7ec5746aSDavid du Colombier 		v4tov6(raddr, gre->dst);
3137dd7cddfSDavid du Colombier 		if(ipcmp(raddr, v4prefix) == 0)
314*7ec5746aSDavid du Colombier 			memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
315*7ec5746aSDavid du Colombier 		v4tov6(laddr, gre->src);
3167dd7cddfSDavid du Colombier 		if(ipcmp(laddr, v4prefix) == 0){
3177dd7cddfSDavid du Colombier 			if(ipcmp(c->laddr, IPnoaddr) == 0)
318*7ec5746aSDavid du Colombier 				/* pick interface closest to dest */
319*7ec5746aSDavid du Colombier 				findlocalip(c->p->f, c->laddr, raddr);
320*7ec5746aSDavid du Colombier 			memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
3217dd7cddfSDavid du Colombier 		}
322*7ec5746aSDavid du Colombier 		hnputs(gre->eproto, c->rport);
3235d82c6aeSDavid du Colombier 	}
3247dd7cddfSDavid du Colombier 
325*7ec5746aSDavid du Colombier 	gre->proto = IP_GREPROTO;
326*7ec5746aSDavid du Colombier 	gre->frag[0] = gre->frag[1] = 0;
3277dd7cddfSDavid du Colombier 
328*7ec5746aSDavid du Colombier 	grepdout++;
329*7ec5746aSDavid 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)334*7ec5746aSDavid du Colombier gredownlink(Conv *c, Block *bp)
3357dd7cddfSDavid du Colombier {
336*7ec5746aSDavid du Colombier 	Metablock *m;
337*7ec5746aSDavid du Colombier 	GREconv *grec;
338*7ec5746aSDavid du Colombier 	GREhdr *gre;
339*7ec5746aSDavid du Colombier 	int hdrlen, suspended, extra;
340*7ec5746aSDavid du Colombier 	ushort flags;
341*7ec5746aSDavid du Colombier 	ulong seq;
3427dd7cddfSDavid du Colombier 
343*7ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
344*7ec5746aSDavid du Colombier 	if(gre->ttl == 1){
345*7ec5746aSDavid du Colombier 		freeb(bp);
3467dd7cddfSDavid du Colombier 		return;
3477dd7cddfSDavid du Colombier 	}
3487dd7cddfSDavid du Colombier 
349*7ec5746aSDavid du Colombier 	/*
350*7ec5746aSDavid du Colombier 	 * We've received a packet with a GRE header and we need to
351*7ec5746aSDavid du Colombier 	 * re-adjust the packet header to strip all unwanted parts
352*7ec5746aSDavid du Colombier 	 * but leave room for only a sequence number.
353*7ec5746aSDavid du Colombier 	 */
354*7ec5746aSDavid du Colombier 	grec   = c->ptcl;
355*7ec5746aSDavid du Colombier 	flags  = nhgets(gre->flags);
356*7ec5746aSDavid du Colombier 	hdrlen = 0;
357*7ec5746aSDavid du Colombier 	if(flags & GRE_cksum)
358*7ec5746aSDavid du Colombier 		hdrlen += 2;
359*7ec5746aSDavid du Colombier 	if(flags & GRE_routing){
360*7ec5746aSDavid du Colombier 		print("%V routing info present.  Discarding packet", gre->src);
361*7ec5746aSDavid du Colombier 		freeb(bp);
362*7ec5746aSDavid du Colombier 		return;
363*7ec5746aSDavid du Colombier 	}
364*7ec5746aSDavid du Colombier 	if(flags & (GRE_cksum|GRE_routing))
365*7ec5746aSDavid du Colombier 		hdrlen += 2;			/* Offset field */
366*7ec5746aSDavid du Colombier 	if(flags & GRE_key)
367*7ec5746aSDavid du Colombier 		hdrlen += 4;
368*7ec5746aSDavid du Colombier 	if(flags & GRE_seq)
369*7ec5746aSDavid du Colombier 		hdrlen += 4;
370*7ec5746aSDavid du Colombier 
371*7ec5746aSDavid du Colombier 	/*
372*7ec5746aSDavid du Colombier 	 * The outgoing packet only has the sequence number set.  Make room
373*7ec5746aSDavid du Colombier 	 * for the sequence number.
374*7ec5746aSDavid du Colombier 	 */
375*7ec5746aSDavid du Colombier 	if(hdrlen != sizeof(ulong)){
376*7ec5746aSDavid du Colombier 		extra = hdrlen - sizeof(ulong);
377*7ec5746aSDavid du Colombier 		if(extra < 0 && bp->rp - bp->base < -extra){
378*7ec5746aSDavid du Colombier 			print("gredownlink: cannot add sequence number\n");
379*7ec5746aSDavid du Colombier 			freeb(bp);
380*7ec5746aSDavid du Colombier 			return;
381*7ec5746aSDavid du Colombier 		}
382*7ec5746aSDavid du Colombier 		memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
383*7ec5746aSDavid du Colombier 		bp->rp += extra;
384*7ec5746aSDavid du Colombier 		assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
385*7ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
386*7ec5746aSDavid du Colombier 	}
387*7ec5746aSDavid du Colombier 	seq = grec->seq++;
388*7ec5746aSDavid du Colombier 	hnputs(gre->flags, GRE_seq);
389*7ec5746aSDavid du Colombier 	hnputl(bp->rp + sizeof(GREhdr), seq);
390*7ec5746aSDavid du Colombier 
391*7ec5746aSDavid du Colombier 	/*
392*7ec5746aSDavid du Colombier 	 * Keep rp and seq at the base.  ipoput4 consumes rp for
393*7ec5746aSDavid du Colombier 	 * refragmentation.
394*7ec5746aSDavid du Colombier 	 */
395*7ec5746aSDavid du Colombier 	assert(bp->rp - bp->base >= sizeof(Metablock));
396*7ec5746aSDavid du Colombier 	m = (Metablock *)bp->base;
397*7ec5746aSDavid du Colombier 	m->rp  = bp->rp;
398*7ec5746aSDavid du Colombier 	m->seq = seq;
399*7ec5746aSDavid du Colombier 
400*7ec5746aSDavid du Colombier 	/*
401*7ec5746aSDavid du Colombier 	 * Here we make a decision what we're doing with the packet.  We're
402*7ec5746aSDavid du Colombier 	 * doing this w/o holding a lock which means that later on in the
403*7ec5746aSDavid du Colombier 	 * process we may discover we've done the wrong thing.  I don't want
404*7ec5746aSDavid du Colombier 	 * to call ipoput with the lock held.
405*7ec5746aSDavid du Colombier 	 */
406*7ec5746aSDavid du Colombier restart:
407*7ec5746aSDavid du Colombier 	suspended = grec->dlsusp;
408*7ec5746aSDavid du Colombier 	if(suspended){
409*7ec5746aSDavid du Colombier 		if(!canqlock(&grec->lock)){
410*7ec5746aSDavid du Colombier 			/*
411*7ec5746aSDavid du Colombier 			 * just give up.  too bad, we lose a packet.  this
412*7ec5746aSDavid du Colombier 			 * is just too hard and my brain already hurts.
413*7ec5746aSDavid du Colombier 			 */
414*7ec5746aSDavid du Colombier 			freeb(bp);
415*7ec5746aSDavid du Colombier 			return;
416*7ec5746aSDavid du Colombier 		}
417*7ec5746aSDavid du Colombier 
418*7ec5746aSDavid du Colombier 		if(!grec->dlsusp){
419*7ec5746aSDavid du Colombier 			/*
420*7ec5746aSDavid du Colombier 			 * suspend race.  We though we were suspended, but
421*7ec5746aSDavid du Colombier 			 * we really weren't.
422*7ec5746aSDavid du Colombier 			 */
423*7ec5746aSDavid du Colombier 			qunlock(&grec->lock);
424*7ec5746aSDavid du Colombier 			goto restart;
425*7ec5746aSDavid du Colombier 		}
426*7ec5746aSDavid du Colombier 
427*7ec5746aSDavid du Colombier 		/* Undo the incorrect ref count addition */
428*7ec5746aSDavid du Colombier 		addring(&grec->dlbuffered, bp);
429*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
430*7ec5746aSDavid du Colombier 		return;
431*7ec5746aSDavid du Colombier 	}
432*7ec5746aSDavid du Colombier 
433*7ec5746aSDavid du Colombier 	/*
434*7ec5746aSDavid du Colombier 	 * When we get here, we're not suspended.  Proceed to send the
435*7ec5746aSDavid du Colombier 	 * packet.
436*7ec5746aSDavid du Colombier 	 */
437*7ec5746aSDavid du Colombier 	memmove(gre->src, grec->coa, sizeof gre->dst);
438*7ec5746aSDavid du Colombier 	memmove(gre->dst, grec->south, sizeof gre->dst);
439*7ec5746aSDavid du Colombier 
440*7ec5746aSDavid du Colombier 	/*
441*7ec5746aSDavid du Colombier 	 * Make sure the packet does not go away.
442*7ec5746aSDavid du Colombier 	 */
443*7ec5746aSDavid du Colombier 	_xinc(&bp->ref);
444*7ec5746aSDavid du Colombier 	assert(bp->ref == 2);
445*7ec5746aSDavid du Colombier 
446*7ec5746aSDavid du Colombier 	ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
447*7ec5746aSDavid du Colombier 	grepdout++;
448*7ec5746aSDavid du Colombier 	grebdout += BLEN(bp);
449*7ec5746aSDavid du Colombier 
450*7ec5746aSDavid du Colombier 	/*
451*7ec5746aSDavid du Colombier 	 * Now make sure we didn't do the wrong thing.
452*7ec5746aSDavid du Colombier 	 */
453*7ec5746aSDavid du Colombier 	if(!canqlock(&grec->lock)){
454*7ec5746aSDavid du Colombier 		freeb(bp);		/* The packet just goes away */
455*7ec5746aSDavid du Colombier 		return;
456*7ec5746aSDavid du Colombier 	}
457*7ec5746aSDavid du Colombier 
458*7ec5746aSDavid du Colombier 	/* We did the right thing */
459*7ec5746aSDavid du Colombier 	addring(&grec->dlpending, bp);
460*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
461*7ec5746aSDavid du Colombier }
462*7ec5746aSDavid du Colombier 
463*7ec5746aSDavid du Colombier static void
greuplink(Conv * c,Block * bp)464*7ec5746aSDavid du Colombier greuplink(Conv *c, Block *bp)
465*7ec5746aSDavid du Colombier {
466*7ec5746aSDavid du Colombier 	GREconv *grec;
467*7ec5746aSDavid du Colombier 	GREhdr *gre;
468*7ec5746aSDavid du Colombier 	ushort flags;
469*7ec5746aSDavid du Colombier 
470*7ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
471*7ec5746aSDavid du Colombier 	if(gre->ttl == 1)
472*7ec5746aSDavid du Colombier 		return;
473*7ec5746aSDavid du Colombier 
474*7ec5746aSDavid du Colombier 	grec = c->ptcl;
475*7ec5746aSDavid du Colombier 	memmove(gre->src, grec->coa, sizeof gre->src);
476*7ec5746aSDavid du Colombier 	memmove(gre->dst, grec->north, sizeof gre->dst);
477*7ec5746aSDavid du Colombier 
478*7ec5746aSDavid du Colombier 	/*
479*7ec5746aSDavid du Colombier 	 * Add a key, if needed.
480*7ec5746aSDavid du Colombier 	 */
481*7ec5746aSDavid du Colombier 	if(grec->ulkey){
482*7ec5746aSDavid du Colombier 		flags = nhgets(gre->flags);
483*7ec5746aSDavid du Colombier 		if(flags & (GRE_cksum|GRE_routing)){
484*7ec5746aSDavid du Colombier 			print("%V routing info present.  Discarding packet\n",
485*7ec5746aSDavid du Colombier 				gre->src);
486*7ec5746aSDavid du Colombier 			freeb(bp);
487*7ec5746aSDavid du Colombier 			return;
488*7ec5746aSDavid du Colombier 		}
489*7ec5746aSDavid du Colombier 
490*7ec5746aSDavid du Colombier 		if((flags & GRE_key) == 0){
491*7ec5746aSDavid du Colombier 			/* Make room for the key */
492*7ec5746aSDavid du Colombier 			if(bp->rp - bp->base < sizeof(ulong)){
493*7ec5746aSDavid du Colombier 				print("%V can't add key\n", gre->src);
494*7ec5746aSDavid du Colombier 				freeb(bp);
495*7ec5746aSDavid du Colombier 				return;
496*7ec5746aSDavid du Colombier 			}
497*7ec5746aSDavid du Colombier 
498*7ec5746aSDavid du Colombier 			bp->rp -= 4;
499*7ec5746aSDavid du Colombier 			memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
500*7ec5746aSDavid du Colombier 
501*7ec5746aSDavid du Colombier 			gre = (GREhdr *)bp->rp;
502*7ec5746aSDavid du Colombier 			hnputs(gre->flags, flags | GRE_key);
503*7ec5746aSDavid du Colombier 		}
504*7ec5746aSDavid du Colombier 
505*7ec5746aSDavid du Colombier 		/* Add the key */
506*7ec5746aSDavid du Colombier 		hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
507*7ec5746aSDavid du Colombier 	}
508*7ec5746aSDavid du Colombier 
509*7ec5746aSDavid du Colombier 	if(!canqlock(&grec->lock)){
510*7ec5746aSDavid du Colombier 		freeb(bp);
511*7ec5746aSDavid du Colombier 		return;
512*7ec5746aSDavid du Colombier 	}
513*7ec5746aSDavid du Colombier 
514*7ec5746aSDavid du Colombier 	if(grec->ulsusp)
515*7ec5746aSDavid du Colombier 		addring(&grec->ulbuffered, bp);
516*7ec5746aSDavid du Colombier 	else{
517*7ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
518*7ec5746aSDavid du Colombier 		grepuout++;
519*7ec5746aSDavid du Colombier 		grebuout += BLEN(bp);
520*7ec5746aSDavid du Colombier 	}
521*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
522*7ec5746aSDavid du Colombier }
523*7ec5746aSDavid du Colombier 
524*7ec5746aSDavid du Colombier static void
greiput(Proto * proto,Ipifc *,Block * bp)525*7ec5746aSDavid du Colombier greiput(Proto *proto, Ipifc *, Block *bp)
526*7ec5746aSDavid du Colombier {
527*7ec5746aSDavid du Colombier 	int len, hdrlen;
528*7ec5746aSDavid du Colombier 	ushort eproto, flags;
529*7ec5746aSDavid du Colombier 	uchar raddr[IPaddrlen];
530*7ec5746aSDavid du Colombier 	Conv *c, **p;
531*7ec5746aSDavid du Colombier 	GREconv *grec;
532*7ec5746aSDavid du Colombier 	GREhdr *gre;
533*7ec5746aSDavid du Colombier 	GREpriv *gpriv;
534*7ec5746aSDavid du Colombier 	Ip4hdr *ip;
535*7ec5746aSDavid du Colombier 
536*7ec5746aSDavid du Colombier 	/*
537*7ec5746aSDavid du Colombier 	 * We don't want to deal with block lists.  Ever.  The problem is
538*7ec5746aSDavid du Colombier 	 * that when the block is forwarded, devether.c puts the block into
539*7ec5746aSDavid du Colombier 	 * a queue that also uses ->next.  Just do not use ->next here!
540*7ec5746aSDavid du Colombier 	 */
541*7ec5746aSDavid du Colombier 	if(bp->next){
542*7ec5746aSDavid du Colombier 		len = blocklen(bp);
543*7ec5746aSDavid du Colombier 		bp  = pullupblock(bp, len);
544*7ec5746aSDavid du Colombier 		assert(BLEN(bp) == len && bp->next == nil);
545*7ec5746aSDavid du Colombier 	}
546*7ec5746aSDavid du Colombier 
547*7ec5746aSDavid du Colombier 	gre = (GREhdr *)bp->rp;
548*7ec5746aSDavid du Colombier 	if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
549*7ec5746aSDavid du Colombier 		freeb(bp);
550*7ec5746aSDavid du Colombier 		return;
551*7ec5746aSDavid du Colombier 	}
552*7ec5746aSDavid du Colombier 
553*7ec5746aSDavid du Colombier 	v4tov6(raddr, gre->src);
554*7ec5746aSDavid du Colombier 	eproto = nhgets(gre->eproto);
555*7ec5746aSDavid du Colombier 	flags  = nhgets(gre->flags);
556*7ec5746aSDavid du Colombier 	hdrlen = sizeof(GREhdr);
557*7ec5746aSDavid du Colombier 
558*7ec5746aSDavid du Colombier 	if(flags & GRE_cksum)
559*7ec5746aSDavid du Colombier 		hdrlen += 2;
560*7ec5746aSDavid du Colombier 	if(flags & GRE_routing){
561*7ec5746aSDavid du Colombier 		print("%I routing info present.  Discarding packet\n", raddr);
562*7ec5746aSDavid du Colombier 		freeb(bp);
563*7ec5746aSDavid du Colombier 		return;
564*7ec5746aSDavid du Colombier 	}
565*7ec5746aSDavid du Colombier 	if(flags & (GRE_cksum|GRE_routing))
566*7ec5746aSDavid du Colombier 		hdrlen += 2;			/* Offset field */
567*7ec5746aSDavid du Colombier 	if(flags & GRE_key)
568*7ec5746aSDavid du Colombier 		hdrlen += 4;
569*7ec5746aSDavid du Colombier 	if(flags & GRE_seq)
570*7ec5746aSDavid du Colombier 		hdrlen += 4;
571*7ec5746aSDavid du Colombier 
572*7ec5746aSDavid du Colombier 	if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
573*7ec5746aSDavid du Colombier 		print("greretunnel: packet too short (s=%V d=%V)\n",
574*7ec5746aSDavid du Colombier 			gre->src, gre->dst);
575*7ec5746aSDavid du Colombier 		freeb(bp);
576*7ec5746aSDavid du Colombier 		return;
577*7ec5746aSDavid du Colombier 	}
578*7ec5746aSDavid du Colombier 	ip = (Ip4hdr *)(bp->rp + hdrlen);
579*7ec5746aSDavid du Colombier 
580*7ec5746aSDavid du Colombier 	qlock(proto);
581*7ec5746aSDavid du Colombier 	/*
582*7ec5746aSDavid du Colombier 	 * Look for a conversation structure for this port and address, or
583*7ec5746aSDavid du Colombier 	 * match the retunnel part, or match on the raw flag.
584*7ec5746aSDavid du Colombier 	 */
585*7ec5746aSDavid du Colombier 	for(p = proto->conv; *p; p++) {
586*7ec5746aSDavid du Colombier 		c = *p;
587*7ec5746aSDavid du Colombier 
588*7ec5746aSDavid du Colombier 		if(c->inuse == 0)
589*7ec5746aSDavid du Colombier 			continue;
590*7ec5746aSDavid du Colombier 
591*7ec5746aSDavid du Colombier 		/*
592*7ec5746aSDavid du Colombier 		 * Do not stop this session - blocking here
593*7ec5746aSDavid du Colombier 		 * implies that etherread is blocked.
594*7ec5746aSDavid du Colombier 		 */
595*7ec5746aSDavid du Colombier 		grec = c->ptcl;
596*7ec5746aSDavid du Colombier 		if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
597*7ec5746aSDavid du Colombier 			grepdin++;
598*7ec5746aSDavid du Colombier 			grebdin += BLEN(bp);
599*7ec5746aSDavid du Colombier 			gredownlink(c, bp);
600*7ec5746aSDavid du Colombier 			qunlock(proto);
601*7ec5746aSDavid du Colombier 			return;
602*7ec5746aSDavid du Colombier 		}
603*7ec5746aSDavid du Colombier 
604*7ec5746aSDavid du Colombier 		if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
605*7ec5746aSDavid du Colombier 			grepuin++;
606*7ec5746aSDavid du Colombier 			grebuin += BLEN(bp);
607*7ec5746aSDavid du Colombier 			greuplink(c, bp);
608*7ec5746aSDavid du Colombier 			qunlock(proto);
609*7ec5746aSDavid du Colombier 			return;
610*7ec5746aSDavid du Colombier 		}
611*7ec5746aSDavid du Colombier 	}
612*7ec5746aSDavid du Colombier 
613*7ec5746aSDavid du Colombier 	/*
614*7ec5746aSDavid du Colombier 	 * when we get here, none of the forwarding tunnels matched.  now
615*7ec5746aSDavid du Colombier 	 * try to match on raw and conversational sessions.
616*7ec5746aSDavid du Colombier 	 */
617*7ec5746aSDavid du Colombier 	for(c = nil, p = proto->conv; *p; p++) {
618*7ec5746aSDavid du Colombier 		c = *p;
619*7ec5746aSDavid du Colombier 
620*7ec5746aSDavid du Colombier 		if(c->inuse == 0)
621*7ec5746aSDavid du Colombier 			continue;
622*7ec5746aSDavid du Colombier 
623*7ec5746aSDavid du Colombier 		/*
624*7ec5746aSDavid du Colombier 		 * Do not stop this session - blocking here
625*7ec5746aSDavid du Colombier 		 * implies that etherread is blocked.
626*7ec5746aSDavid du Colombier 		 */
627*7ec5746aSDavid du Colombier 		grec = c->ptcl;
628*7ec5746aSDavid du Colombier 		if(c->rport == eproto &&
629*7ec5746aSDavid du Colombier 		    (grec->raw || ipcmp(c->raddr, raddr) == 0))
630*7ec5746aSDavid du Colombier 			break;
631*7ec5746aSDavid du Colombier 	}
632*7ec5746aSDavid du Colombier 
633*7ec5746aSDavid du Colombier 	qunlock(proto);
634*7ec5746aSDavid du Colombier 
635*7ec5746aSDavid du Colombier 	if(*p == nil){
636*7ec5746aSDavid du Colombier 		freeb(bp);
637*7ec5746aSDavid du Colombier 		return;
638*7ec5746aSDavid du Colombier 	}
6397dd7cddfSDavid du Colombier 
6407dd7cddfSDavid du Colombier 	/*
6417dd7cddfSDavid du Colombier 	 * Trim the packet down to data size
6427dd7cddfSDavid du Colombier 	 */
643*7ec5746aSDavid du Colombier 	len = nhgets(gre->len) - GRE_IPONLY;
6447dd7cddfSDavid du Colombier 	if(len < GRE_IPPLUSGRE){
645*7ec5746aSDavid du Colombier 		freeb(bp);
6467dd7cddfSDavid du Colombier 		return;
6477dd7cddfSDavid du Colombier 	}
648*7ec5746aSDavid du Colombier 
6497dd7cddfSDavid du Colombier 	bp = trimblock(bp, GRE_IPONLY, len);
6507dd7cddfSDavid du Colombier 	if(bp == nil){
651*7ec5746aSDavid 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 	 */
659*7ec5746aSDavid du Colombier 	if(qlen(c->rq) > GREqlen)
660*7ec5746aSDavid 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;
675*7ec5746aSDavid du Colombier 	return snprint(buf, len,
676*7ec5746aSDavid du Colombier 		"gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
677*7ec5746aSDavid du Colombier 		grepdin, grepdout, grepuin, grepuout,
678*7ec5746aSDavid du Colombier 		grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
679*7ec5746aSDavid du Colombier }
6807dd7cddfSDavid du Colombier 
681*7ec5746aSDavid du Colombier static char *
grectlraw(Conv * c,int,char **)682*7ec5746aSDavid du Colombier grectlraw(Conv *c, int, char **)
683*7ec5746aSDavid du Colombier {
684*7ec5746aSDavid du Colombier 	GREconv *grec;
685*7ec5746aSDavid du Colombier 
686*7ec5746aSDavid du Colombier 	grec = c->ptcl;
687*7ec5746aSDavid du Colombier 	grec->raw = 1;
688*7ec5746aSDavid du Colombier 	return nil;
689*7ec5746aSDavid du Colombier }
690*7ec5746aSDavid du Colombier 
691*7ec5746aSDavid du Colombier static char *
grectlcooked(Conv * c,int,char **)692*7ec5746aSDavid du Colombier grectlcooked(Conv *c, int, char **)
693*7ec5746aSDavid du Colombier {
694*7ec5746aSDavid du Colombier 	GREconv *grec;
695*7ec5746aSDavid du Colombier 
696*7ec5746aSDavid du Colombier 	grec = c->ptcl;
697*7ec5746aSDavid du Colombier 	grec->raw = 0;
698*7ec5746aSDavid du Colombier 	return nil;
699*7ec5746aSDavid du Colombier }
700*7ec5746aSDavid du Colombier 
701*7ec5746aSDavid du Colombier static char *
grectlretunnel(Conv * c,int,char ** argv)702*7ec5746aSDavid du Colombier grectlretunnel(Conv *c, int, char **argv)
703*7ec5746aSDavid du Colombier {
704*7ec5746aSDavid du Colombier 	GREconv *grec;
705*7ec5746aSDavid du Colombier 	uchar ipaddr[4];
706*7ec5746aSDavid du Colombier 
707*7ec5746aSDavid du Colombier 	grec = c->ptcl;
708*7ec5746aSDavid du Colombier 	if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
709*7ec5746aSDavid du Colombier 		return "tunnel already set up";
710*7ec5746aSDavid du Colombier 
711*7ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[1]);
712*7ec5746aSDavid du Colombier 	if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
713*7ec5746aSDavid du Colombier 		return "bad hoa";
714*7ec5746aSDavid du Colombier 	memmove(grec->hoa, ipaddr, sizeof grec->hoa);
715*7ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[2]);
716*7ec5746aSDavid du Colombier 	memmove(grec->north, ipaddr, sizeof grec->north);
717*7ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[3]);
718*7ec5746aSDavid du Colombier 	memmove(grec->south, ipaddr, sizeof grec->south);
719*7ec5746aSDavid du Colombier 	v4parseip(ipaddr, argv[4]);
720*7ec5746aSDavid du Colombier 	memmove(grec->coa, ipaddr, sizeof grec->coa);
721*7ec5746aSDavid du Colombier 	grec->ulsusp = 1;
722*7ec5746aSDavid du Colombier 	grec->dlsusp = 0;
723*7ec5746aSDavid du Colombier 
724*7ec5746aSDavid du Colombier 	return nil;
725*7ec5746aSDavid du Colombier }
726*7ec5746aSDavid du Colombier 
727*7ec5746aSDavid du Colombier static char *
grectlreport(Conv * c,int,char ** argv)728*7ec5746aSDavid du Colombier grectlreport(Conv *c, int, char **argv)
729*7ec5746aSDavid du Colombier {
730*7ec5746aSDavid du Colombier 	ulong seq;
731*7ec5746aSDavid du Colombier 	Block *bp;
732*7ec5746aSDavid du Colombier 	Bring *r;
733*7ec5746aSDavid du Colombier 	GREconv *grec;
734*7ec5746aSDavid du Colombier 	Metablock *m;
735*7ec5746aSDavid du Colombier 
736*7ec5746aSDavid du Colombier 	grec = c->ptcl;
737*7ec5746aSDavid du Colombier 	seq  = strtoul(argv[1], nil, 0);
738*7ec5746aSDavid du Colombier 
739*7ec5746aSDavid du Colombier 	qlock(&grec->lock);
740*7ec5746aSDavid du Colombier 	r = &grec->dlpending;
741*7ec5746aSDavid du Colombier 	while(r->produced - r->consumed > 0){
742*7ec5746aSDavid du Colombier 		bp = r->ring[r->consumed & Ringmask];
743*7ec5746aSDavid du Colombier 
744*7ec5746aSDavid du Colombier 		assert(bp && bp->rp - bp->base >= sizeof(Metablock));
745*7ec5746aSDavid du Colombier 		m = (Metablock *)bp->base;
746*7ec5746aSDavid du Colombier 		if((long)(seq - m->seq) <= 0)
747*7ec5746aSDavid du Colombier 			break;
748*7ec5746aSDavid du Colombier 
749*7ec5746aSDavid du Colombier 		r->ring[r->consumed & Ringmask] = nil;
750*7ec5746aSDavid du Colombier 		r->consumed++;
751*7ec5746aSDavid du Colombier 
752*7ec5746aSDavid du Colombier 		freeb(bp);
753*7ec5746aSDavid du Colombier 	}
754*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
755*7ec5746aSDavid du Colombier 	return nil;
756*7ec5746aSDavid du Colombier }
757*7ec5746aSDavid du Colombier 
758*7ec5746aSDavid du Colombier static char *
grectldlsuspend(Conv * c,int,char **)759*7ec5746aSDavid du Colombier grectldlsuspend(Conv *c, int, char **)
760*7ec5746aSDavid du Colombier {
761*7ec5746aSDavid du Colombier 	GREconv *grec;
762*7ec5746aSDavid du Colombier 
763*7ec5746aSDavid du Colombier 	grec = c->ptcl;
764*7ec5746aSDavid du Colombier 	if(grec->dlsusp)
765*7ec5746aSDavid du Colombier 		return "already suspended";
766*7ec5746aSDavid du Colombier 
767*7ec5746aSDavid du Colombier 	grec->dlsusp = 1;
768*7ec5746aSDavid du Colombier 	return nil;
769*7ec5746aSDavid du Colombier }
770*7ec5746aSDavid du Colombier 
771*7ec5746aSDavid du Colombier static char *
grectlulsuspend(Conv * c,int,char **)772*7ec5746aSDavid du Colombier grectlulsuspend(Conv *c, int, char **)
773*7ec5746aSDavid du Colombier {
774*7ec5746aSDavid du Colombier 	GREconv *grec;
775*7ec5746aSDavid du Colombier 
776*7ec5746aSDavid du Colombier 	grec = c->ptcl;
777*7ec5746aSDavid du Colombier 	if(grec->ulsusp)
778*7ec5746aSDavid du Colombier 		return "already suspended";
779*7ec5746aSDavid du Colombier 
780*7ec5746aSDavid du Colombier 	grec->ulsusp = 1;
781*7ec5746aSDavid du Colombier 	return nil;
782*7ec5746aSDavid du Colombier }
783*7ec5746aSDavid du Colombier 
784*7ec5746aSDavid du Colombier static char *
grectldlresume(Conv * c,int,char **)785*7ec5746aSDavid du Colombier grectldlresume(Conv *c, int, char **)
786*7ec5746aSDavid du Colombier {
787*7ec5746aSDavid du Colombier 	GREconv *grec;
788*7ec5746aSDavid du Colombier 	GREhdr *gre;
789*7ec5746aSDavid du Colombier 	Block *bp;
790*7ec5746aSDavid du Colombier 
791*7ec5746aSDavid du Colombier 	grec = c->ptcl;
792*7ec5746aSDavid du Colombier 
793*7ec5746aSDavid du Colombier 	qlock(&grec->lock);
794*7ec5746aSDavid du Colombier 	if(!grec->dlsusp){
795*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
796*7ec5746aSDavid du Colombier 		return "not suspended";
797*7ec5746aSDavid du Colombier 	}
798*7ec5746aSDavid du Colombier 
799*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil){
800*7ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
801*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
802*7ec5746aSDavid du Colombier 
803*7ec5746aSDavid du Colombier 		/*
804*7ec5746aSDavid du Colombier 		 * Make sure the packet does not go away.
805*7ec5746aSDavid du Colombier 		 */
806*7ec5746aSDavid du Colombier 		_xinc(&bp->ref);
807*7ec5746aSDavid du Colombier 		assert(bp->ref == 2);
808*7ec5746aSDavid du Colombier 
809*7ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
810*7ec5746aSDavid du Colombier 
811*7ec5746aSDavid du Colombier 		qlock(&grec->lock);
812*7ec5746aSDavid du Colombier 		addring(&grec->dlpending, bp);
813*7ec5746aSDavid du Colombier 	}
814*7ec5746aSDavid du Colombier 	grec->dlsusp = 0;
815*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
816*7ec5746aSDavid du Colombier 	return nil;
817*7ec5746aSDavid du Colombier }
818*7ec5746aSDavid du Colombier 
819*7ec5746aSDavid du Colombier static char *
grectlulresume(Conv * c,int,char **)820*7ec5746aSDavid du Colombier grectlulresume(Conv *c, int, char **)
821*7ec5746aSDavid du Colombier {
822*7ec5746aSDavid du Colombier 	GREconv *grec;
823*7ec5746aSDavid du Colombier 	GREhdr *gre;
824*7ec5746aSDavid du Colombier 	Block *bp;
825*7ec5746aSDavid du Colombier 
826*7ec5746aSDavid du Colombier 	grec = c->ptcl;
827*7ec5746aSDavid du Colombier 
828*7ec5746aSDavid du Colombier 	qlock(&grec->lock);
829*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil){
830*7ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
831*7ec5746aSDavid du Colombier 
832*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
833*7ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
834*7ec5746aSDavid du Colombier 		qlock(&grec->lock);
835*7ec5746aSDavid du Colombier 	}
836*7ec5746aSDavid du Colombier 	grec->ulsusp = 0;
837*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
838*7ec5746aSDavid du Colombier 	return nil;
839*7ec5746aSDavid du Colombier }
840*7ec5746aSDavid du Colombier 
841*7ec5746aSDavid du Colombier static char *
grectlforward(Conv * c,int,char ** argv)842*7ec5746aSDavid du Colombier grectlforward(Conv *c, int, char **argv)
843*7ec5746aSDavid du Colombier {
844*7ec5746aSDavid du Colombier 	int len;
845*7ec5746aSDavid du Colombier 	Block *bp, *nbp;
846*7ec5746aSDavid du Colombier 	GREconv *grec;
847*7ec5746aSDavid du Colombier 	GREhdr *gre;
848*7ec5746aSDavid du Colombier 	Metablock *m;
849*7ec5746aSDavid du Colombier 
850*7ec5746aSDavid du Colombier 	grec = c->ptcl;
851*7ec5746aSDavid du Colombier 
852*7ec5746aSDavid du Colombier 	v4parseip(grec->south, argv[1]);
853*7ec5746aSDavid du Colombier 	memmove(grec->north, grec->south, sizeof grec->north);
854*7ec5746aSDavid du Colombier 
855*7ec5746aSDavid du Colombier 	qlock(&grec->lock);
856*7ec5746aSDavid du Colombier 	if(!grec->dlsusp){
857*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
858*7ec5746aSDavid du Colombier 		return "not suspended";
859*7ec5746aSDavid du Colombier 	}
860*7ec5746aSDavid du Colombier 	grec->dlsusp = 0;
861*7ec5746aSDavid du Colombier 	grec->ulsusp = 0;
862*7ec5746aSDavid du Colombier 
863*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlpending)) != nil){
864*7ec5746aSDavid du Colombier 
865*7ec5746aSDavid du Colombier 		assert(bp->rp - bp->base >= sizeof(Metablock));
866*7ec5746aSDavid du Colombier 		m = (Metablock *)bp->base;
867*7ec5746aSDavid du Colombier 		assert(m->rp >= bp->base && m->rp < bp->lim);
868*7ec5746aSDavid du Colombier 
869*7ec5746aSDavid du Colombier 		/*
870*7ec5746aSDavid du Colombier 		 * If the packet is still held inside the IP transmit
871*7ec5746aSDavid du Colombier 		 * system, make a copy of the packet first.
872*7ec5746aSDavid du Colombier 		 */
873*7ec5746aSDavid du Colombier 		if(bp->ref > 1){
874*7ec5746aSDavid du Colombier 			len = bp->wp - m->rp;
875*7ec5746aSDavid du Colombier 			nbp = allocb(len);
876*7ec5746aSDavid du Colombier 			memmove(nbp->wp, m->rp, len);
877*7ec5746aSDavid du Colombier 			nbp->wp += len;
878*7ec5746aSDavid du Colombier 			freeb(bp);
879*7ec5746aSDavid du Colombier 			bp  = nbp;
880*7ec5746aSDavid du Colombier 		}
881*7ec5746aSDavid du Colombier 		else{
882*7ec5746aSDavid du Colombier 			/* Patch up rp */
883*7ec5746aSDavid du Colombier 			bp->rp = m->rp;
884*7ec5746aSDavid du Colombier 		}
885*7ec5746aSDavid du Colombier 
886*7ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
887*7ec5746aSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
888*7ec5746aSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
889*7ec5746aSDavid du Colombier 
890*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
891*7ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
892*7ec5746aSDavid du Colombier 		qlock(&grec->lock);
893*7ec5746aSDavid du Colombier 	}
894*7ec5746aSDavid du Colombier 
895*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->dlbuffered)) != nil){
896*7ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
897*7ec5746aSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
898*7ec5746aSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
899*7ec5746aSDavid du Colombier 
900*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
901*7ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
902*7ec5746aSDavid du Colombier 		qlock(&grec->lock);
903*7ec5746aSDavid du Colombier 	}
904*7ec5746aSDavid du Colombier 
905*7ec5746aSDavid du Colombier 	while((bp = getring(&grec->ulbuffered)) != nil){
906*7ec5746aSDavid du Colombier 		gre = (GREhdr *)bp->rp;
907*7ec5746aSDavid du Colombier 
908*7ec5746aSDavid du Colombier 		memmove(gre->src, grec->coa, sizeof gre->dst);
909*7ec5746aSDavid du Colombier 		memmove(gre->dst, grec->south, sizeof gre->dst);
910*7ec5746aSDavid du Colombier 
911*7ec5746aSDavid du Colombier 		qunlock(&grec->lock);
912*7ec5746aSDavid du Colombier 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
913*7ec5746aSDavid du Colombier 		qlock(&grec->lock);
914*7ec5746aSDavid du Colombier 	}
915*7ec5746aSDavid du Colombier 	qunlock(&grec->lock);
916*7ec5746aSDavid du Colombier 	return nil;
917*7ec5746aSDavid du Colombier }
918*7ec5746aSDavid du Colombier 
919*7ec5746aSDavid du Colombier static char *
grectlulkey(Conv * c,int,char ** argv)920*7ec5746aSDavid du Colombier grectlulkey(Conv *c, int, char **argv)
921*7ec5746aSDavid du Colombier {
922*7ec5746aSDavid du Colombier 	GREconv *grec;
923*7ec5746aSDavid du Colombier 
924*7ec5746aSDavid du Colombier 	grec = c->ptcl;
925*7ec5746aSDavid du Colombier 	grec->ulkey = strtoul(argv[1], nil, 0);
926*7ec5746aSDavid 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 {
932*7ec5746aSDavid du Colombier 	int i;
9335d82c6aeSDavid du Colombier 
934*7ec5746aSDavid du Colombier 	if(n < 1)
935*7ec5746aSDavid du Colombier 		return "too few arguments";
936*7ec5746aSDavid du Colombier 
937*7ec5746aSDavid du Colombier 	for(i = 0; i < Ncmds; i++)
938*7ec5746aSDavid du Colombier 		if(strcmp(f[0], grectls[i].cmd) == 0)
939*7ec5746aSDavid du Colombier 			break;
940*7ec5746aSDavid du Colombier 
941*7ec5746aSDavid du Colombier 	if(i == Ncmds)
942*7ec5746aSDavid du Colombier 		return "no such command";
943*7ec5746aSDavid du Colombier 	if(grectls[i].argc != 0 && grectls[i].argc != n)
944*7ec5746aSDavid du Colombier 		return "incorrect number of arguments";
945*7ec5746aSDavid du Colombier 
946*7ec5746aSDavid 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;
968*7ec5746aSDavid du Colombier 	gre->ptclsize = sizeof(GREconv);
9697dd7cddfSDavid du Colombier 
9707dd7cddfSDavid du Colombier 	Fsproto(fs, gre);
9717dd7cddfSDavid du Colombier }
972