#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "../ip/ip.h" enum { Qtopdir= 1, /* top level directory */ Qtopbase, Qarp= Qtopbase, Qbootp, Qndb, Qiproute, Qipselftab, Qlog, Qprotodir, /* directory for a protocol */ Qprotobase, Qclone= Qprotobase, Qstats, Qconvdir, /* directory for a conversation */ Qconvbase, Qctl= Qconvbase, Qdata, Qerr, Qlisten, Qlocal, Qremote, Qstatus, Qsnoop, Logtype= 5, Masktype= (1<> Shiftconv) & Maskconv ) #define PROTO(x) ( (((ulong)(x).path) >> Shiftproto) & Maskproto ) #define QID(p, c, y) ( ((p)<<(Shiftproto)) | ((c)<dev]->p[PROTO(c->qid)]->conv[CONV(c->qid)]; if(cv->owner == nil) kstrdup(&cv->owner, eve); mkqid(&q, QID(PROTO(c->qid), CONV(c->qid), i), 0, QTFILE); switch(i) { default: return -1; case Qctl: devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); return 1; case Qdata: devdir(c, q, "data", qlen(cv->rq), cv->owner, cv->perm, dp); return 1; case Qerr: devdir(c, q, "err", qlen(cv->eq), cv->owner, cv->perm, dp); return 1; case Qlisten: devdir(c, q, "listen", 0, cv->owner, cv->perm, dp); return 1; case Qlocal: p = "local"; break; case Qremote: p = "remote"; break; case Qsnoop: if(strcmp(cv->p->name, "ipifc") != 0) return -1; devdir(c, q, "snoop", qlen(cv->sq), cv->owner, 0400, dp); return 1; case Qstatus: p = "status"; break; } devdir(c, q, p, 0, cv->owner, 0444, dp); return 1; } static int ip2gen(Chan *c, int i, Dir *dp) { Qid q; switch(i) { case Qclone: mkqid(&q, QID(PROTO(c->qid), 0, Qclone), 0, QTFILE); devdir(c, q, "clone", 0, network, 0666, dp); return 1; case Qstats: mkqid(&q, QID(PROTO(c->qid), 0, Qstats), 0, QTFILE); devdir(c, q, "stats", 0, network, 0444, dp); return 1; } return -1; } static int ip1gen(Chan *c, int i, Dir *dp) { Qid q; char *p; int prot; int len = 0; Fs *f; extern u32 kerndate; f = ipfs[c->dev]; prot = 0666; mkqid(&q, QID(0, 0, i), 0, QTFILE); switch(i) { default: return -1; case Qarp: p = "arp"; prot = 0664; break; case Qbootp: p = "bootp"; break; case Qndb: p = "ndb"; len = strlen(f->ndb); q.vers = f->ndbvers; break; case Qiproute: p = "iproute"; prot = 0664; break; case Qipselftab: p = "ipselftab"; prot = 0444; break; case Qlog: p = "log"; break; } devdir(c, q, p, len, network, prot, dp); if(i == Qndb && f->ndbmtime > kerndate) dp->mtime = f->ndbmtime; return 1; } static int ipgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) { Qid q; Conv *cv; Fs *f; f = ipfs[c->dev]; switch(TYPE(c->qid)) { case Qtopdir: if(s == DEVDOTDOT){ mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); snprint(up->genbuf, sizeof up->genbuf, "#I%ud", c->dev); devdir(c, q, up->genbuf, 0, network, 0555, dp); return 1; } if(s < f->np) { if(f->p[s]->connect == nil) return 0; /* protocol with no user interface */ mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); devdir(c, q, f->p[s]->name, 0, network, 0555, dp); return 1; } s -= f->np; return ip1gen(c, s+Qtopbase, dp); case Qarp: case Qbootp: case Qndb: case Qlog: case Qiproute: case Qipselftab: return ip1gen(c, TYPE(c->qid), dp); case Qprotodir: if(s == DEVDOTDOT){ mkqid(&q, QID(0, 0, Qtopdir), 0, QTDIR); snprint(up->genbuf, sizeof up->genbuf, "#I%ud", c->dev); devdir(c, q, up->genbuf, 0, network, 0555, dp); return 1; } if(s < f->p[PROTO(c->qid)]->ac) { cv = f->p[PROTO(c->qid)]->conv[s]; snprint(up->genbuf, sizeof up->genbuf, "%d", s); mkqid(&q, QID(PROTO(c->qid), s, Qconvdir), 0, QTDIR); devdir(c, q, up->genbuf, 0, cv->owner, 0555, dp); return 1; } s -= f->p[PROTO(c->qid)]->ac; return ip2gen(c, s+Qprotobase, dp); case Qclone: case Qstats: return ip2gen(c, TYPE(c->qid), dp); case Qconvdir: if(s == DEVDOTDOT){ s = PROTO(c->qid); mkqid(&q, QID(s, 0, Qprotodir), 0, QTDIR); devdir(c, q, f->p[s]->name, 0, network, 0555, dp); return 1; } return ip3gen(c, s+Qconvbase, dp); case Qctl: case Qdata: case Qerr: case Qlisten: case Qlocal: case Qremote: case Qstatus: case Qsnoop: return ip3gen(c, TYPE(c->qid), dp); } return -1; } static void ipreset(void) { nullmediumlink(); pktmediumlink(); fmtinstall('i', eipfmt); fmtinstall('I', eipfmt); fmtinstall('E', eipfmt); fmtinstall('V', eipfmt); fmtinstall('M', eipfmt); } IPaux* newipaux(char *owner, char *tag) { IPaux *a; a = smalloc(sizeof(*a)); kstrdup(&a->owner, owner); strncpy(a->tag, tag, sizeof(a->tag)); return a; } #define ATTACHER(c) (((IPaux*)((c)->aux))->owner) static Chan* ipattach(char* spec) { Chan *c; ulong dev; dev = strtoul(spec, nil, 10); if(dev >= Nfs) error(Enodev); qlock(&fslock); if(ipfs[dev] == nil){ extern void (*ipprotoinit[])(Fs*); Fs *f; int i; f = smalloc(sizeof(Fs)); ip_init(f); arpinit(f); netloginit(f); for(i = 0; ipprotoinit[i]; i++) ipprotoinit[i](f); f->dev = dev; ipfs[dev] = f; } qunlock(&fslock); c = devattach('I', spec); mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR); c->dev = dev; c->aux = newipaux(commonuser(), "none"); return c; } static Walkqid* ipwalk(Chan* c, Chan *nc, char **name, s32 nname) { IPaux *a = c->aux; Walkqid* w; w = devwalk(c, nc, name, nname, nil, 0, ipgen); if(w != nil && w->clone != nil) w->clone->aux = newipaux(a->owner, a->tag); return w; } static s32 ipstat(Chan* c, uchar* db, s32 n) { return devstat(c, db, n, nil, 0, ipgen); } static int incoming(void* arg) { Conv *conv; conv = arg; return conv->incall != nil; } static int m2p[] = { [OREAD] 4, [OWRITE] 2, [ORDWR] 6 }; static Chan* ipopen(Chan* c, u32 omode) { Conv *cv, *nc; Proto *p; int perm; Fs *f; perm = m2p[omode&3]; f = ipfs[c->dev]; switch(TYPE(c->qid)) { default: break; case Qndb: if((omode & (OWRITE|OTRUNC)) != 0 && !iseve()) error(Eperm); if((omode & (OWRITE|OTRUNC)) == (OWRITE|OTRUNC)) f->ndb[0] = 0; break; case Qlog: netlogopen(f); break; case Qiproute: case Qarp: if(omode != OREAD && !iseve()) error(Eperm); break; case Qtopdir: case Qprotodir: case Qconvdir: case Qstatus: case Qremote: case Qlocal: case Qstats: case Qbootp: case Qipselftab: if(omode != OREAD) error(Eperm); break; case Qsnoop: if(omode != OREAD) error(Eperm); p = f->p[PROTO(c->qid)]; cv = p->conv[CONV(c->qid)]; if(strcmp(ATTACHER(c), cv->owner) != 0 && !iseve()) error(Eperm); incref(&cv->snoopers); break; case Qclone: p = f->p[PROTO(c->qid)]; qlock(p); cv = Fsprotoclone(p, ATTACHER(c)); qunlock(p); if(cv == nil) { error(Enodev); break; } mkqid(&c->qid, QID(p->x, cv->x, Qctl), 0, QTFILE); break; case Qdata: case Qctl: case Qerr: p = f->p[PROTO(c->qid)]; qlock(p); cv = p->conv[CONV(c->qid)]; qlock(cv); if(waserror()) { qunlock(cv); qunlock(p); nexterror(); } if(strcmp(ATTACHER(c), cv->owner) == 0) perm <<= 6; if((perm & cv->perm) != perm && !iseve()) error(Eperm); if(++cv->inuse == 1){ kstrdup(&cv->owner, ATTACHER(c)); cv->perm = 0660; } qunlock(cv); qunlock(p); poperror(); break; case Qlisten: cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)]; qlock(cv); if(waserror()){ qunlock(cv); nexterror(); } if(strcmp(ATTACHER(c), cv->owner) == 0) perm <<= 6; if((perm & cv->perm) != perm && !iseve()) error(Eperm); if(cv->state != Announced) error("not announced"); cv->inuse++; qunlock(cv); poperror(); if(waserror()){ closeconv(cv); nexterror(); } nc = nil; while(nc == nil) { /* give up if we got a hangup */ if(qisclosed(cv->rq)) error("listen hungup"); qlock(&cv->listenq); if(waserror()) { qunlock(&cv->listenq); nexterror(); } /* wait for a connect */ sleep(&cv->listenr, incoming, cv); qlock(cv); nc = cv->incall; if(nc != nil){ cv->incall = nc->next; mkqid(&c->qid, QID(PROTO(c->qid), nc->x, Qctl), 0, QTFILE); } qunlock(cv); qunlock(&cv->listenq); poperror(); } closeconv(cv); poperror(); break; } c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } static void ipremove(Chan*) { error(Eperm); } static s32 ipwstat(Chan *c, uchar *dp, s32 n) { Dir *dir; Conv *cv; Fs *f; f = ipfs[c->dev]; switch(TYPE(c->qid)) { default: error(Eperm); break; case Qctl: case Qdata: break; } dir = smalloc(sizeof(Dir)+n); if(waserror()){ free(dir); nexterror(); } n = convM2D(dp, n, &dir[0], (char*)&dir[1]); if(n == 0) error(Eshortstat); cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)]; qlock(cv); if(waserror()){ qunlock(cv); nexterror(); } if(strcmp(ATTACHER(c), cv->owner) != 0 && !iseve()) error(Eperm); if(!emptystr(dir->uid)){ if(strcmp(dir->uid, commonuser()) != 0 && !iseve()) error(Eperm); kstrdup(&cv->owner, dir->uid); } if(dir->mode != ~0UL) cv->perm = dir->mode & 0666; qunlock(cv); poperror(); free(dir); poperror(); return n; } void closeconv(Conv *cv) { Conv *nc; Ipmulti *mp; qlock(cv); if(--cv->inuse > 0) { qunlock(cv); return; } /* close all incoming calls since no listen will ever happen */ for(nc = cv->incall; nc != nil; nc = cv->incall){ cv->incall = nc->next; closeconv(nc); } cv->incall = nil; kstrdup(&cv->owner, network); cv->perm = 0660; while((mp = cv->multi) != nil) ipifcremmulti(cv, mp->ma, mp->ia); if(cv->p->close != nil) (*cv->p->close)(cv); cv->state = Idle; qunlock(cv); } static void ipclose(Chan* c) { Fs *f; f = ipfs[c->dev]; switch(TYPE(c->qid)) { default: break; case Qlog: if(c->flag & COPEN) netlogclose(f); break; case Qdata: case Qctl: case Qerr: if(c->flag & COPEN) closeconv(f->p[PROTO(c->qid)]->conv[CONV(c->qid)]); break; case Qsnoop: if(c->flag & COPEN) decref(&f->p[PROTO(c->qid)]->conv[CONV(c->qid)]->snoopers); break; } free(((IPaux*)c->aux)->owner); free(c->aux); } enum { Statelen= 32*1024, }; static s32 ipread(Chan *ch, void *a, s32 n, s64 off) { Conv *c; Proto *x; char *buf, *p; long rv; Fs *f; ulong offset = off; f = ipfs[ch->dev]; p = a; switch(TYPE(ch->qid)) { default: error(Eperm); case Qtopdir: case Qprotodir: case Qconvdir: return devdirread(ch, a, n, 0, 0, ipgen); case Qarp: return arpread(f->arp, a, offset, n); case Qbootp: return 0 /*TODO bootpread(a, offset, n)*/; case Qndb: return readstr(offset, a, n, f->ndb); case Qiproute: return routeread(f, a, offset, n); case Qipselftab: return ipselftabread(f, a, offset, n); case Qlog: return netlogread(f, a, offset, n); case Qctl: buf = smalloc(16); snprint(buf, 16, "%lud", CONV(ch->qid)); rv = readstr(offset, p, n, buf); free(buf); return rv; case Qremote: buf = smalloc(Statelen); x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; if(x->remote == nil) { snprint(buf, Statelen, "%I!%d\n", c->raddr, c->rport); } else { (*x->remote)(c, buf, Statelen-2); } rv = readstr(offset, p, n, buf); free(buf); return rv; case Qlocal: buf = smalloc(Statelen); x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; if(x->local == nil) { snprint(buf, Statelen, "%I!%d\n", c->laddr, c->lport); } else { (*x->local)(c, buf, Statelen-2); } rv = readstr(offset, p, n, buf); free(buf); return rv; case Qstatus: buf = smalloc(Statelen); x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; (*x->state)(c, buf, Statelen-2); rv = readstr(offset, p, n, buf); free(buf); return rv; case Qdata: c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; return qread(c->rq, a, n); case Qerr: c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; return qread(c->eq, a, n); case Qsnoop: c = f->p[PROTO(ch->qid)]->conv[CONV(ch->qid)]; return qread(c->sq, a, n); case Qstats: x = f->p[PROTO(ch->qid)]; if(x->stats == nil) error("stats not implemented"); buf = smalloc(Statelen); (*x->stats)(x, buf, Statelen); rv = readstr(offset, p, n, buf); free(buf); return rv; } } static Block* ipbread(Chan* ch, s32 n, u32 offset) { Conv *c; Proto *x; Fs *f; switch(TYPE(ch->qid)){ case Qdata: f = ipfs[ch->dev]; x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; return qbread(c->rq, n); default: return devbread(ch, n, offset); } } /* * set local address to be that of the ifc closest to remote address */ static void setladdr(Conv* c) { findlocalip(c->p->f, c->laddr, c->raddr); } /* * set a local port making sure the quad of raddr,rport,laddr,lport is unique */ char* setluniqueport(Conv* c, int lport) { Proto *p; Conv *xp; int x; p = c->p; qlock(p); for(x = 0; x < p->nc; x++){ xp = p->conv[x]; if(xp == nil) break; if(xp == c) continue; if((xp->state == Connected || xp->state == Announced) && xp->lport == lport && xp->rport == c->rport && ipcmp(xp->raddr, c->raddr) == 0 && ipcmp(xp->laddr, c->laddr) == 0){ qunlock(p); return "address in use"; } } c->lport = lport; qunlock(p); return nil; } /* * is lport in use by anyone? */ static int lportinuse(Proto *p, ushort lport) { int x; for(x = 0; x < p->nc && p->conv[x]; x++) if(p->conv[x]->lport == lport) return 1; return 0; } /* * pick a local port and set it */ char * setlport(Conv* c) { Proto *p; int i, port; p = c->p; qlock(p); if(c->restricted){ /* Restricted ports cycle between 600 and 1024. */ for(i=0; i<1024-600; i++){ if(p->nextrport >= 1024 || p->nextrport < 600) p->nextrport = 600; port = p->nextrport++; if(!lportinuse(p, port)) goto chosen; } }else{ /* * Unrestricted ports are chosen randomly * between 2^15 and 2^16. There are at most * 4*Nchan = 4096 ports in use at any given time, * so even in the worst case, a random probe has a * 1 - 4096/2^15 = 87% chance of success. * If 64 successive probes fail, there is a bug somewhere * (or a once in 10^58 event has happened, but that's * less likely than a venti collision). */ for(i=0; i<64; i++){ port = (1<<15) + nrand(1<<15); if(!lportinuse(p, port)) goto chosen; } } qunlock(p); return "no ports available"; chosen: c->lport = port; qunlock(p); return nil; } /* * set a local address and port from a string of the form * [address!]port[!r] */ char* setladdrport(Conv* c, char* str, int announcing) { char *p; char *rv; ushort lport; uchar addr[IPaddrlen]; /* * ignore restricted part if it exists. it's * meaningless on local ports. */ p = strchr(str, '!'); if(p != nil){ *p++ = 0; if(strcmp(p, "r") == 0) p = nil; } c->lport = 0; if(p == nil){ if(announcing) ipmove(c->laddr, IPnoaddr); else setladdr(c); p = str; } else { if(strcmp(str, "*") == 0) ipmove(c->laddr, IPnoaddr); else { if(parseip(addr, str) == -1) return Ebadip; if(ipforme(c->p->f, addr) != 0 || ipismulticast(addr)) ipmove(c->laddr, addr); else return "not a local IP address"; } } /* one process can get all connections */ if(announcing && strcmp(p, "*") == 0){ if(!iseve()) error(Eperm); return setluniqueport(c, 0); } str = p; lport = strtol(str, &p, 10); if(p <= str || strchr("!", *p) == nil) return "bad numeric port"; if(lport <= 0) rv = setlport(c); else rv = setluniqueport(c, lport); return rv; } static char* setraddrport(Conv* c, char* str) { char *p; p = strchr(str, '!'); if(p == nil) return "malformed address"; *p++ = 0; if(parseip(c->raddr, str) == -1) return Ebadip; str = p; c->rport = strtol(str, &p, 10); if(p <= str || strchr("!", *p) == nil) return "bad numeric port"; if(strstr(p, "!r") != nil) c->restricted = 1; return nil; } /* * called by protocol connect routine to set addresses */ char* Fsstdconnect(Conv *c, char *argv[], int argc) { char *p; switch(argc) { default: return "bad args to connect"; case 2: p = setraddrport(c, argv[1]); if(p != nil) return p; setladdr(c); p = setlport(c); if (p != nil) return p; break; case 3: p = setraddrport(c, argv[1]); if(p != nil) return p; p = setladdrport(c, argv[2], 0); if(p != nil) return p; } c->ipversion = convipvers(c); return nil; } /* * initiate connection and sleep till its set up */ static int connected(void* a) { return ((Conv*)a)->state == Connected; } static void connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb) { char *p; if(c->state != 0) error(Econinuse); c->state = Connecting; c->cerr[0] = '\0'; if(x->connect == nil) error("connect not supported"); p = x->connect(c, cb->f, cb->nf); if(p != nil) error(p); qunlock(c); if(waserror()){ qlock(c); nexterror(); } sleep(&c->cr, connected, c); qlock(c); poperror(); if(c->cerr[0] != '\0') error(c->cerr); } /* * called by protocol announce routine to set addresses */ char* Fsstdannounce(Conv* c, char* argv[], int argc) { memset(c->raddr, 0, sizeof(c->raddr)); c->rport = 0; switch(argc){ default: break; case 2: return setladdrport(c, argv[1], 1); } return "bad args to announce"; } /* * initiate announcement and sleep till its set up */ static int announced(void* a) { return ((Conv*)a)->state == Announced; } static void announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb) { char *p; if(c->state != 0) error(Econinuse); c->state = Announcing; c->cerr[0] = '\0'; if(x->announce == nil) error("announce not supported"); p = x->announce(c, cb->f, cb->nf); if(p != nil) error(p); qunlock(c); if(waserror()){ qlock(c); nexterror(); } sleep(&c->cr, announced, c); qlock(c); poperror(); if(c->cerr[0] != '\0') error(c->cerr); } /* * called by protocol bind routine to set addresses */ char* Fsstdbind(Conv* c, char* argv[], int argc) { switch(argc){ default: break; case 2: return setladdrport(c, argv[1], 0); } return "bad args to bind"; } static void bindctlmsg(Proto *x, Conv *c, Cmdbuf *cb) { char *p; if(x->bind == nil) p = Fsstdbind(c, cb->f, cb->nf); else p = (*x->bind)(c, cb->f, cb->nf); if(p != nil) error(p); } static void tosctlmsg(Conv *c, Cmdbuf *cb) { if(cb->nf < 2) c->tos = 0; else c->tos = atoi(cb->f[1]); } static void ttlctlmsg(Conv *c, Cmdbuf *cb) { if(cb->nf < 2) c->ttl = MAXTTL; else c->ttl = atoi(cb->f[1]); } static s32 ipwrite(Chan* ch, void *v, s32 n, s64 off) { Conv *c; Proto *x; char *p; Cmdbuf *cb; uchar ia[IPaddrlen], ma[IPaddrlen]; Fs *f; char *a; ulong offset = off; a = v; f = ipfs[ch->dev]; switch(TYPE(ch->qid)){ default: error(Eperm); case Qdata: x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; if(c->wq == nil) error(Eperm); qwrite(c->wq, a, n); break; case Qarp: return arpwrite(f, a, n); case Qiproute: return routewrite(f, ch, a, n); case Qlog: netlogctl(f, a, n); return n; case Qndb: return ndbwrite(f, a, offset, n); break; case Qctl: x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; cb = parsecmd(a, n); qlock(c); if(waserror()) { qunlock(c); free(cb); nexterror(); } if(cb->nf < 1) error("short control request"); if(strcmp(cb->f[0], "connect") == 0) connectctlmsg(x, c, cb); else if(strcmp(cb->f[0], "announce") == 0) announcectlmsg(x, c, cb); else if(strcmp(cb->f[0], "bind") == 0) bindctlmsg(x, c, cb); else if(strcmp(cb->f[0], "ttl") == 0) ttlctlmsg(c, cb); else if(strcmp(cb->f[0], "tos") == 0) tosctlmsg(c, cb); else if(strcmp(cb->f[0], "ignoreadvice") == 0) c->ignoreadvice = 1; else if(strcmp(cb->f[0], "addmulti") == 0){ if(cb->nf < 2) error("addmulti needs interface address"); if(cb->nf == 2){ if(!ipismulticast(c->raddr)) error("addmulti for a non multicast address"); if (parseip(ia, cb->f[1]) == -1) error(Ebadip); ipifcaddmulti(c, c->raddr, ia); } else { if (parseip(ia, cb->f[1]) == -1 || parseip(ma, cb->f[2]) == -1) error(Ebadip); if(!ipismulticast(ma)) error("addmulti for a non multicast address"); ipifcaddmulti(c, ma, ia); } } else if(strcmp(cb->f[0], "remmulti") == 0){ if(cb->nf < 2) error("remmulti needs interface address"); if(!ipismulticast(c->raddr)) error("remmulti for a non multicast address"); if (parseip(ia, cb->f[1]) == -1) error(Ebadip); ipifcremmulti(c, c->raddr, ia); } else if(x->ctl != nil) { p = (*x->ctl)(c, cb->f, cb->nf); if(p != nil) error(p); } else error("unknown control request"); qunlock(c); free(cb); poperror(); } return n; } static s32 ipbwrite(Chan* ch, Block* bp, u32 offset) { Conv *c; Proto *x; Fs *f; switch(TYPE(ch->qid)){ case Qdata: f = ipfs[ch->dev]; x = f->p[PROTO(ch->qid)]; c = x->conv[CONV(ch->qid)]; if(c->wq == nil) error(Eperm); return qbwrite(c->wq, bp); default: return devbwrite(ch, bp, offset); } } Dev ipdevtab = { 'I', "ip", ipreset, devinit, devshutdown, ipattach, ipwalk, ipstat, ipopen, devcreate, ipclose, ipread, ipbread, ipwrite, ipbwrite, ipremove, ipwstat, }; int Fsproto(Fs *f, Proto *p) { if(f->np >= Maxproto) return -1; p->f = f; if(p->ipproto > 0){ if(f->t2p[p->ipproto] != nil) return -1; f->t2p[p->ipproto] = p; } p->qid.type = QTDIR; p->qid.path = QID(f->np, 0, Qprotodir); if(p->nc > Maskconv+1){ print("Fsproto: %s nc %d > %d\n", p->name, p->nc, Maskconv+1); p->nc = Maskconv+1; } p->conv = malloc(sizeof(Conv*)*(p->nc+1)); if(p->conv == nil) panic("Fsproto"); p->x = f->np; p->nextrport = 600; f->p[f->np++] = p; return 0; } /* * return true if this protocol is * built in */ int Fsbuiltinproto(Fs* f, uchar proto) { return f->t2p[proto] != nil; } /* * called with protocol locked */ Conv* Fsprotoclone(Proto *p, char *user) { Conv *c, **pp, **ep; retry: c = nil; ep = &p->conv[p->nc]; for(pp = p->conv; pp < ep; pp++) { c = *pp; if(c == nil){ c = malloc(sizeof(Conv)); if(c == nil) return nil; if(waserror()){ qfree(c->rq); qfree(c->wq); qfree(c->eq); qfree(c->sq); free(c->ptcl); free(c); return nil; } c->p = p; c->x = pp - p->conv; if(p->ptclsize != 0){ c->ptcl = malloc(p->ptclsize); if(c->ptcl == nil) error(Enomem); } c->eq = qopen(1024, Qmsg, 0, 0); if(c->eq == nil) error(Enomem); (*p->create)(c); if(c->rq == nil || c->wq == nil) error(Enomem); poperror(); qlock(c); *pp = c; p->ac++; break; } if(canqlock(c)){ /* * make sure both processes and protocol * are done with this Conv */ if(c->inuse == 0 && (p->inuse == nil || (*p->inuse)(c) == 0)) break; qunlock(c); } } if(pp >= ep) { if(p->gc != nil){ print("Fsprotoclone: garbage collecting %s Convs\n", p->name); if((*p->gc)(p)) goto retry; } return nil; } c->inuse = 1; kstrdup(&c->owner, user); c->perm = 0660; c->state = Idle; ipmove(c->laddr, IPnoaddr); ipmove(c->raddr, IPnoaddr); c->r = nil; c->rgen = 0; c->lport = 0; c->rport = 0; c->restricted = 0; c->ignoreadvice = 0; c->ttl = MAXTTL; c->tos = 0; qreopen(c->rq); qreopen(c->wq); qreopen(c->eq); qunlock(c); return c; } int Fsconnected(Conv* c, char* msg) { if(msg != nil && *msg != '\0') strncpy(c->cerr, msg, ERRMAX-1); switch(c->state){ case Announcing: c->state = Announced; break; case Connecting: c->state = Connected; break; } wakeup(&c->cr); return 0; } Proto* Fsrcvpcol(Fs* f, uchar proto) { if(f->ipmux) return f->ipmux; else return f->t2p[proto]; } Proto* Fsrcvpcolx(Fs *f, uchar proto) { return f->t2p[proto]; } /* * called with protocol locked */ Conv* Fsnewcall(Conv *c, uchar *raddr, ushort rport, uchar *laddr, ushort lport, uchar version) { Conv *nc; Conv **l; int i; qlock(c); i = 0; for(l = &c->incall; *l; l = &(*l)->next) i++; if(i >= Maxincall) { static int beenhere; qunlock(c); if (!beenhere) { beenhere = 1; print("Fsnewcall: incall queue full (%d) on port %d\n", i, c->lport); } return nil; } /* find a free conversation */ nc = Fsprotoclone(c->p, c->owner); if(nc == nil) { qunlock(c); return nil; } ipmove(nc->raddr, raddr); nc->rport = rport; ipmove(nc->laddr, laddr); nc->lport = lport; nc->next = nil; *l = nc; nc->state = Connected; nc->ipversion = version; qunlock(c); wakeup(&c->listenr); return nc; } long ndbwrite(Fs *f, char *a, ulong off, int n) { if(off > strlen(f->ndb)) error(Eio); if(off+n >= sizeof(f->ndb)) error(Eio); memmove(f->ndb+off, a, n); f->ndb[off+n] = 0; f->ndbvers++; f->ndbmtime = seconds(); return n; } ulong scalednconv(void) { if(conf.npage*BY2PG >= 128*MB) return Nchans*4; return Nchans; }