19ef1f84bSDavid du Colombier /*
29ef1f84bSDavid du Colombier * Generic Routing Encapsulation over IPv4, rfc1702
39ef1f84bSDavid du Colombier */
49ef1f84bSDavid du Colombier #include "u.h"
59ef1f84bSDavid du Colombier #include "../port/lib.h"
69ef1f84bSDavid du Colombier #include "mem.h"
79ef1f84bSDavid du Colombier #include "dat.h"
89ef1f84bSDavid du Colombier #include "fns.h"
99ef1f84bSDavid du Colombier #include "../port/error.h"
109ef1f84bSDavid du Colombier
119ef1f84bSDavid du Colombier #include "ip.h"
129ef1f84bSDavid du Colombier
139ef1f84bSDavid du Colombier enum {
149ef1f84bSDavid du Colombier GRE_IPONLY = 12, /* size of ip header */
159ef1f84bSDavid du Colombier GRE_IPPLUSGRE = 12, /* minimum size of GRE header */
169ef1f84bSDavid du Colombier IP_GREPROTO = 47,
179ef1f84bSDavid du Colombier
189ef1f84bSDavid du Colombier GRErxms = 200,
199ef1f84bSDavid du Colombier GREtickms = 100,
209ef1f84bSDavid du Colombier GREmaxxmit = 10,
219ef1f84bSDavid du Colombier
229ef1f84bSDavid du Colombier K = 1024,
239ef1f84bSDavid du Colombier GREqlen = 256 * K,
249ef1f84bSDavid du Colombier
259ef1f84bSDavid du Colombier GRE_cksum = 0x8000,
269ef1f84bSDavid du Colombier GRE_routing = 0x4000,
279ef1f84bSDavid du Colombier GRE_key = 0x2000,
289ef1f84bSDavid du Colombier GRE_seq = 0x1000,
299ef1f84bSDavid du Colombier
309ef1f84bSDavid du Colombier Nring = 1 << 10, /* power of two, please */
319ef1f84bSDavid du Colombier Ringmask = Nring - 1,
329ef1f84bSDavid du Colombier
339ef1f84bSDavid du Colombier GREctlraw = 0,
349ef1f84bSDavid du Colombier GREctlcooked,
359ef1f84bSDavid du Colombier GREctlretunnel,
369ef1f84bSDavid du Colombier GREctlreport,
379ef1f84bSDavid du Colombier GREctldlsuspend,
389ef1f84bSDavid du Colombier GREctlulsuspend,
399ef1f84bSDavid du Colombier GREctldlresume,
409ef1f84bSDavid du Colombier GREctlulresume,
419ef1f84bSDavid du Colombier GREctlforward,
429ef1f84bSDavid du Colombier GREctlulkey,
439ef1f84bSDavid du Colombier Ncmds,
449ef1f84bSDavid du Colombier };
459ef1f84bSDavid du Colombier
469ef1f84bSDavid du Colombier typedef struct GREhdr GREhdr;
479ef1f84bSDavid du Colombier struct GREhdr{
489ef1f84bSDavid du Colombier /* ip header */
499ef1f84bSDavid du Colombier uchar vihl; /* Version and header length */
509ef1f84bSDavid du Colombier uchar tos; /* Type of service */
519ef1f84bSDavid du Colombier uchar len[2]; /* packet length (including headers) */
529ef1f84bSDavid du Colombier uchar id[2]; /* Identification */
539ef1f84bSDavid du Colombier uchar frag[2]; /* Fragment information */
549ef1f84bSDavid du Colombier uchar ttl;
559ef1f84bSDavid du Colombier uchar proto; /* Protocol */
569ef1f84bSDavid du Colombier uchar cksum[2]; /* checksum */
579ef1f84bSDavid du Colombier uchar src[4]; /* Ip source */
589ef1f84bSDavid du Colombier uchar dst[4]; /* Ip destination */
599ef1f84bSDavid du Colombier
609ef1f84bSDavid du Colombier /* gre header */
619ef1f84bSDavid du Colombier uchar flags[2];
629ef1f84bSDavid du Colombier uchar eproto[2]; /* encapsulation protocol */
639ef1f84bSDavid du Colombier };
649ef1f84bSDavid du Colombier
659ef1f84bSDavid du Colombier typedef struct GREpriv GREpriv;
669ef1f84bSDavid du Colombier struct GREpriv{
679ef1f84bSDavid du Colombier /* non-MIB stats */
689ef1f84bSDavid du Colombier ulong lenerr; /* short packet */
699ef1f84bSDavid du Colombier };
709ef1f84bSDavid du Colombier
719ef1f84bSDavid du Colombier typedef struct Bring Bring;
729ef1f84bSDavid du Colombier struct Bring{
739ef1f84bSDavid du Colombier Block *ring[Nring];
749ef1f84bSDavid du Colombier long produced;
759ef1f84bSDavid du Colombier long consumed;
769ef1f84bSDavid du Colombier };
779ef1f84bSDavid du Colombier
789ef1f84bSDavid du Colombier typedef struct GREconv GREconv;
799ef1f84bSDavid du Colombier struct GREconv{
809ef1f84bSDavid du Colombier int raw;
819ef1f84bSDavid du Colombier
829ef1f84bSDavid du Colombier /* Retunnelling information. v4 only */
839ef1f84bSDavid du Colombier uchar north[4]; /* HA */
849ef1f84bSDavid du Colombier uchar south[4]; /* Base station */
859ef1f84bSDavid du Colombier uchar hoa[4]; /* Home address */
869ef1f84bSDavid du Colombier uchar coa[4]; /* Careof address */
879ef1f84bSDavid du Colombier ulong seq; /* Current sequence # */
889ef1f84bSDavid du Colombier int dlsusp; /* Downlink suspended? */
899ef1f84bSDavid du Colombier int ulsusp; /* Uplink suspended? */
909ef1f84bSDavid du Colombier ulong ulkey; /* GRE key */
919ef1f84bSDavid du Colombier
929ef1f84bSDavid du Colombier QLock lock; /* Lock for rings */
939ef1f84bSDavid du Colombier Bring dlpending; /* Ring of pending packets */
949ef1f84bSDavid du Colombier Bring dlbuffered; /* Received while suspended */
959ef1f84bSDavid du Colombier Bring ulbuffered; /* Received while suspended */
969ef1f84bSDavid du Colombier };
979ef1f84bSDavid du Colombier
989ef1f84bSDavid du Colombier typedef struct Metablock Metablock;
999ef1f84bSDavid du Colombier struct Metablock{
1009ef1f84bSDavid du Colombier uchar *rp;
1019ef1f84bSDavid du Colombier ulong seq;
1029ef1f84bSDavid du Colombier };
1039ef1f84bSDavid du Colombier
1049ef1f84bSDavid du Colombier static char *grectlcooked(Conv *, int, char **);
1059ef1f84bSDavid du Colombier static char *grectldlresume(Conv *, int, char **);
1069ef1f84bSDavid du Colombier static char *grectldlsuspend(Conv *, int, char **);
1079ef1f84bSDavid du Colombier static char *grectlforward(Conv *, int, char **);
1089ef1f84bSDavid du Colombier static char *grectlraw(Conv *, int, char **);
1099ef1f84bSDavid du Colombier static char *grectlreport(Conv *, int, char **);
1109ef1f84bSDavid du Colombier static char *grectlretunnel(Conv *, int, char **);
1119ef1f84bSDavid du Colombier static char *grectlulkey(Conv *, int, char **);
1129ef1f84bSDavid du Colombier static char *grectlulresume(Conv *, int, char **);
1139ef1f84bSDavid du Colombier static char *grectlulsuspend(Conv *, int, char **);
1149ef1f84bSDavid du Colombier
1159ef1f84bSDavid du Colombier static struct{
1169ef1f84bSDavid du Colombier char *cmd;
1179ef1f84bSDavid du Colombier int argc;
1189ef1f84bSDavid du Colombier char *(*f)(Conv *, int, char **);
1199ef1f84bSDavid du Colombier } grectls[Ncmds] = {
1209ef1f84bSDavid du Colombier [GREctlraw] = { "raw", 1, grectlraw, },
1219ef1f84bSDavid du Colombier [GREctlcooked] = { "cooked", 1, grectlcooked, },
1229ef1f84bSDavid du Colombier [GREctlretunnel]= { "retunnel", 5, grectlretunnel, },
1239ef1f84bSDavid du Colombier [GREctlreport] = { "report", 2, grectlreport, },
1249ef1f84bSDavid du Colombier [GREctldlsuspend]= { "dlsuspend", 1, grectldlsuspend,},
1259ef1f84bSDavid du Colombier [GREctlulsuspend]= { "ulsuspend", 1, grectlulsuspend,},
1269ef1f84bSDavid du Colombier [GREctldlresume]= { "dlresume", 1, grectldlresume, },
1279ef1f84bSDavid du Colombier [GREctlulresume]= { "ulresume", 1, grectlulresume, },
1289ef1f84bSDavid du Colombier [GREctlforward] = { "forward", 2, grectlforward, },
1299ef1f84bSDavid du Colombier [GREctlulkey] = { "ulkey", 2, grectlulkey, },
1309ef1f84bSDavid du Colombier };
1319ef1f84bSDavid du Colombier
1329ef1f84bSDavid du Colombier static uchar nulladdr[4];
1339ef1f84bSDavid du Colombier static char *sessend = "session end";
1349ef1f84bSDavid du Colombier
1359ef1f84bSDavid du Colombier static void grekick(void *x, Block *bp);
1369ef1f84bSDavid du Colombier static char *gresetup(Conv *, char *, char *, char *);
1379ef1f84bSDavid du Colombier
1389ef1f84bSDavid du Colombier ulong grepdin, grepdout, grebdin, grebdout;
1399ef1f84bSDavid du Colombier ulong grepuin, grepuout, grebuin, grebuout;
1409ef1f84bSDavid du Colombier
1419ef1f84bSDavid du Colombier static Block *
getring(Bring * r)1429ef1f84bSDavid du Colombier getring(Bring *r)
1439ef1f84bSDavid du Colombier {
1449ef1f84bSDavid du Colombier Block *bp;
1459ef1f84bSDavid du Colombier
1469ef1f84bSDavid du Colombier if(r->consumed == r->produced)
1479ef1f84bSDavid du Colombier return nil;
1489ef1f84bSDavid du Colombier
1499ef1f84bSDavid du Colombier bp = r->ring[r->consumed & Ringmask];
1509ef1f84bSDavid du Colombier r->ring[r->consumed & Ringmask] = nil;
1519ef1f84bSDavid du Colombier r->consumed++;
1529ef1f84bSDavid du Colombier return bp;
1539ef1f84bSDavid du Colombier }
1549ef1f84bSDavid du Colombier
1559ef1f84bSDavid du Colombier static void
addring(Bring * r,Block * bp)1569ef1f84bSDavid du Colombier addring(Bring *r, Block *bp)
1579ef1f84bSDavid du Colombier {
1589ef1f84bSDavid du Colombier Block *tbp;
1599ef1f84bSDavid du Colombier
1609ef1f84bSDavid du Colombier if(r->produced - r->consumed > Ringmask){
1619ef1f84bSDavid du Colombier /* Full! */
1629ef1f84bSDavid du Colombier tbp = r->ring[r->produced & Ringmask];
1639ef1f84bSDavid du Colombier assert(tbp);
1649ef1f84bSDavid du Colombier freeb(tbp);
1659ef1f84bSDavid du Colombier r->consumed++;
1669ef1f84bSDavid du Colombier }
1679ef1f84bSDavid du Colombier r->ring[r->produced & Ringmask] = bp;
1689ef1f84bSDavid du Colombier r->produced++;
1699ef1f84bSDavid du Colombier }
1709ef1f84bSDavid du Colombier
1719ef1f84bSDavid du Colombier static char *
greconnect(Conv * c,char ** argv,int argc)1729ef1f84bSDavid du Colombier greconnect(Conv *c, char **argv, int argc)
1739ef1f84bSDavid du Colombier {
1749ef1f84bSDavid du Colombier Proto *p;
1759ef1f84bSDavid du Colombier char *err;
1769ef1f84bSDavid du Colombier Conv *tc, **cp, **ecp;
1779ef1f84bSDavid du Colombier
1789ef1f84bSDavid du Colombier err = Fsstdconnect(c, argv, argc);
1799ef1f84bSDavid du Colombier if(err != nil)
1809ef1f84bSDavid du Colombier return err;
1819ef1f84bSDavid du Colombier
1829ef1f84bSDavid du Colombier /* make sure noone's already connected to this other sys */
1839ef1f84bSDavid du Colombier p = c->p;
1849ef1f84bSDavid du Colombier qlock(p);
1859ef1f84bSDavid du Colombier ecp = &p->conv[p->nc];
1869ef1f84bSDavid du Colombier for(cp = p->conv; cp < ecp; cp++){
1879ef1f84bSDavid du Colombier tc = *cp;
1889ef1f84bSDavid du Colombier if(tc == nil)
1899ef1f84bSDavid du Colombier break;
1909ef1f84bSDavid du Colombier if(tc == c)
1919ef1f84bSDavid du Colombier continue;
1929ef1f84bSDavid du Colombier if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
1939ef1f84bSDavid du Colombier err = "already connected to that addr/proto";
1949ef1f84bSDavid du Colombier ipmove(c->laddr, IPnoaddr);
1959ef1f84bSDavid du Colombier ipmove(c->raddr, IPnoaddr);
1969ef1f84bSDavid du Colombier break;
1979ef1f84bSDavid du Colombier }
1989ef1f84bSDavid du Colombier }
1999ef1f84bSDavid du Colombier qunlock(p);
2009ef1f84bSDavid du Colombier
2019ef1f84bSDavid du Colombier if(err != nil)
2029ef1f84bSDavid du Colombier return err;
2039ef1f84bSDavid du Colombier Fsconnected(c, nil);
2049ef1f84bSDavid du Colombier
2059ef1f84bSDavid du Colombier return nil;
2069ef1f84bSDavid du Colombier }
2079ef1f84bSDavid du Colombier
2089ef1f84bSDavid du Colombier static void
grecreate(Conv * c)2099ef1f84bSDavid du Colombier grecreate(Conv *c)
2109ef1f84bSDavid du Colombier {
2119ef1f84bSDavid du Colombier c->rq = qopen(GREqlen, Qmsg, 0, c);
2129ef1f84bSDavid du Colombier c->wq = qbypass(grekick, c);
2139ef1f84bSDavid du Colombier }
2149ef1f84bSDavid du Colombier
2159ef1f84bSDavid du Colombier static int
grestate(Conv * c,char * state,int n)2169ef1f84bSDavid du Colombier grestate(Conv *c, char *state, int n)
2179ef1f84bSDavid du Colombier {
2189ef1f84bSDavid du Colombier GREconv *grec;
2199ef1f84bSDavid du Colombier char *ep, *p;
2209ef1f84bSDavid du Colombier
2219ef1f84bSDavid du Colombier grec = c->ptcl;
2229ef1f84bSDavid du Colombier p = state;
2239ef1f84bSDavid du Colombier ep = p + n;
2249ef1f84bSDavid du Colombier p = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
2259ef1f84bSDavid du Colombier "pending %uld %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
2269ef1f84bSDavid du Colombier c->inuse? "Open ": "Closed ",
2279ef1f84bSDavid du Colombier grec->raw? "raw ": "",
2289ef1f84bSDavid du Colombier grec->dlsusp? "DL suspended ": "",
2299ef1f84bSDavid du Colombier grec->ulsusp? "UL suspended ": "",
2309ef1f84bSDavid du Colombier grec->hoa, grec->north, grec->south, grec->seq,
2319ef1f84bSDavid du Colombier grec->dlpending.consumed, grec->dlpending.produced,
2329ef1f84bSDavid du Colombier grec->dlbuffered.consumed, grec->dlbuffered.produced,
2339ef1f84bSDavid du Colombier grec->ulbuffered.consumed, grec->ulbuffered.produced,
2349ef1f84bSDavid du Colombier grec->ulkey);
2359ef1f84bSDavid du Colombier return p - state;
2369ef1f84bSDavid du Colombier }
2379ef1f84bSDavid du Colombier
2389ef1f84bSDavid du Colombier static char*
greannounce(Conv *,char **,int)2399ef1f84bSDavid du Colombier greannounce(Conv*, char**, int)
2409ef1f84bSDavid du Colombier {
2419ef1f84bSDavid du Colombier return "gre does not support announce";
2429ef1f84bSDavid du Colombier }
2439ef1f84bSDavid du Colombier
2449ef1f84bSDavid du Colombier static void
greclose(Conv * c)2459ef1f84bSDavid du Colombier greclose(Conv *c)
2469ef1f84bSDavid du Colombier {
2479ef1f84bSDavid du Colombier GREconv *grec;
2489ef1f84bSDavid du Colombier Block *bp;
2499ef1f84bSDavid du Colombier
2509ef1f84bSDavid du Colombier grec = c->ptcl;
2519ef1f84bSDavid du Colombier
2529ef1f84bSDavid du Colombier /* Make sure we don't forward any more packets */
2539ef1f84bSDavid du Colombier memset(grec->hoa, 0, sizeof grec->hoa);
2549ef1f84bSDavid du Colombier memset(grec->north, 0, sizeof grec->north);
2559ef1f84bSDavid du Colombier memset(grec->south, 0, sizeof grec->south);
2569ef1f84bSDavid du Colombier
2579ef1f84bSDavid du Colombier qlock(&grec->lock);
2589ef1f84bSDavid du Colombier while((bp = getring(&grec->dlpending)) != nil)
2599ef1f84bSDavid du Colombier freeb(bp);
2609ef1f84bSDavid du Colombier
2619ef1f84bSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil)
2629ef1f84bSDavid du Colombier freeb(bp);
2639ef1f84bSDavid du Colombier
2649ef1f84bSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil)
2659ef1f84bSDavid du Colombier freeb(bp);
2669ef1f84bSDavid du Colombier
2679ef1f84bSDavid du Colombier grec->dlpending.produced = grec->dlpending.consumed = 0;
2689ef1f84bSDavid du Colombier grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
2699ef1f84bSDavid du Colombier grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
2709ef1f84bSDavid du Colombier qunlock(&grec->lock);
2719ef1f84bSDavid du Colombier
2729ef1f84bSDavid du Colombier grec->raw = 0;
2739ef1f84bSDavid du Colombier grec->seq = 0;
2749ef1f84bSDavid du Colombier grec->dlsusp = grec->ulsusp = 1;
2759ef1f84bSDavid du Colombier
2769ef1f84bSDavid du Colombier qhangup(c->rq, sessend);
2779ef1f84bSDavid du Colombier qhangup(c->wq, sessend);
2789ef1f84bSDavid du Colombier qhangup(c->eq, sessend);
2799ef1f84bSDavid du Colombier ipmove(c->laddr, IPnoaddr);
2809ef1f84bSDavid du Colombier ipmove(c->raddr, IPnoaddr);
2819ef1f84bSDavid du Colombier c->lport = c->rport = 0;
2829ef1f84bSDavid du Colombier }
2839ef1f84bSDavid du Colombier
2849ef1f84bSDavid du Colombier static void
grekick(void * x,Block * bp)2859ef1f84bSDavid du Colombier grekick(void *x, Block *bp)
2869ef1f84bSDavid du Colombier {
2879ef1f84bSDavid du Colombier Conv *c;
2889ef1f84bSDavid du Colombier GREconv *grec;
2899ef1f84bSDavid du Colombier GREhdr *gre;
2909ef1f84bSDavid du Colombier uchar laddr[IPaddrlen], raddr[IPaddrlen];
2919ef1f84bSDavid du Colombier
2929ef1f84bSDavid du Colombier if(bp == nil)
2939ef1f84bSDavid du Colombier return;
2949ef1f84bSDavid du Colombier
2959ef1f84bSDavid du Colombier c = x;
2969ef1f84bSDavid du Colombier grec = c->ptcl;
2979ef1f84bSDavid du Colombier
2989ef1f84bSDavid du Colombier /* Make space to fit ip header (gre header already there) */
2999ef1f84bSDavid du Colombier bp = padblock(bp, GRE_IPONLY);
3009ef1f84bSDavid du Colombier if(bp == nil)
3019ef1f84bSDavid du Colombier return;
3029ef1f84bSDavid du Colombier
3039ef1f84bSDavid du Colombier /* make sure the message has a GRE header */
3049ef1f84bSDavid du Colombier bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
3059ef1f84bSDavid du Colombier if(bp == nil)
3069ef1f84bSDavid du Colombier return;
3079ef1f84bSDavid du Colombier
3089ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
3099ef1f84bSDavid du Colombier gre->vihl = IP_VER4;
3109ef1f84bSDavid du Colombier
3119ef1f84bSDavid du Colombier if(grec->raw == 0){
3129ef1f84bSDavid du Colombier v4tov6(raddr, gre->dst);
3139ef1f84bSDavid du Colombier if(ipcmp(raddr, v4prefix) == 0)
3149ef1f84bSDavid du Colombier memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
3159ef1f84bSDavid du Colombier v4tov6(laddr, gre->src);
3169ef1f84bSDavid du Colombier if(ipcmp(laddr, v4prefix) == 0){
3179ef1f84bSDavid du Colombier if(ipcmp(c->laddr, IPnoaddr) == 0)
3189ef1f84bSDavid du Colombier /* pick interface closest to dest */
3199ef1f84bSDavid du Colombier findlocalip(c->p->f, c->laddr, raddr);
3209ef1f84bSDavid du Colombier memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
3219ef1f84bSDavid du Colombier }
3229ef1f84bSDavid du Colombier hnputs(gre->eproto, c->rport);
3239ef1f84bSDavid du Colombier }
3249ef1f84bSDavid du Colombier
3259ef1f84bSDavid du Colombier gre->proto = IP_GREPROTO;
3269ef1f84bSDavid du Colombier gre->frag[0] = gre->frag[1] = 0;
3279ef1f84bSDavid du Colombier
3289ef1f84bSDavid du Colombier grepdout++;
3299ef1f84bSDavid du Colombier grebdout += BLEN(bp);
3309ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
3319ef1f84bSDavid du Colombier }
3329ef1f84bSDavid du Colombier
3339ef1f84bSDavid du Colombier static void
gredownlink(Conv * c,Block * bp)3349ef1f84bSDavid du Colombier gredownlink(Conv *c, Block *bp)
3359ef1f84bSDavid du Colombier {
3369ef1f84bSDavid du Colombier Metablock *m;
3379ef1f84bSDavid du Colombier GREconv *grec;
3389ef1f84bSDavid du Colombier GREhdr *gre;
3399ef1f84bSDavid du Colombier int hdrlen, suspended, extra;
3409ef1f84bSDavid du Colombier ushort flags;
3419ef1f84bSDavid du Colombier ulong seq;
3429ef1f84bSDavid du Colombier
3439ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
3449ef1f84bSDavid du Colombier if(gre->ttl == 1){
3459ef1f84bSDavid du Colombier freeb(bp);
3469ef1f84bSDavid du Colombier return;
3479ef1f84bSDavid du Colombier }
3489ef1f84bSDavid du Colombier
3499ef1f84bSDavid du Colombier /*
3509ef1f84bSDavid du Colombier * We've received a packet with a GRE header and we need to
3519ef1f84bSDavid du Colombier * re-adjust the packet header to strip all unwanted parts
3529ef1f84bSDavid du Colombier * but leave room for only a sequence number.
3539ef1f84bSDavid du Colombier */
3549ef1f84bSDavid du Colombier grec = c->ptcl;
3559ef1f84bSDavid du Colombier flags = nhgets(gre->flags);
3569ef1f84bSDavid du Colombier hdrlen = 0;
3579ef1f84bSDavid du Colombier if(flags & GRE_cksum)
3589ef1f84bSDavid du Colombier hdrlen += 2;
3599ef1f84bSDavid du Colombier if(flags & GRE_routing){
3609ef1f84bSDavid du Colombier print("%V routing info present. Discarding packet", gre->src);
3619ef1f84bSDavid du Colombier freeb(bp);
3629ef1f84bSDavid du Colombier return;
3639ef1f84bSDavid du Colombier }
3649ef1f84bSDavid du Colombier if(flags & (GRE_cksum|GRE_routing))
3659ef1f84bSDavid du Colombier hdrlen += 2; /* Offset field */
3669ef1f84bSDavid du Colombier if(flags & GRE_key)
3679ef1f84bSDavid du Colombier hdrlen += 4;
3689ef1f84bSDavid du Colombier if(flags & GRE_seq)
3699ef1f84bSDavid du Colombier hdrlen += 4;
3709ef1f84bSDavid du Colombier
3719ef1f84bSDavid du Colombier /*
3729ef1f84bSDavid du Colombier * The outgoing packet only has the sequence number set. Make room
3739ef1f84bSDavid du Colombier * for the sequence number.
3749ef1f84bSDavid du Colombier */
3759ef1f84bSDavid du Colombier if(hdrlen != sizeof(ulong)){
3769ef1f84bSDavid du Colombier extra = hdrlen - sizeof(ulong);
3779ef1f84bSDavid du Colombier if(extra < 0 && bp->rp - bp->base < -extra){
3789ef1f84bSDavid du Colombier print("gredownlink: cannot add sequence number\n");
3799ef1f84bSDavid du Colombier freeb(bp);
3809ef1f84bSDavid du Colombier return;
3819ef1f84bSDavid du Colombier }
3829ef1f84bSDavid du Colombier memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
3839ef1f84bSDavid du Colombier bp->rp += extra;
3849ef1f84bSDavid du Colombier assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
3859ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
3869ef1f84bSDavid du Colombier }
3879ef1f84bSDavid du Colombier seq = grec->seq++;
3889ef1f84bSDavid du Colombier hnputs(gre->flags, GRE_seq);
3899ef1f84bSDavid du Colombier hnputl(bp->rp + sizeof(GREhdr), seq);
3909ef1f84bSDavid du Colombier
3919ef1f84bSDavid du Colombier /*
3929ef1f84bSDavid du Colombier * Keep rp and seq at the base. ipoput4 consumes rp for
3939ef1f84bSDavid du Colombier * refragmentation.
3949ef1f84bSDavid du Colombier */
3959ef1f84bSDavid du Colombier assert(bp->rp - bp->base >= sizeof(Metablock));
3969ef1f84bSDavid du Colombier m = (Metablock *)bp->base;
3979ef1f84bSDavid du Colombier m->rp = bp->rp;
3989ef1f84bSDavid du Colombier m->seq = seq;
3999ef1f84bSDavid du Colombier
4009ef1f84bSDavid du Colombier /*
4019ef1f84bSDavid du Colombier * Here we make a decision what we're doing with the packet. We're
4029ef1f84bSDavid du Colombier * doing this w/o holding a lock which means that later on in the
4039ef1f84bSDavid du Colombier * process we may discover we've done the wrong thing. I don't want
4049ef1f84bSDavid du Colombier * to call ipoput with the lock held.
4059ef1f84bSDavid du Colombier */
4069ef1f84bSDavid du Colombier restart:
4079ef1f84bSDavid du Colombier suspended = grec->dlsusp;
4089ef1f84bSDavid du Colombier if(suspended){
4099ef1f84bSDavid du Colombier if(!canqlock(&grec->lock)){
4109ef1f84bSDavid du Colombier /*
4119ef1f84bSDavid du Colombier * just give up. too bad, we lose a packet. this
4129ef1f84bSDavid du Colombier * is just too hard and my brain already hurts.
4139ef1f84bSDavid du Colombier */
4149ef1f84bSDavid du Colombier freeb(bp);
4159ef1f84bSDavid du Colombier return;
4169ef1f84bSDavid du Colombier }
4179ef1f84bSDavid du Colombier
4189ef1f84bSDavid du Colombier if(!grec->dlsusp){
4199ef1f84bSDavid du Colombier /*
4209ef1f84bSDavid du Colombier * suspend race. We though we were suspended, but
4219ef1f84bSDavid du Colombier * we really weren't.
4229ef1f84bSDavid du Colombier */
4239ef1f84bSDavid du Colombier qunlock(&grec->lock);
4249ef1f84bSDavid du Colombier goto restart;
4259ef1f84bSDavid du Colombier }
4269ef1f84bSDavid du Colombier
4279ef1f84bSDavid du Colombier /* Undo the incorrect ref count addition */
4289ef1f84bSDavid du Colombier addring(&grec->dlbuffered, bp);
4299ef1f84bSDavid du Colombier qunlock(&grec->lock);
4309ef1f84bSDavid du Colombier return;
4319ef1f84bSDavid du Colombier }
4329ef1f84bSDavid du Colombier
4339ef1f84bSDavid du Colombier /*
4349ef1f84bSDavid du Colombier * When we get here, we're not suspended. Proceed to send the
4359ef1f84bSDavid du Colombier * packet.
4369ef1f84bSDavid du Colombier */
4379ef1f84bSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
4389ef1f84bSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
4399ef1f84bSDavid du Colombier
4409ef1f84bSDavid du Colombier /*
4419ef1f84bSDavid du Colombier * Make sure the packet does not go away.
4429ef1f84bSDavid du Colombier */
443*cbdb3703SDavid du Colombier ainc(&bp->ref);
4449ef1f84bSDavid du Colombier assert(bp->ref == 2);
4459ef1f84bSDavid du Colombier
4469ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
4479ef1f84bSDavid du Colombier grepdout++;
4489ef1f84bSDavid du Colombier grebdout += BLEN(bp);
4499ef1f84bSDavid du Colombier
4509ef1f84bSDavid du Colombier /*
4519ef1f84bSDavid du Colombier * Now make sure we didn't do the wrong thing.
4529ef1f84bSDavid du Colombier */
4539ef1f84bSDavid du Colombier if(!canqlock(&grec->lock)){
4549ef1f84bSDavid du Colombier freeb(bp); /* The packet just goes away */
4559ef1f84bSDavid du Colombier return;
4569ef1f84bSDavid du Colombier }
4579ef1f84bSDavid du Colombier
4589ef1f84bSDavid du Colombier /* We did the right thing */
4599ef1f84bSDavid du Colombier addring(&grec->dlpending, bp);
4609ef1f84bSDavid du Colombier qunlock(&grec->lock);
4619ef1f84bSDavid du Colombier }
4629ef1f84bSDavid du Colombier
4639ef1f84bSDavid du Colombier static void
greuplink(Conv * c,Block * bp)4649ef1f84bSDavid du Colombier greuplink(Conv *c, Block *bp)
4659ef1f84bSDavid du Colombier {
4669ef1f84bSDavid du Colombier GREconv *grec;
4679ef1f84bSDavid du Colombier GREhdr *gre;
4689ef1f84bSDavid du Colombier ushort flags;
4699ef1f84bSDavid du Colombier
4709ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
4719ef1f84bSDavid du Colombier if(gre->ttl == 1)
4729ef1f84bSDavid du Colombier return;
4739ef1f84bSDavid du Colombier
4749ef1f84bSDavid du Colombier grec = c->ptcl;
4759ef1f84bSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->src);
4769ef1f84bSDavid du Colombier memmove(gre->dst, grec->north, sizeof gre->dst);
4779ef1f84bSDavid du Colombier
4789ef1f84bSDavid du Colombier /*
4799ef1f84bSDavid du Colombier * Add a key, if needed.
4809ef1f84bSDavid du Colombier */
4819ef1f84bSDavid du Colombier if(grec->ulkey){
4829ef1f84bSDavid du Colombier flags = nhgets(gre->flags);
4839ef1f84bSDavid du Colombier if(flags & (GRE_cksum|GRE_routing)){
4849ef1f84bSDavid du Colombier print("%V routing info present. Discarding packet\n",
4859ef1f84bSDavid du Colombier gre->src);
4869ef1f84bSDavid du Colombier freeb(bp);
4879ef1f84bSDavid du Colombier return;
4889ef1f84bSDavid du Colombier }
4899ef1f84bSDavid du Colombier
4909ef1f84bSDavid du Colombier if((flags & GRE_key) == 0){
4919ef1f84bSDavid du Colombier /* Make room for the key */
4929ef1f84bSDavid du Colombier if(bp->rp - bp->base < sizeof(ulong)){
4939ef1f84bSDavid du Colombier print("%V can't add key\n", gre->src);
4949ef1f84bSDavid du Colombier freeb(bp);
4959ef1f84bSDavid du Colombier return;
4969ef1f84bSDavid du Colombier }
4979ef1f84bSDavid du Colombier
4989ef1f84bSDavid du Colombier bp->rp -= 4;
4999ef1f84bSDavid du Colombier memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
5009ef1f84bSDavid du Colombier
5019ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
5029ef1f84bSDavid du Colombier hnputs(gre->flags, flags | GRE_key);
5039ef1f84bSDavid du Colombier }
5049ef1f84bSDavid du Colombier
5059ef1f84bSDavid du Colombier /* Add the key */
5069ef1f84bSDavid du Colombier hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
5079ef1f84bSDavid du Colombier }
5089ef1f84bSDavid du Colombier
5099ef1f84bSDavid du Colombier if(!canqlock(&grec->lock)){
5109ef1f84bSDavid du Colombier freeb(bp);
5119ef1f84bSDavid du Colombier return;
5129ef1f84bSDavid du Colombier }
5139ef1f84bSDavid du Colombier
5149ef1f84bSDavid du Colombier if(grec->ulsusp)
5159ef1f84bSDavid du Colombier addring(&grec->ulbuffered, bp);
5169ef1f84bSDavid du Colombier else{
5179ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
5189ef1f84bSDavid du Colombier grepuout++;
5199ef1f84bSDavid du Colombier grebuout += BLEN(bp);
5209ef1f84bSDavid du Colombier }
5219ef1f84bSDavid du Colombier qunlock(&grec->lock);
5229ef1f84bSDavid du Colombier }
5239ef1f84bSDavid du Colombier
5249ef1f84bSDavid du Colombier static void
greiput(Proto * proto,Ipifc *,Block * bp)5259ef1f84bSDavid du Colombier greiput(Proto *proto, Ipifc *, Block *bp)
5269ef1f84bSDavid du Colombier {
5279ef1f84bSDavid du Colombier int len, hdrlen;
5289ef1f84bSDavid du Colombier ushort eproto, flags;
5299ef1f84bSDavid du Colombier uchar raddr[IPaddrlen];
5309ef1f84bSDavid du Colombier Conv *c, **p;
5319ef1f84bSDavid du Colombier GREconv *grec;
5329ef1f84bSDavid du Colombier GREhdr *gre;
5339ef1f84bSDavid du Colombier GREpriv *gpriv;
5349ef1f84bSDavid du Colombier Ip4hdr *ip;
5359ef1f84bSDavid du Colombier
5369ef1f84bSDavid du Colombier /*
5379ef1f84bSDavid du Colombier * We don't want to deal with block lists. Ever. The problem is
5389ef1f84bSDavid du Colombier * that when the block is forwarded, devether.c puts the block into
5399ef1f84bSDavid du Colombier * a queue that also uses ->next. Just do not use ->next here!
5409ef1f84bSDavid du Colombier */
5419ef1f84bSDavid du Colombier if(bp->next){
5429ef1f84bSDavid du Colombier len = blocklen(bp);
5439ef1f84bSDavid du Colombier bp = pullupblock(bp, len);
5449ef1f84bSDavid du Colombier assert(BLEN(bp) == len && bp->next == nil);
5459ef1f84bSDavid du Colombier }
5469ef1f84bSDavid du Colombier
5479ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
5489ef1f84bSDavid du Colombier if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
5499ef1f84bSDavid du Colombier freeb(bp);
5509ef1f84bSDavid du Colombier return;
5519ef1f84bSDavid du Colombier }
5529ef1f84bSDavid du Colombier
5539ef1f84bSDavid du Colombier v4tov6(raddr, gre->src);
5549ef1f84bSDavid du Colombier eproto = nhgets(gre->eproto);
5559ef1f84bSDavid du Colombier flags = nhgets(gre->flags);
5569ef1f84bSDavid du Colombier hdrlen = sizeof(GREhdr);
5579ef1f84bSDavid du Colombier
5589ef1f84bSDavid du Colombier if(flags & GRE_cksum)
5599ef1f84bSDavid du Colombier hdrlen += 2;
5609ef1f84bSDavid du Colombier if(flags & GRE_routing){
5619ef1f84bSDavid du Colombier print("%I routing info present. Discarding packet\n", raddr);
5629ef1f84bSDavid du Colombier freeb(bp);
5639ef1f84bSDavid du Colombier return;
5649ef1f84bSDavid du Colombier }
5659ef1f84bSDavid du Colombier if(flags & (GRE_cksum|GRE_routing))
5669ef1f84bSDavid du Colombier hdrlen += 2; /* Offset field */
5679ef1f84bSDavid du Colombier if(flags & GRE_key)
5689ef1f84bSDavid du Colombier hdrlen += 4;
5699ef1f84bSDavid du Colombier if(flags & GRE_seq)
5709ef1f84bSDavid du Colombier hdrlen += 4;
5719ef1f84bSDavid du Colombier
5729ef1f84bSDavid du Colombier if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
5739ef1f84bSDavid du Colombier print("greretunnel: packet too short (s=%V d=%V)\n",
5749ef1f84bSDavid du Colombier gre->src, gre->dst);
5759ef1f84bSDavid du Colombier freeb(bp);
5769ef1f84bSDavid du Colombier return;
5779ef1f84bSDavid du Colombier }
5789ef1f84bSDavid du Colombier ip = (Ip4hdr *)(bp->rp + hdrlen);
5799ef1f84bSDavid du Colombier
5809ef1f84bSDavid du Colombier qlock(proto);
5819ef1f84bSDavid du Colombier /*
5829ef1f84bSDavid du Colombier * Look for a conversation structure for this port and address, or
5839ef1f84bSDavid du Colombier * match the retunnel part, or match on the raw flag.
5849ef1f84bSDavid du Colombier */
5859ef1f84bSDavid du Colombier for(p = proto->conv; *p; p++) {
5869ef1f84bSDavid du Colombier c = *p;
5879ef1f84bSDavid du Colombier
5889ef1f84bSDavid du Colombier if(c->inuse == 0)
5899ef1f84bSDavid du Colombier continue;
5909ef1f84bSDavid du Colombier
5919ef1f84bSDavid du Colombier /*
5929ef1f84bSDavid du Colombier * Do not stop this session - blocking here
5939ef1f84bSDavid du Colombier * implies that etherread is blocked.
5949ef1f84bSDavid du Colombier */
5959ef1f84bSDavid du Colombier grec = c->ptcl;
5969ef1f84bSDavid du Colombier if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
5979ef1f84bSDavid du Colombier grepdin++;
5989ef1f84bSDavid du Colombier grebdin += BLEN(bp);
5999ef1f84bSDavid du Colombier gredownlink(c, bp);
6009ef1f84bSDavid du Colombier qunlock(proto);
6019ef1f84bSDavid du Colombier return;
6029ef1f84bSDavid du Colombier }
6039ef1f84bSDavid du Colombier
6049ef1f84bSDavid du Colombier if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
6059ef1f84bSDavid du Colombier grepuin++;
6069ef1f84bSDavid du Colombier grebuin += BLEN(bp);
6079ef1f84bSDavid du Colombier greuplink(c, bp);
6089ef1f84bSDavid du Colombier qunlock(proto);
6099ef1f84bSDavid du Colombier return;
6109ef1f84bSDavid du Colombier }
6119ef1f84bSDavid du Colombier }
6129ef1f84bSDavid du Colombier
6139ef1f84bSDavid du Colombier /*
6149ef1f84bSDavid du Colombier * when we get here, none of the forwarding tunnels matched. now
6159ef1f84bSDavid du Colombier * try to match on raw and conversational sessions.
6169ef1f84bSDavid du Colombier */
6179ef1f84bSDavid du Colombier for(c = nil, p = proto->conv; *p; p++) {
6189ef1f84bSDavid du Colombier c = *p;
6199ef1f84bSDavid du Colombier
6209ef1f84bSDavid du Colombier if(c->inuse == 0)
6219ef1f84bSDavid du Colombier continue;
6229ef1f84bSDavid du Colombier
6239ef1f84bSDavid du Colombier /*
6249ef1f84bSDavid du Colombier * Do not stop this session - blocking here
6259ef1f84bSDavid du Colombier * implies that etherread is blocked.
6269ef1f84bSDavid du Colombier */
6279ef1f84bSDavid du Colombier grec = c->ptcl;
6289ef1f84bSDavid du Colombier if(c->rport == eproto &&
6299ef1f84bSDavid du Colombier (grec->raw || ipcmp(c->raddr, raddr) == 0))
6309ef1f84bSDavid du Colombier break;
6319ef1f84bSDavid du Colombier }
6329ef1f84bSDavid du Colombier
6339ef1f84bSDavid du Colombier qunlock(proto);
6349ef1f84bSDavid du Colombier
6359ef1f84bSDavid du Colombier if(*p == nil){
6369ef1f84bSDavid du Colombier freeb(bp);
6379ef1f84bSDavid du Colombier return;
6389ef1f84bSDavid du Colombier }
6399ef1f84bSDavid du Colombier
6409ef1f84bSDavid du Colombier /*
6419ef1f84bSDavid du Colombier * Trim the packet down to data size
6429ef1f84bSDavid du Colombier */
6439ef1f84bSDavid du Colombier len = nhgets(gre->len) - GRE_IPONLY;
6449ef1f84bSDavid du Colombier if(len < GRE_IPPLUSGRE){
6459ef1f84bSDavid du Colombier freeb(bp);
6469ef1f84bSDavid du Colombier return;
6479ef1f84bSDavid du Colombier }
6489ef1f84bSDavid du Colombier
6499ef1f84bSDavid du Colombier bp = trimblock(bp, GRE_IPONLY, len);
6509ef1f84bSDavid du Colombier if(bp == nil){
6519ef1f84bSDavid du Colombier gpriv = proto->priv;
6529ef1f84bSDavid du Colombier gpriv->lenerr++;
6539ef1f84bSDavid du Colombier return;
6549ef1f84bSDavid du Colombier }
6559ef1f84bSDavid du Colombier
6569ef1f84bSDavid du Colombier /*
6579ef1f84bSDavid du Colombier * Can't delimit packet so pull it all into one block.
6589ef1f84bSDavid du Colombier */
6599ef1f84bSDavid du Colombier if(qlen(c->rq) > GREqlen)
6609ef1f84bSDavid du Colombier freeb(bp);
6619ef1f84bSDavid du Colombier else{
6629ef1f84bSDavid du Colombier bp = concatblock(bp);
6639ef1f84bSDavid du Colombier if(bp == 0)
6649ef1f84bSDavid du Colombier panic("greiput");
6659ef1f84bSDavid du Colombier qpass(c->rq, bp);
6669ef1f84bSDavid du Colombier }
6679ef1f84bSDavid du Colombier }
6689ef1f84bSDavid du Colombier
6699ef1f84bSDavid du Colombier int
grestats(Proto * gre,char * buf,int len)6709ef1f84bSDavid du Colombier grestats(Proto *gre, char *buf, int len)
6719ef1f84bSDavid du Colombier {
6729ef1f84bSDavid du Colombier GREpriv *gpriv;
6739ef1f84bSDavid du Colombier
6749ef1f84bSDavid du Colombier gpriv = gre->priv;
6759ef1f84bSDavid du Colombier return snprint(buf, len,
6769ef1f84bSDavid du Colombier "gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
6779ef1f84bSDavid du Colombier grepdin, grepdout, grepuin, grepuout,
6789ef1f84bSDavid du Colombier grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
6799ef1f84bSDavid du Colombier }
6809ef1f84bSDavid du Colombier
6819ef1f84bSDavid du Colombier static char *
grectlraw(Conv * c,int,char **)6829ef1f84bSDavid du Colombier grectlraw(Conv *c, int, char **)
6839ef1f84bSDavid du Colombier {
6849ef1f84bSDavid du Colombier GREconv *grec;
6859ef1f84bSDavid du Colombier
6869ef1f84bSDavid du Colombier grec = c->ptcl;
6879ef1f84bSDavid du Colombier grec->raw = 1;
6889ef1f84bSDavid du Colombier return nil;
6899ef1f84bSDavid du Colombier }
6909ef1f84bSDavid du Colombier
6919ef1f84bSDavid du Colombier static char *
grectlcooked(Conv * c,int,char **)6929ef1f84bSDavid du Colombier grectlcooked(Conv *c, int, char **)
6939ef1f84bSDavid du Colombier {
6949ef1f84bSDavid du Colombier GREconv *grec;
6959ef1f84bSDavid du Colombier
6969ef1f84bSDavid du Colombier grec = c->ptcl;
6979ef1f84bSDavid du Colombier grec->raw = 0;
6989ef1f84bSDavid du Colombier return nil;
6999ef1f84bSDavid du Colombier }
7009ef1f84bSDavid du Colombier
7019ef1f84bSDavid du Colombier static char *
grectlretunnel(Conv * c,int,char ** argv)7029ef1f84bSDavid du Colombier grectlretunnel(Conv *c, int, char **argv)
7039ef1f84bSDavid du Colombier {
7049ef1f84bSDavid du Colombier GREconv *grec;
7059ef1f84bSDavid du Colombier uchar ipaddr[4];
7069ef1f84bSDavid du Colombier
7079ef1f84bSDavid du Colombier grec = c->ptcl;
7089ef1f84bSDavid du Colombier if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
7099ef1f84bSDavid du Colombier return "tunnel already set up";
7109ef1f84bSDavid du Colombier
7119ef1f84bSDavid du Colombier v4parseip(ipaddr, argv[1]);
7129ef1f84bSDavid du Colombier if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
7139ef1f84bSDavid du Colombier return "bad hoa";
7149ef1f84bSDavid du Colombier memmove(grec->hoa, ipaddr, sizeof grec->hoa);
7159ef1f84bSDavid du Colombier v4parseip(ipaddr, argv[2]);
7169ef1f84bSDavid du Colombier memmove(grec->north, ipaddr, sizeof grec->north);
7179ef1f84bSDavid du Colombier v4parseip(ipaddr, argv[3]);
7189ef1f84bSDavid du Colombier memmove(grec->south, ipaddr, sizeof grec->south);
7199ef1f84bSDavid du Colombier v4parseip(ipaddr, argv[4]);
7209ef1f84bSDavid du Colombier memmove(grec->coa, ipaddr, sizeof grec->coa);
7219ef1f84bSDavid du Colombier grec->ulsusp = 1;
7229ef1f84bSDavid du Colombier grec->dlsusp = 0;
7239ef1f84bSDavid du Colombier
7249ef1f84bSDavid du Colombier return nil;
7259ef1f84bSDavid du Colombier }
7269ef1f84bSDavid du Colombier
7279ef1f84bSDavid du Colombier static char *
grectlreport(Conv * c,int,char ** argv)7289ef1f84bSDavid du Colombier grectlreport(Conv *c, int, char **argv)
7299ef1f84bSDavid du Colombier {
7309ef1f84bSDavid du Colombier ulong seq;
7319ef1f84bSDavid du Colombier Block *bp;
7329ef1f84bSDavid du Colombier Bring *r;
7339ef1f84bSDavid du Colombier GREconv *grec;
7349ef1f84bSDavid du Colombier Metablock *m;
7359ef1f84bSDavid du Colombier
7369ef1f84bSDavid du Colombier grec = c->ptcl;
7379ef1f84bSDavid du Colombier seq = strtoul(argv[1], nil, 0);
7389ef1f84bSDavid du Colombier
7399ef1f84bSDavid du Colombier qlock(&grec->lock);
7409ef1f84bSDavid du Colombier r = &grec->dlpending;
7419ef1f84bSDavid du Colombier while(r->produced - r->consumed > 0){
7429ef1f84bSDavid du Colombier bp = r->ring[r->consumed & Ringmask];
7439ef1f84bSDavid du Colombier
7449ef1f84bSDavid du Colombier assert(bp && bp->rp - bp->base >= sizeof(Metablock));
7459ef1f84bSDavid du Colombier m = (Metablock *)bp->base;
7469ef1f84bSDavid du Colombier if((long)(seq - m->seq) <= 0)
7479ef1f84bSDavid du Colombier break;
7489ef1f84bSDavid du Colombier
7499ef1f84bSDavid du Colombier r->ring[r->consumed & Ringmask] = nil;
7509ef1f84bSDavid du Colombier r->consumed++;
7519ef1f84bSDavid du Colombier
7529ef1f84bSDavid du Colombier freeb(bp);
7539ef1f84bSDavid du Colombier }
7549ef1f84bSDavid du Colombier qunlock(&grec->lock);
7559ef1f84bSDavid du Colombier return nil;
7569ef1f84bSDavid du Colombier }
7579ef1f84bSDavid du Colombier
7589ef1f84bSDavid du Colombier static char *
grectldlsuspend(Conv * c,int,char **)7599ef1f84bSDavid du Colombier grectldlsuspend(Conv *c, int, char **)
7609ef1f84bSDavid du Colombier {
7619ef1f84bSDavid du Colombier GREconv *grec;
7629ef1f84bSDavid du Colombier
7639ef1f84bSDavid du Colombier grec = c->ptcl;
7649ef1f84bSDavid du Colombier if(grec->dlsusp)
7659ef1f84bSDavid du Colombier return "already suspended";
7669ef1f84bSDavid du Colombier
7679ef1f84bSDavid du Colombier grec->dlsusp = 1;
7689ef1f84bSDavid du Colombier return nil;
7699ef1f84bSDavid du Colombier }
7709ef1f84bSDavid du Colombier
7719ef1f84bSDavid du Colombier static char *
grectlulsuspend(Conv * c,int,char **)7729ef1f84bSDavid du Colombier grectlulsuspend(Conv *c, int, char **)
7739ef1f84bSDavid du Colombier {
7749ef1f84bSDavid du Colombier GREconv *grec;
7759ef1f84bSDavid du Colombier
7769ef1f84bSDavid du Colombier grec = c->ptcl;
7779ef1f84bSDavid du Colombier if(grec->ulsusp)
7789ef1f84bSDavid du Colombier return "already suspended";
7799ef1f84bSDavid du Colombier
7809ef1f84bSDavid du Colombier grec->ulsusp = 1;
7819ef1f84bSDavid du Colombier return nil;
7829ef1f84bSDavid du Colombier }
7839ef1f84bSDavid du Colombier
7849ef1f84bSDavid du Colombier static char *
grectldlresume(Conv * c,int,char **)7859ef1f84bSDavid du Colombier grectldlresume(Conv *c, int, char **)
7869ef1f84bSDavid du Colombier {
7879ef1f84bSDavid du Colombier GREconv *grec;
7889ef1f84bSDavid du Colombier GREhdr *gre;
7899ef1f84bSDavid du Colombier Block *bp;
7909ef1f84bSDavid du Colombier
7919ef1f84bSDavid du Colombier grec = c->ptcl;
7929ef1f84bSDavid du Colombier
7939ef1f84bSDavid du Colombier qlock(&grec->lock);
7949ef1f84bSDavid du Colombier if(!grec->dlsusp){
7959ef1f84bSDavid du Colombier qunlock(&grec->lock);
7969ef1f84bSDavid du Colombier return "not suspended";
7979ef1f84bSDavid du Colombier }
7989ef1f84bSDavid du Colombier
7999ef1f84bSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil){
8009ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
8019ef1f84bSDavid du Colombier qunlock(&grec->lock);
8029ef1f84bSDavid du Colombier
8039ef1f84bSDavid du Colombier /*
8049ef1f84bSDavid du Colombier * Make sure the packet does not go away.
8059ef1f84bSDavid du Colombier */
806*cbdb3703SDavid du Colombier ainc(&bp->ref);
8079ef1f84bSDavid du Colombier assert(bp->ref == 2);
8089ef1f84bSDavid du Colombier
8099ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8109ef1f84bSDavid du Colombier
8119ef1f84bSDavid du Colombier qlock(&grec->lock);
8129ef1f84bSDavid du Colombier addring(&grec->dlpending, bp);
8139ef1f84bSDavid du Colombier }
8149ef1f84bSDavid du Colombier grec->dlsusp = 0;
8159ef1f84bSDavid du Colombier qunlock(&grec->lock);
8169ef1f84bSDavid du Colombier return nil;
8179ef1f84bSDavid du Colombier }
8189ef1f84bSDavid du Colombier
8199ef1f84bSDavid du Colombier static char *
grectlulresume(Conv * c,int,char **)8209ef1f84bSDavid du Colombier grectlulresume(Conv *c, int, char **)
8219ef1f84bSDavid du Colombier {
8229ef1f84bSDavid du Colombier GREconv *grec;
8239ef1f84bSDavid du Colombier GREhdr *gre;
8249ef1f84bSDavid du Colombier Block *bp;
8259ef1f84bSDavid du Colombier
8269ef1f84bSDavid du Colombier grec = c->ptcl;
8279ef1f84bSDavid du Colombier
8289ef1f84bSDavid du Colombier qlock(&grec->lock);
8299ef1f84bSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil){
8309ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
8319ef1f84bSDavid du Colombier
8329ef1f84bSDavid du Colombier qunlock(&grec->lock);
8339ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8349ef1f84bSDavid du Colombier qlock(&grec->lock);
8359ef1f84bSDavid du Colombier }
8369ef1f84bSDavid du Colombier grec->ulsusp = 0;
8379ef1f84bSDavid du Colombier qunlock(&grec->lock);
8389ef1f84bSDavid du Colombier return nil;
8399ef1f84bSDavid du Colombier }
8409ef1f84bSDavid du Colombier
8419ef1f84bSDavid du Colombier static char *
grectlforward(Conv * c,int,char ** argv)8429ef1f84bSDavid du Colombier grectlforward(Conv *c, int, char **argv)
8439ef1f84bSDavid du Colombier {
8449ef1f84bSDavid du Colombier int len;
8459ef1f84bSDavid du Colombier Block *bp, *nbp;
8469ef1f84bSDavid du Colombier GREconv *grec;
8479ef1f84bSDavid du Colombier GREhdr *gre;
8489ef1f84bSDavid du Colombier Metablock *m;
8499ef1f84bSDavid du Colombier
8509ef1f84bSDavid du Colombier grec = c->ptcl;
8519ef1f84bSDavid du Colombier
8529ef1f84bSDavid du Colombier v4parseip(grec->south, argv[1]);
8539ef1f84bSDavid du Colombier memmove(grec->north, grec->south, sizeof grec->north);
8549ef1f84bSDavid du Colombier
8559ef1f84bSDavid du Colombier qlock(&grec->lock);
8569ef1f84bSDavid du Colombier if(!grec->dlsusp){
8579ef1f84bSDavid du Colombier qunlock(&grec->lock);
8589ef1f84bSDavid du Colombier return "not suspended";
8599ef1f84bSDavid du Colombier }
8609ef1f84bSDavid du Colombier grec->dlsusp = 0;
8619ef1f84bSDavid du Colombier grec->ulsusp = 0;
8629ef1f84bSDavid du Colombier
8639ef1f84bSDavid du Colombier while((bp = getring(&grec->dlpending)) != nil){
8649ef1f84bSDavid du Colombier
8659ef1f84bSDavid du Colombier assert(bp->rp - bp->base >= sizeof(Metablock));
8669ef1f84bSDavid du Colombier m = (Metablock *)bp->base;
8679ef1f84bSDavid du Colombier assert(m->rp >= bp->base && m->rp < bp->lim);
8689ef1f84bSDavid du Colombier
8699ef1f84bSDavid du Colombier /*
8709ef1f84bSDavid du Colombier * If the packet is still held inside the IP transmit
8719ef1f84bSDavid du Colombier * system, make a copy of the packet first.
8729ef1f84bSDavid du Colombier */
8739ef1f84bSDavid du Colombier if(bp->ref > 1){
8749ef1f84bSDavid du Colombier len = bp->wp - m->rp;
8759ef1f84bSDavid du Colombier nbp = allocb(len);
8769ef1f84bSDavid du Colombier memmove(nbp->wp, m->rp, len);
8779ef1f84bSDavid du Colombier nbp->wp += len;
8789ef1f84bSDavid du Colombier freeb(bp);
8799ef1f84bSDavid du Colombier bp = nbp;
8809ef1f84bSDavid du Colombier }
8819ef1f84bSDavid du Colombier else{
8829ef1f84bSDavid du Colombier /* Patch up rp */
8839ef1f84bSDavid du Colombier bp->rp = m->rp;
8849ef1f84bSDavid du Colombier }
8859ef1f84bSDavid du Colombier
8869ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
8879ef1f84bSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
8889ef1f84bSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
8899ef1f84bSDavid du Colombier
8909ef1f84bSDavid du Colombier qunlock(&grec->lock);
8919ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8929ef1f84bSDavid du Colombier qlock(&grec->lock);
8939ef1f84bSDavid du Colombier }
8949ef1f84bSDavid du Colombier
8959ef1f84bSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil){
8969ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
8979ef1f84bSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
8989ef1f84bSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
8999ef1f84bSDavid du Colombier
9009ef1f84bSDavid du Colombier qunlock(&grec->lock);
9019ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9029ef1f84bSDavid du Colombier qlock(&grec->lock);
9039ef1f84bSDavid du Colombier }
9049ef1f84bSDavid du Colombier
9059ef1f84bSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil){
9069ef1f84bSDavid du Colombier gre = (GREhdr *)bp->rp;
9079ef1f84bSDavid du Colombier
9089ef1f84bSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
9099ef1f84bSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
9109ef1f84bSDavid du Colombier
9119ef1f84bSDavid du Colombier qunlock(&grec->lock);
9129ef1f84bSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9139ef1f84bSDavid du Colombier qlock(&grec->lock);
9149ef1f84bSDavid du Colombier }
9159ef1f84bSDavid du Colombier qunlock(&grec->lock);
9169ef1f84bSDavid du Colombier return nil;
9179ef1f84bSDavid du Colombier }
9189ef1f84bSDavid du Colombier
9199ef1f84bSDavid du Colombier static char *
grectlulkey(Conv * c,int,char ** argv)9209ef1f84bSDavid du Colombier grectlulkey(Conv *c, int, char **argv)
9219ef1f84bSDavid du Colombier {
9229ef1f84bSDavid du Colombier GREconv *grec;
9239ef1f84bSDavid du Colombier
9249ef1f84bSDavid du Colombier grec = c->ptcl;
9259ef1f84bSDavid du Colombier grec->ulkey = strtoul(argv[1], nil, 0);
9269ef1f84bSDavid du Colombier return nil;
9279ef1f84bSDavid du Colombier }
9289ef1f84bSDavid du Colombier
9299ef1f84bSDavid du Colombier char *
grectl(Conv * c,char ** f,int n)9309ef1f84bSDavid du Colombier grectl(Conv *c, char **f, int n)
9319ef1f84bSDavid du Colombier {
9329ef1f84bSDavid du Colombier int i;
9339ef1f84bSDavid du Colombier
9349ef1f84bSDavid du Colombier if(n < 1)
9359ef1f84bSDavid du Colombier return "too few arguments";
9369ef1f84bSDavid du Colombier
9379ef1f84bSDavid du Colombier for(i = 0; i < Ncmds; i++)
9389ef1f84bSDavid du Colombier if(strcmp(f[0], grectls[i].cmd) == 0)
9399ef1f84bSDavid du Colombier break;
9409ef1f84bSDavid du Colombier
9419ef1f84bSDavid du Colombier if(i == Ncmds)
9429ef1f84bSDavid du Colombier return "no such command";
9439ef1f84bSDavid du Colombier if(grectls[i].argc != 0 && grectls[i].argc != n)
9449ef1f84bSDavid du Colombier return "incorrect number of arguments";
9459ef1f84bSDavid du Colombier
9469ef1f84bSDavid du Colombier return grectls[i].f(c, n, f);
9479ef1f84bSDavid du Colombier }
9489ef1f84bSDavid du Colombier
9499ef1f84bSDavid du Colombier void
greinit(Fs * fs)9509ef1f84bSDavid du Colombier greinit(Fs *fs)
9519ef1f84bSDavid du Colombier {
9529ef1f84bSDavid du Colombier Proto *gre;
9539ef1f84bSDavid du Colombier
9549ef1f84bSDavid du Colombier gre = smalloc(sizeof(Proto));
9559ef1f84bSDavid du Colombier gre->priv = smalloc(sizeof(GREpriv));
9569ef1f84bSDavid du Colombier gre->name = "gre";
9579ef1f84bSDavid du Colombier gre->connect = greconnect;
9589ef1f84bSDavid du Colombier gre->announce = greannounce;
9599ef1f84bSDavid du Colombier gre->state = grestate;
9609ef1f84bSDavid du Colombier gre->create = grecreate;
9619ef1f84bSDavid du Colombier gre->close = greclose;
9629ef1f84bSDavid du Colombier gre->rcv = greiput;
9639ef1f84bSDavid du Colombier gre->ctl = grectl;
9649ef1f84bSDavid du Colombier gre->advise = nil;
9659ef1f84bSDavid du Colombier gre->stats = grestats;
9669ef1f84bSDavid du Colombier gre->ipproto = IP_GREPROTO;
9679ef1f84bSDavid du Colombier gre->nc = 64;
9689ef1f84bSDavid du Colombier gre->ptclsize = sizeof(GREconv);
9699ef1f84bSDavid du Colombier
9709ef1f84bSDavid du Colombier Fsproto(fs, gre);
9719ef1f84bSDavid du Colombier }
972