2 * Generic Routing Encapsulation over IPv4, rfc1702
5 #include "../port/lib.h"
9 #include "../port/error.h"
14 GRE_IPONLY = 12, /* size of ip header */
15 GRE_IPPLUSGRE = 12, /* minimum size of GRE header */
30 Nring = 1 << 10, /* power of two, please */
46 typedef struct GREhdr GREhdr;
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 */
55 uchar proto; /* Protocol */
56 uchar cksum[2]; /* checksum */
57 uchar src[4]; /* Ip source */
58 uchar dst[4]; /* Ip destination */
62 uchar eproto[2]; /* encapsulation protocol */
65 typedef struct GREpriv GREpriv;
68 ulong lenerr; /* short packet */
71 typedef struct Bring Bring;
78 typedef struct GREconv GREconv;
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 */
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 */
98 typedef struct Metablock Metablock;
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 **);
118 char *(*f)(Conv *, int, char **);
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, },
132 static uchar nulladdr[4];
133 static char *sessend = "session end";
135 static void grekick(void *x, Block *bp);
136 static char *gresetup(Conv *, char *, char *, char *);
138 ulong grepdin, grepdout, grebdin, grebdout;
139 ulong grepuin, grepuout, grebuin, grebuout;
146 if(r->consumed == r->produced)
149 bp = r->ring[r->consumed & Ringmask];
150 r->ring[r->consumed & Ringmask] = nil;
156 addring(Bring *r, Block *bp)
160 if(r->produced - r->consumed > Ringmask){
162 tbp = r->ring[r->produced & Ringmask];
167 r->ring[r->produced & Ringmask] = bp;
172 greconnect(Conv *c, char **argv, int argc)
176 Conv *tc, **cp, **ecp;
178 err = Fsstdconnect(c, argv, argc);
182 /* make sure noone's already connected to this other sys */
185 ecp = &p->conv[p->nc];
186 for(cp = p->conv; cp < ecp; cp++){
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);
211 c->rq = qopen(GREqlen, Qmsg, 0, c);
212 c->wq = qbypass(grekick, c);
216 grestate(Conv *c, char *state, int 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,
239 greannounce(Conv*, char**, int)
241 return "gre does not support announce";
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);
258 while((bp = getring(&grec->dlpending)) != nil)
261 while((bp = getring(&grec->dlbuffered)) != nil)
264 while((bp = getring(&grec->ulbuffered)) != nil)
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);
274 grec->dlsusp = grec->ulsusp = 1;
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;
285 grekick(void *x, Block *bp)
290 uchar laddr[IPaddrlen], raddr[IPaddrlen];
298 /* Make space to fit ip header (gre header already there) */
299 bp = padblock(bp, GRE_IPONLY);
303 /* make sure the message has a GRE header */
304 bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
308 gre = (GREhdr *)bp->rp;
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);
322 hnputs(gre->eproto, c->rport);
325 gre->proto = IP_GREPROTO;
326 gre->frag[0] = gre->frag[1] = 0;
329 grebdout += BLEN(bp);
330 ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
334 gredownlink(Conv *c, Block *bp)
339 int hdrlen, suspended, extra;
343 gre = (GREhdr *)bp->rp;
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.
355 flags = nhgets(gre->flags);
357 if(flags & GRE_cksum)
359 if(flags & GRE_routing){
360 print("%V routing info present. Discarding packet", gre->src);
364 if(flags & (GRE_cksum|GRE_routing))
365 hdrlen += 2; /* Offset field */
372 * The outgoing packet only has the sequence number set. Make room
373 * for the sequence number.
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");
382 memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
384 assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
385 gre = (GREhdr *)bp->rp;
388 hnputs(gre->flags, GRE_seq);
389 hnputl(bp->rp + sizeof(GREhdr), seq);
392 * Keep rp and seq at the base. ipoput4 consumes rp for
395 assert(bp->rp - bp->base >= sizeof(Metablock));
396 m = (Metablock *)bp->base;
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.
407 suspended = grec->dlsusp;
409 if(!canqlock(&grec->lock)){
411 * just give up. too bad, we lose a packet. this
412 * is just too hard and my brain already hurts.
420 * suspend race. We though we were suspended, but
423 qunlock(&grec->lock);
427 /* Undo the incorrect ref count addition */
428 addring(&grec->dlbuffered, bp);
429 qunlock(&grec->lock);
434 * When we get here, we're not suspended. Proceed to send the
437 memmove(gre->src, grec->coa, sizeof gre->dst);
438 memmove(gre->dst, grec->south, sizeof gre->dst);
441 * Make sure the packet does not go away.
444 assert(bp->ref == 2);
446 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
448 grebdout += BLEN(bp);
451 * Now make sure we didn't do the wrong thing.
453 if(!canqlock(&grec->lock)){
454 freeb(bp); /* The packet just goes away */
458 /* We did the right thing */
459 addring(&grec->dlpending, bp);
460 qunlock(&grec->lock);
464 greuplink(Conv *c, Block *bp)
470 gre = (GREhdr *)bp->rp;
475 memmove(gre->src, grec->coa, sizeof gre->src);
476 memmove(gre->dst, grec->north, sizeof gre->dst);
479 * Add a key, if needed.
482 flags = nhgets(gre->flags);
483 if(flags & (GRE_cksum|GRE_routing)){
484 print("%V routing info present. Discarding packet\n",
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);
499 memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
501 gre = (GREhdr *)bp->rp;
502 hnputs(gre->flags, flags | GRE_key);
506 hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
509 if(!canqlock(&grec->lock)){
515 addring(&grec->ulbuffered, bp);
517 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
519 grebuout += BLEN(bp);
521 qunlock(&grec->lock);
525 greiput(Proto *proto, Ipifc *, Block *bp)
528 ushort eproto, flags;
529 uchar raddr[IPaddrlen];
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!
543 bp = pullupblock(bp, len);
544 assert(BLEN(bp) == len && bp->next == nil);
547 gre = (GREhdr *)bp->rp;
548 if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
553 v4tov6(raddr, gre->src);
554 eproto = nhgets(gre->eproto);
555 flags = nhgets(gre->flags);
556 hdrlen = sizeof(GREhdr);
558 if(flags & GRE_cksum)
560 if(flags & GRE_routing){
561 print("%I routing info present. Discarding packet\n", raddr);
565 if(flags & (GRE_cksum|GRE_routing))
566 hdrlen += 2; /* Offset field */
572 if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
573 print("greretunnel: packet too short (s=%V d=%V)\n",
578 ip = (Ip4hdr *)(bp->rp + hdrlen);
582 * Look for a conversation structure for this port and address, or
583 * match the retunnel part, or match on the raw flag.
585 for(p = proto->conv; *p; p++) {
592 * Do not stop this session - blocking here
593 * implies that etherread is blocked.
596 if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
604 if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
614 * when we get here, none of the forwarding tunnels matched. now
615 * try to match on raw and conversational sessions.
617 for(c = nil, p = proto->conv; *p; p++) {
624 * Do not stop this session - blocking here
625 * implies that etherread is blocked.
628 if(c->rport == eproto &&
629 (grec->raw || ipcmp(c->raddr, raddr) == 0))
641 * Trim the packet down to data size
643 len = nhgets(gre->len) - GRE_IPONLY;
644 if(len < GRE_IPPLUSGRE){
649 bp = trimblock(bp, GRE_IPONLY, len);
657 * Can't delimit packet so pull it all into one block.
659 if(qlen(c->rq) > GREqlen)
662 bp = concatblock(bp);
670 grestats(Proto *gre, char *buf, int len)
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);
682 grectlraw(Conv *c, int, char **)
692 grectlcooked(Conv *c, int, char **)
702 grectlretunnel(Conv *c, int, char **argv)
708 if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
709 return "tunnel already set up";
711 v4parseip(ipaddr, argv[1]);
712 if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
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);
728 grectlreport(Conv *c, int, char **argv)
737 seq = strtoul(argv[1], nil, 0);
740 r = &grec->dlpending;
741 while(r->produced - r->consumed > 0){
742 bp = r->ring[r->consumed & Ringmask];
744 assert(bp && bp->rp - bp->base >= sizeof(Metablock));
745 m = (Metablock *)bp->base;
746 if((long)(seq - m->seq) <= 0)
749 r->ring[r->consumed & Ringmask] = nil;
754 qunlock(&grec->lock);
759 grectldlsuspend(Conv *c, int, char **)
765 return "already suspended";
772 grectlulsuspend(Conv *c, int, char **)
778 return "already suspended";
785 grectldlresume(Conv *c, int, char **)
795 qunlock(&grec->lock);
796 return "not suspended";
799 while((bp = getring(&grec->dlbuffered)) != nil){
800 gre = (GREhdr *)bp->rp;
801 qunlock(&grec->lock);
804 * Make sure the packet does not go away.
807 assert(bp->ref == 2);
809 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
812 addring(&grec->dlpending, bp);
815 qunlock(&grec->lock);
820 grectlulresume(Conv *c, int, char **)
829 while((bp = getring(&grec->ulbuffered)) != nil){
830 gre = (GREhdr *)bp->rp;
832 qunlock(&grec->lock);
833 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
837 qunlock(&grec->lock);
842 grectlforward(Conv *c, int, char **argv)
852 v4parseip(grec->south, argv[1]);
853 memmove(grec->north, grec->south, sizeof grec->north);
857 qunlock(&grec->lock);
858 return "not suspended";
863 while((bp = getring(&grec->dlpending)) != nil){
865 assert(bp->rp - bp->base >= sizeof(Metablock));
866 m = (Metablock *)bp->base;
867 assert(m->rp >= bp->base && m->rp < bp->lim);
870 * If the packet is still held inside the IP transmit
871 * system, make a copy of the packet first.
874 len = bp->wp - m->rp;
876 memmove(nbp->wp, m->rp, len);
886 gre = (GREhdr *)bp->rp;
887 memmove(gre->src, grec->coa, sizeof gre->dst);
888 memmove(gre->dst, grec->south, sizeof gre->dst);
890 qunlock(&grec->lock);
891 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
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);
900 qunlock(&grec->lock);
901 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
905 while((bp = getring(&grec->ulbuffered)) != nil){
906 gre = (GREhdr *)bp->rp;
908 memmove(gre->src, grec->coa, sizeof gre->dst);
909 memmove(gre->dst, grec->south, sizeof gre->dst);
911 qunlock(&grec->lock);
912 ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
915 qunlock(&grec->lock);
920 grectlulkey(Conv *c, int, char **argv)
925 grec->ulkey = strtoul(argv[1], nil, 0);
930 grectl(Conv *c, char **f, int n)
935 return "too few arguments";
937 for(i = 0; i < Ncmds; i++)
938 if(strcmp(f[0], grectls[i].cmd) == 0)
942 return "no such command";
943 if(grectls[i].argc != 0 && grectls[i].argc != n)
944 return "incorrect number of arguments";
946 return grectls[i].f(c, n, f);
954 gre = smalloc(sizeof(Proto));
955 gre->priv = smalloc(sizeof(GREpriv));
957 gre->connect = greconnect;
958 gre->announce = greannounce;
959 gre->state = grestate;
960 gre->create = grecreate;
961 gre->close = greclose;
965 gre->stats = grestats;
966 gre->ipproto = IP_GREPROTO;
968 gre->ptclsize = sizeof(GREconv);