add erik quanstros vblade utility

front
cinap_lenrek 2014-12-19 02:50:22 +01:00
parent e93cd703a2
commit 6379939642
6 changed files with 836 additions and 1 deletions

View File

@ -271,7 +271,7 @@ Unused.
.\" .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

85
sys/man/8/vblade Normal file
View File

@ -0,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.

View File

@ -14,6 +14,7 @@ DIRS=\
prep\
smart\
# sacfs\
vblade\
OFILES=

View File

@ -0,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;

View File

@ -0,0 +1,12 @@
</$objtype/mkfile
TARG=vblade
OFILES=\
vblade.$O\
HFILES=aoe.h
BIN=/$objtype/bin/disk
</sys/src/cmd/mkone

View File

@ -0,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");
}