Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 *
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
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 *
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
209
grecreate(Conv *c)
210
{
211
	c->rq = qopen(GREqlen, Qmsg, 0, c);
212
	c->wq = qbypass(grekick, c);
213
}
214
 
215
static int
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*
239
greannounce(Conv*, char**, int)
240
{
241
	return "gre does not support announce";
242
}
243
 
244
static void
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
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
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
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
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
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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 *
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
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
}