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
13*7ec5746aSDavid 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,
21*7ec5746aSDavid du Colombier
22*7ec5746aSDavid du Colombier K = 1024,
23*7ec5746aSDavid du Colombier GREqlen = 256 * K,
24*7ec5746aSDavid du Colombier
25*7ec5746aSDavid du Colombier GRE_cksum = 0x8000,
26*7ec5746aSDavid du Colombier GRE_routing = 0x4000,
27*7ec5746aSDavid du Colombier GRE_key = 0x2000,
28*7ec5746aSDavid du Colombier GRE_seq = 0x1000,
29*7ec5746aSDavid du Colombier
30*7ec5746aSDavid du Colombier Nring = 1 << 10, /* power of two, please */
31*7ec5746aSDavid du Colombier Ringmask = Nring - 1,
32*7ec5746aSDavid du Colombier
33*7ec5746aSDavid du Colombier GREctlraw = 0,
34*7ec5746aSDavid du Colombier GREctlcooked,
35*7ec5746aSDavid du Colombier GREctlretunnel,
36*7ec5746aSDavid du Colombier GREctlreport,
37*7ec5746aSDavid du Colombier GREctldlsuspend,
38*7ec5746aSDavid du Colombier GREctlulsuspend,
39*7ec5746aSDavid du Colombier GREctldlresume,
40*7ec5746aSDavid du Colombier GREctlulresume,
41*7ec5746aSDavid du Colombier GREctlforward,
42*7ec5746aSDavid du Colombier GREctlulkey,
43*7ec5746aSDavid du Colombier Ncmds,
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier
46*7ec5746aSDavid du Colombier typedef struct GREhdr GREhdr;
47*7ec5746aSDavid 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 */
54*7ec5746aSDavid 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 */
63*7ec5746aSDavid du Colombier };
647dd7cddfSDavid du Colombier
657dd7cddfSDavid du Colombier typedef struct GREpriv GREpriv;
66*7ec5746aSDavid du Colombier struct GREpriv{
677dd7cddfSDavid du Colombier /* non-MIB stats */
687dd7cddfSDavid du Colombier ulong lenerr; /* short packet */
697dd7cddfSDavid du Colombier };
707dd7cddfSDavid du Colombier
71*7ec5746aSDavid du Colombier typedef struct Bring Bring;
72*7ec5746aSDavid du Colombier struct Bring{
73*7ec5746aSDavid du Colombier Block *ring[Nring];
74*7ec5746aSDavid du Colombier long produced;
75*7ec5746aSDavid du Colombier long consumed;
76*7ec5746aSDavid du Colombier };
77*7ec5746aSDavid du Colombier
78*7ec5746aSDavid du Colombier typedef struct GREconv GREconv;
79*7ec5746aSDavid du Colombier struct GREconv{
80*7ec5746aSDavid du Colombier int raw;
81*7ec5746aSDavid du Colombier
82*7ec5746aSDavid du Colombier /* Retunnelling information. v4 only */
83*7ec5746aSDavid du Colombier uchar north[4]; /* HA */
84*7ec5746aSDavid du Colombier uchar south[4]; /* Base station */
85*7ec5746aSDavid du Colombier uchar hoa[4]; /* Home address */
86*7ec5746aSDavid du Colombier uchar coa[4]; /* Careof address */
87*7ec5746aSDavid du Colombier ulong seq; /* Current sequence # */
88*7ec5746aSDavid du Colombier int dlsusp; /* Downlink suspended? */
89*7ec5746aSDavid du Colombier int ulsusp; /* Uplink suspended? */
90*7ec5746aSDavid du Colombier ulong ulkey; /* GRE key */
91*7ec5746aSDavid du Colombier
92*7ec5746aSDavid du Colombier QLock lock; /* Lock for rings */
93*7ec5746aSDavid du Colombier Bring dlpending; /* Ring of pending packets */
94*7ec5746aSDavid du Colombier Bring dlbuffered; /* Received while suspended */
95*7ec5746aSDavid du Colombier Bring ulbuffered; /* Received while suspended */
96*7ec5746aSDavid du Colombier };
97*7ec5746aSDavid du Colombier
98*7ec5746aSDavid du Colombier typedef struct Metablock Metablock;
99*7ec5746aSDavid du Colombier struct Metablock{
100*7ec5746aSDavid du Colombier uchar *rp;
101*7ec5746aSDavid du Colombier ulong seq;
102*7ec5746aSDavid du Colombier };
103*7ec5746aSDavid du Colombier
104*7ec5746aSDavid du Colombier static char *grectlcooked(Conv *, int, char **);
105*7ec5746aSDavid du Colombier static char *grectldlresume(Conv *, int, char **);
106*7ec5746aSDavid du Colombier static char *grectldlsuspend(Conv *, int, char **);
107*7ec5746aSDavid du Colombier static char *grectlforward(Conv *, int, char **);
108*7ec5746aSDavid du Colombier static char *grectlraw(Conv *, int, char **);
109*7ec5746aSDavid du Colombier static char *grectlreport(Conv *, int, char **);
110*7ec5746aSDavid du Colombier static char *grectlretunnel(Conv *, int, char **);
111*7ec5746aSDavid du Colombier static char *grectlulkey(Conv *, int, char **);
112*7ec5746aSDavid du Colombier static char *grectlulresume(Conv *, int, char **);
113*7ec5746aSDavid du Colombier static char *grectlulsuspend(Conv *, int, char **);
114*7ec5746aSDavid du Colombier
115*7ec5746aSDavid du Colombier static struct{
116*7ec5746aSDavid du Colombier char *cmd;
117*7ec5746aSDavid du Colombier int argc;
118*7ec5746aSDavid du Colombier char *(*f)(Conv *, int, char **);
119*7ec5746aSDavid du Colombier } grectls[Ncmds] = {
120*7ec5746aSDavid du Colombier [GREctlraw] = { "raw", 1, grectlraw, },
121*7ec5746aSDavid du Colombier [GREctlcooked] = { "cooked", 1, grectlcooked, },
122*7ec5746aSDavid du Colombier [GREctlretunnel]= { "retunnel", 5, grectlretunnel, },
123*7ec5746aSDavid du Colombier [GREctlreport] = { "report", 2, grectlreport, },
124*7ec5746aSDavid du Colombier [GREctldlsuspend]= { "dlsuspend", 1, grectldlsuspend,},
125*7ec5746aSDavid du Colombier [GREctlulsuspend]= { "ulsuspend", 1, grectlulsuspend,},
126*7ec5746aSDavid du Colombier [GREctldlresume]= { "dlresume", 1, grectldlresume, },
127*7ec5746aSDavid du Colombier [GREctlulresume]= { "ulresume", 1, grectlulresume, },
128*7ec5746aSDavid du Colombier [GREctlforward] = { "forward", 2, grectlforward, },
129*7ec5746aSDavid du Colombier [GREctlulkey] = { "ulkey", 2, grectlulkey, },
130*7ec5746aSDavid du Colombier };
131*7ec5746aSDavid du Colombier
132*7ec5746aSDavid du Colombier static uchar nulladdr[4];
133*7ec5746aSDavid du Colombier static char *sessend = "session end";
134*7ec5746aSDavid du Colombier
135e6c6b7f8SDavid du Colombier static void grekick(void *x, Block *bp);
136*7ec5746aSDavid du Colombier static char *gresetup(Conv *, char *, char *, char *);
137*7ec5746aSDavid du Colombier
138*7ec5746aSDavid du Colombier ulong grepdin, grepdout, grebdin, grebdout;
139*7ec5746aSDavid du Colombier ulong grepuin, grepuout, grebuin, grebuout;
140*7ec5746aSDavid du Colombier
141*7ec5746aSDavid du Colombier static Block *
getring(Bring * r)142*7ec5746aSDavid du Colombier getring(Bring *r)
143*7ec5746aSDavid du Colombier {
144*7ec5746aSDavid du Colombier Block *bp;
145*7ec5746aSDavid du Colombier
146*7ec5746aSDavid du Colombier if(r->consumed == r->produced)
147*7ec5746aSDavid du Colombier return nil;
148*7ec5746aSDavid du Colombier
149*7ec5746aSDavid du Colombier bp = r->ring[r->consumed & Ringmask];
150*7ec5746aSDavid du Colombier r->ring[r->consumed & Ringmask] = nil;
151*7ec5746aSDavid du Colombier r->consumed++;
152*7ec5746aSDavid du Colombier return bp;
153*7ec5746aSDavid du Colombier }
154*7ec5746aSDavid du Colombier
155*7ec5746aSDavid du Colombier static void
addring(Bring * r,Block * bp)156*7ec5746aSDavid du Colombier addring(Bring *r, Block *bp)
157*7ec5746aSDavid du Colombier {
158*7ec5746aSDavid du Colombier Block *tbp;
159*7ec5746aSDavid du Colombier
160*7ec5746aSDavid du Colombier if(r->produced - r->consumed > Ringmask){
161*7ec5746aSDavid du Colombier /* Full! */
162*7ec5746aSDavid du Colombier tbp = r->ring[r->produced & Ringmask];
163*7ec5746aSDavid du Colombier assert(tbp);
164*7ec5746aSDavid du Colombier freeb(tbp);
165*7ec5746aSDavid du Colombier r->consumed++;
166*7ec5746aSDavid du Colombier }
167*7ec5746aSDavid du Colombier r->ring[r->produced & Ringmask] = bp;
168*7ec5746aSDavid du Colombier r->produced++;
169*7ec5746aSDavid 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 {
211*7ec5746aSDavid 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 {
218*7ec5746aSDavid du Colombier GREconv *grec;
219*7ec5746aSDavid du Colombier char *ep, *p;
220*7ec5746aSDavid du Colombier
221*7ec5746aSDavid du Colombier grec = c->ptcl;
222*7ec5746aSDavid du Colombier p = state;
223*7ec5746aSDavid du Colombier ep = p + n;
224*7ec5746aSDavid du Colombier p = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx "
225*7ec5746aSDavid du Colombier "pending %uld %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n",
226*7ec5746aSDavid du Colombier c->inuse? "Open ": "Closed ",
227*7ec5746aSDavid du Colombier grec->raw? "raw ": "",
228*7ec5746aSDavid du Colombier grec->dlsusp? "DL suspended ": "",
229*7ec5746aSDavid du Colombier grec->ulsusp? "UL suspended ": "",
230*7ec5746aSDavid du Colombier grec->hoa, grec->north, grec->south, grec->seq,
231*7ec5746aSDavid du Colombier grec->dlpending.consumed, grec->dlpending.produced,
232*7ec5746aSDavid du Colombier grec->dlbuffered.consumed, grec->dlbuffered.produced,
233*7ec5746aSDavid du Colombier grec->ulbuffered.consumed, grec->ulbuffered.produced,
234*7ec5746aSDavid du Colombier grec->ulkey);
235*7ec5746aSDavid 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 {
241*7ec5746aSDavid 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 {
247*7ec5746aSDavid du Colombier GREconv *grec;
248*7ec5746aSDavid du Colombier Block *bp;
249*7ec5746aSDavid du Colombier
250*7ec5746aSDavid du Colombier grec = c->ptcl;
251*7ec5746aSDavid du Colombier
252*7ec5746aSDavid du Colombier /* Make sure we don't forward any more packets */
253*7ec5746aSDavid du Colombier memset(grec->hoa, 0, sizeof grec->hoa);
254*7ec5746aSDavid du Colombier memset(grec->north, 0, sizeof grec->north);
255*7ec5746aSDavid du Colombier memset(grec->south, 0, sizeof grec->south);
256*7ec5746aSDavid du Colombier
257*7ec5746aSDavid du Colombier qlock(&grec->lock);
258*7ec5746aSDavid du Colombier while((bp = getring(&grec->dlpending)) != nil)
259*7ec5746aSDavid du Colombier freeb(bp);
260*7ec5746aSDavid du Colombier
261*7ec5746aSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil)
262*7ec5746aSDavid du Colombier freeb(bp);
263*7ec5746aSDavid du Colombier
264*7ec5746aSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil)
265*7ec5746aSDavid du Colombier freeb(bp);
266*7ec5746aSDavid du Colombier
267*7ec5746aSDavid du Colombier grec->dlpending.produced = grec->dlpending.consumed = 0;
268*7ec5746aSDavid du Colombier grec->dlbuffered.produced = grec->dlbuffered.consumed = 0;
269*7ec5746aSDavid du Colombier grec->ulbuffered.produced = grec->ulbuffered.consumed = 0;
270*7ec5746aSDavid du Colombier qunlock(&grec->lock);
271*7ec5746aSDavid du Colombier
272*7ec5746aSDavid du Colombier grec->raw = 0;
273*7ec5746aSDavid du Colombier grec->seq = 0;
274*7ec5746aSDavid du Colombier grec->dlsusp = grec->ulsusp = 1;
275*7ec5746aSDavid du Colombier
276*7ec5746aSDavid du Colombier qhangup(c->rq, sessend);
277*7ec5746aSDavid du Colombier qhangup(c->wq, sessend);
278*7ec5746aSDavid du Colombier qhangup(c->eq, sessend);
2797dd7cddfSDavid du Colombier ipmove(c->laddr, IPnoaddr);
2807dd7cddfSDavid du Colombier ipmove(c->raddr, IPnoaddr);
281*7ec5746aSDavid 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 {
287*7ec5746aSDavid du Colombier Conv *c;
288*7ec5746aSDavid du Colombier GREconv *grec;
289*7ec5746aSDavid 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
295*7ec5746aSDavid du Colombier c = x;
296*7ec5746aSDavid du Colombier grec = c->ptcl;
297*7ec5746aSDavid 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
308*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
309*7ec5746aSDavid du Colombier gre->vihl = IP_VER4;
3107dd7cddfSDavid du Colombier
311*7ec5746aSDavid du Colombier if(grec->raw == 0){
312*7ec5746aSDavid du Colombier v4tov6(raddr, gre->dst);
3137dd7cddfSDavid du Colombier if(ipcmp(raddr, v4prefix) == 0)
314*7ec5746aSDavid du Colombier memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen);
315*7ec5746aSDavid du Colombier v4tov6(laddr, gre->src);
3167dd7cddfSDavid du Colombier if(ipcmp(laddr, v4prefix) == 0){
3177dd7cddfSDavid du Colombier if(ipcmp(c->laddr, IPnoaddr) == 0)
318*7ec5746aSDavid du Colombier /* pick interface closest to dest */
319*7ec5746aSDavid du Colombier findlocalip(c->p->f, c->laddr, raddr);
320*7ec5746aSDavid du Colombier memmove(gre->src, c->laddr + IPv4off, sizeof gre->src);
3217dd7cddfSDavid du Colombier }
322*7ec5746aSDavid du Colombier hnputs(gre->eproto, c->rport);
3235d82c6aeSDavid du Colombier }
3247dd7cddfSDavid du Colombier
325*7ec5746aSDavid du Colombier gre->proto = IP_GREPROTO;
326*7ec5746aSDavid du Colombier gre->frag[0] = gre->frag[1] = 0;
3277dd7cddfSDavid du Colombier
328*7ec5746aSDavid du Colombier grepdout++;
329*7ec5746aSDavid 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)334*7ec5746aSDavid du Colombier gredownlink(Conv *c, Block *bp)
3357dd7cddfSDavid du Colombier {
336*7ec5746aSDavid du Colombier Metablock *m;
337*7ec5746aSDavid du Colombier GREconv *grec;
338*7ec5746aSDavid du Colombier GREhdr *gre;
339*7ec5746aSDavid du Colombier int hdrlen, suspended, extra;
340*7ec5746aSDavid du Colombier ushort flags;
341*7ec5746aSDavid du Colombier ulong seq;
3427dd7cddfSDavid du Colombier
343*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
344*7ec5746aSDavid du Colombier if(gre->ttl == 1){
345*7ec5746aSDavid du Colombier freeb(bp);
3467dd7cddfSDavid du Colombier return;
3477dd7cddfSDavid du Colombier }
3487dd7cddfSDavid du Colombier
349*7ec5746aSDavid du Colombier /*
350*7ec5746aSDavid du Colombier * We've received a packet with a GRE header and we need to
351*7ec5746aSDavid du Colombier * re-adjust the packet header to strip all unwanted parts
352*7ec5746aSDavid du Colombier * but leave room for only a sequence number.
353*7ec5746aSDavid du Colombier */
354*7ec5746aSDavid du Colombier grec = c->ptcl;
355*7ec5746aSDavid du Colombier flags = nhgets(gre->flags);
356*7ec5746aSDavid du Colombier hdrlen = 0;
357*7ec5746aSDavid du Colombier if(flags & GRE_cksum)
358*7ec5746aSDavid du Colombier hdrlen += 2;
359*7ec5746aSDavid du Colombier if(flags & GRE_routing){
360*7ec5746aSDavid du Colombier print("%V routing info present. Discarding packet", gre->src);
361*7ec5746aSDavid du Colombier freeb(bp);
362*7ec5746aSDavid du Colombier return;
363*7ec5746aSDavid du Colombier }
364*7ec5746aSDavid du Colombier if(flags & (GRE_cksum|GRE_routing))
365*7ec5746aSDavid du Colombier hdrlen += 2; /* Offset field */
366*7ec5746aSDavid du Colombier if(flags & GRE_key)
367*7ec5746aSDavid du Colombier hdrlen += 4;
368*7ec5746aSDavid du Colombier if(flags & GRE_seq)
369*7ec5746aSDavid du Colombier hdrlen += 4;
370*7ec5746aSDavid du Colombier
371*7ec5746aSDavid du Colombier /*
372*7ec5746aSDavid du Colombier * The outgoing packet only has the sequence number set. Make room
373*7ec5746aSDavid du Colombier * for the sequence number.
374*7ec5746aSDavid du Colombier */
375*7ec5746aSDavid du Colombier if(hdrlen != sizeof(ulong)){
376*7ec5746aSDavid du Colombier extra = hdrlen - sizeof(ulong);
377*7ec5746aSDavid du Colombier if(extra < 0 && bp->rp - bp->base < -extra){
378*7ec5746aSDavid du Colombier print("gredownlink: cannot add sequence number\n");
379*7ec5746aSDavid du Colombier freeb(bp);
380*7ec5746aSDavid du Colombier return;
381*7ec5746aSDavid du Colombier }
382*7ec5746aSDavid du Colombier memmove(bp->rp + extra, bp->rp, sizeof(GREhdr));
383*7ec5746aSDavid du Colombier bp->rp += extra;
384*7ec5746aSDavid du Colombier assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong));
385*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
386*7ec5746aSDavid du Colombier }
387*7ec5746aSDavid du Colombier seq = grec->seq++;
388*7ec5746aSDavid du Colombier hnputs(gre->flags, GRE_seq);
389*7ec5746aSDavid du Colombier hnputl(bp->rp + sizeof(GREhdr), seq);
390*7ec5746aSDavid du Colombier
391*7ec5746aSDavid du Colombier /*
392*7ec5746aSDavid du Colombier * Keep rp and seq at the base. ipoput4 consumes rp for
393*7ec5746aSDavid du Colombier * refragmentation.
394*7ec5746aSDavid du Colombier */
395*7ec5746aSDavid du Colombier assert(bp->rp - bp->base >= sizeof(Metablock));
396*7ec5746aSDavid du Colombier m = (Metablock *)bp->base;
397*7ec5746aSDavid du Colombier m->rp = bp->rp;
398*7ec5746aSDavid du Colombier m->seq = seq;
399*7ec5746aSDavid du Colombier
400*7ec5746aSDavid du Colombier /*
401*7ec5746aSDavid du Colombier * Here we make a decision what we're doing with the packet. We're
402*7ec5746aSDavid du Colombier * doing this w/o holding a lock which means that later on in the
403*7ec5746aSDavid du Colombier * process we may discover we've done the wrong thing. I don't want
404*7ec5746aSDavid du Colombier * to call ipoput with the lock held.
405*7ec5746aSDavid du Colombier */
406*7ec5746aSDavid du Colombier restart:
407*7ec5746aSDavid du Colombier suspended = grec->dlsusp;
408*7ec5746aSDavid du Colombier if(suspended){
409*7ec5746aSDavid du Colombier if(!canqlock(&grec->lock)){
410*7ec5746aSDavid du Colombier /*
411*7ec5746aSDavid du Colombier * just give up. too bad, we lose a packet. this
412*7ec5746aSDavid du Colombier * is just too hard and my brain already hurts.
413*7ec5746aSDavid du Colombier */
414*7ec5746aSDavid du Colombier freeb(bp);
415*7ec5746aSDavid du Colombier return;
416*7ec5746aSDavid du Colombier }
417*7ec5746aSDavid du Colombier
418*7ec5746aSDavid du Colombier if(!grec->dlsusp){
419*7ec5746aSDavid du Colombier /*
420*7ec5746aSDavid du Colombier * suspend race. We though we were suspended, but
421*7ec5746aSDavid du Colombier * we really weren't.
422*7ec5746aSDavid du Colombier */
423*7ec5746aSDavid du Colombier qunlock(&grec->lock);
424*7ec5746aSDavid du Colombier goto restart;
425*7ec5746aSDavid du Colombier }
426*7ec5746aSDavid du Colombier
427*7ec5746aSDavid du Colombier /* Undo the incorrect ref count addition */
428*7ec5746aSDavid du Colombier addring(&grec->dlbuffered, bp);
429*7ec5746aSDavid du Colombier qunlock(&grec->lock);
430*7ec5746aSDavid du Colombier return;
431*7ec5746aSDavid du Colombier }
432*7ec5746aSDavid du Colombier
433*7ec5746aSDavid du Colombier /*
434*7ec5746aSDavid du Colombier * When we get here, we're not suspended. Proceed to send the
435*7ec5746aSDavid du Colombier * packet.
436*7ec5746aSDavid du Colombier */
437*7ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
438*7ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
439*7ec5746aSDavid du Colombier
440*7ec5746aSDavid du Colombier /*
441*7ec5746aSDavid du Colombier * Make sure the packet does not go away.
442*7ec5746aSDavid du Colombier */
443*7ec5746aSDavid du Colombier _xinc(&bp->ref);
444*7ec5746aSDavid du Colombier assert(bp->ref == 2);
445*7ec5746aSDavid du Colombier
446*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
447*7ec5746aSDavid du Colombier grepdout++;
448*7ec5746aSDavid du Colombier grebdout += BLEN(bp);
449*7ec5746aSDavid du Colombier
450*7ec5746aSDavid du Colombier /*
451*7ec5746aSDavid du Colombier * Now make sure we didn't do the wrong thing.
452*7ec5746aSDavid du Colombier */
453*7ec5746aSDavid du Colombier if(!canqlock(&grec->lock)){
454*7ec5746aSDavid du Colombier freeb(bp); /* The packet just goes away */
455*7ec5746aSDavid du Colombier return;
456*7ec5746aSDavid du Colombier }
457*7ec5746aSDavid du Colombier
458*7ec5746aSDavid du Colombier /* We did the right thing */
459*7ec5746aSDavid du Colombier addring(&grec->dlpending, bp);
460*7ec5746aSDavid du Colombier qunlock(&grec->lock);
461*7ec5746aSDavid du Colombier }
462*7ec5746aSDavid du Colombier
463*7ec5746aSDavid du Colombier static void
greuplink(Conv * c,Block * bp)464*7ec5746aSDavid du Colombier greuplink(Conv *c, Block *bp)
465*7ec5746aSDavid du Colombier {
466*7ec5746aSDavid du Colombier GREconv *grec;
467*7ec5746aSDavid du Colombier GREhdr *gre;
468*7ec5746aSDavid du Colombier ushort flags;
469*7ec5746aSDavid du Colombier
470*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
471*7ec5746aSDavid du Colombier if(gre->ttl == 1)
472*7ec5746aSDavid du Colombier return;
473*7ec5746aSDavid du Colombier
474*7ec5746aSDavid du Colombier grec = c->ptcl;
475*7ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->src);
476*7ec5746aSDavid du Colombier memmove(gre->dst, grec->north, sizeof gre->dst);
477*7ec5746aSDavid du Colombier
478*7ec5746aSDavid du Colombier /*
479*7ec5746aSDavid du Colombier * Add a key, if needed.
480*7ec5746aSDavid du Colombier */
481*7ec5746aSDavid du Colombier if(grec->ulkey){
482*7ec5746aSDavid du Colombier flags = nhgets(gre->flags);
483*7ec5746aSDavid du Colombier if(flags & (GRE_cksum|GRE_routing)){
484*7ec5746aSDavid du Colombier print("%V routing info present. Discarding packet\n",
485*7ec5746aSDavid du Colombier gre->src);
486*7ec5746aSDavid du Colombier freeb(bp);
487*7ec5746aSDavid du Colombier return;
488*7ec5746aSDavid du Colombier }
489*7ec5746aSDavid du Colombier
490*7ec5746aSDavid du Colombier if((flags & GRE_key) == 0){
491*7ec5746aSDavid du Colombier /* Make room for the key */
492*7ec5746aSDavid du Colombier if(bp->rp - bp->base < sizeof(ulong)){
493*7ec5746aSDavid du Colombier print("%V can't add key\n", gre->src);
494*7ec5746aSDavid du Colombier freeb(bp);
495*7ec5746aSDavid du Colombier return;
496*7ec5746aSDavid du Colombier }
497*7ec5746aSDavid du Colombier
498*7ec5746aSDavid du Colombier bp->rp -= 4;
499*7ec5746aSDavid du Colombier memmove(bp->rp, bp->rp + 4, sizeof(GREhdr));
500*7ec5746aSDavid du Colombier
501*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
502*7ec5746aSDavid du Colombier hnputs(gre->flags, flags | GRE_key);
503*7ec5746aSDavid du Colombier }
504*7ec5746aSDavid du Colombier
505*7ec5746aSDavid du Colombier /* Add the key */
506*7ec5746aSDavid du Colombier hnputl(bp->rp + sizeof(GREhdr), grec->ulkey);
507*7ec5746aSDavid du Colombier }
508*7ec5746aSDavid du Colombier
509*7ec5746aSDavid du Colombier if(!canqlock(&grec->lock)){
510*7ec5746aSDavid du Colombier freeb(bp);
511*7ec5746aSDavid du Colombier return;
512*7ec5746aSDavid du Colombier }
513*7ec5746aSDavid du Colombier
514*7ec5746aSDavid du Colombier if(grec->ulsusp)
515*7ec5746aSDavid du Colombier addring(&grec->ulbuffered, bp);
516*7ec5746aSDavid du Colombier else{
517*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
518*7ec5746aSDavid du Colombier grepuout++;
519*7ec5746aSDavid du Colombier grebuout += BLEN(bp);
520*7ec5746aSDavid du Colombier }
521*7ec5746aSDavid du Colombier qunlock(&grec->lock);
522*7ec5746aSDavid du Colombier }
523*7ec5746aSDavid du Colombier
524*7ec5746aSDavid du Colombier static void
greiput(Proto * proto,Ipifc *,Block * bp)525*7ec5746aSDavid du Colombier greiput(Proto *proto, Ipifc *, Block *bp)
526*7ec5746aSDavid du Colombier {
527*7ec5746aSDavid du Colombier int len, hdrlen;
528*7ec5746aSDavid du Colombier ushort eproto, flags;
529*7ec5746aSDavid du Colombier uchar raddr[IPaddrlen];
530*7ec5746aSDavid du Colombier Conv *c, **p;
531*7ec5746aSDavid du Colombier GREconv *grec;
532*7ec5746aSDavid du Colombier GREhdr *gre;
533*7ec5746aSDavid du Colombier GREpriv *gpriv;
534*7ec5746aSDavid du Colombier Ip4hdr *ip;
535*7ec5746aSDavid du Colombier
536*7ec5746aSDavid du Colombier /*
537*7ec5746aSDavid du Colombier * We don't want to deal with block lists. Ever. The problem is
538*7ec5746aSDavid du Colombier * that when the block is forwarded, devether.c puts the block into
539*7ec5746aSDavid du Colombier * a queue that also uses ->next. Just do not use ->next here!
540*7ec5746aSDavid du Colombier */
541*7ec5746aSDavid du Colombier if(bp->next){
542*7ec5746aSDavid du Colombier len = blocklen(bp);
543*7ec5746aSDavid du Colombier bp = pullupblock(bp, len);
544*7ec5746aSDavid du Colombier assert(BLEN(bp) == len && bp->next == nil);
545*7ec5746aSDavid du Colombier }
546*7ec5746aSDavid du Colombier
547*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
548*7ec5746aSDavid du Colombier if(BLEN(bp) < sizeof(GREhdr) || gre->proto != IP_GREPROTO){
549*7ec5746aSDavid du Colombier freeb(bp);
550*7ec5746aSDavid du Colombier return;
551*7ec5746aSDavid du Colombier }
552*7ec5746aSDavid du Colombier
553*7ec5746aSDavid du Colombier v4tov6(raddr, gre->src);
554*7ec5746aSDavid du Colombier eproto = nhgets(gre->eproto);
555*7ec5746aSDavid du Colombier flags = nhgets(gre->flags);
556*7ec5746aSDavid du Colombier hdrlen = sizeof(GREhdr);
557*7ec5746aSDavid du Colombier
558*7ec5746aSDavid du Colombier if(flags & GRE_cksum)
559*7ec5746aSDavid du Colombier hdrlen += 2;
560*7ec5746aSDavid du Colombier if(flags & GRE_routing){
561*7ec5746aSDavid du Colombier print("%I routing info present. Discarding packet\n", raddr);
562*7ec5746aSDavid du Colombier freeb(bp);
563*7ec5746aSDavid du Colombier return;
564*7ec5746aSDavid du Colombier }
565*7ec5746aSDavid du Colombier if(flags & (GRE_cksum|GRE_routing))
566*7ec5746aSDavid du Colombier hdrlen += 2; /* Offset field */
567*7ec5746aSDavid du Colombier if(flags & GRE_key)
568*7ec5746aSDavid du Colombier hdrlen += 4;
569*7ec5746aSDavid du Colombier if(flags & GRE_seq)
570*7ec5746aSDavid du Colombier hdrlen += 4;
571*7ec5746aSDavid du Colombier
572*7ec5746aSDavid du Colombier if(BLEN(bp) - hdrlen < sizeof(Ip4hdr)){
573*7ec5746aSDavid du Colombier print("greretunnel: packet too short (s=%V d=%V)\n",
574*7ec5746aSDavid du Colombier gre->src, gre->dst);
575*7ec5746aSDavid du Colombier freeb(bp);
576*7ec5746aSDavid du Colombier return;
577*7ec5746aSDavid du Colombier }
578*7ec5746aSDavid du Colombier ip = (Ip4hdr *)(bp->rp + hdrlen);
579*7ec5746aSDavid du Colombier
580*7ec5746aSDavid du Colombier qlock(proto);
581*7ec5746aSDavid du Colombier /*
582*7ec5746aSDavid du Colombier * Look for a conversation structure for this port and address, or
583*7ec5746aSDavid du Colombier * match the retunnel part, or match on the raw flag.
584*7ec5746aSDavid du Colombier */
585*7ec5746aSDavid du Colombier for(p = proto->conv; *p; p++) {
586*7ec5746aSDavid du Colombier c = *p;
587*7ec5746aSDavid du Colombier
588*7ec5746aSDavid du Colombier if(c->inuse == 0)
589*7ec5746aSDavid du Colombier continue;
590*7ec5746aSDavid du Colombier
591*7ec5746aSDavid du Colombier /*
592*7ec5746aSDavid du Colombier * Do not stop this session - blocking here
593*7ec5746aSDavid du Colombier * implies that etherread is blocked.
594*7ec5746aSDavid du Colombier */
595*7ec5746aSDavid du Colombier grec = c->ptcl;
596*7ec5746aSDavid du Colombier if(memcmp(ip->dst, grec->hoa, sizeof ip->dst) == 0){
597*7ec5746aSDavid du Colombier grepdin++;
598*7ec5746aSDavid du Colombier grebdin += BLEN(bp);
599*7ec5746aSDavid du Colombier gredownlink(c, bp);
600*7ec5746aSDavid du Colombier qunlock(proto);
601*7ec5746aSDavid du Colombier return;
602*7ec5746aSDavid du Colombier }
603*7ec5746aSDavid du Colombier
604*7ec5746aSDavid du Colombier if(memcmp(ip->src, grec->hoa, sizeof ip->src) == 0){
605*7ec5746aSDavid du Colombier grepuin++;
606*7ec5746aSDavid du Colombier grebuin += BLEN(bp);
607*7ec5746aSDavid du Colombier greuplink(c, bp);
608*7ec5746aSDavid du Colombier qunlock(proto);
609*7ec5746aSDavid du Colombier return;
610*7ec5746aSDavid du Colombier }
611*7ec5746aSDavid du Colombier }
612*7ec5746aSDavid du Colombier
613*7ec5746aSDavid du Colombier /*
614*7ec5746aSDavid du Colombier * when we get here, none of the forwarding tunnels matched. now
615*7ec5746aSDavid du Colombier * try to match on raw and conversational sessions.
616*7ec5746aSDavid du Colombier */
617*7ec5746aSDavid du Colombier for(c = nil, p = proto->conv; *p; p++) {
618*7ec5746aSDavid du Colombier c = *p;
619*7ec5746aSDavid du Colombier
620*7ec5746aSDavid du Colombier if(c->inuse == 0)
621*7ec5746aSDavid du Colombier continue;
622*7ec5746aSDavid du Colombier
623*7ec5746aSDavid du Colombier /*
624*7ec5746aSDavid du Colombier * Do not stop this session - blocking here
625*7ec5746aSDavid du Colombier * implies that etherread is blocked.
626*7ec5746aSDavid du Colombier */
627*7ec5746aSDavid du Colombier grec = c->ptcl;
628*7ec5746aSDavid du Colombier if(c->rport == eproto &&
629*7ec5746aSDavid du Colombier (grec->raw || ipcmp(c->raddr, raddr) == 0))
630*7ec5746aSDavid du Colombier break;
631*7ec5746aSDavid du Colombier }
632*7ec5746aSDavid du Colombier
633*7ec5746aSDavid du Colombier qunlock(proto);
634*7ec5746aSDavid du Colombier
635*7ec5746aSDavid du Colombier if(*p == nil){
636*7ec5746aSDavid du Colombier freeb(bp);
637*7ec5746aSDavid du Colombier return;
638*7ec5746aSDavid du Colombier }
6397dd7cddfSDavid du Colombier
6407dd7cddfSDavid du Colombier /*
6417dd7cddfSDavid du Colombier * Trim the packet down to data size
6427dd7cddfSDavid du Colombier */
643*7ec5746aSDavid du Colombier len = nhgets(gre->len) - GRE_IPONLY;
6447dd7cddfSDavid du Colombier if(len < GRE_IPPLUSGRE){
645*7ec5746aSDavid du Colombier freeb(bp);
6467dd7cddfSDavid du Colombier return;
6477dd7cddfSDavid du Colombier }
648*7ec5746aSDavid du Colombier
6497dd7cddfSDavid du Colombier bp = trimblock(bp, GRE_IPONLY, len);
6507dd7cddfSDavid du Colombier if(bp == nil){
651*7ec5746aSDavid 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 */
659*7ec5746aSDavid du Colombier if(qlen(c->rq) > GREqlen)
660*7ec5746aSDavid 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;
675*7ec5746aSDavid du Colombier return snprint(buf, len,
676*7ec5746aSDavid du Colombier "gre: %lud %lud %lud %lud %lud %lud %lud %lud, lenerrs %lud\n",
677*7ec5746aSDavid du Colombier grepdin, grepdout, grepuin, grepuout,
678*7ec5746aSDavid du Colombier grebdin, grebdout, grebuin, grebuout, gpriv->lenerr);
679*7ec5746aSDavid du Colombier }
6807dd7cddfSDavid du Colombier
681*7ec5746aSDavid du Colombier static char *
grectlraw(Conv * c,int,char **)682*7ec5746aSDavid du Colombier grectlraw(Conv *c, int, char **)
683*7ec5746aSDavid du Colombier {
684*7ec5746aSDavid du Colombier GREconv *grec;
685*7ec5746aSDavid du Colombier
686*7ec5746aSDavid du Colombier grec = c->ptcl;
687*7ec5746aSDavid du Colombier grec->raw = 1;
688*7ec5746aSDavid du Colombier return nil;
689*7ec5746aSDavid du Colombier }
690*7ec5746aSDavid du Colombier
691*7ec5746aSDavid du Colombier static char *
grectlcooked(Conv * c,int,char **)692*7ec5746aSDavid du Colombier grectlcooked(Conv *c, int, char **)
693*7ec5746aSDavid du Colombier {
694*7ec5746aSDavid du Colombier GREconv *grec;
695*7ec5746aSDavid du Colombier
696*7ec5746aSDavid du Colombier grec = c->ptcl;
697*7ec5746aSDavid du Colombier grec->raw = 0;
698*7ec5746aSDavid du Colombier return nil;
699*7ec5746aSDavid du Colombier }
700*7ec5746aSDavid du Colombier
701*7ec5746aSDavid du Colombier static char *
grectlretunnel(Conv * c,int,char ** argv)702*7ec5746aSDavid du Colombier grectlretunnel(Conv *c, int, char **argv)
703*7ec5746aSDavid du Colombier {
704*7ec5746aSDavid du Colombier GREconv *grec;
705*7ec5746aSDavid du Colombier uchar ipaddr[4];
706*7ec5746aSDavid du Colombier
707*7ec5746aSDavid du Colombier grec = c->ptcl;
708*7ec5746aSDavid du Colombier if(memcmp(grec->hoa, nulladdr, sizeof grec->hoa))
709*7ec5746aSDavid du Colombier return "tunnel already set up";
710*7ec5746aSDavid du Colombier
711*7ec5746aSDavid du Colombier v4parseip(ipaddr, argv[1]);
712*7ec5746aSDavid du Colombier if(memcmp(ipaddr, nulladdr, sizeof ipaddr) == 0)
713*7ec5746aSDavid du Colombier return "bad hoa";
714*7ec5746aSDavid du Colombier memmove(grec->hoa, ipaddr, sizeof grec->hoa);
715*7ec5746aSDavid du Colombier v4parseip(ipaddr, argv[2]);
716*7ec5746aSDavid du Colombier memmove(grec->north, ipaddr, sizeof grec->north);
717*7ec5746aSDavid du Colombier v4parseip(ipaddr, argv[3]);
718*7ec5746aSDavid du Colombier memmove(grec->south, ipaddr, sizeof grec->south);
719*7ec5746aSDavid du Colombier v4parseip(ipaddr, argv[4]);
720*7ec5746aSDavid du Colombier memmove(grec->coa, ipaddr, sizeof grec->coa);
721*7ec5746aSDavid du Colombier grec->ulsusp = 1;
722*7ec5746aSDavid du Colombier grec->dlsusp = 0;
723*7ec5746aSDavid du Colombier
724*7ec5746aSDavid du Colombier return nil;
725*7ec5746aSDavid du Colombier }
726*7ec5746aSDavid du Colombier
727*7ec5746aSDavid du Colombier static char *
grectlreport(Conv * c,int,char ** argv)728*7ec5746aSDavid du Colombier grectlreport(Conv *c, int, char **argv)
729*7ec5746aSDavid du Colombier {
730*7ec5746aSDavid du Colombier ulong seq;
731*7ec5746aSDavid du Colombier Block *bp;
732*7ec5746aSDavid du Colombier Bring *r;
733*7ec5746aSDavid du Colombier GREconv *grec;
734*7ec5746aSDavid du Colombier Metablock *m;
735*7ec5746aSDavid du Colombier
736*7ec5746aSDavid du Colombier grec = c->ptcl;
737*7ec5746aSDavid du Colombier seq = strtoul(argv[1], nil, 0);
738*7ec5746aSDavid du Colombier
739*7ec5746aSDavid du Colombier qlock(&grec->lock);
740*7ec5746aSDavid du Colombier r = &grec->dlpending;
741*7ec5746aSDavid du Colombier while(r->produced - r->consumed > 0){
742*7ec5746aSDavid du Colombier bp = r->ring[r->consumed & Ringmask];
743*7ec5746aSDavid du Colombier
744*7ec5746aSDavid du Colombier assert(bp && bp->rp - bp->base >= sizeof(Metablock));
745*7ec5746aSDavid du Colombier m = (Metablock *)bp->base;
746*7ec5746aSDavid du Colombier if((long)(seq - m->seq) <= 0)
747*7ec5746aSDavid du Colombier break;
748*7ec5746aSDavid du Colombier
749*7ec5746aSDavid du Colombier r->ring[r->consumed & Ringmask] = nil;
750*7ec5746aSDavid du Colombier r->consumed++;
751*7ec5746aSDavid du Colombier
752*7ec5746aSDavid du Colombier freeb(bp);
753*7ec5746aSDavid du Colombier }
754*7ec5746aSDavid du Colombier qunlock(&grec->lock);
755*7ec5746aSDavid du Colombier return nil;
756*7ec5746aSDavid du Colombier }
757*7ec5746aSDavid du Colombier
758*7ec5746aSDavid du Colombier static char *
grectldlsuspend(Conv * c,int,char **)759*7ec5746aSDavid du Colombier grectldlsuspend(Conv *c, int, char **)
760*7ec5746aSDavid du Colombier {
761*7ec5746aSDavid du Colombier GREconv *grec;
762*7ec5746aSDavid du Colombier
763*7ec5746aSDavid du Colombier grec = c->ptcl;
764*7ec5746aSDavid du Colombier if(grec->dlsusp)
765*7ec5746aSDavid du Colombier return "already suspended";
766*7ec5746aSDavid du Colombier
767*7ec5746aSDavid du Colombier grec->dlsusp = 1;
768*7ec5746aSDavid du Colombier return nil;
769*7ec5746aSDavid du Colombier }
770*7ec5746aSDavid du Colombier
771*7ec5746aSDavid du Colombier static char *
grectlulsuspend(Conv * c,int,char **)772*7ec5746aSDavid du Colombier grectlulsuspend(Conv *c, int, char **)
773*7ec5746aSDavid du Colombier {
774*7ec5746aSDavid du Colombier GREconv *grec;
775*7ec5746aSDavid du Colombier
776*7ec5746aSDavid du Colombier grec = c->ptcl;
777*7ec5746aSDavid du Colombier if(grec->ulsusp)
778*7ec5746aSDavid du Colombier return "already suspended";
779*7ec5746aSDavid du Colombier
780*7ec5746aSDavid du Colombier grec->ulsusp = 1;
781*7ec5746aSDavid du Colombier return nil;
782*7ec5746aSDavid du Colombier }
783*7ec5746aSDavid du Colombier
784*7ec5746aSDavid du Colombier static char *
grectldlresume(Conv * c,int,char **)785*7ec5746aSDavid du Colombier grectldlresume(Conv *c, int, char **)
786*7ec5746aSDavid du Colombier {
787*7ec5746aSDavid du Colombier GREconv *grec;
788*7ec5746aSDavid du Colombier GREhdr *gre;
789*7ec5746aSDavid du Colombier Block *bp;
790*7ec5746aSDavid du Colombier
791*7ec5746aSDavid du Colombier grec = c->ptcl;
792*7ec5746aSDavid du Colombier
793*7ec5746aSDavid du Colombier qlock(&grec->lock);
794*7ec5746aSDavid du Colombier if(!grec->dlsusp){
795*7ec5746aSDavid du Colombier qunlock(&grec->lock);
796*7ec5746aSDavid du Colombier return "not suspended";
797*7ec5746aSDavid du Colombier }
798*7ec5746aSDavid du Colombier
799*7ec5746aSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil){
800*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
801*7ec5746aSDavid du Colombier qunlock(&grec->lock);
802*7ec5746aSDavid du Colombier
803*7ec5746aSDavid du Colombier /*
804*7ec5746aSDavid du Colombier * Make sure the packet does not go away.
805*7ec5746aSDavid du Colombier */
806*7ec5746aSDavid du Colombier _xinc(&bp->ref);
807*7ec5746aSDavid du Colombier assert(bp->ref == 2);
808*7ec5746aSDavid du Colombier
809*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
810*7ec5746aSDavid du Colombier
811*7ec5746aSDavid du Colombier qlock(&grec->lock);
812*7ec5746aSDavid du Colombier addring(&grec->dlpending, bp);
813*7ec5746aSDavid du Colombier }
814*7ec5746aSDavid du Colombier grec->dlsusp = 0;
815*7ec5746aSDavid du Colombier qunlock(&grec->lock);
816*7ec5746aSDavid du Colombier return nil;
817*7ec5746aSDavid du Colombier }
818*7ec5746aSDavid du Colombier
819*7ec5746aSDavid du Colombier static char *
grectlulresume(Conv * c,int,char **)820*7ec5746aSDavid du Colombier grectlulresume(Conv *c, int, char **)
821*7ec5746aSDavid du Colombier {
822*7ec5746aSDavid du Colombier GREconv *grec;
823*7ec5746aSDavid du Colombier GREhdr *gre;
824*7ec5746aSDavid du Colombier Block *bp;
825*7ec5746aSDavid du Colombier
826*7ec5746aSDavid du Colombier grec = c->ptcl;
827*7ec5746aSDavid du Colombier
828*7ec5746aSDavid du Colombier qlock(&grec->lock);
829*7ec5746aSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil){
830*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
831*7ec5746aSDavid du Colombier
832*7ec5746aSDavid du Colombier qunlock(&grec->lock);
833*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
834*7ec5746aSDavid du Colombier qlock(&grec->lock);
835*7ec5746aSDavid du Colombier }
836*7ec5746aSDavid du Colombier grec->ulsusp = 0;
837*7ec5746aSDavid du Colombier qunlock(&grec->lock);
838*7ec5746aSDavid du Colombier return nil;
839*7ec5746aSDavid du Colombier }
840*7ec5746aSDavid du Colombier
841*7ec5746aSDavid du Colombier static char *
grectlforward(Conv * c,int,char ** argv)842*7ec5746aSDavid du Colombier grectlforward(Conv *c, int, char **argv)
843*7ec5746aSDavid du Colombier {
844*7ec5746aSDavid du Colombier int len;
845*7ec5746aSDavid du Colombier Block *bp, *nbp;
846*7ec5746aSDavid du Colombier GREconv *grec;
847*7ec5746aSDavid du Colombier GREhdr *gre;
848*7ec5746aSDavid du Colombier Metablock *m;
849*7ec5746aSDavid du Colombier
850*7ec5746aSDavid du Colombier grec = c->ptcl;
851*7ec5746aSDavid du Colombier
852*7ec5746aSDavid du Colombier v4parseip(grec->south, argv[1]);
853*7ec5746aSDavid du Colombier memmove(grec->north, grec->south, sizeof grec->north);
854*7ec5746aSDavid du Colombier
855*7ec5746aSDavid du Colombier qlock(&grec->lock);
856*7ec5746aSDavid du Colombier if(!grec->dlsusp){
857*7ec5746aSDavid du Colombier qunlock(&grec->lock);
858*7ec5746aSDavid du Colombier return "not suspended";
859*7ec5746aSDavid du Colombier }
860*7ec5746aSDavid du Colombier grec->dlsusp = 0;
861*7ec5746aSDavid du Colombier grec->ulsusp = 0;
862*7ec5746aSDavid du Colombier
863*7ec5746aSDavid du Colombier while((bp = getring(&grec->dlpending)) != nil){
864*7ec5746aSDavid du Colombier
865*7ec5746aSDavid du Colombier assert(bp->rp - bp->base >= sizeof(Metablock));
866*7ec5746aSDavid du Colombier m = (Metablock *)bp->base;
867*7ec5746aSDavid du Colombier assert(m->rp >= bp->base && m->rp < bp->lim);
868*7ec5746aSDavid du Colombier
869*7ec5746aSDavid du Colombier /*
870*7ec5746aSDavid du Colombier * If the packet is still held inside the IP transmit
871*7ec5746aSDavid du Colombier * system, make a copy of the packet first.
872*7ec5746aSDavid du Colombier */
873*7ec5746aSDavid du Colombier if(bp->ref > 1){
874*7ec5746aSDavid du Colombier len = bp->wp - m->rp;
875*7ec5746aSDavid du Colombier nbp = allocb(len);
876*7ec5746aSDavid du Colombier memmove(nbp->wp, m->rp, len);
877*7ec5746aSDavid du Colombier nbp->wp += len;
878*7ec5746aSDavid du Colombier freeb(bp);
879*7ec5746aSDavid du Colombier bp = nbp;
880*7ec5746aSDavid du Colombier }
881*7ec5746aSDavid du Colombier else{
882*7ec5746aSDavid du Colombier /* Patch up rp */
883*7ec5746aSDavid du Colombier bp->rp = m->rp;
884*7ec5746aSDavid du Colombier }
885*7ec5746aSDavid du Colombier
886*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
887*7ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
888*7ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
889*7ec5746aSDavid du Colombier
890*7ec5746aSDavid du Colombier qunlock(&grec->lock);
891*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
892*7ec5746aSDavid du Colombier qlock(&grec->lock);
893*7ec5746aSDavid du Colombier }
894*7ec5746aSDavid du Colombier
895*7ec5746aSDavid du Colombier while((bp = getring(&grec->dlbuffered)) != nil){
896*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
897*7ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
898*7ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
899*7ec5746aSDavid du Colombier
900*7ec5746aSDavid du Colombier qunlock(&grec->lock);
901*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
902*7ec5746aSDavid du Colombier qlock(&grec->lock);
903*7ec5746aSDavid du Colombier }
904*7ec5746aSDavid du Colombier
905*7ec5746aSDavid du Colombier while((bp = getring(&grec->ulbuffered)) != nil){
906*7ec5746aSDavid du Colombier gre = (GREhdr *)bp->rp;
907*7ec5746aSDavid du Colombier
908*7ec5746aSDavid du Colombier memmove(gre->src, grec->coa, sizeof gre->dst);
909*7ec5746aSDavid du Colombier memmove(gre->dst, grec->south, sizeof gre->dst);
910*7ec5746aSDavid du Colombier
911*7ec5746aSDavid du Colombier qunlock(&grec->lock);
912*7ec5746aSDavid du Colombier ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil);
913*7ec5746aSDavid du Colombier qlock(&grec->lock);
914*7ec5746aSDavid du Colombier }
915*7ec5746aSDavid du Colombier qunlock(&grec->lock);
916*7ec5746aSDavid du Colombier return nil;
917*7ec5746aSDavid du Colombier }
918*7ec5746aSDavid du Colombier
919*7ec5746aSDavid du Colombier static char *
grectlulkey(Conv * c,int,char ** argv)920*7ec5746aSDavid du Colombier grectlulkey(Conv *c, int, char **argv)
921*7ec5746aSDavid du Colombier {
922*7ec5746aSDavid du Colombier GREconv *grec;
923*7ec5746aSDavid du Colombier
924*7ec5746aSDavid du Colombier grec = c->ptcl;
925*7ec5746aSDavid du Colombier grec->ulkey = strtoul(argv[1], nil, 0);
926*7ec5746aSDavid 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 {
932*7ec5746aSDavid du Colombier int i;
9335d82c6aeSDavid du Colombier
934*7ec5746aSDavid du Colombier if(n < 1)
935*7ec5746aSDavid du Colombier return "too few arguments";
936*7ec5746aSDavid du Colombier
937*7ec5746aSDavid du Colombier for(i = 0; i < Ncmds; i++)
938*7ec5746aSDavid du Colombier if(strcmp(f[0], grectls[i].cmd) == 0)
939*7ec5746aSDavid du Colombier break;
940*7ec5746aSDavid du Colombier
941*7ec5746aSDavid du Colombier if(i == Ncmds)
942*7ec5746aSDavid du Colombier return "no such command";
943*7ec5746aSDavid du Colombier if(grectls[i].argc != 0 && grectls[i].argc != n)
944*7ec5746aSDavid du Colombier return "incorrect number of arguments";
945*7ec5746aSDavid du Colombier
946*7ec5746aSDavid 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;
968*7ec5746aSDavid du Colombier gre->ptclsize = sizeof(GREconv);
9697dd7cddfSDavid du Colombier
9707dd7cddfSDavid du Colombier Fsproto(fs, gre);
9717dd7cddfSDavid du Colombier }
972