shithub: riscv

Download patch

ref: 63799396422447f3d19640abc6a3d5374f9977b0
parent: e93cd703a2e7a98d178206c8a066f248aece76e9
author: cinap_lenrek <[email protected]>
date: Thu Dec 18 21:50:22 EST 2014

add erik quanstros vblade utility

--- a/sys/man/3/aoe
+++ b/sys/man/3/aoe
@@ -271,7 +271,7 @@
 .\" .IR vblade (1),
 .IR sd (3),
 .IR sdaoe (3),
-.IR aoesrv (8),
+.IR vblade (8),
 .IR snoopy (8)
 .br
 .BR http://www.coraid.com/documents/AoEr10.txt
--- /dev/null
+++ b/sys/man/8/vblade
@@ -1,0 +1,85 @@
+.TH VBLADE 8
+.SH NAME
+vblade \- virtual AoE target
+.SH SYNOPSIS
+.B disk/vblade
+[
+.B -ir
+] [
+.B -s
+.I size
+] [
+.B -a
+.I shelf.slot
+] [
+.B -c
+.I config
+]
+.B file
+[
+.B -e
+ether
+]
+.SH DESCRIPTION
+For each
+.I file
+specified,
+.I vblade
+serves it as an AoE (ATA-over-Ethernet) target via
+the specified interfaces.  The default interface is
+.BR /net/ether0 .
+Since AoE uses raw Ethernet frames, the target is
+only visible on the local ethernet segment.
+.PP
+All target-related options are reset for each
+.IR file .
+.SS Options
+.TP \w'\fL-e\ \fIetherXX'u
+.B -i
+Initialize the configuration header in 
+.IR file .
+All previous configuration information is lost.  Without this option,
+configuration is read from
+.I file
+and command like options override previous settings.
+.TP
+.B -r
+Raw.  Do not use a configuration header.  This is useful when
+exporting a device or file not generally exported by
+.IR vblade .
+.TP
+.BI -s " n"
+The exported target will have size
+.IR n ,
+rather than the available space in the target.  A size may end in
+.LR p ,
+.LR t ,
+.LR g ,
+.LR m ,
+or
+.LR k
+to specify a customary
+.I binary
+multiplier.
+.TP
+.BI -a " m.n"
+Specify the shelf and slot (or major and minor) address of the target.
+Valid shelf numbers are between 0 and 65534.  Valid slots are 0-255.
+.TP
+.BI -c " s"
+Set the AoE config string to
+.IR s .
+.TP
+.BI -e " ether"
+Listen to the network port
+.IR ether.
+Multiple ports may be specified.
+.SH SEE ALSO
+.IR aoe (3),
+.IR sdaoe (3),
+.IR http://www.coraid.com/documents/AoEr11.txt .
+.SH SOURCE
+.B /sys/src/cmd/disk/vblade
+.SH BUGS
+Security depends on control of the local Ethernet segment.  It may be
+unwise to serve AoE on a segment bridged to a wireless network.
--- a/sys/src/cmd/disk/mkfile
+++ b/sys/src/cmd/disk/mkfile
@@ -14,6 +14,7 @@
 	prep\
 	smart\
 #	sacfs\
+	vblade\
 
 OFILES=
 
