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");
+}