/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Generic Routing Encapsulation over IPv4, rfc1702 */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h" #include "ip.h" enum { GRE_IPONLY = 12, /* size of ip header */ GRE_IPPLUSGRE = 12, /* minimum size of GRE header */ IP_GREPROTO = 47, GRErxms = 200, GREtickms = 100, GREmaxxmit = 10, K = 1024, GREqlen = 256 * K, GRE_cksum = 0x8000, GRE_routing = 0x4000, GRE_key = 0x2000, GRE_seq = 0x1000, Nring = 1 << 10, /* power of two, please */ Ringmask = Nring - 1, GREctlraw = 0, GREctlcooked, GREctlretunnel, GREctlreport, GREctldlsuspend, GREctlulsuspend, GREctldlresume, GREctlulresume, GREctlforward, GREctlulkey, Ncmds,}; typedef struct GREhdr GREhdr;struct GREhdr{ /* ip header */ uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar len[2]; /* packet length (including headers) */ uchar id[2]; /* Identification */ uchar frag[2]; /* Fragment information */ uchar ttl; uchar proto; /* Protocol */ uchar cksum[2]; /* checksum */ uchar src[4]; /* Ip source */ uchar dst[4]; /* Ip destination */ /* gre header */ uchar flags[2]; uchar eproto[2]; /* encapsulation protocol */}; typedef struct GREpriv GREpriv;struct GREpriv{ /* non-MIB stats */ ulong lenerr; /* short packet */}; typedef struct Bring Bring;struct Bring{ Block *ring[Nring]; long produced; long consumed;}; typedef struct GREconv GREconv;struct GREconv{ int raw; /* Retunnelling information. v4 only */ uchar north[4]; /* HA */ uchar south[4]; /* Base station */ uchar hoa[4]; /* Home address */ uchar coa[4]; /* Careof address */ ulong seq; /* Current sequence # */ int dlsusp; /* Downlink suspended? */ int ulsusp; /* Uplink suspended? */ ulong ulkey; /* GRE key */ QLock lock; /* Lock for rings */ Bring dlpending; /* Ring of pending packets */ Bring dlbuffered; /* Received while suspended */ Bring ulbuffered; /* Received while suspended */}; typedef struct Metablock Metablock;struct Metablock{ uchar *rp; ulong seq;}; static char *grectlcooked(Conv *, int, char **);static char *grectldlresume(Conv *, int, char **);static char *grectldlsuspend(Conv *, int, char **);static char *grectlforward(Conv *, int, char **);static char *grectlraw(Conv *, int, char **);static char *grectlreport(Conv *, int, char **);static char *grectlretunnel(Conv *, int, char **);static char *grectlulkey(Conv *, int, char **);static char *grectlulresume(Conv *, int, char **);static char *grectlulsuspend(Conv *, int, char **); static struct{ char *cmd; int argc; char *(*f)(Conv *, int, char **);} grectls[Ncmds] = {[GREctlraw] = { "raw", 1, grectlraw, },[GREctlcooked] = { "cooked", 1, grectlcooked, },[GREctlretunnel]= { "retunnel", 5, grectlretunnel, },[GREctlreport] = { "report", 2, grectlreport, },[GREctldlsuspend]= { "dlsuspend", 1, grectldlsuspend,},[GREctlulsuspend]= { "ulsuspend", 1, grectlulsuspend,},[GREctldlresume]= { "dlresume", 1, grectldlresume, },[GREctlulresume]= { "ulresume", 1, grectlulresume, },[GREctlforward] = { "forward", 2, grectlforward, },[GREctlulkey] = { "ulkey", 2, grectlulkey, },}; static uchar nulladdr[4];static char *sessend = "session end"; static void grekick(void *x, Block *bp);static char *gresetup(Conv *, char *, char *, char *); ulong grepdin, grepdout, grebdin, grebdout;ulong grepuin, grepuout, grebuin, grebuout; static Block *getring(Bring *r){ Block *bp; if(r->consumed == r->produced) return nil; bp = r->ring[r->consumed & Ringmask]; r->ring[r->consumed & Ringmask] = nil; r->consumed++; return bp;} static voidaddring(Bring *r, Block *bp){ Block *tbp; if(r->produced - r->consumed > Ringmask){ /* Full! */ tbp = r->ring[r->produced & Ringmask]; assert(tbp); freeb(tbp); r->consumed++; } r->ring[r->produced & Ringmask] = bp; r->produced++;} static char *greconnect(Conv *c, char **argv, int argc){ Proto *p; char *err; Conv *tc, **cp, **ecp; err = Fsstdconnect(c, argv, argc); if(err != nil) return err; /* make sure noone's already connected to this other sys */ p = c->p; qlock(p); ecp = &p->conv[p->nc]; for(cp = p->conv; cp < ecp; cp++){ tc = *cp; if(tc == nil) break; if(tc == c) continue; if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){ err = "already connected to that addr/proto"; ipmove(c->laddr, IPnoaddr); ipmove(c->raddr, IPnoaddr); break; } } qunlock(p); if(err != nil) return err; Fsconnected(c, nil); return nil;} static voidgrecreate(Conv *c){ c->rq = qopen(GREqlen, Qmsg, 0, c); c->wq = qbypass(grekick, c);} static intgrestate(Conv *c, char *state, int n){ GREconv *grec; char *ep, *p; grec = c->ptcl; p = state; ep = p + n; p = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx " "pending %uld %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n", c->inuse? "Open ": "Closed ", grec->raw? "raw ": "", grec->dlsusp? "DL suspended ": "", grec->ulsusp? "UL suspended ": "", grec->hoa, grec->north, grec->south, grec->seq, grec->dlpending.consumed, grec->dlpending.produced, grec->dlbuffered.consumed, grec->dlbuffered.produced, grec->ulbuffered.consumed, grec->ulbuffered.produced, grec->ulkey); return p - state;} static char*greannounce(Conv*, char**, int){ return "gre does not support announce";} static voidgreclose(Conv *c){ GREconv *grec; Block *bp; grec = c->ptcl; /* Make sure we don't forward any more packets */ memset(grec->hoa, 0, sizeof grec->hoa); memset(grec->north, 0, sizeof grec->north); memset(grec->south, 0, sizeof grec->south); qlock(&grec->lock); while((bp = getring(&grec->dlpending)) != nil) freeb(bp); while((bp = getring(&grec->dlbuffered)) != nil) freeb(bp); while((bp = getring(&grec->ulbuffered)) != nil) freeb(bp); grec->dlpending.produced = grec->dlpending.consumed = 0; grec->dlbuffered.produced = grec->dlbuffered.consumed = 0; grec->ulbuffered.produced = grec->ulbuffered.consumed = 0; qunlock(&grec->lock); grec->raw = 0; grec->seq = 0; grec->dlsusp = grec->ulsusp = 1; qhangup(c->rq, sessend); qhangup(c->wq, sessend); qhangup(c->eq, sessend); ipmove(c->laddr, IPnoaddr); ipmove(c->raddr, IPnoaddr); c->lport = c->rport = 0;} static voidgrekick(void *x, Block *bp){ Conv *c; GREconv *grec; GREhdr *gre; uchar laddr[IPaddrlen], raddr[IPaddrlen]; if(bp == nil) return; c = x; grec = c->ptcl; /* Make space to fit ip header (gre header already there) */ bp = padblock(bp, GRE_IPONLY); if(bp == nil) return; /* make sure the message has a GRE header */ bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE); if(bp == nil) return; gre = (GREhdr *)bp->rp; gre->vihl = IP_VER4; if(grec->raw == 0){ v4tov6(raddr, gre->dst); if(ipcmp(raddr, v4prefix) == 0) memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen); v4tov6(laddr, gre->src); if(ipcmp(laddr, v4prefix) == 0){ if(ipcmp(c->laddr, IPnoaddr) == 0) /* pick interface closest to dest */ findlocalip(c->p->f, c->laddr, raddr); memmove(gre->src, c->laddr + IPv4off, sizeof gre->src); } hnputs(gre->eproto, c->rport); } gre->proto = IP_GREPROTO; gre->frag[0] = gre->frag[1] = 0; grepdout++; grebdout += BLEN(bp); ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);} static voidgredownlink(Conv *c, Block *bp){ Metablock *m; GREconv *grec; GREhdr *gre; int hdrlen, suspended, extra; ushort flags; ulong seq; gre = (GREhdr *)bp->rp; if(gre->ttl == 1){ freeb(bp); return; } /* * We've received a packet with a GRE header and we need to * re-adjust the packet header to strip all unwanted parts * but leave room for only a sequence number. */ grec = c->ptcl; flags = nhgets(gre->flags); hdrlen = 0; if(flags & GRE_cksum) hdrlen += 2; if(flags & GRE_routing){ print("%V routing info present. Discarding packet", gre->src); freeb(bp); return; } if(flags & (GRE_cksum|GRE_routing)) hdrlen += 2; /* Offset field */ if(flags & GRE_key) hdrlen += 4; if(flags & GRE_seq) hdrlen += 4; /* * The outgoing packet only has the sequence number set. Make room * for the sequence number. */ if(hdrlen != sizeof(ulong)){ extra = hdrlen - sizeof(ulong); if(extra < 0 && bp->rp - bp->base < -extra){ print("gredownlink: cannot add sequence number\n"); freeb(bp); return; } memmove(bp->rp + extra, bp->rp, sizeof(GREhdr)); bp->rp += extra; assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong)); gre = (GREhdr *)bp->rp; } seq = grec->seq++; hnputs(gre->flags, GRE_seq); hnputl(bp->rp + sizeof(GREhdr), seq); /* * Keep rp and seq at the base. ipoput4 consumes rp for * refragmentation. */ assert(bp->rp - bp->base >= sizeof(Metablock)); m = (Metablock *)bp->base; m->rp = bp->rp; m->seq = seq; /* * Here we make a decision what we're doing with the packet. We're * doing this w/o holding a lock which means that later on in the * process we may discover we've done the wrong thing. I don't want * to call ipoput with the lock held. */restart: suspended = grec->dlsusp; if(suspended){ if(!canqlock(&grec->lock)){ /* * just give up. too bad, we lose a packet. this * is just too hard and my brain already hurts. */ freeb(bp); return; } if(!grec->dlsusp){ /* * suspend race. We though we were suspended, but * we really weren't. */ qunlock(&grec->lock); goto restart; } /* Undo the incorrect ref count addition */ addring(&grec->dlbuffered, bp); qunlock(&grec->lock); return; } /* * When we get here, we're not suspended. Proceed to send the * packet. */ memmove(gre->src, grec->coa, sizeof gre->dst); memmove(gre->dst, grec->south, sizeof gre->dst); /* * Make sure the packet does not go away. */ _xinc(&bp->ref); assert(bp->ref == 2); ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); grepdout++; grebdout += BLEN(bp); /* * Now make sure we didn't do the wrong thing. */ if(!canqlock(&grec->lock)){ freeb(bp); /* The packet just goes away */ return; } /* We did the right thing */ addring(&grec->dlpending, bp); qunlock(&grec->lock);} static voidgreuplink(Conv *c, Block *bp){ GREconv *grec; GREhdr *gre; ushort flags; gre = (GREhdr *)bp->rp; if(gre->ttl == 1) return; grec = c->ptcl; memmove(gre->src, grec->coa, sizeof gre->src); memmove(gre->dst, grec->north, sizeof gre->dst); /* * Add a key, if needed. */ if(grec->ulkey){ flags = nhgets(gre->flags); if(flags & (GRE_cksum|GRE_routing)){ print("%V routing info present. Discarding packet\n", gre->src); freeb(bp); return; } if((flags & GRE_key) == 0){ /* Make room for the key */Lines 1–500 of 980. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Generic Routing Encapsulation over IPv4, rfc1702 */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "ip.h" enum { GRE_IPONLY = 12, /* size of ip header */ GRE_IPPLUSGRE = 12, /* minimum size of GRE header */ IP_GREPROTO = 47, GRErxms = 200, GREtickms = 100, GREmaxxmit = 10, K = 1024, GREqlen = 256 * K, GRE_cksum = 0x8000, GRE_routing = 0x4000, GRE_key = 0x2000, GRE_seq = 0x1000, Nring = 1 << 10, /* power of two, please */ Ringmask = Nring - 1, GREctlraw = 0, GREctlcooked, GREctlretunnel, GREctlreport, GREctldlsuspend, GREctlulsuspend, GREctldlresume, GREctlulresume, GREctlforward, GREctlulkey, Ncmds, }; typedef struct GREhdr GREhdr; struct GREhdr{ /* ip header */ uchar vihl; /* Version and header length */ uchar tos; /* Type of service */ uchar len[2]; /* packet length (including headers) */ uchar id[2]; /* Identification */ uchar frag[2]; /* Fragment information */ uchar ttl; uchar proto; /* Protocol */ uchar cksum[2]; /* checksum */ uchar src[4]; /* Ip source */ uchar dst[4]; /* Ip destination */ /* gre header */ uchar flags[2]; uchar eproto[2]; /* encapsulation protocol */ }; typedef struct GREpriv GREpriv; struct GREpriv{ /* non-MIB stats */ ulong lenerr; /* short packet */ }; typedef struct Bring Bring; struct Bring{ Block *ring[Nring]; long produced; long consumed; }; typedef struct GREconv GREconv; struct GREconv{ int raw; /* Retunnelling information. v4 only */ uchar north[4]; /* HA */ uchar south[4]; /* Base station */ uchar hoa[4]; /* Home address */ uchar coa[4]; /* Careof address */ ulong seq; /* Current sequence # */ int dlsusp; /* Downlink suspended? */ int ulsusp; /* Uplink suspended? */ ulong ulkey; /* GRE key */ QLock lock; /* Lock for rings */ Bring dlpending; /* Ring of pending packets */ Bring dlbuffered; /* Received while suspended */ Bring ulbuffered; /* Received while suspended */ }; typedef struct Metablock Metablock; struct Metablock{ uchar *rp; ulong seq; }; static char *grectlcooked(Conv *, int, char **); static char *grectldlresume(Conv *, int, char **); static char *grectldlsuspend(Conv *, int, char **); static char *grectlforward(Conv *, int, char **); static char *grectlraw(Conv *, int, char **); static char *grectlreport(Conv *, int, char **); static char *grectlretunnel(Conv *, int, char **); static char *grectlulkey(Conv *, int, char **); static char *grectlulresume(Conv *, int, char **); static char *grectlulsuspend(Conv *, int, char **); static struct{ char *cmd; int argc; char *(*f)(Conv *, int, char **); } grectls[Ncmds] = { [GREctlraw] = { "raw", 1, grectlraw, }, [GREctlcooked] = { "cooked", 1, grectlcooked, }, [GREctlretunnel]= { "retunnel", 5, grectlretunnel, }, [GREctlreport] = { "report", 2, grectlreport, }, [GREctldlsuspend]= { "dlsuspend", 1, grectldlsuspend,}, [GREctlulsuspend]= { "ulsuspend", 1, grectlulsuspend,}, [GREctldlresume]= { "dlresume", 1, grectldlresume, }, [GREctlulresume]= { "ulresume", 1, grectlulresume, }, [GREctlforward] = { "forward", 2, grectlforward, }, [GREctlulkey] = { "ulkey", 2, grectlulkey, }, }; static uchar nulladdr[4]; static char *sessend = "session end"; static void grekick(void *x, Block *bp); static char *gresetup(Conv *, char *, char *, char *); ulong grepdin, grepdout, grebdin, grebdout; ulong grepuin, grepuout, grebuin, grebuout; static Block * getring(Bring *r) { Block *bp; if(r->consumed == r->produced) return nil; bp = r->ring[r->consumed & Ringmask]; r->ring[r->consumed & Ringmask] = nil; r->consumed++; return bp; } static void addring(Bring *r, Block *bp) { Block *tbp; if(r->produced - r->consumed > Ringmask){ /* Full! */ tbp = r->ring[r->produced & Ringmask]; assert(tbp); freeb(tbp); r->consumed++; } r->ring[r->produced & Ringmask] = bp; r->produced++; } static char * greconnect(Conv *c, char **argv, int argc) { Proto *p; char *err; Conv *tc, **cp, **ecp; err = Fsstdconnect(c, argv, argc); if(err != nil) return err; /* make sure noone's already connected to this other sys */ p = c->p; qlock(p); ecp = &p->conv[p->nc]; for(cp = p->conv; cp < ecp; cp++){ tc = *cp; if(tc == nil) break; if(tc == c) continue; if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){ err = "already connected to that addr/proto"; ipmove(c->laddr, IPnoaddr); ipmove(c->raddr, IPnoaddr); break; } } qunlock(p); if(err != nil) return err; Fsconnected(c, nil); return nil; } static void grecreate(Conv *c) { c->rq = qopen(GREqlen, Qmsg, 0, c); c->wq = qbypass(grekick, c); } static int grestate(Conv *c, char *state, int n) { GREconv *grec; char *ep, *p; grec = c->ptcl; p = state; ep = p + n; p = seprint(p, ep, "%s%s%s%shoa %V north %V south %V seq %ulx " "pending %uld %uld buffered dl %uld %uld ul %uld %uld ulkey %.8ulx\n", c->inuse? "Open ": "Closed ", grec->raw? "raw ": "", grec->dlsusp? "DL suspended ": "", grec->ulsusp? "UL suspended ": "", grec->hoa, grec->north, grec->south, grec->seq, grec->dlpending.consumed, grec->dlpending.produced, grec->dlbuffered.consumed, grec->dlbuffered.produced, grec->ulbuffered.consumed, grec->ulbuffered.produced, grec->ulkey); return p - state; } static char* greannounce(Conv*, char**, int) { return "gre does not support announce"; } static void greclose(Conv *c) { GREconv *grec; Block *bp; grec = c->ptcl; /* Make sure we don't forward any more packets */ memset(grec->hoa, 0, sizeof grec->hoa); memset(grec->north, 0, sizeof grec->north); memset(grec->south, 0, sizeof grec->south); qlock(&grec->lock); while((bp = getring(&grec->dlpending)) != nil) freeb(bp); while((bp = getring(&grec->dlbuffered)) != nil) freeb(bp); while((bp = getring(&grec->ulbuffered)) != nil) freeb(bp); grec->dlpending.produced = grec->dlpending.consumed = 0; grec->dlbuffered.produced = grec->dlbuffered.consumed = 0; grec->ulbuffered.produced = grec->ulbuffered.consumed = 0; qunlock(&grec->lock); grec->raw = 0; grec->seq = 0; grec->dlsusp = grec->ulsusp = 1; qhangup(c->rq, sessend); qhangup(c->wq, sessend); qhangup(c->eq, sessend); ipmove(c->laddr, IPnoaddr); ipmove(c->raddr, IPnoaddr); c->lport = c->rport = 0; } static void grekick(void *x, Block *bp) { Conv *c; GREconv *grec; GREhdr *gre; uchar laddr[IPaddrlen], raddr[IPaddrlen]; if(bp == nil) return; c = x; grec = c->ptcl; /* Make space to fit ip header (gre header already there) */ bp = padblock(bp, GRE_IPONLY); if(bp == nil) return; /* make sure the message has a GRE header */ bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE); if(bp == nil) return; gre = (GREhdr *)bp->rp; gre->vihl = IP_VER4; if(grec->raw == 0){ v4tov6(raddr, gre->dst); if(ipcmp(raddr, v4prefix) == 0) memmove(gre->dst, c->raddr + IPv4off, IPv4addrlen); v4tov6(laddr, gre->src); if(ipcmp(laddr, v4prefix) == 0){ if(ipcmp(c->laddr, IPnoaddr) == 0) /* pick interface closest to dest */ findlocalip(c->p->f, c->laddr, raddr); memmove(gre->src, c->laddr + IPv4off, sizeof gre->src); } hnputs(gre->eproto, c->rport); } gre->proto = IP_GREPROTO; gre->frag[0] = gre->frag[1] = 0; grepdout++; grebdout += BLEN(bp); ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil); } static void gredownlink(Conv *c, Block *bp) { Metablock *m; GREconv *grec; GREhdr *gre; int hdrlen, suspended, extra; ushort flags; ulong seq; gre = (GREhdr *)bp->rp; if(gre->ttl == 1){ freeb(bp); return; } /* * We've received a packet with a GRE header and we need to * re-adjust the packet header to strip all unwanted parts * but leave room for only a sequence number. */ grec = c->ptcl; flags = nhgets(gre->flags); hdrlen = 0; if(flags & GRE_cksum) hdrlen += 2; if(flags & GRE_routing){ print("%V routing info present. Discarding packet", gre->src); freeb(bp); return; } if(flags & (GRE_cksum|GRE_routing)) hdrlen += 2; /* Offset field */ if(flags & GRE_key) hdrlen += 4; if(flags & GRE_seq) hdrlen += 4; /* * The outgoing packet only has the sequence number set. Make room * for the sequence number. */ if(hdrlen != sizeof(ulong)){ extra = hdrlen - sizeof(ulong); if(extra < 0 && bp->rp - bp->base < -extra){ print("gredownlink: cannot add sequence number\n"); freeb(bp); return; } memmove(bp->rp + extra, bp->rp, sizeof(GREhdr)); bp->rp += extra; assert(BLEN(bp) >= sizeof(GREhdr) + sizeof(ulong)); gre = (GREhdr *)bp->rp; } seq = grec->seq++; hnputs(gre->flags, GRE_seq); hnputl(bp->rp + sizeof(GREhdr), seq); /* * Keep rp and seq at the base. ipoput4 consumes rp for * refragmentation. */ assert(bp->rp - bp->base >= sizeof(Metablock)); m = (Metablock *)bp->base; m->rp = bp->rp; m->seq = seq; /* * Here we make a decision what we're doing with the packet. We're * doing this w/o holding a lock which means that later on in the * process we may discover we've done the wrong thing. I don't want * to call ipoput with the lock held. */ restart: suspended = grec->dlsusp; if(suspended){ if(!canqlock(&grec->lock)){ /* * just give up. too bad, we lose a packet. this * is just too hard and my brain already hurts. */ freeb(bp); return; } if(!grec->dlsusp){ /* * suspend race. We though we were suspended, but * we really weren't. */ qunlock(&grec->lock); goto restart; } /* Undo the incorrect ref count addition */ addring(&grec->dlbuffered, bp); qunlock(&grec->lock); return; } /* * When we get here, we're not suspended. Proceed to send the * packet. */ memmove(gre->src, grec->coa, sizeof gre->dst); memmove(gre->dst, grec->south, sizeof gre->dst); /* * Make sure the packet does not go away. */ _xinc(&bp->ref); assert(bp->ref == 2); ipoput4(c->p->f, bp, 0, gre->ttl - 1, gre->tos, nil); grepdout++; grebdout += BLEN(bp); /* * Now make sure we didn't do the wrong thing. */ if(!canqlock(&grec->lock)){ freeb(bp); /* The packet just goes away */ return; } /* We did the right thing */ addring(&grec->dlpending, bp); qunlock(&grec->lock); } static void greuplink(Conv *c, Block *bp) { GREconv *grec; GREhdr *gre; ushort flags; gre = (GREhdr *)bp->rp; if(gre->ttl == 1) return; grec = c->ptcl; memmove(gre->src, grec->coa, sizeof gre->src); memmove(gre->dst, grec->north, sizeof gre->dst); /* * Add a key, if needed. */ if(grec->ulkey){ flags = nhgets(gre->flags); if(flags & (GRE_cksum|GRE_routing)){ print("%V routing info present. Discarding packet\n", gre->src); freeb(bp); return; } if((flags & GRE_key) == 0){ /* Make room for the key */