#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/ns.h>
#include <9pm/devip.h>
enum
{
Qtopdir,
Qprotodir,
Qclonus,
Qconvdir,
Qdata,
Qctl,
Qstatus,
Qremote,
Qlocal,
Qlisten,
MAXPROTO = 4
};
#define TYPE(x) ((ulong)(x).path & 0xf)
#define CONV(x) (((ulong)(x).path >> 4)&0xfff)
#define PROTO(x) (((ulong)(x).path >> 16)&0xff)
#define QID(p, c, y) (((p)<<16) | ((c)<<4) | (y))
static int np;
static Proto proto[MAXPROTO];
static int eipconv(va_list*, Fconv*);
static Conv* cloneproto(Proto*, char*);
static void setladdr(Conv*);
static ulong parseip(char*, char*);
#define DIRQID(q, p) mkqid(q, p, 0, QTDIR)
#define FILEQID(q, p) mkqid(q, p, 0, QTFILE)
static int
ipgen(Chan *c, char *un0, Dirtab *d, int nd, int s, Dir *dp)
{
Qid q;
Conv *cv;
char name[16], *p;
USED(un0);
USED(d);
USED(nd);
switch(TYPE(c->qid)) {
case Qtopdir:
if(s == DEVDOTDOT){
DIRQID(&q, QID(0,0,Qtopdir));
devdir(c, q, "#I", 0, pm_conf.eve, DMDIR|0555, dp);
return 1;
}
if(s >= np)
return -1;
DIRQID(&q, QID(s, 0, Qprotodir));
devdir(c, q, proto[s].name, 0, pm_conf.eve, DMDIR|0555, dp);
return 1;
case Qprotodir:
if(s == DEVDOTDOT){
DIRQID(&q, QID(0,0,Qtopdir));
devdir(c, q, "#I", 0, pm_conf.eve, DMDIR|0555, dp);
return 1;
}
if(s < proto[PROTO(c->qid)].nc) {
cv = proto[PROTO(c->qid)].conv[s];
sprint(name, "%d", s);
DIRQID(&q, QID(PROTO(c->qid), s, Qconvdir));
devdir(c, q, name, 0, cv->owner, DMDIR|0555, dp);
return 1;
}
if(s > proto[PROTO(c->qid)].nc)
return -1;
FILEQID(&q, QID(PROTO(c->qid), 0, Qclonus));
devdir(c, q, "clone", 0, pm_conf.eve, 0555, dp);
return 1;
case Qclonus:
devdir(c, c->qid, "clone", 0, pm_conf.eve, 0555, dp);
return 1;
case Qconvdir:
if(s == DEVDOTDOT){
DIRQID(&q, QID(PROTO(c->qid), 0, Qprotodir));
devdir(c, q, proto[PROTO(c->qid)].name, 0, pm_conf.eve, DMDIR|0555, dp);
return 1;
}
cv = proto[PROTO(c->qid)].conv[CONV(c->qid)];
switch(s) {
default:
return -1;
case 0:
FILEQID(&q, QID(PROTO(c->qid), CONV(c->qid), Qdata));
devdir(c, q, "data", 0, cv->owner, cv->perm, dp);
return 1;
case 1:
FILEQID(&q, QID(PROTO(c->qid), CONV(c->qid), Qctl));
devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp);
return 1;
case 2:
FILEQID(&q, QID(PROTO(c->qid), CONV(c->qid), Qstatus));
p = "status";
break;
case 3:
FILEQID(&q, QID(PROTO(c->qid), CONV(c->qid), Qremote));
p = "remote";
break;
case 4:
FILEQID(&q, QID(PROTO(c->qid), CONV(c->qid), Qlocal));
p = "local";
break;
case 5:
FILEQID(&q, QID(PROTO(c->qid), CONV(c->qid), Qlisten));
p = "listen";
break;
}
devdir(c, q, p, 0, cv->owner, 0444, dp);
return 1;
case Qdata:
p = "data";
goto Genperm;
case Qctl:
p = "ctl";
Genperm:
cv = proto[PROTO(c->qid)].conv[CONV(c->qid)];
devdir(c, c->qid, p, 0, cv->owner, cv->perm, dp);
return 1;
case Qstatus:
p = "remote";
goto Gen444;
case Qremote:
p = "status";
goto Gen444;
case Qlocal:
p = "local";
goto Gen444;
case Qlisten:
p = "listen";
Gen444:
cv = proto[PROTO(c->qid)].conv[CONV(c->qid)];
devdir(c, c->qid, p, 0, cv->owner, cv->perm, dp);
return 1;
}
return -1;
}
static void
installipproto(Proto *xp)
{
int l;
Proto *p;
if(np >= MAXPROTO) {
print("no %s: increase MAXPROTO", xp->name);
return;
}
p = &proto[np];
p->name = xp->name;
p->read = xp->read;
p->write = xp->write;
p->clone = xp->clone;
p->connect = xp->connect;
p->announce = xp->announce;
p->listen = xp->listen;
p->close = xp->close;
p->maxconv = xp->maxconv;
DIRQID(&p->qid, QID(np, 0, Qprotodir));
p->x = np++;
l = sizeof(Conv*)*(p->maxconv+1);
p->conv = smalloc(l);
}
static void
ipreset(void)
{
fmtinstall('i', eipconv);
fmtinstall('I', eipconv);
fmtinstall('E', eipconv);
ipinit(installipproto);
}
static Chan*
ipattach(char *spec)
{
Chan *c;
c = devattach('I', spec);
DIRQID(&c->qid, QID(0, 0, Qtopdir));
return c;
}
static Walkqid*
ipwalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, 0, 0, ipgen);
}
static int
ipstat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, 0, 0, ipgen);
}
static void
closeconv(Conv *cc)
{
lock(&cc->r.l);
if(--cc->r.ref > 0){
unlock(&cc->r.l);
return;
}
unlock(&cc->r.l);
cc->owner = pm_conf.eve;
cc->perm = 0666;
cc->state = "Closed";
cc->laddr = 0;
cc->raddr = 0;
cc->lport = 0;
cc->rport = 0;
if(cc->p->close == nil)
abort();
cc->p->close(cc);
}
static Chan*
ipopen(Chan *c, int omode)
{
Proto *p;
int perm;
Conv *cv, *lcv;
/* BUG: can't we use devopen here? */
omode &= 3;
switch(omode) {
default:
error(Eperm);
case OREAD:
perm = 4;
break;
case OWRITE:
perm = 2;
break;
case ORDWR:
perm = 6;
break;
}
switch(TYPE(c->qid)) {
default:
panic("ipopen");
case Qtopdir:
case Qprotodir:
case Qconvdir:
case Qstatus:
case Qremote:
case Qlocal:
if(omode != OREAD)
error(Eperm);
break;
case Qclonus:
p = &proto[PROTO(c->qid)];
cv = cloneproto(p, pm_conf.eve);
if(cv == 0)
error(Enodev);
if(waserror()){
closeconv(cv);
nexterror();
}
p->clone(cv);
poperror();
c->qid.path = QID(p->x, cv->x, Qctl);
c->qid.vers = 0;
break;
case Qdata:
case Qctl:
p = &proto[PROTO(c->qid)];
lock(&p->l);
cv = p->conv[CONV(c->qid)];
lock(&cv->r.l);
if((perm & (cv->perm>>6)) != perm) {
if(strcmp(pm_conf.eve, cv->owner) != 0 ||
(perm & cv->perm) != perm) {
unlock(&cv->r.l);
unlock(&p->l);
error(Eperm);
}
}
cv->r.ref++;
if(cv->r.ref == 1) {
cv->owner = pm_conf.eve;
cv->perm = 0660;
}
unlock(&cv->r.l);
unlock(&p->l);
break;
case Qlisten:
p = &proto[PROTO(c->qid)];
lcv = p->conv[CONV(c->qid)];
cv = cloneproto(p, pm_conf.eve);
if(cv == 0)
error(Enodev);
if(waserror()){
closeconv(cv);
nexterror();
}
p->listen(cv, lcv);
poperror();
cv->state = "Established";
c->qid.path = QID(p->x, cv->x, Qctl);
break;
}
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
static void
ipclose(Chan *c)
{
Proto *x;
Conv *cc;
switch(TYPE(c->qid)) {
case Qdata:
case Qctl:
if((c->flag & COPEN) == 0)
break;
x = &proto[PROTO(c->qid)];
cc = x->conv[CONV(c->qid)];
closeconv(cc);
break;
}
}
static long
ipread(Chan *ch, void *a, long n, vlong offset)
{
Conv *c;
Proto *x;
uchar ip[4];
char buf[128], *p;
p = a;
switch(TYPE(ch->qid)) {
default:
error(Eperm);
case Qprotodir:
case Qtopdir:
case Qconvdir:
return devdirread(ch, a, n, 0, 0, ipgen);
case Qctl:
sprint(buf, "%lud", CONV(ch->qid));
return readstr(offset, p, n, buf);
case Qremote:
c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
hnputl(ip, c->raddr);
sprint(buf, "%I!%d\n", ip, c->rport);
return readstr(offset, p, n, buf);
case Qlocal:
c = proto[PROTO(ch->qid)].conv[CONV(ch->qid)];
hnputl(ip, c->laddr);
sprint(buf, "%I!%d\n", ip, c->lport);
return readstr(offset, p, n, buf);
case Qstatus:
x = &proto[PROTO(ch->qid)];
c = x->conv[CONV(ch->qid)];
sprint(buf, "%s/%d %ld %s \n",
c->p->name, c->x, c->r.ref, c->state);
return readstr(offset, p, n, buf);
case Qdata:
x = &proto[PROTO(ch->qid)];
c = x->conv[CONV(ch->qid)];
return x->read(c, a, n);
}
}
static void
setladdrport(Conv *c, char *str)
{
char *p, addr[4];
p = strchr(str, '!');
if(p == 0) {
p = str;
c->laddr = 0;
}
else {
*p++ = 0;
parseip(addr, str);
c->laddr = nhgetl((uchar*)addr);
}
if(*p == '*')
c->lport = 0;
else
c->lport = atoi(p);
}
static char*
setraddrport(Conv *c, char *str)
{
char *p, addr[4];
p = strchr(str, '!');
if(p == 0)
return "malformed address";
*p++ = 0;
parseip(addr, str);
c->raddr = nhgetl((uchar*)addr);
c->rport = atoi(p);
p = strchr(p, '!');
if(p) {
if(strcmp(p, "!r") == 0)
c->restricted = 1;
}
return 0;
}
long
ipwrite(Chan *ch, void *a, long n, vlong offset)
{
Conv *c;
Proto *x;
int r, nf;
char *p, *fields[3], buf[128];
USED(offset);
switch(TYPE(ch->qid)) {
default:
error(Eperm);
case Qctl:
x = &proto[PROTO(ch->qid)];
c = x->conv[CONV(ch->qid)];
if(n > sizeof(buf)-1)
n = sizeof(buf)-1;
memmove(buf, a, n);
buf[n] = '\0';
nf = getfields(buf, fields, 3, 1, " ");
if(strcmp(fields[0], "connect") == 0){
switch(nf) {
default:
error("bad args to connect");
case 2:
p = setraddrport(c, fields[1]);
if(p != 0)
error(p);
break;
case 3:
p = setraddrport(c, fields[1]);
if(p != 0)
error(p);
c->lport = atoi(fields[2]);
break;
}
x->connect(c);
c->state = "Established";
return n;
}
if(strcmp(fields[0], "announce") == 0) {
switch(nf){
default:
error("bad args to announce");
case 2:
setladdrport(c, fields[1]);
break;
}
x->announce(c);
c->state = "Announced";
return n;
}
if(strcmp(fields[0], "bind") == 0){
switch(nf){
default:
error("bad args to bind");
case 2:
c->lport = atoi(fields[1]);
break;
}
// setlport(c);
return n;
}
error("bad control message");
case Qdata:
x = &proto[PROTO(ch->qid)];
c = x->conv[CONV(ch->qid)];
r = x->write(c, a, n);
return r;
}
return n;
}
static Conv*
cloneproto(Proto *p, char *user)
{
Conv *c, **pp, **ep;
c = 0;
lock(&p->l);
if(waserror()) {
unlock(&p->l);
nexterror();
}
ep = &p->conv[p->maxconv];
for(pp = p->conv; pp < ep; pp++) {
c = *pp;
if(c == 0) {
c = smalloc(sizeof(Conv));
if(c == 0)
error(Enomem);
lock(&c->r.l);
c->r.ref = 1;
c->p = p;
c->x = pp - p->conv;
p->nc++;
*pp = c;
break;
}
lock(&c->r.l);
if(c->r.ref == 0) {
c->r.ref++;
break;
}
unlock(&c->r.l);
}
if(pp >= ep) {
unlock(&p->l);
poperror();
return 0;
}
c->owner = user;
c->perm = 0660;
c->state = "Closed";
c->restricted = 0;
c->laddr = 0;
c->raddr = 0;
c->lport = 0;
c->rport = 0;
unlock(&c->r.l);
unlock(&p->l);
poperror();
return c;
}
static int
eipconv(va_list *v, Fconv *f)
{
static char buf[64];
static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
static char *ifmt = "%d.%d.%d.%d";
uchar *p, ip[4];
switch(f->chr) {
case 'E': /* Ethernet address */
p = va_arg(*v, uchar*);
sprint(buf, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
break;
case 'I': /* Ip address */
p = va_arg(*v, uchar*);
sprint(buf, ifmt, p[0], p[1], p[2], p[3]);
break;
case 'i':
hnputl(ip, va_arg(*v, ulong));
sprint(buf, ifmt, ip[0], ip[1], ip[2], ip[3]);
break;
default:
strcpy(buf, "(eipconv)");
}
strconv(buf, f);
return 0;
}
void
hnputl(void *p, ulong v)
{
unsigned char *a;
a = p;
a[0] = v>>24;
a[1] = v>>16;
a[2] = v>>8;
a[3] = v;
}
void
hnputs(void *p, ushort v)
{
unsigned char *a;
a = p;
a[0] = v>>8;
a[1] = v;
}
ulong
nhgetl(void *p)
{
unsigned char *a;
a = p;
return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
}
ushort
nhgets(void *p)
{
unsigned char *a;
a = p;
return (a[0]<<8)|(a[1]<<0);
}
#define CLASS(p) ((*(unsigned char*)(p))>>6)
static ulong
parseip(char *to, char *from)
{
int i;
char *p;
p = from;
memset(to, 0, 4);
for(i = 0; i < 4 && *p; i++){
to[i] = strtoul(p, &p, 0);
if(*p == '.')
p++;
}
switch(CLASS(to)){
case 0: /* class A - 1 byte net */
case 1:
if(i == 3){
to[3] = to[2];
to[2] = to[1];
to[1] = 0;
} else if (i == 2){
to[3] = to[1];
to[1] = 0;
}
break;
case 2: /* class B - 2 byte net */
if(i == 3){
to[3] = to[2];
to[2] = 0;
}
break;
}
return nhgetl(to);
}
Dev devip = {
'I',
"ip",
ipreset,
ipattach,
ipwalk,
ipstat,
ipopen,
devcreate,
ipclose,
ipread,
devbread,
ipwrite,
devbwrite,
devremove,
devwstat,
};
syntax highlighted by Code2HTML, v. 0.9.1