123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- /*
- * 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);
- }
|