10774058cSDavid du Colombier /*
20774058cSDavid du Colombier * Generic Routing Encapsulation over IPv4, rfc1702
30774058cSDavid du Colombier */
47dd7cddfSDavid du Colombier #include "u.h"
57dd7cddfSDavid du Colombier #include "../port/lib.h"
67dd7cddfSDavid du Colombier #include "mem.h"
77dd7cddfSDavid du Colombier #include "dat.h"
87dd7cddfSDavid du Colombier #include "fns.h"
97dd7cddfSDavid du Colombier #include "../port/error.h"
107dd7cddfSDavid du Colombier
117dd7cddfSDavid du Colombier #include "ip.h"
127dd7cddfSDavid du Colombier
137ec5746aSDavid du Colombier enum {
147dd7cddfSDavid du Colombier GRE_IPONLY = 12, /* size of ip header */
157dd7cddfSDavid du Colombier GRE_IPPLUSGRE = 12, /* minimum size of GRE header */
167dd7cddfSDavid du Colombier IP_GREPROTO = 47,
177dd7cddfSDavid du Colombier
187dd7cddfSDavid du Colombier GRErxms = 200,
197dd7cddfSDavid du Colombier GREtickms = 100,
207dd7cddfSDavid du Colombier GREmaxxmit = 10,
217ec5746aSDavid du Colombier
227ec5746aSDavid du Colombier K = 1024,
237ec5746aSDavid du Colombier GREqlen = 256 * K,
247ec5746aSDavid du Colombier
257ec5746aSDavid du Colombier GRE_cksum = 0x8000,
267ec5746aSDavid du Colombier GRE_routing = 0x4000,
277ec5746aSDavid du Colombier GRE_key = 0x2000,
287ec5746aSDavid du Colombier GRE_seq = 0x1000,
297ec5746aSDavid du Colombier
307ec5746aSDavid du Colombier Nring = 1 << 10, /* power of two, please */
317ec5746aSDavid du Colombier Ringmask = Nring - 1,
327ec5746aSDavid du Colombier
337ec5746aSDavid du Colombier GREctlraw = 0,
347ec5746aSDavid du Colombier GREctlcooked,
357ec5746aSDavid du Colombier GREctlretunnel,
367ec5746aSDavid du Colombier GREctlreport,
377ec5746aSDavid du Colombier GREctldlsuspend,
387ec5746aSDavid du Colombier GREctlulsuspend,
397ec5746aSDavid du Colombier GREctldlresume,
407ec5746aSDavid du Colombier GREctlulresume,
417ec5746aSDavid du Colombier GREctlforward,
427ec5746aSDavid du Colombier GREctlulkey,
437ec5746aSDavid du Colombier Ncmds,
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier
467ec5746aSDavid du Colombier typedef struct GREhdr GREhdr;
477ec5746aSDavid du Colombier struct GREhdr{
487dd7cddfSDavid du Colombier /* ip header */
497dd7cddfSDavid du Colombier uchar vihl; /* Version and header length */
507dd7cddfSDavid du Colombier uchar tos; /* Type of service */
517dd7cddfSDavid du Colombier uchar len[2]; /* packet length (including headers) */
527dd7cddfSDavid du Colombier uchar id[2]; /* Identification */
537dd7cddfSDavid du Colombier uchar frag[2]; /* Fragment information */
547ec5746aSDavid du Colombier uchar ttl;
557dd7cddfSDavid du Colombier uchar proto; /* Protocol */
567dd7cddfSDavid du Colombier uchar cksum[2]; /* checksum */
577dd7cddfSDavid du Colombier uchar src[4]; /* Ip source */
587dd7cddfSDavid du Colombier uchar dst[4]; /* Ip destination */
597dd7cddfSDavid du Colombier
607dd7cddfSDavid du Colombier /* gre header */
617dd7cddfSDavid du Colombier uchar flags[2];
627dd7cddfSDavid du Colombier uchar eproto[2]; /* encapsulation protocol */
637ec5746aSDavid du Colombier };
647dd7cddfSDavid du Colombier
657dd7cddfSDavid du Colombier typedef struct GREpriv GREpriv;
667ec5746aSDavid du Colombier struct GREpriv{
677dd7cddfSDavid du Colombier /* non-MIB stats */
687dd7cddfSDavid du Colombier ulong lenerr; /* short packet */
697dd7cddfSDavid du Colombier };
707dd7cddfSDavid du Colombier
717ec5746aSDavid du Colombier typedef struct Bring Bring;
727ec5746aSDavid du Colombier struct Bring{
737ec5746aSDavid du Colombier Block *ring[Nring];
747ec5746aSDavid du Colombier long produced;
757ec5746aSDavid du Colombier long consumed;
767ec5746aSDavid du Colombier };
777ec5746aSDavid du Colombier
787ec5746aSDavid du Colombier typedef struct GREconv GREconv;
797ec5746aSDavid du Colombier struct GREconv{
807ec5746aSDavid du Colombier int raw;
817ec5746aSDavid du Colombier
827ec5746aSDavid du Colombier /* Retunnelling information. v4 only */
837ec5746aSDavid du Colombier uchar north[4]; /* HA */
847ec5746aSDavid du Colombier uchar south[4]; /* Base station */
857ec5746aSDavid du Colombier uchar hoa[4]; /* Home address */
867ec5746aSDavid du Colombier uchar coa[4]; /* Careof address */
877ec5746aSDavid du Colombier ulong seq; /* Current sequence # */
887ec5746aSDavid du Colombier int dlsusp; /* Downlink suspended? */
897ec5746aSDavid du Colombier int ulsusp; /* Uplink suspended? */
907ec5746aSDavid du Colombier ulong ulkey; /* GRE key */
917ec5746aSDavid du Colombier
927ec5746aSDavid du Colombier QLock lock; /* Lock for rings */
937ec5746aSDavid du Colombier Bring dlpending; /* Ring of pending packets */
947ec5746aSDavid du Colombier Bring dlbuffered; /* Received while suspended */
957ec5746aSDavid du Colombier Bring ulbuffered; /* Received while suspended */
967ec5746aSDavid du Colombier };
977ec5746aSDavid du Colombier
987ec5746aSDavid du Colombier typedef struct Metablock Metablock;
997ec5746aSDavid du Colombier struct Metablock{
1007ec5746aSDavid du Colombier uchar *rp;
1017ec5746aSDavid du Colombier ulong seq;
1027ec5746aSDavid du Colombier };
1037ec5746aSDavid du Colombier
1047ec5746aSDavid du Colombier static char *grectlcooked(Conv *, int, char **);
1057ec5746aSDavid du Colombier static char *grectldlresume(Conv *, int, char **);
1067ec5746aSDavid du Colombier static char *grectldlsuspend(Conv *, int, char **);
1077ec5746aSDavid du Colombier static char *grectlforward(Conv *, int, char **);
1087ec5746aSDavid du Colombier static char *grectlraw(Conv *, int, char **);
1097ec5746aSDavid du Colombier static char *grectlreport(Conv *, int, char **);
1107ec5746aSDavid du Colombier static char *grectlretunnel(Conv *, int, char **);
1117ec5746aSDavid du Colombier static char *grectlulkey(Conv *, int, char **);
1127ec5746aSDavid du Colombier static char *grectlulresume(Conv *, int, char **);
1137ec5746aSDavid du Colombier static char *grectlulsuspend(Conv *, int, char **);
1147ec5746aSDavid du Colombier
1157ec5746aSDavid du Colombier static struct{
1167ec5746aSDavid du Colombier char *cmd;
1177ec5746aSDavid du Colombier int argc;
1187ec5746aSDavid du Colombier char *(*f)(Conv *, int, char **);
1197ec5746aSDavid du Colombier } grectls[Ncmds] = {
1207ec5746aSDavid du Colombier [GREctlraw] = { "raw", 1, grectlraw, },
1217ec5746aSDavid du Colombier [GREctlcooked] = { "cooked", 1, grectlcooked, },
1227ec5746aSDavid du Colombier [GREctlretunnel]= { "retunnel", 5, grectlretunnel, },
1237ec5746aSDavid du Colombier [GREctlreport] = { "report", 2, grectlreport, },
1247ec5746aSDavid du Colombier [GREctldlsuspend]= { "dlsuspend", 1, grectldlsuspend,},
1257ec5746aSDavid du Colombier [GREctlulsuspend]= { "ulsuspend", 1, grectlulsuspend,},
1267ec5746aSDavid du Colombier [GREctldlresume]= { "dlresume", 1, grectldlresume, },
1277ec5746aSDavid du Colombier [GREctlulresume]= { "ulresume", 1, grectlulresume, },
1287ec5746aSDavid du Colombier [GREctlforward] = { "forward", 2, grectlforward, },
1297ec5746aSDavid du Colombier [GREctlulkey] = { "ulkey", 2, grectlulkey, },
1307ec5746aSDavid du Colombier };
1317ec5746aSDavid du Colombier
1327ec5746aSDavid du Colombier static uchar nulladdr[4];
1337ec5746aSDavid du Colombier static char *sessend = "session end";
1347ec5746aSDavid du Colombier
135e6c6b7f8SDavid du Colombier static void grekick(void *x, Block *bp);
1367ec5746aSDavid du Colombier static char *gresetup(Conv *, char *, char *, char *);
1377ec5746aSDavid du Colombier
1387ec5746aSDavid du Colombier ulong grepdin, grepdout, grebdin, grebdout;
1397ec5746aSDavid du Colombier ulong grepuin, grepuout, grebuin, grebuout;
1407ec5746aSDavid du Colombier
1417ec5746aSDavid du Colombier static Block *
getring(Bring * r)1427ec5746aSDavid du Colombier getring(Bring *r)
1437ec5746aSDavid du Colombier {
1447ec5746aSDavid du Colombier Block *bp;
1457ec5746aSDavid du Colombier
1467ec5746aSDavid du Colombier if(r->consumed == r->produced)
1477ec5746aSDavid du Colombier return nil;
1487ec5746aSDavid du Colombier
1497ec5746aSDavid du Colombier bp = r->ring[r->consumed & Ringmask];
1507ec5746aSDavid du Colombier r->ring[r->consumed & Ringmask] = nil;
1517ec5746aSDavid du Colombier r->consumed++;
1527ec5746aSDavid du Colombier return bp;
1537ec5746aSDavid du Colombier }
1547ec5746aSDavid du Colombier
1557ec5746aSDavid du Colombier static void
addring(Bring * r,Block * bp)1567ec5746aSDavid du Colombier addring(Bring *r, Block *bp)
1577ec5746aSDavid du Colombier {
1587ec5746aSDavid du Colombier Block *tbp;
1597ec5746aSDavid du Colombier
1607ec5746aSDavid du Colombier if(r->produced - r->consumed > Ringmask){
1617ec5746aSDavid du Colombier /* Full! */
1627ec5746aSDavid du Colombier tbp = r->ring[r->produced & Ringmask];
1637ec5746aSDavid du Colombier assert(tbp);
1647ec5746aSDavid du Colombier freeb(tbp);
1657ec5746aSDavid du Colombier r->consumed++;
1667ec5746aSDavid du Colombier }
1677ec5746aSDavid du Colombier r->ring[r->produced & Ringmask] = bp;
1687ec5746aSDavid du Colombier r->produced++;
1697ec5746aSDavid du Colombier }
1703ff48bf5SDavid du Colombier
1717dd7cddfSDavid du Colombier static char *
greconnect(Conv * c,char ** argv,int argc)1727dd7cddfSDavid du Colombier greconnect(Conv *c, char **argv, int argc)
1737dd7cddfSDavid du Colombier {
1747dd7cddfSDavid du Colombier Proto *p;
1757dd7cddfSDavid du Colombier char *err;
1767dd7cddfSDavid du Colombier Conv *tc, **cp, **ecp;
1777dd7cddfSDavid du Colombier
1787dd7cddfSDavid du Colombier err = Fsstdconnect(c, argv, argc);
1797dd7cddfSDavid du Colombier if(err != nil)
1807dd7cddfSDavid du Colombier return err;
1817dd7cddfSDavid du Colombier
1827dd7cddfSDavid du Colombier /* make sure noone's already connected to this other sys */
1837dd7cddfSDavid du Colombier p = c->p;
1847dd7cddfSDavid du Colombier qlock(p);
1857dd7cddfSDavid du Colombier ecp = &p->conv[p->nc];
1867dd7cddfSDavid du Colombier for(cp = p->conv; cp < ecp; cp++){
1877dd7cddfSDavid du Colombier tc = *cp;
1887dd7cddfSDavid du Colombier if(tc == nil)
1897dd7cddfSDavid du Colombier break;
1907dd7cddfSDavid du Colombier if(tc == c)
1917dd7cddfSDavid du Colombier continue;
1927dd7cddfSDavid du Colombier if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
1937dd7cddfSDavid du Colombier err = "already connected to that addr/proto";
1947dd7cddfSDavid du Colombier ipmove(c->laddr, IPnoaddr);
1957dd7cddfSDavid du Colombier ipmove(c->raddr, IPnoaddr);
1967dd7cddfSDavid du Colombier break;
1977dd7cddfSDavid du Colombier }
1987dd7cddfSDavid du Colombier }
1997dd7cddfSDavid du Colombier qunlock(p);
2007dd7cddfSDavid du Colombier
2017dd7cddfSDavid du Colombier if(err != nil)
2027dd7cddfSDavid du Colombier return err;
2037dd7cddfSDavid du Colombier Fsconnected(c, nil);
2047dd7cddfSDavid du Colombier
2057dd7cddfSDavid du Colombier return nil;
2067dd7cddfSDavid du Colombier }
2077dd7cddfSDavid du Colombier
2083ff48bf5SDavid du Colombier static void
grecreate(Conv * c)2093ff48bf5SDavid du Colombier grecreate(Conv *c)
2103ff48bf5SDavid du Colombier {
2117ec5746aSDavid du Colombier c->rq = qopen(GREqlen, Qmsg, 0, c);
212e6c6b7f8SDavid du Colombier c->wq = qbypass(grekick, c);
2133ff48bf5SDavid du Colombier }
2143ff48bf5SDavid du Colombier
2157dd7cddfSDavid du Colombier static int
grestate(Conv * c,char * state,int n)2167dd7cddfSDavid du Colombier grestate(Conv *c, char *state, int n)
2177dd7cddfSDavid du Colombier {
2187ec5746aSDavid du Colombier GREconv *grec;
2197ec5746aSDavid du Colombier char *ep, *p;
2207ec5746aSDavid du Colombier
2217ec5746aSDavid du Colombier grec = c->ptcl;
2227ec5746aSDavid du Colombier p = state;
2237ec5746aSDavid du Colombier ep = p + n;
2247ec5746aSDavid du Colombier p = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
2257ec5746aSDavid du Colombier "pending %uld %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
2267ec5746aSDavid du Colombier c->inuse? "Open ": "Closed ",
2277ec5746aSDavid du Colombier grec->raw? "raw ": "",
2287ec5746aSDavid du Colombier grec->dlsusp? "DL suspended ": "",
2297ec5746aSDavid du Colombier grec->ulsusp? "UL suspended ": "",
2307ec5746aSDavid du Colombier grec->hoa, grec->north, grec->south, grec->seq,
2317ec5746aSDavid du Colombier grec->dlpending.consumed, grec->dlpending.produced,
2327ec5746aSDavid du Colombier grec->dlbuffered.consumed, grec->dlbuffered.produced,
2337ec5746aSDavid du Colombier grec->ulbuffered.consumed, grec->ulbuffered.produced,
2347ec5746aSDavid du Colombier grec->ulkey);
2357ec5746aSDavid du Colombier return p - state;
2367dd7cddfSDavid du Colombier }
2377dd7cddfSDavid du Colombier
2387dd7cddfSDavid du Colombier static char*
greannounce(Conv *,char **,int)2397dd7cddfSDavid du Colombier greannounce(Conv*, char**, int)
2407dd7cddfSDavid du Colombier {
2417ec5746aSDavid du Colombier return "gre does not support announce";
2427dd7cddfSDavid du Colombier }
2437dd7cddfSDavid du Colombier
2447dd7cddfSDavid du Colombier static void
greclose(Conv * c)2457dd7cddfSDavid du Colombier greclose(Conv *c)
2467dd7cddfSDavid du Colombier {
2477ec5746aSDavid du Colombier GREconv *grec;
2487ec5746aSDavid du Colombier Block *bp;
2497ec5746aSDavid du Colombier
2507ec5746aSDavid du Colombier grec = c->ptcl;
2517ec5746aSDavid du Colombier
2527ec5746aSDavid du Colombier /* Make sure we don't forward any more packets */
2537ec5746aSDavid du Colombier memset(grec->hoa, 0, sizeof grec->hoa);
2547ec5746aSDavid du Colombier memset(grec->north, 0, sizeof grec->north);
2557ec5746aSDavid du Colombier memset(grec->south, 0, sizeof grec->south);
2567ec5746aSDavid du Colombier
2577ec5746aSDavid du Colombier qlock(&grec->lock);
2587ec5746aSDavid du Colombier while((bp = getring(&grec->dlpending)) != nil)
2597ec5746aSDavid du Colombier freeb(bp);
2607ec5746aSDavid du Colombier
2617ec5746aSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil)
2627ec5746aSDavid du Colombier freeb(bp);
2637ec5746aSDavid du Colombier
2647ec5746aSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil)
2657ec5746aSDavid du Colombier freeb(bp);
2667ec5746aSDavid du Colombier
2677ec5746aSDavid du Colombier grec->dlpending.produced = grec->dlpending.consumed = 0;
2687ec5746aSDavid du Colombier grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
2697ec5746aSDavid du Colombier grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
2707ec5746aSDavid du Colombier qunlock(&grec->lock);
2717ec5746aSDavid du Colombier
2727ec5746aSDavid du Colombier grec->raw = 0;
2737ec5746aSDavid du Colombier grec->seq = 0;
2747ec5746aSDavid du Colombier grec->dlsusp = grec->ulsusp = 1;
2757ec5746aSDavid du Colombier
2767ec5746aSDavid du Colombier qhangup(c->rq, sessend);
2777ec5746aSDavid du Colombier qhangup(c->wq, sessend);
2787ec5746aSDavid du Colombier qhangup(c->eq, sessend);
2797dd7cddfSDavid du Colombier ipmove(c->laddr, IPnoaddr);
2807dd7cddfSDavid du Colombier ipmove(c->raddr, IPnoaddr);
2817ec5746aSDavid du Colombier c->lport = c->rport = 0;
2827dd7cddfSDavid du Colombier }
2837dd7cddfSDavid du Colombier
2847dd7cddfSDavid du Colombier static void
grekick(void * x,Block * bp)285e6c6b7f8SDavid du Colombier grekick(void *x, Block *bp)
2867dd7cddfSDavid du Colombier {
2877ec5746aSDavid du Colombier Conv *c;
2887ec5746aSDavid du Colombier GREconv *grec;
2897ec5746aSDavid du Colombier GREhdr *gre;
2907dd7cddfSDavid du Colombier uchar laddr[IPaddrlen], raddr[IPaddrlen];
2917dd7cddfSDavid du Colombier
2927dd7cddfSDavid du Colombier if(bp == nil)
2937dd7cddfSDavid du Colombier return;
2947dd7cddfSDavid du Colombier
2957ec5746aSDavid du Colombier c = x;
2967ec5746aSDavid du Colombier grec = c->ptcl;
2977ec5746aSDavid du Colombier
2987dd7cddfSDavid du Colombier /* Make space to fit ip header (gre header already there) */
2997dd7cddfSDavid du Colombier bp = padblock(bp, GRE_IPONLY);
3007dd7cddfSDavid du Colombier if(bp == nil)
3017dd7cddfSDavid du Colombier return;
3027dd7cddfSDavid du Colombier
3037dd7cddfSDavid du Colombier /* make sure the message has a GRE header */
3047dd7cddfSDavid du Colombier bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
3057dd7cddfSDavid du Colombier if(bp == nil)
3067dd7cddfSDavid du Colombier return;
3077dd7cddfSDavid du Colombier
3087ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
3097ec5746aSDavid du Colombier gre->vihl = IP_VER4;
3107dd7cddfSDavid du Colombier
3117ec5746aSDavid du Colombier if(grec->raw == 0){
3127ec5746aSDavid du Colombier v4tov6(raddr, gre->dst);
3137dd7cddfSDavid du Colombier if(ipcmp(raddr, v4prefix) == 0)
3147ec5746aSDavid du Colombier memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
3157ec5746aSDavid du Colombier v4tov6(laddr, gre->src);
3167dd7cddfSDavid du Colombier if(ipcmp(laddr, v4prefix) == 0){
3177dd7cddfSDavid du Colombier if(ipcmp(c->laddr, IPnoaddr) == 0)
3187ec5746aSDavid du Colombier /* pick interface closest to dest */
3197ec5746aSDavid du Colombier findlocalip(c->p->f, c->laddr, raddr);
3207ec5746aSDavid du Colombier memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
3217dd7cddfSDavid du Colombier }
3227ec5746aSDavid du Colombier hnputs(gre->eproto, c->rport);
3235d82c6aeSDavid du Colombier }
3247dd7cddfSDavid du Colombier
3257ec5746aSDavid du Colombier gre->proto = IP_GREPROTO;
3267ec5746aSDavid du Colombier gre->frag[0] = gre->frag[1] = 0;
3277dd7cddfSDavid du Colombier
3287ec5746aSDavid du Colombier grepdout++;
3297ec5746aSDavid du Colombier grebdout += BLEN(bp);
330a6a9e072SDavid du Colombier ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
3317dd7cddfSDavid du Colombier }
3327dd7cddfSDavid du Colombier
3337dd7cddfSDavid du Colombier static void
gredownlink(Conv * c,Block * bp)3347ec5746aSDavid du Colombier gredownlink(Conv *c, Block *bp)
3357dd7cddfSDavid du Colombier {
3367ec5746aSDavid du Colombier Metablock *m;
3377ec5746aSDavid du Colombier GREconv *grec;
3387ec5746aSDavid du Colombier GREhdr *gre;
3397ec5746aSDavid du Colombier int hdrlen, suspended, extra;
3407ec5746aSDavid du Colombier ushort flags;
3417ec5746aSDavid du Colombier ulong seq;
3427dd7cddfSDavid du Colombier
3437ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
3447ec5746aSDavid du Colombier if(gre->ttl == 1){
3457ec5746aSDavid du Colombier freeb(bp);
3467dd7cddfSDavid du Colombier return;
3477dd7cddfSDavid du Colombier }
3487dd7cddfSDavid du Colombier
3497ec5746aSDavid du Colombier /*
3507ec5746aSDavid du Colombier * We've received a packet with a GRE header and we need to
3517ec5746aSDavid du Colombier * re-adjust the packet header to strip all unwanted parts
3527ec5746aSDavid du Colombier * but leave room for only a sequence number.
3537ec5746aSDavid du Colombier */
3547ec5746aSDavid du Colombier grec = c->ptcl;
3557ec5746aSDavid du Colombier flags = nhgets(gre->flags);
3567ec5746aSDavid du Colombier hdrlen = 0;
3577ec5746aSDavid du Colombier if(flags & GRE_cksum)
3587ec5746aSDavid du Colombier hdrlen += 2;
3597ec5746aSDavid du Colombier if(flags & GRE_routing){
3607ec5746aSDavid du Colombier print("%V routing info present. Discarding packet", gre->src);
3617ec5746aSDavid du Colombier freeb(bp);
3627ec5746aSDavid du Colombier return;
3637ec5746aSDavid du Colombier }
3647ec5746aSDavid du Colombier if(flags & (GRE_cksum|GRE_routing))
3657ec5746aSDavid du Colombier hdrlen += 2; /* Offset field */
3667ec5746aSDavid du Colombier if(flags & GRE_key)
3677ec5746aSDavid du Colombier hdrlen += 4;
3687ec5746aSDavid du Colombier if(flags & GRE_seq)
3697ec5746aSDavid du Colombier hdrlen += 4;
3707ec5746aSDavid du Colombier
3717ec5746aSDavid du Colombier /*
3727ec5746aSDavid du Colombier * The outgoing packet only has the sequence number set. Make room
3737ec5746aSDavid du Colombier * for the sequence number.
3747ec5746aSDavid du Colombier */
3757ec5746aSDavid du Colombier if(hdrlen != sizeof(ulong)){
3767ec5746aSDavid du Colombier extra = hdrlen - sizeof(ulong);
3777ec5746aSDavid du Colombier if(extra < 0 && bp->rp - bp->base < -extra){
3787ec5746aSDavid du Colombier print("gredownlink: cannot add sequence number\n");
3797ec5746aSDavid du Colombier freeb(bp);
3807ec5746aSDavid du Colombier return;
3817ec5746aSDavid du Colombier }
3827ec5746aSDavid du Colombier memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
3837ec5746aSDavid du Colombier bp->rp += extra;
3847ec5746aSDavid du Colombier assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
3857ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
3867ec5746aSDavid du Colombier }
3877ec5746aSDavid du Colombier seq = grec->seq++;
3887ec5746aSDavid du Colombier hnputs(gre->flags, GRE_seq);
3897ec5746aSDavid du Colombier hnputl(bp->rp + sizeof(GREhdr), seq);
3907ec5746aSDavid du Colombier
3917ec5746aSDavid du Colombier /*
3927ec5746aSDavid du Colombier * Keep rp and seq at the base. ipoput4 consumes rp for
3937ec5746aSDavid du Colombier * refragmentation.
3947ec5746aSDavid du Colombier */
3957ec5746aSDavid du Colombier assert(bp->rp - bp->base >= sizeof(Metablock));
3967ec5746aSDavid du Colombier m = (Metablock *)bp->base;
3977ec5746aSDavid du Colombier m->rp = bp->rp;
3987ec5746aSDavid du Colombier m->seq = seq;
3997ec5746aSDavid du Colombier
4007ec5746aSDavid du Colombier /*
4017ec5746aSDavid du Colombier * Here we make a decision what we're doing with the packet. We're
4027ec5746aSDavid du Colombier * doing this w/o holding a lock which means that later on in the
4037ec5746aSDavid du Colombier * process we may discover we've done the wrong thing. I don't want
4047ec5746aSDavid du Colombier * to call ipoput with the lock held.
4057ec5746aSDavid du Colombier */
4067ec5746aSDavid du Colombier restart:
4077ec5746aSDavid du Colombier suspended = grec->dlsusp;
4087ec5746aSDavid du Colombier if(suspended){
4097ec5746aSDavid du Colombier if(!canqlock(&grec->lock)){
4107ec5746aSDavid du Colombier /*
4117ec5746aSDavid du Colombier * just give up. too bad, we lose a packet. this
4127ec5746aSDavid du Colombier * is just too hard and my brain already hurts.
4137ec5746aSDavid du Colombier */
4147ec5746aSDavid du Colombier freeb(bp);
4157ec5746aSDavid du Colombier return;
4167ec5746aSDavid du Colombier }
4177ec5746aSDavid du Colombier
4187ec5746aSDavid du Colombier if(!grec->dlsusp){
4197ec5746aSDavid du Colombier /*
4207ec5746aSDavid du Colombier * suspend race. We though we were suspended, but
4217ec5746aSDavid du Colombier * we really weren't.
4227ec5746aSDavid du Colombier */
4237ec5746aSDavid du Colombier qunlock(&grec->lock);
4247ec5746aSDavid du Colombier goto restart;
4257ec5746aSDavid du Colombier }
4267ec5746aSDavid du Colombier
4277ec5746aSDavid du Colombier /* Undo the incorrect ref count addition */
4287ec5746aSDavid du Colombier addring(&grec->dlbuffered, bp);
4297ec5746aSDavid du Colombier qunlock(&grec->lock);
4307ec5746aSDavid du Colombier return;
4317ec5746aSDavid du Colombier }
4327ec5746aSDavid du Colombier
4337ec5746aSDavid du Colombier /*
4347ec5746aSDavid du Colombier * When we get here, we're not suspended. Proceed to send the
4357ec5746aSDavid du Colombier * packet.
4367ec5746aSDavid du Colombier */
4377ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
4387ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
4397ec5746aSDavid du Colombier
4407ec5746aSDavid du Colombier /*
4417ec5746aSDavid du Colombier * Make sure the packet does not go away.
4427ec5746aSDavid du Colombier */
443*61d44851SDavid du Colombier ainc(&bp->ref);
4447ec5746aSDavid du Colombier assert(bp->ref == 2);
4457ec5746aSDavid du Colombier
4467ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
4477ec5746aSDavid du Colombier grepdout++;
4487ec5746aSDavid du Colombier grebdout += BLEN(bp);
4497ec5746aSDavid du Colombier
4507ec5746aSDavid du Colombier /*
4517ec5746aSDavid du Colombier * Now make sure we didn't do the wrong thing.
4527ec5746aSDavid du Colombier */
4537ec5746aSDavid du Colombier if(!canqlock(&grec->lock)){
4547ec5746aSDavid du Colombier freeb(bp); /* The packet just goes away */
4557ec5746aSDavid du Colombier return;
4567ec5746aSDavid du Colombier }
4577ec5746aSDavid du Colombier
4587ec5746aSDavid du Colombier /* We did the right thing */
4597ec5746aSDavid du Colombier addring(&grec->dlpending, bp);
4607ec5746aSDavid du Colombier qunlock(&grec->lock);
4617ec5746aSDavid du Colombier }
4627ec5746aSDavid du Colombier
4637ec5746aSDavid du Colombier static void
greuplink(Conv * c,Block * bp)4647ec5746aSDavid du Colombier greuplink(Conv *c, Block *bp)
4657ec5746aSDavid du Colombier {
4667ec5746aSDavid du Colombier GREconv *grec;
4677ec5746aSDavid du Colombier GREhdr *gre;
4687ec5746aSDavid du Colombier ushort flags;
4697ec5746aSDavid du Colombier
4707ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
4717ec5746aSDavid du Colombier if(gre->ttl == 1)
4727ec5746aSDavid du Colombier return;
4737ec5746aSDavid du Colombier
4747ec5746aSDavid du Colombier grec = c->ptcl;
4757ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->src);
4767ec5746aSDavid du Colombier memmove(gre->dst, grec->north, sizeof gre->dst);
4777ec5746aSDavid du Colombier
4787ec5746aSDavid du Colombier /*
4797ec5746aSDavid du Colombier * Add a key, if needed.
4807ec5746aSDavid du Colombier */
4817ec5746aSDavid du Colombier if(grec->ulkey){
4827ec5746aSDavid du Colombier flags = nhgets(gre->flags);
4837ec5746aSDavid du Colombier if(flags & (GRE_cksum|GRE_routing)){
4847ec5746aSDavid du Colombier print("%V routing info present. Discarding packet\n",
4857ec5746aSDavid du Colombier gre->src);
4867ec5746aSDavid du Colombier freeb(bp);
4877ec5746aSDavid du Colombier return;
4887ec5746aSDavid du Colombier }
4897ec5746aSDavid du Colombier
4907ec5746aSDavid du Colombier if((flags & GRE_key) == 0){
4917ec5746aSDavid du Colombier /* Make room for the key */
4927ec5746aSDavid du Colombier if(bp->rp - bp->base < sizeof(ulong)){
4937ec5746aSDavid du Colombier print("%V can't add key\n", gre->src);
4947ec5746aSDavid du Colombier freeb(bp);
4957ec5746aSDavid du Colombier return;
4967ec5746aSDavid du Colombier }
4977ec5746aSDavid du Colombier
4987ec5746aSDavid du Colombier bp->rp -= 4;
4997ec5746aSDavid du Colombier memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
5007ec5746aSDavid du Colombier
5017ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
5027ec5746aSDavid du Colombier hnputs(gre->flags, flags | GRE_key);
5037ec5746aSDavid du Colombier }
5047ec5746aSDavid du Colombier
5057ec5746aSDavid du Colombier /* Add the key */
5067ec5746aSDavid du Colombier hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
5077ec5746aSDavid du Colombier }
5087ec5746aSDavid du Colombier
5097ec5746aSDavid du Colombier if(!canqlock(&grec->lock)){
5107ec5746aSDavid du Colombier freeb(bp);
5117ec5746aSDavid du Colombier return;
5127ec5746aSDavid du Colombier }
5137ec5746aSDavid du Colombier
5147ec5746aSDavid du Colombier if(grec->ulsusp)
5157ec5746aSDavid du Colombier addring(&grec->ulbuffered, bp);
5167ec5746aSDavid du Colombier else{
5177ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
5187ec5746aSDavid du Colombier grepuout++;
5197ec5746aSDavid du Colombier grebuout += BLEN(bp);
5207ec5746aSDavid du Colombier }
5217ec5746aSDavid du Colombier qunlock(&grec->lock);
5227ec5746aSDavid du Colombier }
5237ec5746aSDavid du Colombier
5247ec5746aSDavid du Colombier static void
greiput(Proto * proto,Ipifc *,Block * bp)5257ec5746aSDavid du Colombier greiput(Proto *proto, Ipifc *, Block *bp)
5267ec5746aSDavid du Colombier {
5277ec5746aSDavid du Colombier int len, hdrlen;
5287ec5746aSDavid du Colombier ushort eproto, flags;
5297ec5746aSDavid du Colombier uchar raddr[IPaddrlen];
5307ec5746aSDavid du Colombier Conv *c, **p;
5317ec5746aSDavid du Colombier GREconv *grec;
5327ec5746aSDavid du Colombier GREhdr *gre;
5337ec5746aSDavid du Colombier GREpriv *gpriv;
5347ec5746aSDavid du Colombier Ip4hdr *ip;
5357ec5746aSDavid du Colombier
5367ec5746aSDavid du Colombier /*
5377ec5746aSDavid du Colombier * We don't want to deal with block lists. Ever. The problem is
5387ec5746aSDavid du Colombier * that when the block is forwarded, devether.c puts the block into
5397ec5746aSDavid du Colombier * a queue that also uses ->next. Just do not use ->next here!
5407ec5746aSDavid du Colombier */
5417ec5746aSDavid du Colombier if(bp->next){
5427ec5746aSDavid du Colombier len = blocklen(bp);
5437ec5746aSDavid du Colombier bp = pullupblock(bp, len);
5447ec5746aSDavid du Colombier assert(BLEN(bp) == len && bp->next == nil);
5457ec5746aSDavid du Colombier }
5467ec5746aSDavid du Colombier
5477ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
5487ec5746aSDavid du Colombier if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
5497ec5746aSDavid du Colombier freeb(bp);
5507ec5746aSDavid du Colombier return;
5517ec5746aSDavid du Colombier }
5527ec5746aSDavid du Colombier
5537ec5746aSDavid du Colombier v4tov6(raddr, gre->src);
5547ec5746aSDavid du Colombier eproto = nhgets(gre->eproto);
5557ec5746aSDavid du Colombier flags = nhgets(gre->flags);
5567ec5746aSDavid du Colombier hdrlen = sizeof(GREhdr);
5577ec5746aSDavid du Colombier
5587ec5746aSDavid du Colombier if(flags & GRE_cksum)
5597ec5746aSDavid du Colombier hdrlen += 2;
5607ec5746aSDavid du Colombier if(flags & GRE_routing){
5617ec5746aSDavid du Colombier print("%I routing info present. Discarding packet\n", raddr);
5627ec5746aSDavid du Colombier freeb(bp);
5637ec5746aSDavid du Colombier return;
5647ec5746aSDavid du Colombier }
5657ec5746aSDavid du Colombier if(flags & (GRE_cksum|GRE_routing))
5667ec5746aSDavid du Colombier hdrlen += 2; /* Offset field */
5677ec5746aSDavid du Colombier if(flags & GRE_key)
5687ec5746aSDavid du Colombier hdrlen += 4;
5697ec5746aSDavid du Colombier if(flags & GRE_seq)
5707ec5746aSDavid du Colombier hdrlen += 4;
5717ec5746aSDavid du Colombier
5727ec5746aSDavid du Colombier if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
5737ec5746aSDavid du Colombier print("greretunnel: packet too short (s=%V d=%V)\n",
5747ec5746aSDavid du Colombier gre->src, gre->dst);
5757ec5746aSDavid du Colombier freeb(bp);
5767ec5746aSDavid du Colombier return;
5777ec5746aSDavid du Colombier }
5787ec5746aSDavid du Colombier ip = (Ip4hdr *)(bp->rp + hdrlen);
5797ec5746aSDavid du Colombier
5807ec5746aSDavid du Colombier qlock(proto);
5817ec5746aSDavid du Colombier /*
5827ec5746aSDavid du Colombier * Look for a conversation structure for this port and address, or
5837ec5746aSDavid du Colombier * match the retunnel part, or match on the raw flag.
5847ec5746aSDavid du Colombier */
5857ec5746aSDavid du Colombier for(p = proto->conv; *p; p++) {
5867ec5746aSDavid du Colombier c = *p;
5877ec5746aSDavid du Colombier
5887ec5746aSDavid du Colombier if(c->inuse == 0)
5897ec5746aSDavid du Colombier continue;
5907ec5746aSDavid du Colombier
5917ec5746aSDavid du Colombier /*
5927ec5746aSDavid du Colombier * Do not stop this session - blocking here
5937ec5746aSDavid du Colombier * implies that etherread is blocked.
5947ec5746aSDavid du Colombier */
5957ec5746aSDavid du Colombier grec = c->ptcl;
5967ec5746aSDavid du Colombier if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
5977ec5746aSDavid du Colombier grepdin++;
5987ec5746aSDavid du Colombier grebdin += BLEN(bp);
5997ec5746aSDavid du Colombier gredownlink(c, bp);
6007ec5746aSDavid du Colombier qunlock(proto);
6017ec5746aSDavid du Colombier return;
6027ec5746aSDavid du Colombier }
6037ec5746aSDavid du Colombier
6047ec5746aSDavid du Colombier if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
6057ec5746aSDavid du Colombier grepuin++;
6067ec5746aSDavid du Colombier grebuin += BLEN(bp);
6077ec5746aSDavid du Colombier greuplink(c, bp);
6087ec5746aSDavid du Colombier qunlock(proto);
6097ec5746aSDavid du Colombier return;
6107ec5746aSDavid du Colombier }
6117ec5746aSDavid du Colombier }
6127ec5746aSDavid du Colombier
6137ec5746aSDavid du Colombier /*
6147ec5746aSDavid du Colombier * when we get here, none of the forwarding tunnels matched. now
6157ec5746aSDavid du Colombier * try to match on raw and conversational sessions.
6167ec5746aSDavid du Colombier */
6177ec5746aSDavid du Colombier for(c = nil, p = proto->conv; *p; p++) {
6187ec5746aSDavid du Colombier c = *p;
6197ec5746aSDavid du Colombier
6207ec5746aSDavid du Colombier if(c->inuse == 0)
6217ec5746aSDavid du Colombier continue;
6227ec5746aSDavid du Colombier
6237ec5746aSDavid du Colombier /*
6247ec5746aSDavid du Colombier * Do not stop this session - blocking here
6257ec5746aSDavid du Colombier * implies that etherread is blocked.
6267ec5746aSDavid du Colombier */
6277ec5746aSDavid du Colombier grec = c->ptcl;
6287ec5746aSDavid du Colombier if(c->rport == eproto &&
6297ec5746aSDavid du Colombier (grec->raw || ipcmp(c->raddr, raddr) == 0))
6307ec5746aSDavid du Colombier break;
6317ec5746aSDavid du Colombier }
6327ec5746aSDavid du Colombier
6337ec5746aSDavid du Colombier qunlock(proto);
6347ec5746aSDavid du Colombier
6357ec5746aSDavid du Colombier if(*p == nil){
6367ec5746aSDavid du Colombier freeb(bp);
6377ec5746aSDavid du Colombier return;
6387ec5746aSDavid du Colombier }
6397dd7cddfSDavid du Colombier
6407dd7cddfSDavid du Colombier /*
6417dd7cddfSDavid du Colombier * Trim the packet down to data size
6427dd7cddfSDavid du Colombier */
6437ec5746aSDavid du Colombier len = nhgets(gre->len) - GRE_IPONLY;
6447dd7cddfSDavid du Colombier if(len < GRE_IPPLUSGRE){
6457ec5746aSDavid du Colombier freeb(bp);
6467dd7cddfSDavid du Colombier return;
6477dd7cddfSDavid du Colombier }
6487ec5746aSDavid du Colombier
6497dd7cddfSDavid du Colombier bp = trimblock(bp, GRE_IPONLY, len);
6507dd7cddfSDavid du Colombier if(bp == nil){
6517ec5746aSDavid du Colombier gpriv = proto->priv;
6527dd7cddfSDavid du Colombier gpriv->lenerr++;
6537dd7cddfSDavid du Colombier return;
6547dd7cddfSDavid du Colombier }
6557dd7cddfSDavid du Colombier
6567dd7cddfSDavid du Colombier /*
6577dd7cddfSDavid du Colombier * Can't delimit packet so pull it all into one block.
6587dd7cddfSDavid du Colombier */
6597ec5746aSDavid du Colombier if(qlen(c->rq) > GREqlen)
6607ec5746aSDavid du Colombier freeb(bp);
6617dd7cddfSDavid du Colombier else{
6627dd7cddfSDavid du Colombier bp = concatblock(bp);
6637dd7cddfSDavid du Colombier if(bp == 0)
6647dd7cddfSDavid du Colombier panic("greiput");
6657dd7cddfSDavid du Colombier qpass(c->rq, bp);
6667dd7cddfSDavid du Colombier }
6677dd7cddfSDavid du Colombier }
6687dd7cddfSDavid du Colombier
6697dd7cddfSDavid du Colombier int
grestats(Proto * gre,char * buf,int len)6707dd7cddfSDavid du Colombier grestats(Proto *gre, char *buf, int len)
6717dd7cddfSDavid du Colombier {
6727dd7cddfSDavid du Colombier GREpriv *gpriv;
6737dd7cddfSDavid du Colombier
6747dd7cddfSDavid du Colombier gpriv = gre->priv;
6757ec5746aSDavid du Colombier return snprint(buf, len,
6767ec5746aSDavid du Colombier "gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
6777ec5746aSDavid du Colombier grepdin, grepdout, grepuin, grepuout,
6787ec5746aSDavid du Colombier grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
6797ec5746aSDavid du Colombier }
6807dd7cddfSDavid du Colombier
6817ec5746aSDavid du Colombier static char *
grectlraw(Conv * c,int,char **)6827ec5746aSDavid du Colombier grectlraw(Conv *c, int, char **)
6837ec5746aSDavid du Colombier {
6847ec5746aSDavid du Colombier GREconv *grec;
6857ec5746aSDavid du Colombier
6867ec5746aSDavid du Colombier grec = c->ptcl;
6877ec5746aSDavid du Colombier grec->raw = 1;
6887ec5746aSDavid du Colombier return nil;
6897ec5746aSDavid du Colombier }
6907ec5746aSDavid du Colombier
6917ec5746aSDavid du Colombier static char *
grectlcooked(Conv * c,int,char **)6927ec5746aSDavid du Colombier grectlcooked(Conv *c, int, char **)
6937ec5746aSDavid du Colombier {
6947ec5746aSDavid du Colombier GREconv *grec;
6957ec5746aSDavid du Colombier
6967ec5746aSDavid du Colombier grec = c->ptcl;
6977ec5746aSDavid du Colombier grec->raw = 0;
6987ec5746aSDavid du Colombier return nil;
6997ec5746aSDavid du Colombier }
7007ec5746aSDavid du Colombier
7017ec5746aSDavid du Colombier static char *
grectlretunnel(Conv * c,int,char ** argv)7027ec5746aSDavid du Colombier grectlretunnel(Conv *c, int, char **argv)
7037ec5746aSDavid du Colombier {
7047ec5746aSDavid du Colombier GREconv *grec;
7057ec5746aSDavid du Colombier uchar ipaddr[4];
7067ec5746aSDavid du Colombier
7077ec5746aSDavid du Colombier grec = c->ptcl;
7087ec5746aSDavid du Colombier if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
7097ec5746aSDavid du Colombier return "tunnel already set up";
7107ec5746aSDavid du Colombier
7117ec5746aSDavid du Colombier v4parseip(ipaddr, argv[1]);
7127ec5746aSDavid du Colombier if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
7137ec5746aSDavid du Colombier return "bad hoa";
7147ec5746aSDavid du Colombier memmove(grec->hoa, ipaddr, sizeof grec->hoa);
7157ec5746aSDavid du Colombier v4parseip(ipaddr, argv[2]);
7167ec5746aSDavid du Colombier memmove(grec->north, ipaddr, sizeof grec->north);
7177ec5746aSDavid du Colombier v4parseip(ipaddr, argv[3]);
7187ec5746aSDavid du Colombier memmove(grec->south, ipaddr, sizeof grec->south);
7197ec5746aSDavid du Colombier v4parseip(ipaddr, argv[4]);
7207ec5746aSDavid du Colombier memmove(grec->coa, ipaddr, sizeof grec->coa);
7217ec5746aSDavid du Colombier grec->ulsusp = 1;
7227ec5746aSDavid du Colombier grec->dlsusp = 0;
7237ec5746aSDavid du Colombier
7247ec5746aSDavid du Colombier return nil;
7257ec5746aSDavid du Colombier }
7267ec5746aSDavid du Colombier
7277ec5746aSDavid du Colombier static char *
grectlreport(Conv * c,int,char ** argv)7287ec5746aSDavid du Colombier grectlreport(Conv *c, int, char **argv)
7297ec5746aSDavid du Colombier {
7307ec5746aSDavid du Colombier ulong seq;
7317ec5746aSDavid du Colombier Block *bp;
7327ec5746aSDavid du Colombier Bring *r;
7337ec5746aSDavid du Colombier GREconv *grec;
7347ec5746aSDavid du Colombier Metablock *m;
7357ec5746aSDavid du Colombier
7367ec5746aSDavid du Colombier grec = c->ptcl;
7377ec5746aSDavid du Colombier seq = strtoul(argv[1], nil, 0);
7387ec5746aSDavid du Colombier
7397ec5746aSDavid du Colombier qlock(&grec->lock);
7407ec5746aSDavid du Colombier r = &grec->dlpending;
7417ec5746aSDavid du Colombier while(r->produced - r->consumed > 0){
7427ec5746aSDavid du Colombier bp = r->ring[r->consumed & Ringmask];
7437ec5746aSDavid du Colombier
7447ec5746aSDavid du Colombier assert(bp && bp->rp - bp->base >= sizeof(Metablock));
7457ec5746aSDavid du Colombier m = (Metablock *)bp->base;
7467ec5746aSDavid du Colombier if((long)(seq - m->seq) <= 0)
7477ec5746aSDavid du Colombier break;
7487ec5746aSDavid du Colombier
7497ec5746aSDavid du Colombier r->ring[r->consumed & Ringmask] = nil;
7507ec5746aSDavid du Colombier r->consumed++;
7517ec5746aSDavid du Colombier
7527ec5746aSDavid du Colombier freeb(bp);
7537ec5746aSDavid du Colombier }
7547ec5746aSDavid du Colombier qunlock(&grec->lock);
7557ec5746aSDavid du Colombier return nil;
7567ec5746aSDavid du Colombier }
7577ec5746aSDavid du Colombier
7587ec5746aSDavid du Colombier static char *
grectldlsuspend(Conv * c,int,char **)7597ec5746aSDavid du Colombier grectldlsuspend(Conv *c, int, char **)
7607ec5746aSDavid du Colombier {
7617ec5746aSDavid du Colombier GREconv *grec;
7627ec5746aSDavid du Colombier
7637ec5746aSDavid du Colombier grec = c->ptcl;
7647ec5746aSDavid du Colombier if(grec->dlsusp)
7657ec5746aSDavid du Colombier return "already suspended";
7667ec5746aSDavid du Colombier
7677ec5746aSDavid du Colombier grec->dlsusp = 1;
7687ec5746aSDavid du Colombier return nil;
7697ec5746aSDavid du Colombier }
7707ec5746aSDavid du Colombier
7717ec5746aSDavid du Colombier static char *
grectlulsuspend(Conv * c,int,char **)7727ec5746aSDavid du Colombier grectlulsuspend(Conv *c, int, char **)
7737ec5746aSDavid du Colombier {
7747ec5746aSDavid du Colombier GREconv *grec;
7757ec5746aSDavid du Colombier
7767ec5746aSDavid du Colombier grec = c->ptcl;
7777ec5746aSDavid du Colombier if(grec->ulsusp)
7787ec5746aSDavid du Colombier return "already suspended";
7797ec5746aSDavid du Colombier
7807ec5746aSDavid du Colombier grec->ulsusp = 1;
7817ec5746aSDavid du Colombier return nil;
7827ec5746aSDavid du Colombier }
7837ec5746aSDavid du Colombier
7847ec5746aSDavid du Colombier static char *
grectldlresume(Conv * c,int,char **)7857ec5746aSDavid du Colombier grectldlresume(Conv *c, int, char **)
7867ec5746aSDavid du Colombier {
7877ec5746aSDavid du Colombier GREconv *grec;
7887ec5746aSDavid du Colombier GREhdr *gre;
7897ec5746aSDavid du Colombier Block *bp;
7907ec5746aSDavid du Colombier
7917ec5746aSDavid du Colombier grec = c->ptcl;
7927ec5746aSDavid du Colombier
7937ec5746aSDavid du Colombier qlock(&grec->lock);
7947ec5746aSDavid du Colombier if(!grec->dlsusp){
7957ec5746aSDavid du Colombier qunlock(&grec->lock);
7967ec5746aSDavid du Colombier return "not suspended";
7977ec5746aSDavid du Colombier }
7987ec5746aSDavid du Colombier
7997ec5746aSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil){
8007ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
8017ec5746aSDavid du Colombier qunlock(&grec->lock);
8027ec5746aSDavid du Colombier
8037ec5746aSDavid du Colombier /*
8047ec5746aSDavid du Colombier * Make sure the packet does not go away.
8057ec5746aSDavid du Colombier */
806*61d44851SDavid du Colombier ainc(&bp->ref);
8077ec5746aSDavid du Colombier assert(bp->ref == 2);
8087ec5746aSDavid du Colombier
8097ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8107ec5746aSDavid du Colombier
8117ec5746aSDavid du Colombier qlock(&grec->lock);
8127ec5746aSDavid du Colombier addring(&grec->dlpending, bp);
8137ec5746aSDavid du Colombier }
8147ec5746aSDavid du Colombier grec->dlsusp = 0;
8157ec5746aSDavid du Colombier qunlock(&grec->lock);
8167ec5746aSDavid du Colombier return nil;
8177ec5746aSDavid du Colombier }
8187ec5746aSDavid du Colombier
8197ec5746aSDavid du Colombier static char *
grectlulresume(Conv * c,int,char **)8207ec5746aSDavid du Colombier grectlulresume(Conv *c, int, char **)
8217ec5746aSDavid du Colombier {
8227ec5746aSDavid du Colombier GREconv *grec;
8237ec5746aSDavid du Colombier GREhdr *gre;
8247ec5746aSDavid du Colombier Block *bp;
8257ec5746aSDavid du Colombier
8267ec5746aSDavid du Colombier grec = c->ptcl;
8277ec5746aSDavid du Colombier
8287ec5746aSDavid du Colombier qlock(&grec->lock);
8297ec5746aSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil){
8307ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
8317ec5746aSDavid du Colombier
8327ec5746aSDavid du Colombier qunlock(&grec->lock);
8337ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8347ec5746aSDavid du Colombier qlock(&grec->lock);
8357ec5746aSDavid du Colombier }
8367ec5746aSDavid du Colombier grec->ulsusp = 0;
8377ec5746aSDavid du Colombier qunlock(&grec->lock);
8387ec5746aSDavid du Colombier return nil;
8397ec5746aSDavid du Colombier }
8407ec5746aSDavid du Colombier
8417ec5746aSDavid du Colombier static char *
grectlforward(Conv * c,int,char ** argv)8427ec5746aSDavid du Colombier grectlforward(Conv *c, int, char **argv)
8437ec5746aSDavid du Colombier {
8447ec5746aSDavid du Colombier int len;
8457ec5746aSDavid du Colombier Block *bp, *nbp;
8467ec5746aSDavid du Colombier GREconv *grec;
8477ec5746aSDavid du Colombier GREhdr *gre;
8487ec5746aSDavid du Colombier Metablock *m;
8497ec5746aSDavid du Colombier
8507ec5746aSDavid du Colombier grec = c->ptcl;
8517ec5746aSDavid du Colombier
8527ec5746aSDavid du Colombier v4parseip(grec->south, argv[1]);
8537ec5746aSDavid du Colombier memmove(grec->north, grec->south, sizeof grec->north);
8547ec5746aSDavid du Colombier
8557ec5746aSDavid du Colombier qlock(&grec->lock);
8567ec5746aSDavid du Colombier if(!grec->dlsusp){
8577ec5746aSDavid du Colombier qunlock(&grec->lock);
8587ec5746aSDavid du Colombier return "not suspended";
8597ec5746aSDavid du Colombier }
8607ec5746aSDavid du Colombier grec->dlsusp = 0;
8617ec5746aSDavid du Colombier grec->ulsusp = 0;
8627ec5746aSDavid du Colombier
8637ec5746aSDavid du Colombier while((bp = getring(&grec->dlpending)) != nil){
8647ec5746aSDavid du Colombier
8657ec5746aSDavid du Colombier assert(bp->rp - bp->base >= sizeof(Metablock));
8667ec5746aSDavid du Colombier m = (Metablock *)bp->base;
8677ec5746aSDavid du Colombier assert(m->rp >= bp->base && m->rp < bp->lim);
8687ec5746aSDavid du Colombier
8697ec5746aSDavid du Colombier /*
8707ec5746aSDavid du Colombier * If the packet is still held inside the IP transmit
8717ec5746aSDavid du Colombier * system, make a copy of the packet first.
8727ec5746aSDavid du Colombier */
8737ec5746aSDavid du Colombier if(bp->ref > 1){
8747ec5746aSDavid du Colombier len = bp->wp - m->rp;
8757ec5746aSDavid du Colombier nbp = allocb(len);
8767ec5746aSDavid du Colombier memmove(nbp->wp, m->rp, len);
8777ec5746aSDavid du Colombier nbp->wp += len;
8787ec5746aSDavid du Colombier freeb(bp);
8797ec5746aSDavid du Colombier bp = nbp;
8807ec5746aSDavid du Colombier }
8817ec5746aSDavid du Colombier else{
8827ec5746aSDavid du Colombier /* Patch up rp */
8837ec5746aSDavid du Colombier bp->rp = m->rp;
8847ec5746aSDavid du Colombier }
8857ec5746aSDavid du Colombier
8867ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
8877ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
8887ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
8897ec5746aSDavid du Colombier
8907ec5746aSDavid du Colombier qunlock(&grec->lock);
8917ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
8927ec5746aSDavid du Colombier qlock(&grec->lock);
8937ec5746aSDavid du Colombier }
8947ec5746aSDavid du Colombier
8957ec5746aSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil){
8967ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
8977ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
8987ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
8997ec5746aSDavid du Colombier
9007ec5746aSDavid du Colombier qunlock(&grec->lock);
9017ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9027ec5746aSDavid du Colombier qlock(&grec->lock);
9037ec5746aSDavid du Colombier }
9047ec5746aSDavid du Colombier
9057ec5746aSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil){
9067ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
9077ec5746aSDavid du Colombier
9087ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
9097ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
9107ec5746aSDavid du Colombier
9117ec5746aSDavid du Colombier qunlock(&grec->lock);
9127ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
9137ec5746aSDavid du Colombier qlock(&grec->lock);
9147ec5746aSDavid du Colombier }
9157ec5746aSDavid du Colombier qunlock(&grec->lock);
9167ec5746aSDavid du Colombier return nil;
9177ec5746aSDavid du Colombier }
9187ec5746aSDavid du Colombier
9197ec5746aSDavid du Colombier static char *
grectlulkey(Conv * c,int,char ** argv)9207ec5746aSDavid du Colombier grectlulkey(Conv *c, int, char **argv)
9217ec5746aSDavid du Colombier {
9227ec5746aSDavid du Colombier GREconv *grec;
9237ec5746aSDavid du Colombier
9247ec5746aSDavid du Colombier grec = c->ptcl;
9257ec5746aSDavid du Colombier grec->ulkey = strtoul(argv[1], nil, 0);
9267ec5746aSDavid du Colombier return nil;
9277dd7cddfSDavid du Colombier }
9287dd7cddfSDavid du Colombier
9295d82c6aeSDavid du Colombier char *
grectl(Conv * c,char ** f,int n)9305d82c6aeSDavid du Colombier grectl(Conv *c, char **f, int n)
9315d82c6aeSDavid du Colombier {
9327ec5746aSDavid du Colombier int i;
9335d82c6aeSDavid du Colombier
9347ec5746aSDavid du Colombier if(n < 1)
9357ec5746aSDavid du Colombier return "too few arguments";
9367ec5746aSDavid du Colombier
9377ec5746aSDavid du Colombier for(i = 0; i < Ncmds; i++)
9387ec5746aSDavid du Colombier if(strcmp(f[0], grectls[i].cmd) == 0)
9397ec5746aSDavid du Colombier break;
9407ec5746aSDavid du Colombier
9417ec5746aSDavid du Colombier if(i == Ncmds)
9427ec5746aSDavid du Colombier return "no such command";
9437ec5746aSDavid du Colombier if(grectls[i].argc != 0 && grectls[i].argc != n)
9447ec5746aSDavid du Colombier return "incorrect number of arguments";
9457ec5746aSDavid du Colombier
9467ec5746aSDavid du Colombier return grectls[i].f(c, n, f);
9475d82c6aeSDavid du Colombier }
9485d82c6aeSDavid du Colombier
9497dd7cddfSDavid du Colombier void
greinit(Fs * fs)9507dd7cddfSDavid du Colombier greinit(Fs *fs)
9517dd7cddfSDavid du Colombier {
9527dd7cddfSDavid du Colombier Proto *gre;
9537dd7cddfSDavid du Colombier
9547dd7cddfSDavid du Colombier gre = smalloc(sizeof(Proto));
9557dd7cddfSDavid du Colombier gre->priv = smalloc(sizeof(GREpriv));
9567dd7cddfSDavid du Colombier gre->name = "gre";
9577dd7cddfSDavid du Colombier gre->connect = greconnect;
9587dd7cddfSDavid du Colombier gre->announce = greannounce;
9597dd7cddfSDavid du Colombier gre->state = grestate;
9607dd7cddfSDavid du Colombier gre->create = grecreate;
9617dd7cddfSDavid du Colombier gre->close = greclose;
9627dd7cddfSDavid du Colombier gre->rcv = greiput;
9635d82c6aeSDavid du Colombier gre->ctl = grectl;
9647dd7cddfSDavid du Colombier gre->advise = nil;
9657dd7cddfSDavid du Colombier gre->stats = grestats;
9667dd7cddfSDavid du Colombier gre->ipproto = IP_GREPROTO;
9677dd7cddfSDavid du Colombier gre->nc = 64;
9687ec5746aSDavid du Colombier gre->ptclsize = sizeof(GREconv);
9697dd7cddfSDavid du Colombier
9707dd7cddfSDavid du Colombier Fsproto(fs, gre);
9717dd7cddfSDavid du Colombier }
972