xref: /plan9/sys/src/9/ip/gre.c (revision 7ec5746a5244cc505568e3d45ab9d5421abbdc7d)
1 /*
2  * Generic Routing Encapsulation over IPv4, rfc1702
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "../port/error.h"
10 
11 #include "ip.h"
12 
13 enum {
14 	GRE_IPONLY	= 12,		/* size of ip header */
15 	GRE_IPPLUSGRE	= 12,		/* minimum size of GRE header */
16 	IP_GREPROTO	= 47,
17 
18 	GRErxms		= 200,
19 	GREtickms	= 100,
20 	GREmaxxmit	= 10,
21 
22 	K		= 1024,
23 	GREqlen		= 256 * K,
24 
25 	GRE_cksum	= 0x8000,
26 	GRE_routing	= 0x4000,
27 	GRE_key		= 0x2000,
28 	GRE_seq		= 0x1000,
29 
30 	Nring		= 1 << 10,	/* power of two, please */
31 	Ringmask	= Nring - 1,
32 
33 	GREctlraw	= 0,
34 	GREctlcooked,
35 	GREctlretunnel,
36 	GREctlreport,
37 	GREctldlsuspend,
38 	GREctlulsuspend,
39 	GREctldlresume,
40 	GREctlulresume,
41 	GREctlforward,
42 	GREctlulkey,
43 	Ncmds,
44 };
45 
46 typedef struct GREhdr GREhdr;
47 struct GREhdr{
48 	/* ip header */
49 	uchar	vihl;		/* Version and header length */
50 	uchar	tos;		/* Type of service */
51 	uchar	len[2];		/* packet length (including headers) */
52 	uchar	id[2];		/* Identification */
53 	uchar	frag[2];	/* Fragment information */
54 	uchar	ttl;
55 	uchar	proto;		/* Protocol */
56 	uchar	cksum[2];	/* checksum */
57 	uchar	src[4];		/* Ip source */
58 	uchar	dst[4];		/* Ip destination */
59 
60 	/* gre header */
61 	uchar	flags[2];
62 	uchar	eproto[2];	/* encapsulation protocol */
63 };
64 
65 typedef struct GREpriv GREpriv;
66 struct GREpriv{
67 	/* non-MIB stats */
68 	ulong	lenerr;			/* short packet */
69 };
70 
71 typedef struct Bring	Bring;
72 struct Bring{
73 	Block	*ring[Nring];
74 	long	produced;
75 	long	consumed;
76 };
77 
78 typedef struct GREconv	GREconv;
79 struct GREconv{
80 	int	raw;
81 
82 	/* Retunnelling information.  v4 only */
83 	uchar	north[4];			/* HA */
84 	uchar	south[4];			/* Base station */
85 	uchar	hoa[4];				/* Home address */
86 	uchar	coa[4];				/* Careof address */
87 	ulong	seq;				/* Current sequence # */
88 	int	dlsusp;				/* Downlink suspended? */
89 	int	ulsusp;				/* Uplink suspended? */
90 	ulong	ulkey;				/* GRE key */
91 
92 	QLock	lock;				/* Lock for rings */
93 	Bring	dlpending;			/* Ring of pending packets */
94 	Bring	dlbuffered;			/* Received while suspended */
95 	Bring	ulbuffered;			/* Received while suspended */
96 };
97 
98 typedef struct Metablock Metablock;
99 struct Metablock{
100 	uchar	*rp;
101 	ulong	seq;
102 };
103 
104 static char *grectlcooked(Conv *, int, char **);
105 static char *grectldlresume(Conv *, int, char **);
106 static char *grectldlsuspend(Conv *, int, char **);
107 static char *grectlforward(Conv *, int, char **);
108 static char *grectlraw(Conv *, int, char **);
109 static char *grectlreport(Conv *, int, char **);
110 static char *grectlretunnel(Conv *, int, char **);
111 static char *grectlulkey(Conv *, int, char **);
112 static char *grectlulresume(Conv *, int, char **);
113 static char *grectlulsuspend(Conv *, int, char **);
114 
115 static struct{
116 	char	*cmd;
117 	int	argc;
118 	char	*(*f)(Conv *, int, char **);
119 } grectls[Ncmds] = {
120 [GREctlraw]	=	{	"raw",		1,	grectlraw,	},
121 [GREctlcooked]	=	{	"cooked",	1,	grectlcooked,	},
122 [GREctlretunnel]=	{	"retunnel",	5,	grectlretunnel,	},
123 [GREctlreport]	=	{	"report",	2,	grectlreport,	},
124 [GREctldlsuspend]=	{	"dlsuspend",	1,	grectldlsuspend,},
125 [GREctlulsuspend]=	{	"ulsuspend",	1,	grectlulsuspend,},
126 [GREctldlresume]=	{	"dlresume",	1,	grectldlresume,	},
127 [GREctlulresume]=	{	"ulresume",	1,	grectlulresume,	},
128 [GREctlforward]	=	{	"forward",	2,	grectlforward,	},
129 [GREctlulkey]	=	{	"ulkey",	2,	grectlulkey,	},
130 };
131 
132 static uchar nulladdr[4];
133 static char *sessend = "session end";
134 
135 static void grekick(void *x, Block *bp);
136 static char *gresetup(Conv *, char *, char *, char *);
137 
138 ulong grepdin, grepdout, grebdin, grebdout;
139 ulong grepuin, grepuout, grebuin, grebuout;
140 
141 static Block *
getring(Bring * r)142 getring(Bring *r)
143 {
144 	Block *bp;
145 
146 	if(r->consumed == r->produced)
147 		return nil;
148 
149 	bp = r->ring[r->consumed & Ringmask];
150 	r->ring[r->consumed & Ringmask] = nil;
151 	r->consumed++;
152 	return bp;
153 }
154 
155 static void
addring(Bring * r,Block * bp)156 addring(Bring *r, Block *bp)
157 {
158 	Block *tbp;
159 
160 	if(r->produced - r->consumed > Ringmask){
161 		/* Full! */
162 		tbp = r->ring[r->produced & Ringmask];
163 		assert(tbp);
164 		freeb(tbp);
165 		r->consumed++;
166 	}
167 	r->ring[r->produced & Ringmask] = bp;
168 	r->produced++;
169 }
170 
171 static char *
greconnect(Conv * c,char ** argv,int argc)172 greconnect(Conv *c, char **argv, int argc)
173 {
174 	Proto *p;
175 	char *err;
176 	Conv *tc, **cp, **ecp;
177 
178 	err = Fsstdconnect(c, argv, argc);
179 	if(err != nil)
180 		return err;
181 
182 	/* make sure noone's already connected to this other sys */
183 	p = c->p;
184 	qlock(p);
185 	ecp = &p->conv[p->nc];
186 	for(cp = p->conv; cp < ecp; cp++){
187 		tc = *cp;
188 		if(tc == nil)
189 			break;
190 		if(tc == c)
191 			continue;
192 		if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
193 			err = "already connected to that addr/proto";
194 			ipmove(c->laddr, IPnoaddr);
195 			ipmove(c->raddr, IPnoaddr);
196 			break;
197 		}
198 	}
199 	qunlock(p);
200 
201 	if(err != nil)
202 		return err;
203 	Fsconnected(c, nil);
204 
205 	return nil;
206 }
207 
208 static void
grecreate(Conv * c)209 grecreate(Conv *c)
210 {
211 	c->rq = qopen(GREqlen, Qmsg, 0, c);
212 	c->wq = qbypass(grekick, c);
213 }
214 
215 static int
grestate(Conv * c,char * state,int n)216 grestate(Conv *c, char *state, int n)
217 {
218 	GREconv *grec;
219 	char *ep, *p;
220 
221 	grec = c->ptcl;
222 	p    = state;
223 	ep   = p + n;
224 	p    = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
225 	 "pending %uld  %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
226 			c->inuse? "Open ": "Closed ",
227 			grec->raw? "raw ": "",
228 			grec->dlsusp? "DL suspended ": "",
229 			grec->ulsusp? "UL suspended ": "",
230 			grec->hoa, grec->north, grec->south, grec->seq,
231 			grec->dlpending.consumed, grec->dlpending.produced,
232 			grec->dlbuffered.consumed, grec->dlbuffered.produced,
233 			grec->ulbuffered.consumed, grec->ulbuffered.produced,
234 			grec->ulkey);
235 	return p - state;
236 }
237 
238 static char*
greannounce(Conv *,char **,int)239 greannounce(Conv*, char**, int)
240 {
241 	return "gre does not support announce";
242 }
243 
244 static void
greclose(Conv * c)245 greclose(Conv *c)
246 {
247 	GREconv *grec;
248 	Block *bp;
249 
250 	grec = c->ptcl;
251 
252 	/* Make sure we don't forward any more packets */
253 	memset(grec->hoa, 0, sizeof grec->hoa);
254 	memset(grec->north, 0, sizeof grec->north);
255 	memset(grec->south, 0, sizeof grec->south);
256 
257 	qlock(&grec->lock);
258 	while((bp = getring(&grec->dlpending)) != nil)
259 		freeb(bp);
260 
261 	while((bp = getring(&grec->dlbuffered)) != nil)
262 		freeb(bp);
263 
264 	while((bp = getring(&grec->ulbuffered)) != nil)
265 		freeb(bp);
266 
267 	grec->dlpending.produced = grec->dlpending.consumed = 0;
268 	grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
269 	grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
270 	qunlock(&grec->lock);
271 
272 	grec->raw = 0;
273 	grec->seq = 0;
274 	grec->dlsusp = grec->ulsusp = 1;
275 
276 	qhangup(c->rq, sessend);
277 	qhangup(c->wq, sessend);
278 	qhangup(c->eq, sessend);
279 	ipmove(c->laddr, IPnoaddr);
280 	ipmove(c->raddr, IPnoaddr);
281 	c->lport = c->rport = 0;
282 }
283 
284 static void
grekick(void * x,Block * bp)285 grekick(void *x, Block *bp)
286 {
287 	Conv *c;
288 	GREconv *grec;
289 	GREhdr *gre;
290 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
291 
292 	if(bp == nil)
293 		return;
294 
295 	c    = x;
296 	grec = c->ptcl;
297 
298 	/* Make space to fit ip header (gre header already there) */
299 	bp = padblock(bp, GRE_IPONLY);
300 	if(bp == nil)
301 		return;
302 
303 	/* make sure the message has a GRE header */
304 	bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
305 	if(bp == nil)
306 		return;
307 
308 	gre = (GREhdr *)bp->rp;
309 	gre->vihl = IP_VER4;
310 
311 	if(grec->raw == 0){
312 		v4tov6(raddr, gre->dst);
313 		if(ipcmp(raddr, v4prefix) == 0)
314 			memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
315 		v4tov6(laddr, gre->src);
316 		if(ipcmp(laddr, v4prefix) == 0){
317 			if(ipcmp(c->laddr, IPnoaddr) == 0)
318 				/* pick interface closest to dest */
319 				findlocalip(c->p->f, c->laddr, raddr);
320 			memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
321 		}
322 		hnputs(gre->eproto, c->rport);
323 	}
324 
325 	gre->proto = IP_GREPROTO;
326 	gre->frag[0] = gre->frag[1] = 0;
327 
328 	grepdout++;
329 	grebdout += BLEN(bp);
330 	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
331 }
332 
333 static void
gredownlink(Conv * c,Block * bp)334 gredownlink(Conv *c, Block *bp)
335 {
336 	Metablock *m;
337 	GREconv *grec;
338 	GREhdr *gre;
339 	int hdrlen, suspended, extra;
340 	ushort flags;
341 	ulong seq;
342 
343 	gre = (GREhdr *)bp->rp;
344 	if(gre->ttl == 1){
345 		freeb(bp);
346 		return;
347 	}
348 
349 	/*
350 	 * We've received a packet with a GRE header and we need to
351 	 * re-adjust the packet header to strip all unwanted parts
352 	 * but leave room for only a sequence number.
353 	 */
354 	grec   = c->ptcl;
355 	flags  = nhgets(gre->flags);
356 	hdrlen = 0;
357 	if(flags & GRE_cksum)
358 		hdrlen += 2;
359 	if(flags & GRE_routing){
360 		print("%V routing info present.  Discarding packet", gre->src);
361 		freeb(bp);
362 		return;
363 	}
364 	if(flags & (GRE_cksum|GRE_routing))
365 		hdrlen += 2;			/* Offset field */
366 	if(flags & GRE_key)
367 		hdrlen += 4;
368 	if(flags & GRE_seq)
369 		hdrlen += 4;
370 
371 	/*
372 	 * The outgoing packet only has the sequence number set.  Make room
373 	 * for the sequence number.
374 	 */
375 	if(hdrlen != sizeof(ulong)){
376 		extra = hdrlen - sizeof(ulong);
377 		if(extra < 0 && bp->rp - bp->base < -extra){
378 			print("gredownlink: cannot add sequence number\n");
379 			freeb(bp);
380 			return;
381 		}
382 		memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
383 		bp->rp += extra;
384 		assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
385 		gre = (GREhdr *)bp->rp;
386 	}
387 	seq = grec->seq++;
388 	hnputs(gre->flags, GRE_seq);
389 	hnputl(bp->rp + sizeof(GREhdr), seq);
390 
391 	/*
392 	 * Keep rp and seq at the base.  ipoput4 consumes rp for
393 	 * refragmentation.
394 	 */
395 	assert(bp->rp - bp->base >= sizeof(Metablock));
396 	m = (Metablock *)bp->base;
397 	m->rp  = bp->rp;
398 	m->seq = seq;
399 
400 	/*
401 	 * Here we make a decision what we're doing with the packet.  We're
402 	 * doing this w/o holding a lock which means that later on in the
403 	 * process we may discover we've done the wrong thing.  I don't want
404 	 * to call ipoput with the lock held.
405 	 */
406 restart:
407 	suspended = grec->dlsusp;
408 	if(suspended){
409 		if(!canqlock(&grec->lock)){
410 			/*
411 			 * just give up.  too bad, we lose a packet.  this
412 			 * is just too hard and my brain already hurts.
413 			 */
414 			freeb(bp);
415 			return;
416 		}
417 
418 		if(!grec->dlsusp){
419 			/*
420 			 * suspend race.  We though we were suspended, but
421 			 * we really weren't.
422 			 */
423 			qunlock(&grec->lock);
424 			goto restart;
425 		}
426 
427 		/* Undo the incorrect ref count addition */
428 		addring(&grec->dlbuffered, bp);
429 		qunlock(&grec->lock);
430 		return;
431 	}
432 
433 	/*
434 	 * When we get here, we're not suspended.  Proceed to send the
435 	 * packet.
436 	 */
437 	memmove(gre->src, grec->coa, sizeof gre->dst);
438 	memmove(gre->dst, grec->south, sizeof gre->dst);
439 
440 	/*
441 	 * Make sure the packet does not go away.
442 	 */
443 	_xinc(&bp->ref);
444 	assert(bp->ref == 2);
445 
446 	ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
447 	grepdout++;
448 	grebdout += BLEN(bp);
449 
450 	/*
451 	 * Now make sure we didn't do the wrong thing.
452 	 */
453 	if(!canqlock(&grec->lock)){
454 		freeb(bp);		/* The packet just goes away */
455 		return;
456 	}
457 
458 	/* We did the right thing */
459 	addring(&grec->dlpending, bp);
460 	qunlock(&grec->lock);
461 }
462 
463 static void
greuplink(Conv * c,Block * bp)464 greuplink(Conv *c, Block *bp)
465 {
466 	GREconv *grec;
467 	GREhdr *gre;
468 	ushort flags;
469 
470 	gre = (GREhdr *)bp->rp;
471 	if(gre->ttl == 1)
472 		return;
473 
474 	grec = c->ptcl;
475 	memmove(gre->src, grec->coa, sizeof gre->src);
476 	memmove(gre->dst, grec->north, sizeof gre->dst);
477 
478 	/*
479 	 * Add a key, if needed.
480 	 */
481 	if(grec->ulkey){
482 		flags = nhgets(gre->flags);
483 		if(flags & (GRE_cksum|GRE_routing)){
484 			print("%V routing info present.  Discarding packet\n",
485 				gre->src);
486 			freeb(bp);
487 			return;
488 		}
489 
490 		if((flags & GRE_key) == 0){
491 			/* Make room for the key */
492 			if(bp->rp - bp->base < sizeof(ulong)){
493 				print("%V can't add key\n", gre->src);
494 				freeb(bp);
495 				return;
496 			}
497 
498 			bp->rp -= 4;
499 			memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
500 
501 			gre = (GREhdr *)bp->rp;
502 			hnputs(gre->flags, flags | GRE_key);
503 		}
504 
505 		/* Add the key */
506 		hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
507 	}
508 
509 	if(!canqlock(&grec->lock)){
510 		freeb(bp);
511 		return;
512 	}
513 
514 	if(grec->ulsusp)
515 		addring(&grec->ulbuffered, bp);
516 	else{
517 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
518 		grepuout++;
519 		grebuout += BLEN(bp);
520 	}
521 	qunlock(&grec->lock);
522 }
523 
524 static void
greiput(Proto * proto,Ipifc *,Block * bp)525 greiput(Proto *proto, Ipifc *, Block *bp)
526 {
527 	int len, hdrlen;
528 	ushort eproto, flags;
529 	uchar raddr[IPaddrlen];
530 	Conv *c, **p;
531 	GREconv *grec;
532 	GREhdr *gre;
533 	GREpriv *gpriv;
534 	Ip4hdr *ip;
535 
536 	/*
537 	 * We don't want to deal with block lists.  Ever.  The problem is
538 	 * that when the block is forwarded, devether.c puts the block into
539 	 * a queue that also uses ->next.  Just do not use ->next here!
540 	 */
541 	if(bp->next){
542 		len = blocklen(bp);
543 		bp  = pullupblock(bp, len);
544 		assert(BLEN(bp) == len && bp->next == nil);
545 	}
546 
547 	gre = (GREhdr *)bp->rp;
548 	if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
549 		freeb(bp);
550 		return;
551 	}
552 
553 	v4tov6(raddr, gre->src);
554 	eproto = nhgets(gre->eproto);
555 	flags  = nhgets(gre->flags);
556 	hdrlen = sizeof(GREhdr);
557 
558 	if(flags & GRE_cksum)
559 		hdrlen += 2;
560 	if(flags & GRE_routing){
561 		print("%I routing info present.  Discarding packet\n", raddr);
562 		freeb(bp);
563 		return;
564 	}
565 	if(flags & (GRE_cksum|GRE_routing))
566 		hdrlen += 2;			/* Offset field */
567 	if(flags & GRE_key)
568 		hdrlen += 4;
569 	if(flags & GRE_seq)
570 		hdrlen += 4;
571 
572 	if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
573 		print("greretunnel: packet too short (s=%V d=%V)\n",
574 			gre->src, gre->dst);
575 		freeb(bp);
576 		return;
577 	}
578 	ip = (Ip4hdr *)(bp->rp + hdrlen);
579 
580 	qlock(proto);
581 	/*
582 	 * Look for a conversation structure for this port and address, or
583 	 * match the retunnel part, or match on the raw flag.
584 	 */
585 	for(p = proto->conv; *p; p++) {
586 		c = *p;
587 
588 		if(c->inuse == 0)
589 			continue;
590 
591 		/*
592 		 * Do not stop this session - blocking here
593 		 * implies that etherread is blocked.
594 		 */
595 		grec = c->ptcl;
596 		if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
597 			grepdin++;
598 			grebdin += BLEN(bp);
599 			gredownlink(c, bp);
600 			qunlock(proto);
601 			return;
602 		}
603 
604 		if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
605 			grepuin++;
606 			grebuin += BLEN(bp);
607 			greuplink(c, bp);
608 			qunlock(proto);
609 			return;
610 		}
611 	}
612 
613 	/*
614 	 * when we get here, none of the forwarding tunnels matched.  now
615 	 * try to match on raw and conversational sessions.
616 	 */
617 	for(c = nil, p = proto->conv; *p; p++) {
618 		c = *p;
619 
620 		if(c->inuse == 0)
621 			continue;
622 
623 		/*
624 		 * Do not stop this session - blocking here
625 		 * implies that etherread is blocked.
626 		 */
627 		grec = c->ptcl;
628 		if(c->rport == eproto &&
629 		    (grec->raw || ipcmp(c->raddr, raddr) == 0))
630 			break;
631 	}
632 
633 	qunlock(proto);
634 
635 	if(*p == nil){
636 		freeb(bp);
637 		return;
638 	}
639 
640 	/*
641 	 * Trim the packet down to data size
642 	 */
643 	len = nhgets(gre->len) - GRE_IPONLY;
644 	if(len < GRE_IPPLUSGRE){
645 		freeb(bp);
646 		return;
647 	}
648 
649 	bp = trimblock(bp, GRE_IPONLY, len);
650 	if(bp == nil){
651 		gpriv = proto->priv;
652 		gpriv->lenerr++;
653 		return;
654 	}
655 
656 	/*
657 	 *  Can't delimit packet so pull it all into one block.
658 	 */
659 	if(qlen(c->rq) > GREqlen)
660 		freeb(bp);
661 	else{
662 		bp = concatblock(bp);
663 		if(bp == 0)
664 			panic("greiput");
665 		qpass(c->rq, bp);
666 	}
667 }
668 
669 int
grestats(Proto * gre,char * buf,int len)670 grestats(Proto *gre, char *buf, int len)
671 {
672 	GREpriv *gpriv;
673 
674 	gpriv = gre->priv;
675 	return snprint(buf, len,
676 		"gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
677 		grepdin, grepdout, grepuin, grepuout,
678 		grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
679 }
680 
681 static char *
grectlraw(Conv * c,int,char **)682 grectlraw(Conv *c, int, char **)
683 {
684 	GREconv *grec;
685 
686 	grec = c->ptcl;
687 	grec->raw = 1;
688 	return nil;
689 }
690 
691 static char *
grectlcooked(Conv * c,int,char **)692 grectlcooked(Conv *c, int, char **)
693 {
694 	GREconv *grec;
695 
696 	grec = c->ptcl;
697 	grec->raw = 0;
698 	return nil;
699 }
700 
701 static char *
grectlretunnel(Conv * c,int,char ** argv)702 grectlretunnel(Conv *c, int, char **argv)
703 {
704 	GREconv *grec;
705 	uchar ipaddr[4];
706 
707 	grec = c->ptcl;
708 	if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
709 		return "tunnel already set up";
710 
711 	v4parseip(ipaddr, argv[1]);
712 	if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
713 		return "bad hoa";
714 	memmove(grec->hoa, ipaddr, sizeof grec->hoa);
715 	v4parseip(ipaddr, argv[2]);
716 	memmove(grec->north, ipaddr, sizeof grec->north);
717 	v4parseip(ipaddr, argv[3]);
718 	memmove(grec->south, ipaddr, sizeof grec->south);
719 	v4parseip(ipaddr, argv[4]);
720 	memmove(grec->coa, ipaddr, sizeof grec->coa);
721 	grec->ulsusp = 1;
722 	grec->dlsusp = 0;
723 
724 	return nil;
725 }
726 
727 static char *
grectlreport(Conv * c,int,char ** argv)728 grectlreport(Conv *c, int, char **argv)
729 {
730 	ulong seq;
731 	Block *bp;
732 	Bring *r;
733 	GREconv *grec;
734 	Metablock *m;
735 
736 	grec = c->ptcl;
737 	seq  = strtoul(argv[1], nil, 0);
738 
739 	qlock(&grec->lock);
740 	r = &grec->dlpending;
741 	while(r->produced - r->consumed > 0){
742 		bp = r->ring[r->consumed & Ringmask];
743 
744 		assert(bp && bp->rp - bp->base >= sizeof(Metablock));
745 		m = (Metablock *)bp->base;
746 		if((long)(seq - m->seq) <= 0)
747 			break;
748 
749 		r->ring[r->consumed & Ringmask] = nil;
750 		r->consumed++;
751 
752 		freeb(bp);
753 	}
754 	qunlock(&grec->lock);
755 	return nil;
756 }
757 
758 static char *
grectldlsuspend(Conv * c,int,char **)759 grectldlsuspend(Conv *c, int, char **)
760 {
761 	GREconv *grec;
762 
763 	grec = c->ptcl;
764 	if(grec->dlsusp)
765 		return "already suspended";
766 
767 	grec->dlsusp = 1;
768 	return nil;
769 }
770 
771 static char *
grectlulsuspend(Conv * c,int,char **)772 grectlulsuspend(Conv *c, int, char **)
773 {
774 	GREconv *grec;
775 
776 	grec = c->ptcl;
777 	if(grec->ulsusp)
778 		return "already suspended";
779 
780 	grec->ulsusp = 1;
781 	return nil;
782 }
783 
784 static char *
grectldlresume(Conv * c,int,char **)785 grectldlresume(Conv *c, int, char **)
786 {
787 	GREconv *grec;
788 	GREhdr *gre;
789 	Block *bp;
790 
791 	grec = c->ptcl;
792 
793 	qlock(&grec->lock);
794 	if(!grec->dlsusp){
795 		qunlock(&grec->lock);
796 		return "not suspended";
797 	}
798 
799 	while((bp = getring(&grec->dlbuffered)) != nil){
800 		gre = (GREhdr *)bp->rp;
801 		qunlock(&grec->lock);
802 
803 		/*
804 		 * Make sure the packet does not go away.
805 		 */
806 		_xinc(&bp->ref);
807 		assert(bp->ref == 2);
808 
809 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
810 
811 		qlock(&grec->lock);
812 		addring(&grec->dlpending, bp);
813 	}
814 	grec->dlsusp = 0;
815 	qunlock(&grec->lock);
816 	return nil;
817 }
818 
819 static char *
grectlulresume(Conv * c,int,char **)820 grectlulresume(Conv *c, int, char **)
821 {
822 	GREconv *grec;
823 	GREhdr *gre;
824 	Block *bp;
825 
826 	grec = c->ptcl;
827 
828 	qlock(&grec->lock);
829 	while((bp = getring(&grec->ulbuffered)) != nil){
830 		gre = (GREhdr *)bp->rp;
831 
832 		qunlock(&grec->lock);
833 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
834 		qlock(&grec->lock);
835 	}
836 	grec->ulsusp = 0;
837 	qunlock(&grec->lock);
838 	return nil;
839 }
840 
841 static char *
grectlforward(Conv * c,int,char ** argv)842 grectlforward(Conv *c, int, char **argv)
843 {
844 	int len;
845 	Block *bp, *nbp;
846 	GREconv *grec;
847 	GREhdr *gre;
848 	Metablock *m;
849 
850 	grec = c->ptcl;
851 
852 	v4parseip(grec->south, argv[1]);
853 	memmove(grec->north, grec->south, sizeof grec->north);
854 
855 	qlock(&grec->lock);
856 	if(!grec->dlsusp){
857 		qunlock(&grec->lock);
858 		return "not suspended";
859 	}
860 	grec->dlsusp = 0;
861 	grec->ulsusp = 0;
862 
863 	while((bp = getring(&grec->dlpending)) != nil){
864 
865 		assert(bp->rp - bp->base >= sizeof(Metablock));
866 		m = (Metablock *)bp->base;
867 		assert(m->rp >= bp->base && m->rp < bp->lim);
868 
869 		/*
870 		 * If the packet is still held inside the IP transmit
871 		 * system, make a copy of the packet first.
872 		 */
873 		if(bp->ref > 1){
874 			len = bp->wp - m->rp;
875 			nbp = allocb(len);
876 			memmove(nbp->wp, m->rp, len);
877 			nbp->wp += len;
878 			freeb(bp);
879 			bp  = nbp;
880 		}
881 		else{
882 			/* Patch up rp */
883 			bp->rp = m->rp;
884 		}
885 
886 		gre = (GREhdr *)bp->rp;
887 		memmove(gre->src, grec->coa, sizeof gre->dst);
888 		memmove(gre->dst, grec->south, sizeof gre->dst);
889 
890 		qunlock(&grec->lock);
891 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
892 		qlock(&grec->lock);
893 	}
894 
895 	while((bp = getring(&grec->dlbuffered)) != nil){
896 		gre = (GREhdr *)bp->rp;
897 		memmove(gre->src, grec->coa, sizeof gre->dst);
898 		memmove(gre->dst, grec->south, sizeof gre->dst);
899 
900 		qunlock(&grec->lock);
901 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
902 		qlock(&grec->lock);
903 	}
904 
905 	while((bp = getring(&grec->ulbuffered)) != nil){
906 		gre = (GREhdr *)bp->rp;
907 
908 		memmove(gre->src, grec->coa, sizeof gre->dst);
909 		memmove(gre->dst, grec->south, sizeof gre->dst);
910 
911 		qunlock(&grec->lock);
912 		ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
913 		qlock(&grec->lock);
914 	}
915 	qunlock(&grec->lock);
916 	return nil;
917 }
918 
919 static char *
grectlulkey(Conv * c,int,char ** argv)920 grectlulkey(Conv *c, int, char **argv)
921 {
922 	GREconv *grec;
923 
924 	grec = c->ptcl;
925 	grec->ulkey = strtoul(argv[1], nil, 0);
926 	return nil;
927 }
928 
929 char *
grectl(Conv * c,char ** f,int n)930 grectl(Conv *c, char **f, int n)
931 {
932 	int i;
933 
934 	if(n < 1)
935 		return "too few arguments";
936 
937 	for(i = 0; i < Ncmds; i++)
938 		if(strcmp(f[0], grectls[i].cmd) == 0)
939 			break;
940 
941 	if(i == Ncmds)
942 		return "no such command";
943 	if(grectls[i].argc != 0 && grectls[i].argc != n)
944 		return "incorrect number of arguments";
945 
946 	return grectls[i].f(c, n, f);
947 }
948 
949 void
greinit(Fs * fs)950 greinit(Fs *fs)
951 {
952 	Proto *gre;
953 
954 	gre = smalloc(sizeof(Proto));
955 	gre->priv = smalloc(sizeof(GREpriv));
956 	gre->name = "gre";
957 	gre->connect = greconnect;
958 	gre->announce = greannounce;
959 	gre->state = grestate;
960 	gre->create = grecreate;
961 	gre->close = greclose;
962 	gre->rcv = greiput;
963 	gre->ctl = grectl;
964 	gre->advise = nil;
965 	gre->stats = grestats;
966 	gre->ipproto = IP_GREPROTO;
967 	gre->nc = 64;
968 	gre->ptclsize = sizeof(GREconv);
969 
970 	Fsproto(fs, gre);
971 }
972