/* * Coraid ethernet console — serial replacement. */ #include "all.h" #include "../ip/ip.h" #include "io.h" #include "etherif.h" #include "mem.h" enum { Ncbuf = 4096, Ncmask = Ncbuf-1, Namelen = 128, }; enum{ Tinita = 0, Tinitb, Tinitc, Tdata, Tack, Tdiscover, Toffer, Treset, }; enum{ Cunused = 0, Cinitb, Clogin, Copen, }; typedef struct{ uchar valid; uchar ea[Easize]; int i; Queue *reply; }If; typedef struct{ uchar dst[Easize]; uchar src[Easize]; uchar etype[2]; uchar type; uchar conn; uchar seq; uchar len; uchar data[0x100]; }Pkt; typedef struct{ QLock; Lock; uchar ea[Easize]; /* along with cno, the key to the connection */ uchar cno; /* connection number on remote host */ uchar stalled; /* cectimer needs to kick it -- cecputs while !islo() */ uchar state; /* connection state */ char retries; /* remaining retries */ long idle; /* last reply tick. */ int to; /* ticks to timeout */ Msgbuf *m; /* unacked message */ If *ifc; /* interface for this connection */ uchar sndseq; /* sequence number of last sent message */ uchar rcvseq; /* sequence number of last rcv'd message */ char cbuf[Ncbuf]; /* curcular buffer */ int r, w; /* indexes into cbuf */ int pwi; /* index into passwd; */ char passwd[32]; /* password typed by connection */ }Conn; enum { CMconfig = 1, CMpasswd, CMstat, CMtrace, Nconns = 15, }; static uchar broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static Conn conn[Nconns]; static int early = 1; static If iftab[MaxEther]; static char passwd[Namelen]; static int rsnd; static uchar tflag; static Rendez trendez; static int xmit; typedef struct { int index; char *name; int narg[2]; char *usage; } Cmdtab; static Cmdtab cmdtab[] = { CMconfig, "config", 1, 2, "cec config [macaddr]", CMpasswd, "password", 2, -1, "cec password [password]", CMstat, "stat", 1, -1, "cec stat", CMtrace, "trace", 1, -1, "cec trace", }; static char *types[] = { "Tinita", "Tinitb", "Tinitc", "Tdata", "Tack", "Tdiscover", "Toffer", "Treset", "*GOK*", }; /* * Since this code is in the output chain of procedures for console * output, we can't use the general printf functions. See the ones * at the bottom of this file. It assumes the serial port. */ static int cecprint(char *fmt, ...) { int n; va_list arg; char buf[PRINTSIZE]; va_start(arg, fmt); n = vseprint(buf, buf+sizeof buf, fmt, arg)-buf; va_end(arg); uartputs(buf); return n; } static void reply(If *i, Msgbuf *m) { if(i == 0){ print("reply i is nil %#p\n", getcallerpc(&i)); return; } send(i->reply, m); } static int cbget(Conn *cp) { int c; if(cp->r == cp->w) return -1; c = cp->cbuf[cp->r]; cp->r = (cp->r+1)&Ncmask; return c; } static void cbput(Conn *cp, int c) { if(cp->r == (cp->w+1)&Ncmask) return; cp->cbuf[cp->w] = c; cp->w = (cp->w+1)&Ncmask; } static void pkttrace(Pkt *p) { if(tflag == 0) return; cecprint("%E > %E) seq %d, type %s, len %d, conn %d\n", p->src, p->dst, p->seq, types[p->type], p->len, p->conn); } static void trace(Msgbuf *m) { pkttrace((Pkt*)m->data); } static Msgbuf* sethdr(If *ifc, uchar *ea, Pkt **pkt, int len) { Msgbuf *m; Pkt *p; len += 18; if(len < 60) len = 60; m = mballoc(len, 0, Mbcec); m->count = len; p = (Pkt*)m->data; memmove(p->dst, ea, Easize); memmove(p->src, ifc->ea, Easize); p->etype[0] = 0xbc; p->etype[1] = 0xbc; p->seq = 0; *pkt = p; return m; } static Msgbuf* mbclone(Msgbuf *a) { Msgbuf *m; m = mballoc(a->count, a->chan, a->category); memmove(m->data, a->data, a->count); return m; } static void pktsend(Conn *cp, Msgbuf *m) { if(cp->m != nil) panic("cecsend: cp->m not nil\n"); cp->m = mbclone(m); trace(m); reply(cp->ifc, m); cp->to = 4; cp->retries = 3; xmit++; } static void senddata(Conn *cp, void *data, int len) { Msgbuf *m; Pkt *p; m = sethdr(cp->ifc, cp->ea, &p, len); memmove(p->data, data, len); p->len = len; p->seq = ++cp->sndseq; p->conn = cp->cno; p->type = Tdata; pktsend(cp, m); } static void resend(Conn *cp) { Msgbuf *m; trace(m = mbclone(cp->m)); reply(cp->ifc, m); cp->to = 4; rsnd++; } static void ack(Conn *c) { if(c->m) mbfree(c->m); c->m = nil; c->to = 0; c->retries = 0; } static void start(Conn *cp) { char buf[250]; int n, c; if(cp->m) return; ilock(cp); for(n = 0; n < sizeof buf; n++){ if((c = cbget(cp)) == -1) break; buf[n] = c; } iunlock(cp); if(n != 0) senddata(cp, buf, n); } void cecputs(char *str, int n) { int i, c, w; Conn *cp; if(early || predawn) return; w = 0; for(cp = conn; cp < conn+Nconns; cp++){ ilock(cp); if(cp->state == Copen){ for (i = 0; i < n; i++){ c = str[i]; if(c == '\n') cbput(cp, '\r'); cbput(cp, c); } w = 1; cp->stalled = 1; } iunlock(cp); } if(w == 1) wakeup(&trendez); } static void conputs(Conn *c, char *s) { for(; *s; s++) cbput(c, *s); } static void cectimer(void) { Conn *c; for(;;){ tsleep(&trendez, no, 0, 250); for(c = conn; c < conn+Nconns; c++){ qlock(c); if(c->m != nil){ if(--c->to <= 0){ if(--c->retries <= 0){ mbfree(c->m); c->m = nil; // c->state = Cunused; }else resend(c); } }else if(c->stalled){ c->stalled = 0; start(c); } qunlock(c); } } } static void discover(If *ifc, Pkt *p) { uchar *a; Msgbuf *m; Pkt *q; if(p) a = p->src; else a = broadcast; m = sethdr(ifc, a, &q, 0); q->type = Toffer; q->len = snprint((char *)q->data, sizeof q->data, "%d %s", -1, service); trace(m); reply(ifc, m); } static Conn* findconn(uchar *ea, uchar cno) { Conn *c, *n; n = nil; for(c = conn; c < &conn[Nconns]; c++){ if(n == nil && c->state == Cunused) n = c; if(memcmp(ea, c->ea, Easize) == 0 && cno == c->cno) return c; } return n; } static void checkpw(Conn *cp, char *str, int len) { int i, c; if(passwd[0] == 0) return; for(i = 0; i < len; i++){ c = str[i]; if(c != '\n' && c != '\r'){ if(cp->pwi < sizeof cp->passwd-1) cp->passwd[cp->pwi++] = c; cbput(cp, '#'); cecprint("%c", c); continue; } // is newline; check password cp->passwd[cp->pwi] = 0; if(strcmp(cp->passwd, passwd) == 0){ cp->state = Copen; cp->pwi = 0; print("\r\n%E logged in\r\n", cp->ea); }else{ conputs(cp, "\r\nBad password\r\npassword: "); cp->pwi = 0; } } start(cp); } //struct{ // Rendez; // uchar buf[8192]; // power of 2. // ushort r; // ushort w; // ushort m; //} ibuf = { //.m = 8192-1, //}; //static void //cecinputs(uchar *s, int l) //{ // int c; // // for(; l != 0; l--){ // if((ibuf.w+1&ibuf.m) == ibuf.r) // // this should sleep but i'm afraid of hanging // // the ethernet since we only have one etheri // // process per port. // break; // if((c = *s++) == '\r') // c = '\n'; // ibuf.buf[ibuf.w++] = c; // kbdchar(c); // } //} //int //cecgetc(void) //{ // int c; // if(ibuf.r == ibuf.w) // return 0; // c = ibuf.buf[ibuf.r++]; // ibuf.r &= ibuf.m; // return c; //} static struct{ int c; long ticks; } ibuf; static void cecinputs(uchar *s, int l) { int c; for(; l != 0; l--){ if((c = *s++) == '\r') c = '\n'; kbdchar(c); ibuf.c = c; ibuf.ticks = Ticks; } } int cecgetc(void) { if(ibuf.c == 0) return 0; if(Ticks-ibuf.ticks > HZ/3) return 0; ibuf.ticks = 0; return ibuf.c; } static void incoming(Conn *cp, If *ifc, Pkt *p) { Pkt *np; Msgbuf *m; /* ack it no matter what its sequence number */ m = sethdr(ifc, p->src, &np, 0); np->type = Tack; np->seq = p->seq; np->conn = cp->cno; np->len = 0; trace(m); reply(ifc, m); if(cp->state == Cunused){ /* stale connection */ discover(ifc, p); return; } if(p->seq == cp->rcvseq) return; cp->rcvseq = p->seq; if(cp->state == Copen) cecinputs(p->data, p->len); else if(cp->state == Clogin) checkpw(cp, (char *)p->data, p->len); } static void inita(Conn *c, If *ifc, Pkt *p) { Pkt *q; Msgbuf *m; c->ifc = ifc; c->state = Cinitb; memmove(c->ea, p->src, Easize); c->cno = p->conn; m = sethdr(ifc, p->src, &q, 0); q->type = Tinitb; q->conn = c->cno; q->len = 0; pktsend(c, m); } static If* findif(Ifc *f) { int i; for(i = 0; i < nelem(iftab); i++) if(iftab[i].valid && memcmp(iftab[i].ea, f->ea, Easize) == 0) return iftab+i; return 0; } void cecreceive(Enpkt *ep, int, Ifc *i) { If *if0; Pkt *p; Conn *c; p = (Pkt*)ep; pkttrace(p); if((if0 = findif(i)) == nil) return; c = findconn(p->src, p->conn); if(c == nil){ cecprint("cec: out of connection structures\n"); return; } qlock(c); c->idle = Ticks; switch(p->type){ case Tinita: if(c->m){ cecprint("cec: reset with bp\n"); mbfree(c->m); c->m = 0; } inita(c, if0, p); break; case Tinitb: cecprint("cec: unexpected initb\n"); break; case Tinitc: if(c->state == Cinitb){ ack(c); if(c->passwd[0]){ c->state = Clogin; conputs(c, "password: "); start(c); }else c->state = Copen; } break; case Tdata: incoming(c, if0, p); break; case Tack: if(c->state == Clogin || c->state == Copen){ ack(c); start(c); } break; case Tdiscover: discover(if0, p); break; case Toffer: // cecprint("cec: unexpected offer\n"); from ourselves. break; case Treset: if(c->m) mbfree(c->m); c->m = 0; c->state = Cunused; break; default: cecprint("bad cec type: %d\n", p->type); break; } qunlock(c); } static char *cstate[] = { "unused", "initb", "login", "open" }; static int hexdigit(int c){ if(c >= '0' && c <= '9') return c-'0'; if(c >= 'A' && c <= 'Z') return c-'A'+10; if(c >= 'a' && c <= 'z') return c-'a'+10; return -1; } int eacvt(uchar *t, char *s){ int i, c; for(i = 0; c = s[i]; i++) if(hexdigit(c) == -1) break; if(i != 12) return -1; for(i = 0; i < Easize; i++) t[i] = hexdigit(s[2*i])<<4|hexdigit(s[2*i+1]); return 0; } static int eaqueue(uchar *ea) { int i; for(i = 0; i < nether; i++) if(memcmp(etherif[i].ea, ea, Easize) == 0) return i; return -1; } static int cecconfig0(int idx) { Ether *e; If *i; i = iftab+idx; e = etherif+idx; if(!e->ifc.reply) return -1; i->valid ^= 1; i->i = idx; memmove(i->ea, e->ea, Easize); i->reply = e->ifc.reply; if(i->valid) discover(i, 0); return 0; } static void cecconfig(char *eas) { uchar ea[Easize]; int i; if(strcmp(eas, "allow") == 0){ for(i = 0; i < nelem(iftab); i++) iftab[i].valid = 0; for(i = 0; i < nether; i++) cecconfig0(i); }else if(strcmp(eas, "disallow") == 0){ for(i = 0; i < nelem(iftab); i++) iftab[i].valid = 0; }else if(strlen(eas) < Easize*2){ if((i = strtoul(eas, &eas, 10)) >= nether || cecconfig0(i) == -1) print("bad interface index %d\n", i); }else if(eacvt(ea, eas) == 0){ if((i = eaqueue(ea)) != -1) cecconfig0(i); else print("no interface claims %E\n", ea); }else print("bad mac address\n"); } static Cmdtab * lookupcmd(char *name, int argc, Cmdtab *t, int n) { int i; for(i = 0; i < n; i++) if(strcmp(name, t[i].name) == 0) goto found; return 0; found: t = t+i; if(argc != t->narg[0] && argc != t->narg[1]){ print(t->usage); return 0; } return t; } static void cecusage(void) { print( "\t" "cec config [macaddr|allow|disallow]\n" "\t" "cec passwd [passwd]\n" "\t" "stat\n" "\t" "trace\n"); } static void ceccmd0(int argc, char **argv) { Cmdtab *t; If *i; Conn *c; int j; t = lookupcmd(*argv, argc, cmdtab, nelem(cmdtab)); if(t == 0){ cecusage(); return; } switch(t->index){ case CMconfig: for(j = 2; j < argc; j++) cecconfig(argv[j]); for(i = iftab; i < iftab+nelem(iftab); i++) if(i->valid) print("%d %E\n", i->i, i->ea); break; case CMpasswd: if(argc == 2) snprint(passwd, sizeof passwd, "%s", argv[1]); print("%s\n", passwd); break; case CMstat: for(c = conn; c < &conn[Nconns]; c++) if(c->state != Cunused) print("%d %E %3d %-6s %12ld %d %d\n", c->ifc->i, c->ea, c->cno, cstate[c->state], Ticks-c->idle, c->to, c->retries); break; case CMtrace: tflag ^= 1; print("tflag = %d\n", tflag); break; } } void ceccmd(int c, char **v) { if(c > 1) ceccmd0(c-1, v+1); else cecusage(); } void cecinit(void) { Ifc *e; cmd_install("cec", "subcommand -- cec control", ceccmd); for(e = enets; e; e = e->next) if(e->flag&Fcec) cecconfig0(e->idx); userinit(cectimer, nil, "cec"); early = 0; cmd_exec("cec config"); }