diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ip/gre.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ip/gre.c')
-rwxr-xr-x | sys/src/9/ip/gre.c | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/sys/src/9/ip/gre.c b/sys/src/9/ip/gre.c new file mode 100755 index 000000000..b7982dc33 --- /dev/null +++ b/sys/src/9/ip/gre.c @@ -0,0 +1,971 @@ +/* + * Generic Routing Encapsulation over IPv4, rfc1702 + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "ip.h" + +enum { + GRE_IPONLY = 12, /* size of ip header */ + GRE_IPPLUSGRE = 12, /* minimum size of GRE header */ + IP_GREPROTO = 47, + + GRErxms = 200, + GREtickms = 100, + GREmaxxmit = 10, + + K = 1024, + GREqlen = 256 * K, + + GRE_cksum = 0x8000, + GRE_routing = 0x4000, + GRE_key = 0x2000, + GRE_seq = 0x1000, + + Nring = 1 << 10, /* power of two, please */ + Ringmask = Nring - 1, + + GREctlraw = 0, + GREctlcooked, + GREctlretunnel, + GREctlreport, + GREctldlsuspend, + GREctlulsuspend, + GREctldlresume, + GREctlulresume, + GREctlforward, + GREctlulkey, + Ncmds, +}; + +typedef struct GREhdr GREhdr; +struct GREhdr{ + /* ip header */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar len[2]; /* packet length (including headers) */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; + uchar proto; /* Protocol */ + uchar cksum[2]; /* checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ + + /* gre header */ + uchar flags[2]; + uchar eproto[2]; /* encapsulation protocol */ +}; + +typedef struct GREpriv GREpriv; +struct GREpriv{ + /* non-MIB stats */ + ulong lenerr; /* short packet */ +}; + +typedef struct Bring Bring; +struct Bring{ + Block *ring[Nring]; + long produced; + long consumed; +}; + +typedef struct GREconv GREconv; +struct GREconv{ + int raw; + + /* Retunnelling information. v4 only */ + uchar north[4]; /* HA */ + uchar south[4]; /* Base station */ + uchar hoa[4]; /* Home address */ + uchar coa[4]; /* Careof address */ + ulong seq; /* Current sequence # */ + int dlsusp; /* Downlink suspended? */ + int ulsusp; /* Uplink suspended? */ + ulong ulkey; /* GRE key */ + + QLock lock; /* Lock for rings */ + Bring dlpending; /* Ring of pending packets */ + Bring dlbuffered; /* Received while suspended */ + Bring ulbuffered; /* Received while suspended */ +}; + +typedef struct Metablock Metablock; +struct Metablock{ + uchar *rp; + ulong seq; +}; + +static char *grectlcooked(Conv *, int, char **); +static char *grectldlresume(Conv *, int, char **); +static char *grectldlsuspend(Conv *, int, char **); +static char *grectlforward(Conv *, int, char **); +static char *grectlraw(Conv *, int, char **); +static char *grectlreport(Conv *, int, char **); +static char *grectlretunnel(Conv *, int, char **); +static char *grectlulkey(Conv *, int, char **); +static char *grectlulresume(Conv *, int, char **); +static char *grectlulsuspend(Conv *, int, char **); + +static struct{ + char *cmd; + int argc; + char *(*f)(Conv *, int, char **); +} grectls[Ncmds] = { +[GREctlraw] = { "raw", 1, grectlraw, }, +[GREctlcooked] = { "cooked", 1, grectlcooked, }, +[GREctlretunnel]= { "retunnel", 5, grectlretunnel, }, +[GREctlreport] = { "report", 2, grectlreport, }, +[GREctldlsuspend]= { "dlsuspend", 1, grectldlsuspend,}, +[GREctlulsuspend]= { "ulsuspend", 1, grectlulsuspend,}, +[GREctldlresume]= { "dlresume", 1, grectldlresume, }, +[GREctlulresume]= { "ulresume", 1, grectlulresume, }, +[GREctlforward] = { "forward", 2, grectlforward, }, +[GREctlulkey] = { "ulkey", 2, grectlulkey, }, +}; + +static uchar nulladdr[4]; +static char *sessend = "session end"; + +static void grekick(void *x, Block *bp); +static char *gresetup(Conv *, char *, char *, char *); + +ulong grepdin, grepdout, grebdin, grebdout; +ulong grepuin, grepuout, grebuin, grebuout; + +static Block * +getring(Bring *r) +{ + Block *bp; + + if(r->consumed == r->produced) + return nil; + + bp = r->ring[r->consumed & Ringmask]; + r->ring[r->consumed & Ringmask] = nil; + r->consumed++; + return bp; +} + +static void +addring(Bring *r, Block *bp) +{ + Block *tbp; + + if(r->produced - r->consumed > Ringmask){ + /* Full! */ + tbp = r->ring[r->produced & Ringmask]; + assert(tbp); + freeb(tbp); + r->consumed++; + } + r->ring[r->produced & Ringmask] = bp; + r->produced++; +} + +static char * +greconnect(Conv *c, char **argv, int argc) +{ + Proto *p; + char *err; + Conv *tc, **cp, **ecp; + + err = Fsstdconnect(c, argv, argc); + if(err != nil) + return err; + + /* make sure noone's already connected to this other sys */ + p = c->p; + qlock(p); + ecp = &p->conv[p->nc]; + for(cp = p->conv; cp < ecp; cp++){ + tc = *cp; + if(tc == nil) + break; + if(tc == c) + continue; + if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){ + err = "already connected to that addr/proto"; + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + break; + } + } + qunlock(p); + + if(err != nil) + return err; + Fsconnected(c, nil); + + return nil; +} + +static void +grecreate(Conv *c) +{ + c->rq = qopen(GREqlen, Qmsg, 0, c); + c->wq = qbypass(grekick, c); +} + +static int +grestate(Conv *c, char *state, int n) +{ + GREconv *grec; + char *ep, *p; + + grec = c->ptcl; + p = state; + ep = p + n; + p = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx " + "pending %uld %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n", + c->inuse? "Open ": "Closed ", + grec->raw? "raw ": "", + grec->dlsusp? "DL suspended ": "", + grec->ulsusp? "UL suspended ": "", + grec->hoa, grec->north, grec->south, grec->seq, + grec->dlpending.consumed, grec->dlpending.produced, + grec->dlbuffered.consumed, grec->dlbuffered.produced, + grec->ulbuffered.consumed, grec->ulbuffered.produced, + grec->ulkey); + return p - state; +} + +static char* +greannounce(Conv*, char**, int) +{ + return "gre does not support announce"; +} + +static void +greclose(Conv *c) +{ + GREconv *grec; + Block *bp; + + grec = c->ptcl; + + /* Make sure we don't forward any more packets */ + memset(grec->hoa, 0, sizeof grec->hoa); + memset(grec->north, 0, sizeof grec->north); + memset(grec->south, 0, sizeof grec->south); + + qlock(&grec->lock); + while((bp = getring(&grec->dlpending)) != nil) + freeb(bp); + + while((bp = getring(&grec->dlbuffered)) != nil) + freeb(bp); + + while((bp = getring(&grec->ulbuffered)) != nil) + freeb(bp); + + grec->dlpending.produced = grec->dlpending.consumed = 0; + grec->dlbuffered.produced = grec->dlbuffered.consumed = 0; + grec->ulbuffered.produced = grec->ulbuffered.consumed = 0; + qunlock(&grec->lock); + + grec->raw = 0; + grec->seq = 0; + grec->dlsusp = grec->ulsusp = 1; + + qhangup(c->rq, sessend); + qhangup(c->wq, sessend); + qhangup(c->eq, sessend); + ipmove(c->laddr, IPnoaddr); + ipmove(c->raddr, IPnoaddr); + c->lport = c->rport = 0; +} + +static void +grekick(void *x, Block *bp) +{ + Conv *c; + GREconv *grec; + GREhdr *gre; + uchar laddr[IPaddrlen], raddr[IPaddrlen]; + + if(bp == nil) + return; + + c = x; + grec = c->ptcl; + + /* Make space to fit ip header (gre header already there) */ + bp = padblock(bp, GRE_IPONLY); + if(bp == nil) + return; + + /* make sure the message has a GRE header */ + bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE); + if(bp == nil) + return; + + gre = (GREhdr *)bp->rp; + gre->vihl = IP_VER4; + + if(grec->raw == 0){ + v4tov6(raddr, gre->dst); + if(ipcmp(raddr, v4prefix) == 0) + memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen); + v4tov6(laddr, gre->src); + if(ipcmp(laddr, v4prefix) == 0){ + if(ipcmp(c->laddr, IPnoaddr) == 0) + /* pick interface closest to dest */ + findlocalip(c->p->f, c->laddr, raddr); + memmove(gre->src, c->laddr + IPv4off, sizeof gre->src); + } + hnputs(gre->eproto, c->rport); + } + + gre->proto = IP_GREPROTO; + gre->frag[0] = gre->frag[1] = 0; + + grepdout++; + grebdout += BLEN(bp); + ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil); +} + +static void +gredownlink(Conv *c, Block *bp) +{ + Metablock *m; + GREconv *grec; + GREhdr *gre; + int hdrlen, suspended, extra; + ushort flags; + ulong seq; + + gre = (GREhdr *)bp->rp; + if(gre->ttl == 1){ + freeb(bp); + return; + } + + /* + * We've received a packet with a GRE header and we need to + * re-adjust the packet header to strip all unwanted parts + * but leave room for only a sequence number. + */ + grec = c->ptcl; + flags = nhgets(gre->flags); + hdrlen = 0; + if(flags & GRE_cksum) + hdrlen += 2; + if(flags & GRE_routing){ + print("%V routing info present. Discarding packet", gre->src); + freeb(bp); + return; + } + if(flags & (GRE_cksum|GRE_routing)) + hdrlen += 2; /* Offset field */ + if(flags & GRE_key) + hdrlen += 4; + if(flags & GRE_seq) + hdrlen += 4; + + /* + * The outgoing packet only has the sequence number set. Make room + * for the sequence number. + */ + if(hdrlen != sizeof(ulong)){ + extra = hdrlen - sizeof(ulong); + if(extra < 0 && bp->rp - bp->base < -extra){ + print("gredownlink: cannot add sequence number\n"); + freeb(bp); + return; + } + memmove(bp->rp + extra, bp->rp, sizeof(GREhdr)); + bp->rp += extra; + assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong)); + gre = (GREhdr *)bp->rp; + } + seq = grec->seq++; + hnputs(gre->flags, GRE_seq); + hnputl(bp->rp + sizeof(GREhdr), seq); + + /* + * Keep rp and seq at the base. ipoput4 consumes rp for + * refragmentation. + */ + assert(bp->rp - bp->base >= sizeof(Metablock)); + m = (Metablock *)bp->base; + m->rp = bp->rp; + m->seq = seq; + + /* + * Here we make a decision what we're doing with the packet. We're + * doing this w/o holding a lock which means that later on in the + * process we may discover we've done the wrong thing. I don't want + * to call ipoput with the lock held. + */ +restart: + suspended = grec->dlsusp; + if(suspended){ + if(!canqlock(&grec->lock)){ + /* + * just give up. too bad, we lose a packet. this + * is just too hard and my brain already hurts. + */ + freeb(bp); + return; + } + + if(!grec->dlsusp){ + /* + * suspend race. We though we were suspended, but + * we really weren't. + */ + qunlock(&grec->lock); + goto restart; + } + + /* Undo the incorrect ref count addition */ + addring(&grec->dlbuffered, bp); + qunlock(&grec->lock); + return; + } + + /* + * When we get here, we're not suspended. Proceed to send the + * packet. + */ + memmove(gre->src, grec->coa, sizeof gre->dst); + memmove(gre->dst, grec->south, sizeof gre->dst); + + /* + * Make sure the packet does not go away. + */ + _xinc(&bp->ref); + assert(bp->ref == 2); + + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + grepdout++; + grebdout += BLEN(bp); + + /* + * Now make sure we didn't do the wrong thing. + */ + if(!canqlock(&grec->lock)){ + freeb(bp); /* The packet just goes away */ + return; + } + + /* We did the right thing */ + addring(&grec->dlpending, bp); + qunlock(&grec->lock); +} + +static void +greuplink(Conv *c, Block *bp) +{ + GREconv *grec; + GREhdr *gre; + ushort flags; + + gre = (GREhdr *)bp->rp; + if(gre->ttl == 1) + return; + + grec = c->ptcl; + memmove(gre->src, grec->coa, sizeof gre->src); + memmove(gre->dst, grec->north, sizeof gre->dst); + + /* + * Add a key, if needed. + */ + if(grec->ulkey){ + flags = nhgets(gre->flags); + if(flags & (GRE_cksum|GRE_routing)){ + print("%V routing info present. Discarding packet\n", + gre->src); + freeb(bp); + return; + } + + if((flags & GRE_key) == 0){ + /* Make room for the key */ + if(bp->rp - bp->base < sizeof(ulong)){ + print("%V can't add key\n", gre->src); + freeb(bp); + return; + } + + bp->rp -= 4; + memmove(bp->rp, bp->rp + 4, sizeof(GREhdr)); + + gre = (GREhdr *)bp->rp; + hnputs(gre->flags, flags | GRE_key); + } + + /* Add the key */ + hnputl(bp->rp + sizeof(GREhdr), grec->ulkey); + } + + if(!canqlock(&grec->lock)){ + freeb(bp); + return; + } + + if(grec->ulsusp) + addring(&grec->ulbuffered, bp); + else{ + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + grepuout++; + grebuout += BLEN(bp); + } + qunlock(&grec->lock); +} + +static void +greiput(Proto *proto, Ipifc *, Block *bp) +{ + int len, hdrlen; + ushort eproto, flags; + uchar raddr[IPaddrlen]; + Conv *c, **p; + GREconv *grec; + GREhdr *gre; + GREpriv *gpriv; + Ip4hdr *ip; + + /* + * We don't want to deal with block lists. Ever. The problem is + * that when the block is forwarded, devether.c puts the block into + * a queue that also uses ->next. Just do not use ->next here! + */ + if(bp->next){ + len = blocklen(bp); + bp = pullupblock(bp, len); + assert(BLEN(bp) == len && bp->next == nil); + } + + gre = (GREhdr *)bp->rp; + if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){ + freeb(bp); + return; + } + + v4tov6(raddr, gre->src); + eproto = nhgets(gre->eproto); + flags = nhgets(gre->flags); + hdrlen = sizeof(GREhdr); + + if(flags & GRE_cksum) + hdrlen += 2; + if(flags & GRE_routing){ + print("%I routing info present. Discarding packet\n", raddr); + freeb(bp); + return; + } + if(flags & (GRE_cksum|GRE_routing)) + hdrlen += 2; /* Offset field */ + if(flags & GRE_key) + hdrlen += 4; + if(flags & GRE_seq) + hdrlen += 4; + + if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){ + print("greretunnel: packet too short (s=%V d=%V)\n", + gre->src, gre->dst); + freeb(bp); + return; + } + ip = (Ip4hdr *)(bp->rp + hdrlen); + + qlock(proto); + /* + * Look for a conversation structure for this port and address, or + * match the retunnel part, or match on the raw flag. + */ + for(p = proto->conv; *p; p++) { + c = *p; + + if(c->inuse == 0) + continue; + + /* + * Do not stop this session - blocking here + * implies that etherread is blocked. + */ + grec = c->ptcl; + if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){ + grepdin++; + grebdin += BLEN(bp); + gredownlink(c, bp); + qunlock(proto); + return; + } + + if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){ + grepuin++; + grebuin += BLEN(bp); + greuplink(c, bp); + qunlock(proto); + return; + } + } + + /* + * when we get here, none of the forwarding tunnels matched. now + * try to match on raw and conversational sessions. + */ + for(c = nil, p = proto->conv; *p; p++) { + c = *p; + + if(c->inuse == 0) + continue; + + /* + * Do not stop this session - blocking here + * implies that etherread is blocked. + */ + grec = c->ptcl; + if(c->rport == eproto && + (grec->raw || ipcmp(c->raddr, raddr) == 0)) + break; + } + + qunlock(proto); + + if(*p == nil){ + freeb(bp); + return; + } + + /* + * Trim the packet down to data size + */ + len = nhgets(gre->len) - GRE_IPONLY; + if(len < GRE_IPPLUSGRE){ + freeb(bp); + return; + } + + bp = trimblock(bp, GRE_IPONLY, len); + if(bp == nil){ + gpriv = proto->priv; + gpriv->lenerr++; + return; + } + + /* + * Can't delimit packet so pull it all into one block. + */ + if(qlen(c->rq) > GREqlen) + freeb(bp); + else{ + bp = concatblock(bp); + if(bp == 0) + panic("greiput"); + qpass(c->rq, bp); + } +} + +int +grestats(Proto *gre, char *buf, int len) +{ + GREpriv *gpriv; + + gpriv = gre->priv; + return snprint(buf, len, + "gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n", + grepdin, grepdout, grepuin, grepuout, + grebdin, grebdout, grebuin, grebuout, gpriv->lenerr); +} + +static char * +grectlraw(Conv *c, int, char **) +{ + GREconv *grec; + + grec = c->ptcl; + grec->raw = 1; + return nil; +} + +static char * +grectlcooked(Conv *c, int, char **) +{ + GREconv *grec; + + grec = c->ptcl; + grec->raw = 0; + return nil; +} + +static char * +grectlretunnel(Conv *c, int, char **argv) +{ + GREconv *grec; + uchar ipaddr[4]; + + grec = c->ptcl; + if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa)) + return "tunnel already set up"; + + v4parseip(ipaddr, argv[1]); + if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0) + return "bad hoa"; + memmove(grec->hoa, ipaddr, sizeof grec->hoa); + v4parseip(ipaddr, argv[2]); + memmove(grec->north, ipaddr, sizeof grec->north); + v4parseip(ipaddr, argv[3]); + memmove(grec->south, ipaddr, sizeof grec->south); + v4parseip(ipaddr, argv[4]); + memmove(grec->coa, ipaddr, sizeof grec->coa); + grec->ulsusp = 1; + grec->dlsusp = 0; + + return nil; +} + +static char * +grectlreport(Conv *c, int, char **argv) +{ + ulong seq; + Block *bp; + Bring *r; + GREconv *grec; + Metablock *m; + + grec = c->ptcl; + seq = strtoul(argv[1], nil, 0); + + qlock(&grec->lock); + r = &grec->dlpending; + while(r->produced - r->consumed > 0){ + bp = r->ring[r->consumed & Ringmask]; + + assert(bp && bp->rp - bp->base >= sizeof(Metablock)); + m = (Metablock *)bp->base; + if((long)(seq - m->seq) <= 0) + break; + + r->ring[r->consumed & Ringmask] = nil; + r->consumed++; + + freeb(bp); + } + qunlock(&grec->lock); + return nil; +} + +static char * +grectldlsuspend(Conv *c, int, char **) +{ + GREconv *grec; + + grec = c->ptcl; + if(grec->dlsusp) + return "already suspended"; + + grec->dlsusp = 1; + return nil; +} + +static char * +grectlulsuspend(Conv *c, int, char **) +{ + GREconv *grec; + + grec = c->ptcl; + if(grec->ulsusp) + return "already suspended"; + + grec->ulsusp = 1; + return nil; +} + +static char * +grectldlresume(Conv *c, int, char **) +{ + GREconv *grec; + GREhdr *gre; + Block *bp; + + grec = c->ptcl; + + qlock(&grec->lock); + if(!grec->dlsusp){ + qunlock(&grec->lock); + return "not suspended"; + } + + while((bp = getring(&grec->dlbuffered)) != nil){ + gre = (GREhdr *)bp->rp; + qunlock(&grec->lock); + + /* + * Make sure the packet does not go away. + */ + _xinc(&bp->ref); + assert(bp->ref == 2); + + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + + qlock(&grec->lock); + addring(&grec->dlpending, bp); + } + grec->dlsusp = 0; + qunlock(&grec->lock); + return nil; +} + +static char * +grectlulresume(Conv *c, int, char **) +{ + GREconv *grec; + GREhdr *gre; + Block *bp; + + grec = c->ptcl; + + qlock(&grec->lock); + while((bp = getring(&grec->ulbuffered)) != nil){ + gre = (GREhdr *)bp->rp; + + qunlock(&grec->lock); + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + qlock(&grec->lock); + } + grec->ulsusp = 0; + qunlock(&grec->lock); + return nil; +} + +static char * +grectlforward(Conv *c, int, char **argv) +{ + int len; + Block *bp, *nbp; + GREconv *grec; + GREhdr *gre; + Metablock *m; + + grec = c->ptcl; + + v4parseip(grec->south, argv[1]); + memmove(grec->north, grec->south, sizeof grec->north); + + qlock(&grec->lock); + if(!grec->dlsusp){ + qunlock(&grec->lock); + return "not suspended"; + } + grec->dlsusp = 0; + grec->ulsusp = 0; + + while((bp = getring(&grec->dlpending)) != nil){ + + assert(bp->rp - bp->base >= sizeof(Metablock)); + m = (Metablock *)bp->base; + assert(m->rp >= bp->base && m->rp < bp->lim); + + /* + * If the packet is still held inside the IP transmit + * system, make a copy of the packet first. + */ + if(bp->ref > 1){ + len = bp->wp - m->rp; + nbp = allocb(len); + memmove(nbp->wp, m->rp, len); + nbp->wp += len; + freeb(bp); + bp = nbp; + } + else{ + /* Patch up rp */ + bp->rp = m->rp; + } + + gre = (GREhdr *)bp->rp; + memmove(gre->src, grec->coa, sizeof gre->dst); + memmove(gre->dst, grec->south, sizeof gre->dst); + + qunlock(&grec->lock); + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + qlock(&grec->lock); + } + + while((bp = getring(&grec->dlbuffered)) != nil){ + gre = (GREhdr *)bp->rp; + memmove(gre->src, grec->coa, sizeof gre->dst); + memmove(gre->dst, grec->south, sizeof gre->dst); + + qunlock(&grec->lock); + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + qlock(&grec->lock); + } + + while((bp = getring(&grec->ulbuffered)) != nil){ + gre = (GREhdr *)bp->rp; + + memmove(gre->src, grec->coa, sizeof gre->dst); + memmove(gre->dst, grec->south, sizeof gre->dst); + + qunlock(&grec->lock); + ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); + qlock(&grec->lock); + } + qunlock(&grec->lock); + return nil; +} + +static char * +grectlulkey(Conv *c, int, char **argv) +{ + GREconv *grec; + + grec = c->ptcl; + grec->ulkey = strtoul(argv[1], nil, 0); + return nil; +} + +char * +grectl(Conv *c, char **f, int n) +{ + int i; + + if(n < 1) + return "too few arguments"; + + for(i = 0; i < Ncmds; i++) + if(strcmp(f[0], grectls[i].cmd) == 0) + break; + + if(i == Ncmds) + return "no such command"; + if(grectls[i].argc != 0 && grectls[i].argc != n) + return "incorrect number of arguments"; + + return grectls[i].f(c, n, f); +} + +void +greinit(Fs *fs) +{ + Proto *gre; + + gre = smalloc(sizeof(Proto)); + gre->priv = smalloc(sizeof(GREpriv)); + gre->name = "gre"; + gre->connect = greconnect; + gre->announce = greannounce; + gre->state = grestate; + gre->create = grecreate; + gre->close = greclose; + gre->rcv = greiput; + gre->ctl = grectl; + gre->advise = nil; + gre->stats = grestats; + gre->ipproto = IP_GREPROTO; + gre->nc = 64; + gre->ptclsize = sizeof(GREconv); + + Fsproto(fs, gre); +} |