/* * vblade -- virtual aoe target * copyright © 2007 erik quanstrom */ #include #include #include #include /* irony */ #include 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)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<0 && j < nblade; j++){ if((vec & 1<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"); }