--- /dev/null
+++ b/sys/src/cmd/disk/vblade/aoe.h
@@ -1,0 +1,74 @@
+enum{
+	ACata,
+	ACconfig,
+};
+
+enum{
+	AQCread,
+	AQCtest,
+	AQCprefix,
+	AQCset,
+	AQCfset,
+};
+
+enum{
+	AEcmd	= 1,
+	AEarg,
+	AEdev,
+	AEcfg,
+	AEver,
+};
+
+enum{
+	Aoetype		= 0x88a2,
+	Aoesectsz 	= 512,
+	Aoemaxcfg	= 1024,
+
+	Aoehsz		= 24,
+	Aoeatasz	= 12,
+	Aoecfgsz		= 8,
+	Aoerrsz		= 2,
+	Aoemsz		= 4,
+	Aoemdsz	= 8,
+
+	Aoever		= 1,
+
+	AFerr		= 1<<2,
+	AFrsp		= 1<<3,
+
+	AAFwrite	= 1,
+	AAFext		= 1<<6,
+};
+
+#pragma pack on
+
+typedef struct{
+	uchar	dst[Eaddrlen];
+	uchar	src[Eaddrlen];
+	uchar	type[2];
+	uchar	verflag;
+	uchar	error;
+	uchar	major[2];
+	uchar	minor;
+	uchar	cmd;
+	uchar	tag[4];
+}Aoehdr;
+
+typedef struct{
+	Aoehdr;
+	uchar	aflag;
+	uchar	errfeat;
+	uchar	scnt;
+	uchar	cmdstat;
+	uchar	lba[6];
+	uchar	res[2];
+}Aoeata;
+
+typedef struct{
+	Aoehdr;
+	uchar	bufcnt[2];
+	uchar	fwver[2];
+	uchar	scnt;
+	uchar	verccmd;
+	uchar	cslen[2];
+}Aoeqc;
--- /dev/null
+++ b/sys/src/cmd/disk/vblade/mkfile
@@ -1,0 +1,12 @@
+</$objtype/mkfile
+
+TARG=vblade
+
+OFILES=\
+	vblade.$O\
+
+HFILES=aoe.h
+
+BIN=/$objtype/bin/disk
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/disk/vblade/vblade.c
@@ -1,0 +1,663 @@
+/*
+ * vblade -- virtual aoe target
+ * copyright © 2007 erik quanstrom
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <ip.h>			/* irony */
+#include <fis.h>
+
+enum {
+	Eaddrlen	= 6,		/* only defined in kernel */
+};
+#include "aoe.h"
+
+enum {
+	Fclone,
+	Fdata,
+	Flast,
+
+	Fraw	= 1<<0,
+
+	Nether	= 8,
+	Nvblade	= 8,
+	Maxpkt	= 10000,
+	Hdrlba	= 128,
+	Conflen	= 1024,
+};
+
+typedef struct {
+	int	iflag;
+	int	flag;
+	int	shelf;
+	int	slot;
+	uvlong	maxlba;
+	char	*config;
+} Conf;
+
+typedef struct {
+	char	magic[32];
+	char	size[32];
+	char	address[16];
+	char	configlen[6];
+	char	pad[512-32-32-16-6];
+	char	config[Conflen];
+} Vbhdr;
+
+typedef struct {
+	Vbhdr	hdr;
+	vlong	maxlba;
+	vlong	hdrsz;
+	int	shelf;
+	int	slot;
+	int	clen;
+	int	flag;
+	int	fd;
+} Vblade;
+
+static	Vblade	vblade[Nvblade];
+static	int	nblade;
+
+static	char	*ethertab[Nether] = {
+	"/net/ether0",
+};
+static	int	etheridx = 1;
+static	int	efdtab[Nether*Flast];
+static	char	pkttab[Nether][Maxpkt];
+static	char	bctab[Nether][Maxpkt];
+static	int	mtutab[Nether];
+static	char	Magic[] = "aoe vblade\n";
+
+static int
+getmtu(char *p)
+{
+	char buf[50];
+	int fd, mtu;
+
+	snprint(buf, sizeof buf, "%s/mtu", p);
+	if((fd = open(buf, OREAD)) == -1)
+		return 2;
+	if(read(fd, buf, 36) < 0)
+		return 2;
+	close(fd);
+	buf[36] = 0;
+	mtu = strtoul(buf+12, 0, 0)-sizeof(Aoehdr);
+	return mtu>>9;
+}
+
+int
+parseshelf(char *s, int *shelf, int *slot)
+{
+	int a, b;
+
+	a = strtoul(s, &s, 0);
+	if(*s++ != '.')
+		return -1;
+	b = strtoul(s, &s, 0);
+	if(*s != 0)
+		return -1;
+	*shelf = a;
+	*slot = b;
+	return 0;
+}
+
+static vlong
+getsize(char *s)
+{
+	static char tab[] = "ptgmk";
+	char *p;
+	vlong v;
+
+	v = strtoull(s, &s, 0);
+	while((p = strchr(tab, *s++)) && *p)
+		while(*p++)
+			v *= 1024;
+	if(s[-1])
+		return -1;
+	return v;
+}
+
+vlong
+sizetolba(vlong size)
+{
+	if(size < 512 || size & 0x1ff){
+		fprint(2, "invalid size %lld\n", size);
+		exits("size");
+	}
+	return size>>9;
+}
+
+static int
+savevblade(int fd, Vblade *vb)
+{
+	int n, r;
+	char *p;
+
+	sprint(vb->hdr.size, "%lld", vb->maxlba<<9);
+	sprint(vb->hdr.address, "%d.%d", vb->shelf, vb->slot);
+	sprint(vb->hdr.configlen, "%d", vb->clen);
+
+	if(vb->flag & Fraw)
+		return 0;	
+	p = (char*)vb;
+	for(n = 0; n < sizeof *vb; n += r)
+		if((r = pwrite(fd, p+n, sizeof *vb-n, n)) <= 0)
+			break;
+	if(n != sizeof *vb)
+		return -1;
+	return 0;
+}
+
+static char*
+chkvblade(int fd, Vblade *vb)
+{
+	Vbhdr *h;
+
+	h = &vb->hdr;
+	if(readn(fd, (char*)h, sizeof *h) != sizeof *h)
+		return "bad read";
+	if(memcmp(h->magic, Magic, sizeof Magic))
+		return "bad magic";
+	h->size[sizeof h->size-1] = 0;
+	vb->maxlba = sizetolba(strtoull(h->size, 0, 0));
+	if(parseshelf(h->address, &vb->shelf, &vb->slot) == -1)
+		return "bad shelf";
+	h->configlen[sizeof h->configlen-1] = 0;
+	vb->clen = strtoul(h->configlen, 0, 0);
+	return 0;
+}
+
+void
+checkfile(char *s, Vblade *vb, int iflag)
+{
+	char *e;
+
+	vb->fd = open(s, ORDWR);
+	if(vb->fd == -1)
+		sysfatal("can't open backing store: %r");
+	if(iflag == 0 && (e = chkvblade(vb->fd, vb)))
+		sysfatal("invalid vblade %s", e);
+}
+
+void
+recheck(int fd, Vblade *vb)
+{
+	Dir *d;
+	vlong v;
+
+	d = dirfstat(fd);
+	if(d == 0)
+		sysfatal("can't stat: %r");
+	if((vb->flag & Fraw) == 0)
+		vb->hdrsz = Hdrlba;
+	v = sizetolba(d->length & ~0x1ff) - vb->hdrsz;
+	free(d);
+	if(vb->maxlba > v)
+		sysfatal("cmdline size too large (%lld sector overhead)", vb->hdrsz);
+	if(vb->maxlba == 0)
+		vb->maxlba = v;
+
+	savevblade(fd, vb);
+}
+
+int
+aoeopen(char *e, int fds[])
+{
+	char buf[128], ctl[13];
+	int n;
+
+	snprint(buf, sizeof buf, "%s/clone", e);
+	if((fds[Fclone] = open(buf, ORDWR)) == -1)
+		return -1;
+	memset(ctl, 0, sizeof ctl);
+	if(read(fds[Fclone], ctl, sizeof ctl - 1) < 0)
+		return -1;
+	n = atoi(ctl);
+	snprint(buf, sizeof buf, "connect %d", Aoetype);
+	if(write(fds[Fclone], buf, strlen(buf)) != strlen(buf))
+		return -1;
+	snprint(buf, sizeof buf, "%s/%d/data", e, n);
+	fds[Fdata] = open(buf, ORDWR);
+	return fds[Fdata];
+}
+
+void
+replyhdr(Aoehdr *h, Vblade *vblade)
+{
+	uchar	ea[Eaddrlen];
+
+	memmove(ea, h->dst, Eaddrlen);
+	memmove(h->dst, h->src, Eaddrlen);
+	memmove(h->src, ea, Eaddrlen);
+
+	hnputs(h->major, vblade->shelf);
+	h->minor = vblade->slot;
+	h->verflag |= AFrsp;
+}
+
+static int
+serveconfig(Aoeqc *q, Vblade *vb, int mtu)
+{
+	int cmd, reqlen, len;
+	char *cfg;
+
+	if(memcmp(q->src, q->dst, Eaddrlen) == 0)
+		return -1;
+
+	reqlen = nhgets(q->cslen);
+	len = vb->clen;
+	cmd = q->verccmd&0xf;
+	cfg = (char*)(q+1);
+
+	switch(cmd){
+	case AQCtest:
+		if(reqlen != len)
+			return -1;
+	case AQCprefix:
+		if(reqlen > len)
+			return -1;
+		if(memcmp(vb->hdr.config, cfg, reqlen) != 0)
+			return -1;
+	case AQCread:
+		break;
+	case AQCset:
+		if(len && len != reqlen || memcmp(vb->hdr.config, cfg, reqlen) != 0){
+			q->verflag |= AFerr;
+			q->error = AEcfg;
+			break;
+		}
+	case AQCfset:
+		if(reqlen > Conflen){
+			q->verflag |= AFerr;
+			q->error = AEarg;
+			break;
+		}
+		memset(vb->hdr.config, 0, sizeof vb->hdr.config);
+		memmove(vb->hdr.config, cfg, reqlen);
+		vb->clen = len = reqlen;
+		savevblade(vb->fd, vb);
+		break;
+	default:
+		q->verflag |= AFerr;
+		q->error = AEarg;
+	}
+
+	memmove(cfg, vb->hdr.config, len);
+	hnputs(q->cslen, len);
+	hnputs(q->bufcnt, 24);
+	q->scnt = mtu;
+	hnputs(q->fwver, 2323);
+	q->verccmd = Aoever<<4 | cmd;
+
+	return len+sizeof *q;
+}
+
+static ushort ident[256] = {
+	[47] 0x8000,
+	[49] 0x0200,
+	[50] 0x4000,
+	[83] 0x5400,
+	[84] 0x4000,
+	[86] 0x1400,
+	[87] 0x4000,
+	[93] 0x400b,
+};
+
+static void
+idmoveto(char *a, int idx, int len, char *s)
+{
+	char *p;
+
+	p = a+idx*2;
+	for(; len > 0; len -= 2) {
+		if(*s == 0)
+			p[1] = ' ';
+		else
+			p[1] = *s++;
+		if (*s == 0)
+			p[0] = ' ';
+		else
+			p[0] = *s++;
+		p += 2;
+	}
+}
+
+static void
+lbamoveto(char *p, int idx, int n, vlong lba)
+{
+	int i;
+
+	p += idx*2;
+	for(i = 0; i < n; i++)
+		*p++ = lba>>i*8;
+}
+
+enum {
+	Crd		= 0x20,
+	Crdext		= 0x24,
+	Cwr		= 0x30,
+	Cwrext		= 0x34,
+	Cid		= 0xec,
+};
+
+static uvlong
+getlba(uchar *p)
+{
+	uvlong v;
+
+	v = p[0];
+	v |= p[1]<<8;
+	v |= p[2]<<16;
+	v |= p[3]<<24;
+	v |= (uvlong)p[4]<<32;
+	v |= (uvlong)p[5]<<40;
+	return v;
+}
+
+static void
+putlba(uchar *p, vlong lba)
+{
+	p[0] = lba;
+	p[1] = lba>>8;
+	p[2] = lba>>16;
+	p[3] = lba>>24;
+	p[5] = lba>>32;
+	p[6] = lba>>40;
+}
+
+static int
+serveata(Aoeata *a, Vblade *vb, int mtu)
+{
+	char *buf;
+	int rbytes, bytes, len;
+	vlong lba, off;
+
+	buf = (char*)(a+1);
+	lba = getlba(a->lba);
+	len = a->scnt<<9;
+	off = lba+vb->hdrsz<<9;
+
+	if(a->scnt > mtu || a->scnt == 0){
+		a->verflag |= AFerr;
+		a->cmdstat = ASdrdy|ASerr;
+		a->error = AEarg;
+		return 0;
+	}
+	
+	if(a->cmdstat != Cid)
+	if(lba+a->scnt > vb->maxlba){
+		a->errfeat = Eidnf;
+		a->cmdstat = ASdrdy|ASerr;
+		return 0;
+	}
+
+	if(a->cmdstat&0xf0 == 0x20)
+		lba &= 0xfffffff;
+	switch(a->cmdstat){
+	default:
+		a->errfeat = Eabrt;
+		a->cmdstat = ASdrdy|ASerr;
+		return 0;
+	case Cid:
+		memmove(buf, ident, sizeof ident);
+		idmoveto(buf, 27, 40, "Plan 9 Vblade");
+		idmoveto(buf, 10, 20, "serial#");
+		idmoveto(buf, 23, 8, "2");
+		lbamoveto(buf, 60, 4, vb->maxlba);
+		lbamoveto(buf, 100, 8, vb->maxlba);
+		a->cmdstat = ASdrdy;
+		return 512;
+		break;
+	case Crd:
+	case Crdext:
+		bytes = pread(vb->fd, buf, len, off);
+		rbytes = bytes;
+		break;
+	case Cwr:
+	case Cwrext:
+		bytes = pwrite(vb->fd, buf, len, off);
+		rbytes  = 0;
+		break;
+	}
+	if(bytes != len){
+		a->errfeat = Eabrt;
+		a->cmdstat = ASdf|ASerr;
+		putlba(a->lba, lba+(len-bytes)>>9);
+		return 0;
+	}
+
+	putlba(a->lba, lba+a->scnt);
+	a->scnt = 0;
+	a->errfeat = 0;
+	a->cmdstat = ASdrdy;
+
+	return rbytes;
+}
+
+static int
+myea(uchar ea[6], char *p)
+{
+	char buf[50];
+	int fd;
+
+	snprint(buf, sizeof buf, "%s/addr", p);
+	if((fd = open(buf, OREAD)) == -1)
+		return -1;
+	if(read(fd, buf, 12) < 12)
+		return -1;
+	close(fd);
+	return parseether(ea, buf);
+}
+
+static int
+bcastpkt(Aoeqc *h, uint shelf, uint slot, int i)
+{
+	myea(h->dst, ethertab[i]);
+	memset(h->src, 0xff, Eaddrlen);
+	hnputs(h->type, Aoetype);
+	hnputs(h->major, shelf);
+	h->minor = slot;
+	h->cmd = ACconfig;
+	*(u32int*)h->tag = 0;
+	return Aoehsz + Aoecfgsz;
+}
+
+int
+bladereply(Vblade *v, int i, int fd, char *pkt)
+{
+	int n;
+	Aoehdr *h;
+
+	h = (Aoehdr*)pkt;
+	switch(h->cmd){
+	case ACata:
+		n = serveata((Aoeata*)h, v, mtutab[i]);
+		n += sizeof(Aoeata);
+		break;
+	case ACconfig:
+		n = serveconfig((Aoeqc*)h, v, mtutab[i]);
+		break;
+	default:
+		n = -1;
+		break;
+	}
+	if(n == -1)
+		return -1;
+	replyhdr(h, v);
+	if(n < 60){
+		memset(pkt+n, 0, 60-n);
+		n = 60;
+	}
+	if(write(fd, h, n) != n){
+		fprint(2, "write to %s failed: %r\n", ethertab[i]);
+		return -1;
+	}
+	return 0;
+}
+
+void
+serve(void *v)
+{
+	int i, j, popcnt, vec, n, s, efd;
+	char *pkt, *bcpkt;
+	Aoehdr *h;
+
+fmtinstall('E', eipfmt);
+	i = (int)(uintptr)v;
+
+	efd = efdtab[i*Flast+Fdata];
+	pkt = pkttab[i];
+	bcpkt = bctab[i];
+
+	n = 60;
+	h = (Aoehdr*)pkt;
+	bcastpkt((Aoeqc*)pkt, 0xffff, 0xff, i);
+	goto start;
+
+	for(;;){
+		n = read(efd, pkt, Maxpkt);
+	start:
+		if(n < 60 || h->verflag & AFrsp)
+			continue;
+		s = nhgets(h->major);
+		popcnt = 0;
+		vec = 0;
+		for(j = 0; j < nblade; j++){
+			if((vblade[j].shelf == s || s == 0xffff)
+			&& (vblade[j].slot == h->minor || h->minor == 0xff)){
+				popcnt++;
+				vec |= 1<<j;
+			}
+		}
+		for(j = 0; popcnt>0 && j < nblade; j++){
+			if((vec & 1<<j) == 0)
+				continue;
+			if(popcnt>0){
+				memcpy(bcpkt, pkt, n);
+				bladereply(vblade + j, i, efd, bcpkt);
+			}else
+				bladereply(vblade + j, i, efd, pkt);
+			popcnt--;
+		}
+	}
+}
+
+void
+launch(char *tab[], int fdtab[])
+{
+	int i;
+
+	for(i = 0; tab[i]; i++){
+		if(aoeopen(tab[i], fdtab+Flast*i) < 0)
+			sysfatal("network open: %r");
+		/*
+		 * use proc not threads.  otherwise we will block on read/write.
+		 */
+		proccreate(serve, (void*)i, 32*1024);
+	}
+}
+
+void
+usage(void)
+{
+	fprint(2, "vblade [-ir] [-s size] [-a shelf.slot] [-c config] [-e ether] file\n");
+	exits("usage");
+}
+
+void
+goblade(Vblade *vblade, char *file, Conf *c)
+{
+	char *anal;
+
+	if(c->iflag == 1)
+		memcpy(vblade->hdr.magic, Magic, sizeof Magic);
+	checkfile(file, vblade, c->iflag);
+
+	vblade->flag = c->flag;
+	if(c->shelf != -1){
+		vblade->shelf = c->shelf;
+		vblade->slot = c->slot;
+	}
+	if(c->maxlba > 0)
+		vblade->maxlba = c->maxlba;
+	if(c->config != nil)
+		memmove(vblade->hdr.config, c->config, vblade->clen = strlen(c->config));
+
+	recheck(vblade->fd, vblade);
+
+	anal = "";
+	if(vblade->maxlba > 1)
+		anal = "s";
+	fprint(2, "lblade %d.%d %lld sector%s\n", vblade->shelf, vblade->slot, vblade->maxlba, anal);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	int i, lastc, anye;
+	Conf c;
+
+	anye = 0;
+	for(;;){
+		if(nblade == nelem(vblade))
+			sysfatal("too many blades");
+		c = (Conf){0, 0, -1, -1, 0, nil};
+		lastc = 0;
+		ARGBEGIN{
+		case 'a':
+			lastc = 'a';
+			if(parseshelf(EARGF(usage()), &c.shelf, &c.slot) == -1)
+				sysfatal("bad vblade address");
+			break;
+		case 'c':
+			lastc = 'c';
+			c.config = EARGF(usage());
+			break;
+		case 'e':
+			lastc = 'e';
+			if(anye++ == 0)
+				etheridx = 0;
+			if(etheridx == nelem(ethertab))
+				sysfatal("too many interfaces");
+			ethertab[etheridx++] = EARGF(usage());
+			break;
+		case 'i':
+			lastc = 'i';
+			c.iflag = 1;
+			break;
+		case 'r':
+			lastc = 'r';
+			c.flag |= Fraw;
+			c.iflag = 1;
+			break;
+		case 's':
+			lastc = 's';
+			c.maxlba = sizetolba(getsize(EARGF(usage())));
+			break;
+		default:
+			lastc = '?';
+			usage();
+		}ARGEND;
+
+		if(argc == 0 && lastc == 'e')
+			break;
+		if(argc == 0)
+			usage();
+		goblade(vblade + nblade++, *argv, &c);
+		if(argc == 1)
+			break;
+	}
+
+	if(nblade == 0)
+		usage();	
+	for(i = 0; i < etheridx; i++)
+		mtutab[i] = getmtu(ethertab[i]);
+
+	launch(ethertab, efdtab);
+
+	for(; sleep(1*1000) != -1;)
+		;
+	threadexitsall("interrupted");
+}