980 lines · 19.8 KB · C
RawDownload
/*  * 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.