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