From 1216123949d23d5cd0921e2bfe162b52bea875df Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Wed, 24 Apr 1991 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1991-04-24 --- port/arp.h | 42 ++ port/devarp.c | 233 ++++++++++ port/devip.c | 882 ++++++++++++++++++++++++++++++++++++ port/ipdat.h | 440 ++++++++++++++++++ port/lib.h | 2 +- port/stip.c | 631 ++++++++++++++++++++++++++ port/tcpif.c | 84 ++++ port/tcpinput.c | 1126 ++++++++++++++++++++++++++++++++++++++++++++++ port/tcpoutput.c | 283 ++++++++++++ port/tcptimer.c | 129 ++++++ power/conf.h | 4 + power/dat.h | 5 + power/errno.h | 9 + power/fns.h | 3 + power/ipdat.h | 440 ++++++++++++++++++ power/main.c | 4 + 16 files changed, 4316 insertions(+), 1 deletion(-) create mode 100644 port/arp.h create mode 100644 port/devarp.c create mode 100644 port/devip.c create mode 100644 port/ipdat.h create mode 100644 port/stip.c create mode 100644 port/tcpif.c create mode 100644 port/tcpinput.c create mode 100644 port/tcpoutput.c create mode 100644 port/tcptimer.c create mode 100644 power/ipdat.h diff --git a/port/arp.h b/port/arp.h new file mode 100644 index 0000000000000000000000000000000000000000..80570b311bba2744e9a2d45bf8c692ad0209ebff --- /dev/null +++ b/port/arp.h @@ -0,0 +1,42 @@ +typedef struct Arppkt Arppkt; +typedef struct Arpentry Arpentry; +typedef struct Arpstats Arpstats; + +/* Format of ethernet arp request */ +struct Arppkt { + uchar d[6]; + uchar s[6]; + uchar type[2]; + uchar hrd[2]; + uchar pro[2]; + uchar hln; + uchar pln; + uchar op[2]; + uchar sha[6]; + uchar spa[4]; + uchar tha[6]; + uchar tpa[4]; + }; + +#define ARPSIZE 42 + +/* Format of request from starp to user level arpd */ +struct Arpentry { + uchar etaddr[6]; + uchar ipaddr[4]; + }; + +/* Arp cache statistics */ +struct Arpstats { + int hit; + int miss; + int failed; + }; + +#define ET_ARP 0x0806 +#define ET_RARP 0x8035 + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 +#define RARP_REQUEST 3 +#define RARP_REPLY 4 diff --git a/port/devarp.c b/port/devarp.c new file mode 100644 index 0000000000000000000000000000000000000000..e026aae838bb1de0d61bc8ee92f082460fe5fcc8 --- /dev/null +++ b/port/devarp.c @@ -0,0 +1,233 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +#include "devtab.h" + +Arpcache *arp; +Arpcache **arphash; +Arpstats arpstats; +Queue *Servq; + +#define ARP_ENTRYLEN 50 +char *padstr = " "; + + +extern Arpcache *arplruhead; +extern Arpcache *arplrutail; + +enum{ + arpdirqid, + arpstatqid, + arpctlqid, + arpdataqid, +}; + +Dirtab arptab[]={ + "stats", {arpstatqid}, 0, 0600, + "ctl", {arpctlqid}, 0, 0600, + "data", {arpdataqid}, 0, 0600, +}; +#define Narptab (sizeof(arptab)/sizeof(Dirtab)) + +void +arpreset(void) +{ + Arpcache *ap, *ep; + + arp = (Arpcache *)ialloc(sizeof(Arpcache) * conf.arp, 0); + arphash = (Arpcache **)ialloc(sizeof(Arpcache *) * Arphashsize, 0); + + ep = &arp[conf.arp]; + for(ap = arp; ap < ep; ap++) { + ap->frwd = ap+1; + ap->prev = ap-1; + ap->type = ARP_FREE; + ap->status = ARP_TEMP; + } + + arp[0].prev = 0; + arplruhead = arp; + ap = &arp[conf.arp-1]; + ap->frwd = 0; + arplrutail = ap; +} + +void +arpinit(void) +{ +} + +Chan * +arpattach(char *spec) +{ + return devattach('a', spec); +} + +Chan * +arpclone(Chan *c, Chan *nc) +{ + return devclone(c, nc); +} + +int +arpwalk(Chan *c, char *name) +{ + return devwalk(c, name, arptab, (long)Narptab, devgen); +} + +Chan* +arpclwalk(Chan *c, char *name) +{ + return devclwalk(c, name); +} + +void +arpstat(Chan *c, char *db) +{ + devstat(c, db, arptab, (long)Narptab, devgen); +} + +Chan * +arpopen(Chan *c, int omode) +{ + + if(c->qid.path == CHDIR){ + if(omode != OREAD) + error(Eperm); + } + + switch(STREAMTYPE(c->qid.path)) { + case arpdataqid: + break; + case arpstatqid: + if(omode != OREAD) + error(Ebadarg); + break; + case arpctlqid: + break; + } + + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +arpcreate(Chan *c, char *name, int omode, ulong perm) +{ + error(Eperm); +} + +void +arpremove(Chan *c) +{ + error(Eperm); +} + +void +arpwstat(Chan *c, char *dp) +{ + error(Eperm); +} + +void +arpclose(Chan *c) +{ + streamclose(c); +} + +long +arpread(Chan *c, void *a, long n, ulong offset) +{ + char buf[100]; + Arpcache *ap, *ep; + int part, bytes, size; + char *ptr, *ststr; + + switch((int)(c->qid.path&~CHDIR)){ + case arpdirqid: + return devdirread(c, a, n, arptab, Narptab, devgen); + case arpdataqid: + bytes = c->offset; + while(bytes < conf.arp*ARP_ENTRYLEN && n) { + ap = &arp[bytes/ARP_ENTRYLEN]; + part = bytes%ARP_ENTRYLEN; + + if(ap->status != ARP_OK) + ststr = "invalid"; + else + ststr = (ap->type == ARP_TEMP ? "temp" : "perm"); + + sprint(buf,"%d.%d.%d.%d to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s%s", + ap->eip[0], ap->eip[1], ap->eip[2], ap->eip[3], + ap->et[0], ap->et[1], ap->et[2], ap->et[3], + ap->et[4], ap->et[5], + ststr, padstr); + + buf[ARP_ENTRYLEN-1] = '\n'; + + size = ARP_ENTRYLEN - part; + size = MIN(n, size); + memmove(a, buf+part, size); + + a = (void *)((int)a + size); + n -= size; + bytes += size; + } + return bytes - c->offset; + break; + case arpstatqid: + sprint(buf, "hits: %d miss: %d failed: %d\n", + arpstats.hit, arpstats.miss, arpstats.failed); + + return stringread(c, a, n, buf, offset); + default: + n=0; + break; + } + return n; +} + +long +arpwrite(Chan *c, char *a, long n, ulong offset) +{ + Arpentry entry; + char buf[20], *field[5]; + int m; + + switch(STREAMTYPE(c->qid.path)) { + case arpctlqid: + + strncpy(buf, a, sizeof buf); + m = getfields(buf, field, 5, ' '); + + if(strncmp(field[0], "flush", 5) == 0) + arp_flush(); + else if(strcmp(field[0], "delete") == 0) { + if(m != 2) + error(Ebadarg); + + if(arp_delete(field[1]) < 0) + error(Eaddrnotfound); + } + case arpdataqid: + if(n != sizeof(Arpentry)) + error(Emsgsize); + memmove(&entry, a, sizeof(Arpentry)); + arp_enter(&entry, ARP_TEMP); + break; + default: + error(Ebadusefd); + } + + return n; +} + diff --git a/port/devip.c b/port/devip.c new file mode 100644 index 0000000000000000000000000000000000000000..da85e5899dd698cdf162553bed9b549ec904cb6b --- /dev/null +++ b/port/devip.c @@ -0,0 +1,882 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +#include "devtab.h" + +enum{ + Nrprotocol = 2, /* Number of protocols supported by this driver */ + Nipsubdir = 4, /* Number of subdirectory entries per connection */ +}; + +int udpsum = 1; + +Queue *Tcpoutput; /* Tcp to lance output channel */ +Ipifc *ipifc; /* IP protocol interfaces for stip */ +Ipconv *ipconv[Nrprotocol]; /* Connections for each protocol */ +Dirtab *ipdir[Nrprotocol]; /* Connection directory structures */ +QLock ipalloc; /* Protocol port allocation lock */ + +/* ARPA User Datagram Protocol */ +void udpstiput(Queue *, Block *); +void udpstoput(Queue *, Block *); +void udpstopen(Queue *, Stream *); +void udpstclose(Queue *); +/* ARPA Transmission Control Protocol */ +void tcpstiput(Queue *, Block *); +void tcpstoput(Queue *, Block *); +void tcpstopen(Queue *, Stream *); +void tcpstclose(Queue *); + +Qinfo tcpinfo = { tcpstiput, tcpstoput, tcpstopen, tcpstclose, "tcp" }; +Qinfo udpinfo = { udpstiput, udpstoput, udpstopen, udpstclose, "udp" }; + +Qinfo *protocols[] = { &tcpinfo, &udpinfo, 0 }; + +enum{ + ipdirqid, + iplistenqid, + iplportqid, + iprportqid, + ipstatusqid, + ipchanqid, + + ipcloneqid +}; + +Dirtab ipsubdir[]={ + "listen", {iplistenqid}, 0, 0600, + "local", {iplportqid}, 0, 0600, + "remote", {iprportqid}, 0, 0600, + "status", {ipstatusqid}, 0, 0600, +}; + +void +ipreset(void) +{ + int i; + + ipifc = (Ipifc *)ialloc(sizeof(Ipifc) * conf.ip, 0); + + for(i = 0; i < Nrprotocol; i++) { + ipconv[i] = (Ipconv *)ialloc(sizeof(Ipconv) * conf.ip, 0); + ipdir[i] = (Dirtab *)ialloc(sizeof(Dirtab) * (conf.ip+1), 0); + ipmkdir(protocols[i], ipdir[i], ipconv[i]); + newqinfo(protocols[i]); + } + + initfrag(conf.frag); +} + +void +ipmkdir(Qinfo *stproto, Dirtab *dir, Ipconv *cp) +{ + Dirtab *etab; + int i; + + etab = &dir[conf.ip]; + for(i = 0; dir < etab; i++, cp++, dir++) { + cp->stproto = stproto; + sprint(dir->name, "%d", i); + dir->qid.path = CHDIR|STREAMQID(i, ipchanqid); + dir->qid.vers = 0; + dir->length = 0; + dir->perm = 0600; + } + + /* Make the clone */ + strcpy(dir->name, "clone"); + dir->qid.path = ipcloneqid; + dir->qid.vers = 0; + dir->length = 0; + dir->perm = 0600; +} + +void +ipinit(void) +{ +} + +Chan * +ipattach(char *spec) +{ + Chan *c; + int i; + + for(i = 0; protocols[i]; i++) { + if(strcmp(spec, protocols[i]->name) == 0) { + c = devattach('I', spec); + c->dev = i; + + return (c); + } + } + + error(Enoproto); +} + +Chan * +ipclone(Chan *c, Chan *nc) +{ + return devclone(c, nc); +} + +int +ipwalk(Chan *c, char *name) +{ + if(c->qid.path == CHDIR) + return devwalk(c, name, ipdir[c->dev], conf.ip+1, devgen); + else + return devwalk(c, name, ipsubdir, Nipsubdir, streamgen); +} + +Chan* +ipclwalk(Chan *c, char *name) +{ + return devclwalk(c, name); +} + +void +ipstat(Chan *c, char *db) +{ + if(c->qid.path == CHDIR) + devstat(c, db, ipdir[c->dev], conf.ip+1, devgen); + else if(c->qid.path == ipcloneqid) + devstat(c, db, &ipdir[c->dev][conf.ip], 1, devgen); + else + devstat(c, db, ipsubdir, Nipsubdir, streamgen); +} + +Chan * +ipopen(Chan *c, int omode) +{ + Ipconv *cp; + + cp = &ipconv[c->dev][STREAMID(c->qid.path)]; + if(c->qid.path & CHDIR) { + if(omode != OREAD) + error(Eperm); + } + else switch(STREAMTYPE(c->qid.path)) { + case ipcloneqid: + ipclonecon(c); + break; + case iplportqid: + case iprportqid: + case ipstatusqid: + if(omode != OREAD) + error(Ebadarg); + break; + case iplistenqid: + if(cp->stproto != &tcpinfo) + error(Eprotonosup); + + if(cp->backlog == 0) + cp->backlog = 1; + + streamopen(c, &ipinfo); + if(c->stream->devq->next->info != cp->stproto) + pushq(c->stream, cp->stproto); + + if(cp->stproto == &tcpinfo) + open_tcp(cp, TCP_PASSIVE, Streamhi, 0); + + iplisten(c, cp, ipconv[c->dev]); + break; + case Sdataqid: + streamopen(c, &ipinfo); + if(c->stream->devq->next->info != cp->stproto) + pushq(c->stream, cp->stproto); + + if(cp->stproto == &tcpinfo) + open_tcp(cp, TCP_ACTIVE, Streamhi, 0); + break; + case Sctlqid: + streamopen(c, &ipinfo); + if(c->stream->devq->next->info != cp->stproto) + pushq(c->stream, cp->stproto); + break; + } + + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +Ipconv * +ipclonecon(Chan *c) +{ + Ipconv *base, *new, *etab; + + base = ipconv[c->dev]; + etab = &base[conf.ip]; + for(new = base; new < etab; new++) { + if(new->ref == 0 && canqlock(new)) { + if(new->ref || + (new->stproto == &tcpinfo && + new->tcpctl.state != CLOSED)) { + qunlock(new); + continue; + } + new->ref++; + c->qid.path = CHDIR|STREAMQID(new-base, ipchanqid); + devwalk(c, "ctl", 0, 0, streamgen); + qunlock(new); + + streamopen(c, &ipinfo); + pushq(c->stream, new->stproto); + new->ref--; + return new; + } + } + + error(Enodev); +} + +void +ipcreate(Chan *c, char *name, int omode, ulong perm) +{ + error(Eperm); +} + +void +ipremove(Chan *c) +{ + error(Eperm); +} + +void +ipwstat(Chan *c, char *dp) +{ + error(Eperm); +} + +void +ipclose(Chan *c) +{ + if(c->qid.path != CHDIR) + streamclose(c); +} + +long +ipread(Chan *c, void *a, long n, ulong offset) +{ + int t, connection; + Ipconv *cp; + char buf[WORKBUF]; + + t = STREAMTYPE(c->qid.path); + if(t >= Slowqid) + return streamread(c, a, n); + + if(c->qid.path == CHDIR) + return devdirread(c, a, n, ipdir[c->dev], conf.ip+1, devgen); + if(c->qid.path & CHDIR) + return devdirread(c, a, n, ipsubdir, Nipsubdir, streamgen); + + connection = STREAMID(c->qid.path); + cp = &ipconv[c->dev][connection]; + + switch(t) { + case iprportqid: + sprint(buf, "%d.%d.%d.%d %d\n", fmtaddr(cp->dst), cp->pdst); + return stringread(c, a, n, buf, offset); + case iplportqid: + sprint(buf, "%d.%d.%d.%d %d\n", fmtaddr(Myip), cp->psrc); + return stringread(c, a, n, buf, offset); + case ipstatusqid: + if(cp->stproto == &tcpinfo) { + sprint(buf, "tcp/%d %d %s %s\n", connection, + cp->ref, tcpstate[cp->tcpctl.state], + cp->tcpctl.flags & CLONE ? "listen" : "connect"); + } + else + sprint(buf, "%s/%d %d\n", cp->stproto->name, + connection, cp->ref); + + return stringread(c, a, n, buf, offset); + } + + return Eperm; +} + +long +ipwrite(Chan *c, char *a, long n, ulong offset) +{ + int m, backlog, type; + char *field[5], buf[256]; + Ipconv *cp; + Port port, base; + + type = STREAMTYPE(c->qid.path); + if (type == Sdataqid) + return streamwrite(c, a, n, 0); + + if (type == Sctlqid) { + cp = &ipconv[c->dev][STREAMID(c->qid.path)]; + if(cp->stproto == &tcpinfo && cp->tcpctl.state != CLOSED) + return Edevbusy; + + strncpy(buf, a, sizeof buf); + m = getfields(buf, field, 5, ' '); + + if(strcmp(field[0], "connect") == 0) { + if(m != 2) + return Ebadarg; + + switch(getfields(field[1], field, 5, '!')) { + default: + return Ebadarg; + case 2: + base = PORTALLOC; + break; + case 3: + if(strcmp(field[2], "r") != 0) + return Eperm; + base = PRIVPORTALLOC; + break; + } + cp->dst = ipparse(field[0]); + cp->pdst = atoi(field[1]); + + /* If we have no local port assign one */ + qlock(&ipalloc); + if(cp->psrc == 0) + cp->psrc = nextport(ipconv[c->dev], base); + qunlock(&ipalloc); + + } + else if(strcmp(field[0], "announce") == 0 || + strcmp(field[0], "reserve") == 0) { + if(m != 2) + return Ebadarg; + port = atoi(field[1]); + + qlock(&ipalloc); + if(portused(ipconv[c->dev], port)) { + qunlock(&ipalloc); + return Einuse; + } + cp->psrc = port; + cp->ptype = *field[0]; + qunlock(&ipalloc); + } + else if(strcmp(field[0], "backlog") == 0) { + if(m != 2) + return Ebadarg; + backlog = atoi(field[1]); + if(backlog == 0) + return Ebadarg; + if(backlog > 5) + backlog = 5; + cp->backlog = backlog; + } + else + return streamwrite(c, a, n, 0); + + return n; + } + + return Eperm; +} + + +void +udpstiput(Queue *q, Block *bp) +{ + if(bp->type == M_CTL) + PUTNEXT(q, bp); + else + panic("udpstiput: Why am I here"); +} + +/* + * udprcvmsg - called by stip to multiplex udp ports onto conversations + */ +void +udprcvmsg(Ipconv *muxed, Block *bp) +{ + Ipconv *ifc, *etab; + Udphdr *uh; + Port dport; + ushort sum, len; + Ipaddr addr; + + uh = (Udphdr *)(bp->rptr); + + /* Put back pseudo header for checksum */ + uh->Unused = 0; + len = nhgets(uh->udplen); + hnputs(uh->udpplen, len); + + addr = nhgetl(uh->udpsrc); + + if(udpsum && nhgets(uh->udpcksum)) { + if(sum = ptcl_csum(bp, UDP_EHSIZE, len+UDP_PHDRSIZE)) { + print("udp: checksum error %x (%d.%d.%d.%d)\n", + sum, fmtaddr(addr)); + + freeb(bp); + return; + } + } + + dport = nhgets(uh->udpdport); + + /* Look for a conversation structure for this port */ + etab = &muxed[conf.ip]; + for(ifc = muxed; ifc < etab; ifc++) { + if(ifc->psrc == dport && ifc->ref) { + /* Trim the packet down to data size */ + len = len - (UDP_HDRSIZE-UDP_PHDRSIZE); + bp = btrim(bp, UDP_EHSIZE+UDP_HDRSIZE, len); + if(bp == 0) + return; + + /* Stuff the src address into the remote file */ + ifc->dst = addr; + ifc->pdst = nhgets(uh->udpsport); + PUTNEXT(ifc->readq, bp); + return; + } + } + + freeb(bp); +} + +void +udpstoput(Queue *q, Block *bp) +{ + Ipconv *ipc; + Udphdr *uh; + int dlen, ptcllen, newlen; + + /* Prepend udp header to packet and pass on to ip layer */ + ipc = (Ipconv *)(q->ptr); + if(ipc->psrc == 0) + error(Enoport); + + if(bp->type != M_DATA) { + freeb(bp); + error(Ebadctl); + } + + /* Only allow atomic udp writes to form datagrams */ + if(!(bp->flags & S_DELIM)) { + freeb(bp); + error(Emsgsize); + } + + /* Round packet up to even number of bytes and check we can + * send it + */ + dlen = blen(bp); + if(dlen > UDP_DATMAX) { + freeb(bp); + error(Emsgsize); + } + newlen = bround(bp, 1); + + /* Make space to fit udp & ip & ethernet header */ + bp = padb(bp, UDP_EHSIZE + UDP_HDRSIZE); + + uh = (Udphdr *)(bp->rptr); + + ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE); + uh->Unused = 0; + uh->udpproto = IP_UDPPROTO; + hnputs(uh->udpplen, ptcllen); + hnputl(uh->udpdst, ipc->dst); + hnputl(uh->udpsrc, Myip); + hnputs(uh->udpsport, ipc->psrc); + hnputs(uh->udpdport, ipc->pdst); + hnputs(uh->udplen, ptcllen); + uh->udpcksum[0] = 0; + uh->udpcksum[1] = 0; + + hnputs(uh->udpcksum, ptcl_csum(bp, UDP_EHSIZE, newlen+UDP_HDRSIZE)); + PUTNEXT(q, bp); +} + +void +udpstclose(Queue *q) +{ + Ipconv *ipc; + + ipc = (Ipconv *)(q->ptr); + + /* If the port was bound rather than reserved, clear the allocation */ + qlock(ipc); + if(--ipc->ref == 0 && ipc->ptype == 'b') + ipc->psrc = 0; + qunlock(ipc); + + closeipifc(ipc->ipinterface); +} + +void +udpstopen(Queue *q, Stream *s) +{ + Ipconv *ipc; + + ipc = &ipconv[s->dev][s->id]; + ipc->ipinterface = newipifc(IP_UDPPROTO, udprcvmsg, ipconv[s->dev], + 1500, 512, ETHER_HDR, "UDP"); + + qlock(ipc); + ipc->ref++; + qunlock(ipc); + ipc->readq = RD(q); + RD(q)->ptr = (void *)ipc; + WR(q)->next->ptr = (void *)ipc->ipinterface; + WR(q)->ptr = (void *)ipc; +} + +void +tcpstiput(Queue *q, Block *bp) +{ + if(bp->type == M_CTL) + PUTNEXT(q, bp); + else + panic("tcpstiput: Why am I here"); +} + +void +tcpstoput(Queue *q, Block *bp) +{ + Ipconv *s; + Tcpctl *tcb; + int len, errnum, oob = 0; + + s = (Ipconv *)(q->ptr); + len = blen(bp); + tcb = &s->tcpctl; + + if(s->psrc == 0) + error(Enoport); + + /* Report asynchronous errors */ + if(s->err) + error(s->err); + + switch(tcb->state) { + case LISTEN: + tcb->flags |= ACTIVE; + send_syn(tcb); + setstate(s, SYN_SENT); + /* No break */ + case SYN_SENT: + case SYN_RECEIVED: + case ESTABLISHED: + case CLOSE_WAIT: + qlock(tcb); + if(oob == 0) { + appendb(&tcb->sndq, bp); + tcb->sndcnt += len; + } + else { + if(tcb->snd.up == tcb->snd.una) + tcb->snd.up = tcb->snd.ptr; + appendb(&tcb->sndoobq, bp); + tcb->sndoobcnt += len; + } + + tcprcvwin(s); + tcp_output(s); + qunlock(tcb); + break; + default: + freeb(bp); + error(Ehungup); + } +} + +void +tcpstopen(Queue *q, Stream *s) +{ + Ipconv *ipc; + + /* Start tcp service processes */ + if(!Tcpoutput) { + Tcpoutput = WR(q); + kproc("tcpack", tcpackproc, 0); + kproc("tcpflow", tcpflow, &ipconv[s->dev]); + } + + ipc = &ipconv[s->dev][s->id]; + ipc->ipinterface = newipifc(IP_TCPPROTO, tcp_input, ipconv[s->dev], + 1500, 512, ETHER_HDR, "TCP"); + + qlock(ipc); + ipc->ref++; + qunlock(ipc); + + ipc->readq = RD(q); + ipc->readq->rp = &tcpflowr; + + RD(q)->ptr = (void *)ipc; + WR(q)->next->ptr = (void *)ipc->ipinterface; + WR(q)->ptr = (void *)ipc; +} + +int +tcp_havecon(Ipconv *s) +{ + return s->curlog; +} + +void +iplisten(Chan *c, Ipconv *s, Ipconv *base) +{ + Ipconv *etab, *new; + + qlock(&s->listenq); + + for(;;) { + sleep(&s->listenr, tcp_havecon, s); + + /* Search for the new connection, clone the control channel and + * return an open channel to the listener + */ + for(new = base, etab = &base[conf.ip]; new < etab; new++) { + if(new->psrc == s->psrc && new->pdst != 0 && + new->dst && (new->tcpctl.flags & CLONE) == 0) { + new->ref++; + + /* Remove the listen channel reference */ + streamclose(c); + + s->curlog--; + /* Attach the control channel to the new connection */ + c->qid.path = CHDIR|STREAMQID(new-base, ipchanqid); + devwalk(c, "ctl", 0, 0, streamgen); + streamopen(c, &ipinfo); + pushq(c->stream, new->stproto); + new->ref--; + + qunlock(&s->listenq); + return; + } + } + } +} + +void +tcpstclose(Queue *q) +{ + Ipconv *s; + Tcpctl *tcb; + + s = (Ipconv *)(q->ptr); + tcb = &s->tcpctl; + + qlock(s); + s->ref--; + qunlock(s); + + /* Not interested in data anymore */ + s->readq = 0; + + switch(tcb->state){ + case CLOSED: + case LISTEN: + case SYN_SENT: + close_self(s, 0); + break; + case SYN_RECEIVED: + case ESTABLISHED: + tcb->sndcnt++; + tcb->snd.nxt++; + setstate(s, FINWAIT1); + goto output; + case CLOSE_WAIT: + tcb->sndcnt++; + tcb->snd.nxt++; + setstate(s, LAST_ACK); + output: + qlock(tcb); + tcp_output(s); + qunlock(tcb); + break; + } +} + +/* + * Network byte order functions + */ + +void +hnputs(uchar *ptr, ushort val) +{ + ptr[0] = val>>8; + ptr[1] = val; +} + +void +hnputl(uchar *ptr, ulong val) +{ + ptr[0] = val>>24; + ptr[1] = val>>16; + ptr[2] = val>>8; + ptr[3] = val; +} + +ulong +nhgetl(uchar *ptr) +{ + return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]); +} + +ushort +nhgets(uchar *ptr) +{ + return ((ptr[0]<<8) | ptr[1]); +} + +/* + * ptcl_csum - protcol cecksum routine + */ +ushort +ptcl_csum(Block *bp, int offset, int len) +{ + uchar *addr; + ulong losum = 0, hisum = 0; + ushort csum; + int odd, blen, x; + + /* Correct to front of data area */ + while(bp && offset && offset >= BLEN(bp)) { + offset -= BLEN(bp); + bp = bp->next; + } + if(bp == 0) + return 0; + + addr = bp->rptr + offset; + blen = BLEN(bp) - offset; + odd = 0; + while(len) { + if(odd) { + losum += *addr++; + blen--; + len--; + odd = 0; + } + for(x = MIN(len, blen); x > 1; x -= 2) { + hisum += addr[0]; + losum += addr[1]; + len -= 2; + blen -= 2; + addr += 2; + } + if(blen && x) { + odd = 1; + hisum += addr[0]; + len--; + } + bp = bp->next; + if(bp == 0) + break; + blen = BLEN(bp); + addr = bp->rptr; + + } + + losum += hisum>>8; + losum += (hisum&0xff)<<8; + while((csum = losum>>16) != 0) + losum = csum + (losum & 0xffff); + + losum &= 0xffff; + + return ~losum & 0xffff; +} + +Block * +btrim(Block *bp, int offset, int len) +{ + Block *nb, *startb; + ulong l; + + if(blen(bp) < offset+len) { + freeb(bp); + return 0; + } + + while((l = BLEN(bp)) < offset) { + offset -= l; + nb = bp->next; + bp->next = 0; + freeb(bp); + bp = nb; + } + + startb = bp; + bp->rptr += offset; + + while((l = BLEN(bp)) < len) { + len -= l; + bp = bp->next; + } + + bp->wptr -= (BLEN(bp) - len); + bp->flags |= S_DELIM; + + if(bp->next) { + freeb(bp->next); + bp->next = 0; + } + + return(startb); +} + +Ipconv * +portused(Ipconv *ic, Port port) +{ + Ipconv *ifc, *etab; + + etab = &ic[conf.ip]; + for(ifc = ic; ifc < etab; ifc++) { + if(ifc->psrc == port) + return ifc; + } + + return 0; +} + +Port +nextport(Ipconv *ic, Port base) +{ + Port i; + + for(i = base; i < PORTMAX; i++) { + if(!portused(ic, i)) + return(i); + } + return(0); +} + +/* NEEDS HASHING ! */ + +Ipconv * +ip_conn(Ipconv *ic, Port dst, Port src, Ipaddr dest, char proto) +{ + Ipconv *s, *etab; + + /* Look for a conversation structure for this port */ + etab = &ic[conf.ip]; + for(s = ic; s < etab; s++) { + if(s->psrc == dst && s->pdst == src && + (s->dst == dest || dest == 0)) + return s; + } + + return 0; +} + diff --git a/port/ipdat.h b/port/ipdat.h new file mode 100644 index 0000000000000000000000000000000000000000..37ecd18825c57dd84a4f614cf8d5555749d0a027 --- /dev/null +++ b/port/ipdat.h @@ -0,0 +1,440 @@ +typedef struct Ipconv Ipconv; +typedef struct Ipifc Ipifc; +typedef struct Fragq Fragq; +typedef struct Ipfrag Ipfrag; +typedef ulong Ipaddr; +typedef struct Arpcache Arpcache; +typedef ushort Port; +typedef struct Udphdr Udphdr; +typedef struct Etherhdr Etherhdr; +typedef struct Reseq Reseq; +typedef struct Tcp Tcp; +typedef struct Tcpctl Tcpctl; +typedef struct Tcphdr Tcphdr; +typedef struct Timer Timer; + +struct Etherhdr { +#define ETHER_HDR 14 + uchar d[6]; + uchar s[6]; + uchar type[2]; + + /* Now we have the ip fields */ +#define ETHER_IPHDR 20 + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ +}; + +/* Ethernet packet types */ +#define ET_IP 0x0800 + +/* A userlevel data gram */ +struct Udphdr { +#define UDP_EHSIZE 22 + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* Udp pseudo ip really starts here */ +#define UDP_PHDRSIZE 12 +#define UDP_HDRSIZE 20 + uchar Unused; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +#define TCP_PKT (TCP_EHSIZE+TCP_IPLEN+TCP_PHDRSIZE) + +struct Tcphdr { +#define TCP_EHSIZE 14 + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ +#define TCP_IPLEN 8 + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + +#define TCP_PHDRSIZE 12 + uchar Unused; + uchar proto; + uchar tcplen[2]; + uchar tcpsrc[4]; + uchar tcpdst[4]; + +#define TCP_HDRSIZE 20 + uchar tcpsport[2]; + uchar tcpdport[2]; + uchar tcpseq[4]; + uchar tcpack[4]; + uchar tcpflag[2]; + uchar tcpwin[2]; + uchar tcpcksum[2]; + uchar tcpurg[2]; + + /* Options segment */ + uchar tcpopt[2]; + uchar tcpmss[2]; + }; + + + +struct Timer { + Timer *next; + Timer *prev; + int state; + int start; + int count; + void (*func)(void*); + void *arg; + }; + +struct Tcpctl { + QLock; + uchar state; /* Connection state */ + uchar type; /* Listening or active connection */ + uchar code; /* Icmp code */ + struct { + int una; /* Unacked data pointer */ + int nxt; /* Next sequence expected */ + int ptr; /* Data pointer */ + ushort wnd; /* Tcp send window */ + int up; /* Urgent data pointer */ + int wl1; + int wl2; + } snd; + int iss; + ushort cwind; + ushort ssthresh; + int resent; + struct { + int nxt; + ushort wnd; + int up; + } rcv; + int irs; + ushort mss; + int rerecv; + ushort window; + int max_snd; + int last_ack; + char backoff; + char flags; + char tos; + + Block *rcvq; + ushort rcvcnt; + + Block *rcvoobq; + ushort rcvoobcnt; + + Block *sndq; /* List of data going out */ + ushort sndcnt; /* Amount of data in send queue */ + + Block *sndoobq; /* List of blocks going oob */ + ushort sndoobcnt; /* Size of out of band queue */ + ushort oobmark; /* Out of band sequence mark */ + char oobflags; /* Out of band data flags */ + + Reseq *reseq; /* Resequencing queue */ + Timer timer; + Timer acktimer; /* Acknoledge timer */ + Timer rtt_timer; /* Round trip timer */ + int rttseq; /* Round trip sequence */ + int srtt; /* Shortened round trip */ + int mdev; /* Mean deviation of round trip */ +}; + +struct Tcp { + Port source; + Port dest; + int seq; + int ack; + char flags; + ushort wnd; + ushort up; + ushort mss; + }; + +struct Reseq { + Reseq *next; + Tcp seg; + Block *bp; + ushort length; + char tos; + }; + +/* An ip interface used for UDP/TCP/ARP/ICMP */ +struct Ipconv { + QLock; /* Ref count lock */ + int ref; + Qinfo *stproto; /* Stream protocol for this device */ + Ipaddr dst; /* Destination from connect */ + + Port psrc; /* Source port */ + Port pdst; /* Destination port */ + + uchar ptype; /* Source port type */ + Ipifc *ipinterface; /* Ip protocol interface */ + Queue *readq; /* Pointer to upstream read q */ + + QLock listenq; /* List of people waiting incoming cons */ + Rendez listenr; /* Some where to sleep while waiting */ + Ipconv *listen; + + char err; /* Async protocol error */ + int backlog; /* Maximum number of waiting connections */ + int curlog; /* Number of waiting connections */ + int contype; + Tcpctl tcpctl; /* Tcp control block */ +}; + +#define MAX_TIME 100000000 /* Forever */ +#define TCP_ACK 200 /* Timed ack sequence every 200ms */ + +#define URG 0x20 +#define ACK 0x10 +#define PSH 0x08 +#define RST 0x04 +#define SYN 0x02 +#define FIN 0x01 + +#define EOL_KIND 0 +#define NOOP_KIND 1 +#define MSS_KIND 2 + +#define MSS_LENGTH 4 +#define MSL2 10 +#define MSPTICK 200 +#define DEF_MSS 1024 +#define DEF_RTT 1000 +#define TCPOOB_HADDATA 1 +#define TCPOOB_HAVEDATA 2 + +#define TCP_PASSIVE 0 +#define TCP_ACTIVE 1 + +#define MAXBACKOFF 5 +#define FORCE 1 +#define CLONE 2 +#define RETRAN 4 +#define ACTIVE 8 +#define SYNACK 16 +#define AGAIN 8 +#define DGAIN 4 + +#define TIMER_STOP 0 +#define TIMER_RUN 1 +#define TIMER_EXPIRE 2 + +#define set_timer(t,x) (((t)->start) = (x)/MSPTICK) +#define dur_timer(t) ((t)->start) +#define read_timer(t) ((t)->count) +#define run_timer(t) ((t)->state == TIMER_RUN) + +enum { + CLOSED = 0, + LISTEN, + SYN_SENT, + SYN_RECEIVED, + ESTABLISHED, + FINWAIT1, + FINWAIT2, + CLOSE_WAIT, + CLOSING, + LAST_ACK, + TIME_WAIT + }; + +/* + * Ip interface structure. We have one for each active protocol driver + */ +struct Ipifc { + QLock; + int ref; + uchar protocol; /* Ip header protocol number */ + char name[NAMELEN]; /* Protocol name */ + void (*iprcv) (Ipconv *, Block *); /* Receive demultiplexor */ + Ipconv *connections; /* Connection list */ + int maxmtu; /* Maximum transfer unit */ + int minmtu; /* Minumum tranfer unit */ + int hsize; /* Media header size */ + Lock; +}; + +struct Fragq { + QLock; + Block *blist; + Fragq *next; + Ipaddr src; + Ipaddr dst; + ushort id; + }; + +struct Ipfrag { + ushort foff; + ushort flen; + }; + +struct Arpcache { + uchar status; /* Entry status */ + uchar type; /* Entry type */ + Ipaddr ip; /* Host byte order */ + uchar eip[4]; /* Network byte order */ + uchar et[6]; /* Ethernet address for this ip */ + int age; /* Entry timeout */ + Arpcache *hash; + Arpcache **hashhd; + Arpcache *frwd; + Arpcache *prev; +}; +#define ARP_FREE 0 +#define ARP_OK 1 +#define ARP_ASKED 2 +#define ARP_TEMP 0 +#define ARP_PERM 1 +#define Arphashsize 32 +#define ARPHASH(p) arphash[((p[2]^p[3])%Arphashsize)] +#define ARP_WAITMS 2500 /* Wait for arp replys */ + +#define IP_VER 0x40 /* Using IP version 4 */ +#define IP_HLEN 0x05 /* Header length in characters */ +#define IP_DF 0x4000 /* Don't fragment */ +#define IP_MF 0x2000 /* More fragments */ + +#define ICMP_ECHOREPLY 0 /* Echo Reply */ +#define ICMP_UNREACH 3 /* Destination Unreachable */ +#define ICMP_SOURCEQUENCH 4 /* Source Quench */ +#define ICMP_REDIRECT 5 /* Redirect */ +#define ICMP_ECHO 8 /* Echo Request */ +#define ICMP_TIMXCEED 11 /* Time-to-live Exceeded */ +#define ICMP_PARAMPROB 12 /* Parameter Problem */ +#define ICMP_TSTAMP 13 /* Timestamp */ +#define ICMP_TSTAMPREPLY 14 /* Timestamp Reply */ +#define ICMP_IREQ 15 /* Information Request */ +#define ICMP_IREQREPLY 16 /* Information Reply */ + +/* Sizes */ +#define IP_MAX 8192 /* Maximum Internet packet size */ +#define UDP_MAX (IP_MAX-ETHER_IPHDR) /* Maximum UDP datagram size */ +#define UDP_DATMAX (UDP_MAX-UDP_HDRSIZE) /* Maximum amount of udp data */ + +/* Protocol numbers */ +#define IP_UDPPROTO 17 +#define IP_TCPPROTO 6 + +/* Protocol port numbers */ +#define PORTALLOC 5000 /* First automatic allocated port */ +#define PRIVPORTALLOC 600 /* First priveleged port allocated */ +#define PORTMAX 30000 /* Last port to allocte */ + +/* Stuff to go in funs.h someday */ +Ipifc *newipifc(uchar, void (*)(Ipconv *, Block*), Ipconv *, int, int, int, char*); +void closeipifc(Ipifc*); +ushort ip_csum(uchar*); +int arp_lookup(uchar*, uchar*); +Ipaddr ipparse(char*); +void hnputs(uchar*, ushort); +void hnputl(uchar*, ulong); +ulong nhgetl(uchar*); +ushort nhgets(uchar*); +ushort ptcl_csum(Block*bp, int, int); +void ppkt(Block*); +void udprcvmsg(Ipconv *, Block*); +Block *btrim(Block*, int, int); +Block *ip_reassemble(int, Block*, Etherhdr*); +Ipconv *portused(Ipconv *, Port); +Port nextport(Ipconv *, Port); +void arp_enter(Arpentry*, int); +void arp_flush(void); +int arp_delete(char*); +void arplinkhead(Arpcache*); +Fragq *ipfragallo(void); +void ipfragfree(Fragq*); +void iproute(uchar*, uchar*); +void initfrag(int); +Block *copyb(Block*, int); +int ntohtcp(Tcp*, Block**); +void reset(Ipaddr, Ipaddr, char, ushort, Tcp*); +void proc_syn(Ipconv*, char, Tcp*); +void send_syn(Tcpctl*); +void tcp_output(Ipconv*); +int seq_within(int, int, int); +void update(Ipconv *, Tcp *); +int trim(Tcpctl *, Tcp *, Block **, ushort *); +void add_reseq(Tcpctl *, char, Tcp *, Block *, ushort); +void close_self(Ipconv *, int); +int seq_gt(int, int); +void appendb(Block **, Block *); +Ipconv *ip_conn(Ipconv *, Port, Port, Ipaddr dest, char proto); +void ipmkdir(Qinfo *, Dirtab *, Ipconv *); +int inb_window(Tcpctl *, int); +Block *htontcp(Tcp *, Block *, Tcphdr *); +void start_timer(Timer *); +void stop_timer(Timer *); +int copyupb(Block **, uchar *, int); +void init_tcpctl(Ipconv *); +void close_self(Ipconv *, int); +int iss(void); +int seq_within(int, int, int); +int seq_lt(int, int); +int seq_le(int, int); +int seq_gt(int, int); +int seq_ge(int, int); +void setstate(Ipconv *, char); +void tcpackproc(void*); +Block *htontcp(Tcp *, Block *, Tcphdr *); +int ntohtcp(Tcp *, Block **); +void extract_oob(Block **, Block **, Tcp *); +void get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *); +void state_upcall(Ipconv*, char oldstate, char newstate); +int backoff(int); +int dupb(Block **, Block *, int, int); +void tcp_input(Ipconv *, Block *); +void tcprcvwin(Ipconv *); +void open_tcp(Ipconv *, int, ushort, char); +void tcpflow(void*); +void tcp_timeout(void *); +void tcp_acktimer(void *); +Ipconv *ipclonecon(Chan *); +void iplisten(Chan *, Ipconv *, Ipconv *); + +#define fmtaddr(xx) (xx>>24)&0xff,(xx>>16)&0xff,(xx>>8)&0xff,xx&0xff +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define BLKIP(xp) ((Etherhdr *)((xp)->rptr)) +#define BLKFRAG(xp) ((Ipfrag *)((xp)->rptr)) +#define PREC(x) ((x)>>5 & 7) + +#define WORKBUF 64 + +extern Ipaddr Myip; +extern Ipaddr Mymask; +extern Ipaddr classmask[4]; +extern Ipconv *ipconv[]; +extern char *tcpstate[]; +extern Rendez tcpflowr; +extern Qinfo tcpinfo; +extern Qinfo ipinfo; +extern Qinfo udpinfo; diff --git a/port/lib.h b/port/lib.h index 1d2c96d5a7448893b62604ec1397a811153d8f44..8d5d195f1795ab8687954117e0055b2a5ce0d92d 100644 --- a/port/lib.h +++ b/port/lib.h @@ -47,7 +47,7 @@ extern char *doprint(char*, char*, char*, void*); extern int fmtinstall(char, int (*)(Op*)); extern int sprint(char*, char*, ...); extern int print(char*, ...); - +extern int atoi(char *); /* * one-of-a-kind */ diff --git a/port/stip.c b/port/stip.c new file mode 100644 index 0000000000000000000000000000000000000000..04738de475c4e34102316efa376c2721ac3c285e --- /dev/null +++ b/port/stip.c @@ -0,0 +1,631 @@ +/* + * ethernet specific multiplexor for ip + * + * this line discipline gets pushed onto an ethernet channel + * to demultiplex/multiplex ip conversations. + */ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +#define DPRINT if(pip)print +int pip = 0; +int ipcksum = 1; +extern Ipifc *ipifc; +int Id = 1; + +Fragq *flisthead; +Fragq *fragfree; +QLock fraglock; + +Queue *Etherq; + +Ipaddr Myip; +Ipaddr Mymask; + +/* Predeclaration */ +static void ipetherclose(Queue*); +static void ipetheriput(Queue*, Block*); +static void ipetheropen(Queue*, Stream*); +static void ipetheroput(Queue*, Block*); + +/* + * the ethernet multiplexor stream module definition + */ +Qinfo ipinfo = +{ + ipetheriput, + ipetheroput, + ipetheropen, + ipetherclose, + "internet" +}; + +void +initfrag(int size) +{ + Fragq *fq, *eq; + + fragfree = (Fragq*)ialloc(sizeof(Fragq) * size, 0); + + eq = &fragfree[size]; + for(fq = fragfree; fq < eq; fq++) + fq->next = fq+1; + + fragfree[size-1].next = 0; +} + +/* + * set up an ether interface + */ +static void +ipetheropen(Queue *q, Stream *s) +{ + /* First open is by ipconfig and sets up channel + * to ethernet + */ + if(!Etherq) + Etherq = WR(q); + + DPRINT("ipetheropen EQ %lux dev=%d id=%d RD %lux WR %lux\n", + Etherq, s->dev, s->id, RD(q), WR(q)); +} + +/* + * newipifc - Attach to or Create a new protocol interface + */ + +Ipifc * +newipifc(uchar ptcl, void (*recvfun)(Ipconv *, Block *bp), + Ipconv *con, int max, int min, int hdrsize, char *name) +{ + Ipifc *ifc, *free; + + free = 0; + for(ifc = ipifc; ifc < &ipifc[conf.ipif]; ifc++) { + qlock(ifc); + if(ifc->protocol == ptcl) { + ifc->ref++; + qunlock(ifc); + return(ifc); + } + if(!free && ifc->ref == 0) { + ifc->ref = 1; + free = ifc; + } + else + qunlock(ifc); + } + + if(!free) + error(Enoifc); + + free->iprcv = recvfun; + + /* If media supports large transfer units limit maxmtu + * to max ip size */ + if(max > IP_MAX) + max = IP_MAX; + free->maxmtu = max; + free->minmtu = min; + free->hsize = hdrsize; + free->connections = con; + + free->protocol = ptcl; + strncpy(free->name, name, NAMELEN); + + qunlock(free); + return(free); +} + +static void +ipetherclose(Queue *q) +{ + if(q == Etherq) { + print("stip: Clearing ether channel\n"); + Etherq = 0; + } + + DPRINT("ipetherclose RD %lux WR %lux\n", RD(q), WR(q)); +} + +void +closeipifc(Ipifc *ifc) +{ + /* If this is the last reference to the protocol multiplexor + * cancel upcalls from this stream + */ + qlock(ifc); + if(--ifc->ref == 0) { + ifc->protocol = 0; + ifc->name[0] = 0; + } + qunlock(ifc); +} + +static void +ipetheroput(Queue *q, Block *bp) +{ + Etherhdr *eh, *feh; + int lid, len, seglen, chunk, dlen, blklen, offset; + Ipifc *ifp; + ushort fragoff; + Block *xp, *nb; + uchar *ptr; + + if(bp->type != M_DATA){ + /* Allow one setting of the ip address */ + if(!Myip && streamparse("setip", bp)) { + ptr = bp->rptr; + Myip = ipparse((char *)ptr); + Mymask = classmask[Myip>>30]; + while(*ptr != ' ' && *ptr) + ptr++; + if(*ptr) + Mymask = ipparse((char *)ptr); + freeb(bp); + } + else + PUTNEXT(Etherq, bp); + return; + } + + ifp = (Ipifc *)(q->ptr); + + /* Number of bytes in ip and media header to write */ + len = blen(bp); + + /* Fill out the ip header */ + eh = (Etherhdr *)(bp->rptr); + eh->vihl = IP_VER|IP_HLEN; + eh->tos = 0; + eh->ttl = 255; + + /* If we dont need to fragment just send it */ + if(len <= ifp->maxmtu) { + hnputs(eh->length, len-ETHER_HDR); + eh->frag[0] = 0; + eh->frag[1] = 0; + eh->cksum[0] = 0; + eh->cksum[1] = 0; + hnputs(eh->cksum, ip_csum(&eh->vihl)); + + /* Finally put in the ethernet level information */ + hnputs(eh->type, ET_IP); + if(!arp_lookup(eh->dst, eh->d)) { + freeb(bp); + return; + } + + PUTNEXT(Etherq, bp); + return; + } + + if(eh->frag[0] & (IP_DF>>8)) + goto drop; + + seglen = (ifp->minmtu - (ETHER_HDR+ETHER_IPHDR)) & ~7; + if(seglen < 8) + goto drop; + + /* Make prototype output header */ + hnputs(eh->type, ET_IP); + if(!arp_lookup(eh->dst, eh->d)) { + freeb(bp); + return; + } + + dlen = len - (ETHER_HDR+ETHER_IPHDR); + xp = bp; + lid = Id++; + + offset = ETHER_HDR+ETHER_IPHDR; + while(xp && offset && offset >= BLEN(xp)) { + offset -= BLEN(xp); + xp = xp->next; + } + xp->rptr += offset; + + for(fragoff = 0; fragoff < dlen; fragoff += seglen) { + nb = allocb(ETHER_HDR+ETHER_IPHDR+seglen); + feh = (Etherhdr *)(nb->rptr); + + memmove(nb->wptr, eh, ETHER_HDR+ETHER_IPHDR); + nb->wptr += ETHER_HDR+ETHER_IPHDR; + + if((fragoff + seglen) >= dlen) { + seglen = dlen - fragoff; + hnputs(feh->frag, fragoff>>3); + } + else { + hnputs(feh->frag, (fragoff>>3)|IP_MF); + } + + hnputs(feh->length, seglen + ETHER_IPHDR); + hnputs(feh->id, lid); + + /* Copy up the data area */ + chunk = seglen; + while(chunk) { + if(!xp) { + freeb(nb); + goto drop; + } + blklen = MIN(BLEN(xp), chunk); + memmove(nb->wptr, xp->rptr, blklen); + nb->wptr += blklen; + xp->rptr += blklen; + chunk -= blklen; + if(xp->rptr == xp->wptr) + xp = xp->next; + } + + feh->cksum[0] = 0; + feh->cksum[1] = 0; + hnputs(feh->cksum, ip_csum(&feh->vihl)); + + nb->flags |= S_DELIM; + PUTNEXT(Etherq, nb); + } + +drop: + freeb(bp); +} + + +/* + * Input a packet and use the ip protocol to select the correct + * device to pass it to. + * + */ +static void +ipetheriput(Queue *q, Block *bp) +{ + Ipifc *ep, *ifp; + Etherhdr *h; + ushort frag; + + if(bp->type != M_DATA){ + PUTNEXT(q, bp); + return; + } + + h = (Etherhdr *)(bp->rptr); + + /* Ensure we have enough data to process */ + if(BLEN(bp) < (ETHER_HDR+ETHER_IPHDR)) { + bp = pullup(bp, ETHER_HDR+ETHER_IPHDR); + if(!bp) + return; + } + + if(ipcksum && ip_csum(&h->vihl)) { + print("ip: checksum error (from %d.%d.%d.%d ?)\n", + h->src[0], h->src[1], h->src[2], h->src[3]); + goto drop; + } + + /* Check header length and version */ + if(h->vihl != (IP_VER|IP_HLEN)) + goto drop; + + frag = nhgets(h->frag); + if(frag) { + h->tos = frag & IP_MF ? 1 : 0; + bp = ip_reassemble(frag, bp, h); + if(!bp) + return; + } + + /* + * Look for an ip interface attached to this protocol + */ + ep = &ipifc[conf.ipif]; + for(ifp = ipifc; ifp < ep; ifp++) { + if(ifp->protocol == h->proto) { + (*ifp->iprcv)(ifp->connections, bp); + return; + } + } + +drop: + freeb(bp); +} + +Block * +ip_reassemble(int offset, Block *bp, Etherhdr *ip) +{ + Fragq *f; + Ipaddr src, dst; + ushort id; + Block *bl, **l, *last, *prev; + int ovlap, len, fragsize; + + src = nhgetl(ip->src); + dst = nhgetl(ip->dst); + id = nhgets(ip->id); + + for(f = flisthead; f; f = f->next) { + if(f->src == src && f->dst == dst && f->id == id) + break; + } + + if(!ip->tos && (offset & ~(IP_MF|IP_DF)) == 0) { + if(f) { + qlock(f); + ipfragfree(f); + } + return(bp); + } + + BLKFRAG(bp)->foff = offset<<3; + BLKFRAG(bp)->flen = nhgets(ip->length) - ETHER_IPHDR; /* Ip data length */ + bp->flags &= ~S_DELIM; + + /* First fragment allocates a reassembly queue */ + if(f == 0) { + f = ipfragallo(); + if(f == 0) { + freeb(bp); + return 0; + } + qlock(f); + f->id = id; + f->src = src; + f->dst = dst; + + f->blist = bp; + /* Check lance has handed us a contiguous buffer */ + if(bp->next) + panic("ip: reass ?"); + + qunlock(f); + return 0; + } + qlock(f); + + prev = 0; + l = &f->blist; + for(bl = f->blist; bl && BLKFRAG(bp)->foff > BLKFRAG(bl)->foff; bl = bl->next) { + prev = bl; + l = &bl->next; + } + + /* Check overlap of a previous fragment - trim away as necessary */ + if(prev) { + ovlap = BLKFRAG(prev)->foff + BLKFRAG(prev)->flen - BLKFRAG(bp)->foff; + if(ovlap > 0) { + if(ovlap > BLKFRAG(bp)->flen) { + freeb(bp); + qunlock(f); + return 0; + } + BLKFRAG(bp)->flen -= ovlap; + } + } + + /* Link onto assembly queue */ + bp->next = *l; + *l = bp; + + /* Check to see if suceeding segments overlap */ + if(bp->next) { + l = &bp->next; + end = BLKFRAG(bp)->foff + BLKFRAG(bp)->flen; + /* Take completely covered segements out */ + while(*l && (ovlap = (end - BLKFRAG(*l)->foff)) > 0) { + if(ovlap < BLKFRAG(*l)->flen) { + BLKFRAG(*l)->flen -= ovlap; + (*l)->rptr += ovlap; + break; + } + last = *l; + freeb(*l); + *l = last; + } + } + + /* Now look for a completed queue */ + for(bl = f->blist; bl; bl = bl->next) { + if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) + goto complete; + if(bl->next == 0 || + BLKFRAG(bl)->foff+BLKFRAG(bl)->flen != BLKFRAG(bl->next)->foff) + break; + } + qunlock(f); + return 0; + +complete: + bl = f->blist; + last = bl; + len = nhgets(BLKIP(bl)->length); + bl->wptr = bl->rptr + len + ETHER_HDR; + + /* Pullup all the fragment headers and return a complete packet */ + for(bl = bl->next; bl; bl = bl->next) { + fragsize = BLKFRAG(bl)->flen; + len += fragsize; + bl->rptr += (ETHER_HDR+ETHER_IPHDR); + bl->wptr = bl->rptr + fragsize; + last = bl; + } + + last->flags |= S_DELIM; + bl = f->blist; + f->blist = 0; + ipfragfree(f); + + ip = BLKIP(bl); + hnputs(ip->length, len); + + return(bl); +} + +/* + * ipfragfree - Free a list of fragments, fragment list must be locked + */ + +void +ipfragfree(Fragq *frag) +{ + Block *f, *next; + Fragq *fl, **l; + + for(f = frag->blist; f; f = next) { + next = f->next; + freeb(f); + } + + frag->src = 0; + frag->id = 0; + qunlock(frag); + + qlock(&fraglock); + + l = &flisthead; + for(fl = *l; fl; fl = fl->next) { + if(fl == frag) { + *l = frag->next; + break; + } + l = &fl->next; + } + + frag->next = fragfree; + fragfree = frag; + + qunlock(&fraglock); + + +} + +/* + * ipfragallo - allocate a reassembly queue + */ +Fragq * +ipfragallo(void) +{ + Fragq *f; + + qlock(&fraglock); + if(!fragfree) { + qunlock(&fraglock); + print("ipfragallo: no queue\n"); + return 0; + } + f = fragfree; + fragfree = f->next; + f->next = flisthead; + flisthead = f; + + qunlock(&fraglock); + return f; + +} + +/* + * ip_csum - Compute internet header checksums + */ +ushort +ip_csum(uchar *addr) +{ + int len; + ulong sum = 0; + + len = (addr[0]&0xf)<<2; + + while(len > 0) { + sum += addr[0]<<8 | addr[1] ; + len -= 2; + addr += 2; + } + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + + return (sum^0xffff); +} + +/* + * ipparse - Parse an ip address out of a string + */ + +Ipaddr classmask[4] = { + 0xff000000, + 0xff000000, + 0xffff0000, + 0xffffff00 +}; + +Ipaddr +ipparse(char *ipa) +{ + Ipaddr address = 0; + int shift; + Ipaddr net; + + shift = 24; + + while(shift >= 0 && ipa != (char *)1) { + address |= atoi(ipa) << shift; + shift -= 8; + ipa = strchr(ipa, '.')+1; + } + net = address & classmask[address>>30]; + + shift += 8; + return net | ((address & ~classmask[address>>30])>>shift); +} + +/* + * XXX debugging code ! + */ +void +ppkt(Block *bp) +{ + uchar *x; + int len, cnt = 0; + Block *xp; + uchar ch; + + if(bp) + print("ppkt: %lux len %d\n", bp, blen(bp)); + else + print("ppkt: Null packet pointer\n"); + + xp = bp; + while(bp) { + len = BLEN(bp); + x = bp->rptr; + while(len--) { + if(cnt%64 == 0) + print("\n%.04d: ", cnt); + print("%.02x ", *x++); + cnt++; + } + bp = bp->next; + } + bp = xp; + cnt = 0; + while(bp) { + len = BLEN(bp); + x = bp->rptr; + while(len--) { + if(cnt%64 == 0) + print("\n%.04d: ", cnt); + ch = *x++; + print("%c ", ch < ' ' || ch >= 0x7f ? '.' : ch); + cnt++; + } + bp = bp->next; + } + print("\n"); + +} diff --git a/port/tcpif.c b/port/tcpif.c new file mode 100644 index 0000000000000000000000000000000000000000..ccfd78af786e5b74f0afe631175c53e62fc4b21a --- /dev/null +++ b/port/tcpif.c @@ -0,0 +1,84 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +extern int tcpdbg; +#define DPRINT if(tcpdbg) print + +void +state_upcall(Ipconv *s, char oldstate, char newstate) +{ + Block *bp; + int len; + char *ptr = 0; + + SET(len); + + DPRINT("state_upcall: %s -> %s err %d\n", + tcpstate[oldstate], tcpstate[newstate], s->err); + + if(oldstate == newstate) + return; + + switch(newstate) { + case CLOSED: + s->psrc = 0; + s->pdst = 0; + s->dst = 0; + case CLOSE_WAIT: /* Remote closes */ + if(s->readq == 0) + break; + if(s->err) { + ptr = errstrtab[s->err]; + len = strlen(ptr)+1; + bp = allocb(len); + } + else + bp = allocb(0); + + if(bp) { + if(ptr) { + strcpy((char *)bp->wptr, ptr); + bp->wptr += len; + } + bp->flags |= S_DELIM; + bp->type = M_HANGUP; + PUTNEXT(s->readq, bp); + } + } +} + +void +open_tcp(Ipconv *s, int mode, ushort window, char tos) +{ + Tcpctl *tcb = &s->tcpctl; + + if(tcb->state != CLOSED) + return; + + init_tcpctl(s); + + tcb->window = tcb->rcv.wnd = window; + tcb->tos = tos; + + switch(mode){ + case TCP_PASSIVE: + tcb->flags |= CLONE; + setstate(s, LISTEN); + break; + case TCP_ACTIVE: + /* Send SYN, go into SYN_SENT state */ + tcb->flags |= ACTIVE; + qlock(tcb); + send_syn(tcb); + setstate(s, SYN_SENT); + tcp_output(s); + qunlock(tcb); + break; + } +} diff --git a/port/tcpinput.c b/port/tcpinput.c new file mode 100644 index 0000000000000000000000000000000000000000..bf263ebfc50f144c897525331592840039b00d78 --- /dev/null +++ b/port/tcpinput.c @@ -0,0 +1,1126 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +int tcpdbg = 0; +#define DPRINT if(tcpdbg) print +#define LPRINT print + +extern Queue *Tcpoutput; +QLock reseqlock; +Reseq *reseqfree; + +char *tcpstate[] = { + "Closed", "Listen", "Syn_sent", "Syn_received", + "Established", "Finwait1", "Finwait2", "Close_wait", + "Closing", "Last_ack", "Time_wait" }; + +void +tcp_input(Ipconv *ipc, Block *bp) +{ + Ipconv *s, *new, *etab; + Tcpctl *tcb; + Tcphdr *h; + Tcp seg; + int hdrlen; + Block *oobbp; + Ipaddr source, dest; + char tos; + ushort length; + + DPRINT("tcp_input.\n"); + + h = (Tcphdr *)(bp->rptr); + dest = nhgetl(h->tcpdst); + source = nhgetl(h->tcpsrc); + + tos = h->tos; + length = nhgets(h->length); + + if (dest == source) { + if (!(bp = copyb(bp, blen(bp)))) { + print("tcpin: allocb failure."); + return; + } + DPRINT("tcpin: Duplicate packet %lux\n", bp); + } + + h->Unused = 0; + hnputs(h->tcplen, length - (TCP_IPLEN+TCP_PHDRSIZE)); + + if(ptcl_csum(bp, TCP_EHSIZE+TCP_IPLEN, length - TCP_IPLEN)) { + DPRINT("tcpin: Bad checksum.\n"); + freeb(bp); + return; + } + + if((hdrlen = ntohtcp(&seg, &bp)) < 0) + return; + + /* Adjust the data length */ + length -= (hdrlen+TCP_IPLEN+TCP_PHDRSIZE); + + DPRINT("tcpin: lport = %d, rport = %d hdrlen %d", + seg.dest, seg.source, hdrlen); + DPRINT(" flags = 0x%lux, seqo = %d, seqi = %d len %d\n", + seg.flags, seg.seq, seg.ack, length); + + /* Trim the packet down to just the data */ + bp = btrim(bp, hdrlen+TCP_PKT, length); + if(bp == 0) + return; + + if (!(s = ip_conn(ipc, seg.dest, seg.source, source, IP_TCPPROTO))) { + LPRINT("tcpin: look for listen on %d\n", seg.dest); + + if(!(seg.flags & SYN)) { + LPRINT("tcpin: No SYN\n"); + clear: + LPRINT("tcpin: call cleared\n"); + freeb(bp); + reset(source, dest, tos, length, &seg); + return; + } + + if(!(s = ip_conn(ipc, seg.dest, 0, 0, IP_TCPPROTO))) { + LPRINT("tcpin: No socket dest on %d\n", seg.dest); + goto clear; + } + + if(s->curlog >= s->backlog) { + LPRINT("too many pending\n"); + goto clear; + } + + /* Find a conversation to clone onto */ + etab = &ipc[conf.ip]; + for(new = ipc; new < etab; new++) { + if(new->ref == 0 && canqlock(new)) { + if(new->ref || new->tcpctl.state != CLOSED) { + qunlock(new); + continue; + } + new->ref++; + qunlock(new); + break; + } + } + + if(new == etab) + goto clear; + + s->curlog++; + LPRINT("tcpin: cloning socket\n"); + new->psrc = s->psrc; + new->pdst = seg.source; + new->dst = source; + memmove(&new->tcpctl, &s->tcpctl, sizeof(Tcpctl)); + new->tcpctl.flags &= ~CLONE; + new->tcpctl.timer.arg = new; + new->tcpctl.timer.state = TIMER_STOP; + new->tcpctl.acktimer.arg = new; + new->tcpctl.acktimer.state = TIMER_STOP; + + new->ipinterface = s->ipinterface; + s->ipinterface->ref++; + + /* Wake the sleeping dodo */ + wakeup(&s->listenr); + s = new; + } + + tcb = &s->tcpctl; + qlock(tcb); + + switch(tcb->state) { + case CLOSED: + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + case LISTEN: + if(seg.flags & RST) { + freeb(bp); + goto done; + } + if(seg.flags & ACK) { + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + } + if(seg.flags & SYN) { + proc_syn(s, tos, &seg); + send_syn(tcb); + setstate(s, SYN_RECEIVED); + if(length != 0 || (seg.flags & FIN)) + break; + freeb(bp); + goto output; + } + freeb(bp); + goto done; + case SYN_SENT: + if(seg.flags & ACK) { + if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) { + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + } + } + if(seg.flags & RST) { + if(seg.flags & ACK) + close_self(s, Econrefused); + freeb(bp); + goto done; + } + + if((seg.flags & ACK) && PREC(tos) != PREC(tcb->tos)){ + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + } + if(seg.flags & SYN) { + proc_syn(s, tos, &seg); + if(seg.flags & ACK){ + update(s, &seg); + setstate(s, ESTABLISHED); + } + else + setstate(s, SYN_RECEIVED); + + if(length != 0 || (seg.flags & FIN)) + break; + + freeb(bp); + goto output; + } + else + freeb(bp); + goto done; + } + + /* Trim segment to fit receive window. */ + if(trim(tcb, &seg, &bp, &length) == -1) { + if(!(seg.flags & RST)) { + tcb->flags |= FORCE; + goto output; + } + goto done; + } + + /* If we have no opens and the other end is sending data then + * reply with a reset + */ + if(s->readq == 0 && length) { + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + } + + if(seg.seq != tcb->rcv.nxt + && (length != 0 || (seg.flags & (SYN|FIN)) )) { + add_reseq(tcb, tos, &seg, bp, length); + tcb->flags |= FORCE; + goto output; + } + + for(;;) { + if(seg.flags & RST) { + if(tcb->state == SYN_RECEIVED + && !(tcb->flags & (CLONE|ACTIVE))) + setstate(s, LISTEN); + else + close_self(s, Econrefused); + + freeb(bp); + goto done; + } + + if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){ + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + } + + if(!(seg.flags & ACK)) { + freeb(bp); + goto done; + } + + switch(tcb->state) { + case SYN_RECEIVED: + if(seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){ + update(s, &seg); + setstate(s, ESTABLISHED); + } + else { + freeb(bp); + reset(source, dest, tos, length, &seg); + goto done; + } + break; + case ESTABLISHED: + case CLOSE_WAIT: + update(s, &seg); + break; + case FINWAIT1: + update(s, &seg); + if(tcb->sndcnt == 0) + setstate(s, FINWAIT2); + break; + case FINWAIT2: + update(s, &seg); + break; + case CLOSING: + update(s, &seg); + if(tcb->sndcnt == 0){ + setstate(s, TIME_WAIT); + tcb->timer.start = MSL2 * (1000 / MSPTICK); + start_timer(&tcb->timer); + } + break; + case LAST_ACK: + update(s, &seg); + if(tcb->sndcnt == 0) { + close_self(s, 0); + goto done; + } + case TIME_WAIT: + tcb->flags |= FORCE; + start_timer(&tcb->timer); + } + + if ((seg.flags&URG) && seg.up) { + DPRINT("tcpin: oob: up = %u seq = %u rcv.up = %u\n", + seg.up, seg.seq, tcb->rcv.up); + if (seq_gt(seg.up + seg.seq, tcb->rcv.up)) { + tcb->rcv.up = seg.up + seg.seq; + tcb->oobflags &= ~(TCPOOB_HAVEDATA|TCPOOB_HADDATA); + extract_oob(&bp, &oobbp, &seg); + if (oobbp) { + DPRINT("tcpin: oob delivered\n"); + appendb(&tcb->rcvoobq, oobbp); + tcb->rcvoobcnt += blen(oobbp); + tcb->oobmark = tcb->rcvcnt; + tcb->oobflags |= TCPOOB_HAVEDATA; +#ifdef NOTIFY + urg_signal(s); +#endif + } + } + } + else if (seq_gt(tcb->rcv.nxt, tcb->rcv.up)) + tcb->rcv.up = tcb->rcv.nxt; + + DPRINT("tcpin: Append pkt len=%d state=%s\n", + length, tcpstate[tcb->state]); + + if(length != 0){ + switch(tcb->state){ + case SYN_RECEIVED: + case ESTABLISHED: + case FINWAIT1: + case FINWAIT2: + /* Place on receive queue */ + tcb->rcvcnt += blen(bp); + if(s->readq && bp) { + PUTNEXT(s->readq, bp); + bp = 0; + } + tcb->rcv.nxt += length; + + tcprcvwin(s); + + start_timer(&tcb->acktimer); + + if (tcb->max_snd <= tcb->rcv.nxt-tcb->last_ack) + tcb->flags |= FORCE; + break; + default: + /* Ignore segment text */ + freeb(bp); + break; + } + } + + if(seg.flags & FIN) { + tcb->flags |= FORCE; + + switch(tcb->state) { + case SYN_RECEIVED: + case ESTABLISHED: + tcb->rcv.nxt++; + setstate(s, CLOSE_WAIT); + break; + case FINWAIT1: + tcb->rcv.nxt++; + if(tcb->sndcnt == 0) { + setstate(s, TIME_WAIT); + tcb->timer.start = MSL2 * (1000/MSPTICK); + start_timer(&tcb->timer); + } + else + setstate(s, CLOSING); + break; + case FINWAIT2: + tcb->rcv.nxt++; + setstate(s, TIME_WAIT); + tcb->timer.start = MSL2 * (1000/MSPTICK); + start_timer(&tcb->timer); + break; + case CLOSE_WAIT: + case CLOSING: + case LAST_ACK: + break; + case TIME_WAIT: + start_timer(&tcb->timer); + break; + } + } + while(tcb->reseq != 0 && + seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq)){ + get_reseq(tcb, &tos, &seg, &bp, &length); + if(trim(tcb, &seg, &bp, &length) == 0) + goto gotone; + } + break; +gotone:; + } +output: + tcp_output(s); +done: + qunlock(tcb); +} + +void +tcp_icmp(Ipconv *ipc, Ipaddr source, Ipaddr dest, char type, char code, Block **bpp) +{ + Tcp seg; + Tcpctl *tcb; + Ipconv *s; + + ntohtcp(&seg, bpp); + if(!(s = ip_conn(ipc, seg.source, seg.dest, dest, IP_TCPPROTO))) + return; + + tcb = &s->tcpctl; + + if(!seq_within(seg.seq, tcb->snd.una, tcb->snd.nxt)) + return; + + switch((uchar)type) { + case ICMP_UNREACH: + tcb->type = type; + tcb->code = code; + if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED) + close_self(s, Enetunreach); + break; + case ICMP_TIMXCEED: + tcb->type = type; + tcb->code = code; + if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED) + close_self(s, Etimedout); + break; + case ICMP_SOURCEQUENCH: + tcb->cwind /= 2; + tcb->cwind = MAX(tcb->mss,tcb->cwind); + break; + } +} + +void +reset(Ipaddr source, Ipaddr dest, char tos, ushort length, Tcp *seg) +{ + Block *hbp; + Port tmp; + char rflags; + Tcphdr ph; + + if(seg->flags & RST) + return; + + hnputl(ph.tcpsrc, dest); + hnputl(ph.tcpdst, source); + ph.proto = IP_TCPPROTO; + hnputs(ph.tcplen, TCP_HDRSIZE); + + /* Swap port numbers */ + tmp = seg->dest; + seg->dest = seg->source; + seg->source = tmp; + + rflags = RST; + if(seg->flags & ACK) { + /* This reset is being sent to clear a half-open connection. + * Set the sequence number of the RST to the incoming ACK + * so it will be acceptable. + */ + seg->seq = seg->ack; + seg->ack = 0; + } + else { + /* We're rejecting a connect request (SYN) from LISTEN state + * so we have to "acknowledge" their SYN. + */ + rflags |= ACK; + seg->ack = seg->seq; + seg->seq = 0; + if(seg->flags & SYN) + seg->ack++; + seg->ack += length; + if(seg->flags & FIN) + seg->ack++; + } + seg->flags = rflags; + seg->wnd = 0; + seg->up = 0; + seg->mss = 0; + if((hbp = htontcp(seg, 0, &ph)) == 0) + return; + + DPRINT("Reset: seq = %lux ack = %d flags = %lux\n", + seg->seq, seg->ack, seg->flags); + + PUTNEXT(Tcpoutput, hbp); +} + +void +update(Ipconv *s, Tcp *seg) +{ + ushort acked; + ushort oobacked; + ushort expand; + Tcpctl *tcb = &s->tcpctl; + + if(seq_gt(seg->ack, tcb->snd.nxt)) { + tcb->flags |= FORCE; + return; + } + + if(seq_gt(seg->seq,tcb->snd.wl1) || ((seg->seq == tcb->snd.wl1) + && seq_ge(seg->ack,tcb->snd.wl2))) { + if(tcb->snd.wnd == 0 && seg->wnd != 0) + tcb->snd.ptr = tcb->snd.una; + tcb->snd.wnd = seg->wnd; + tcb->snd.wl1 = seg->seq; + tcb->snd.wl2 = seg->ack; + } + + if(!seq_gt(seg->ack, tcb->snd.una)) + return; + + acked = seg->ack - tcb->snd.una; + + if(tcb->cwind < tcb->snd.wnd) { + if(tcb->cwind < tcb->ssthresh) + expand = MIN(acked,tcb->mss); + else + expand = ((long)tcb->mss * tcb->mss) / tcb->cwind; + + if(tcb->cwind + expand < tcb->cwind) + expand = 65535 - tcb->cwind; + + if(tcb->cwind + expand > tcb->snd.wnd) + expand = tcb->snd.wnd - tcb->cwind; + + if(expand != 0) + tcb->cwind += expand; + + } + + /* Round trip time estimation */ + if(run_timer(&tcb->rtt_timer) && seq_ge(seg->ack, tcb->rttseq)) { + stop_timer(&tcb->rtt_timer); + if(!(tcb->flags & RETRAN)) { + int rtt; /* measured round trip time */ + int abserr; /* abs(rtt - srtt) */ + + rtt = tcb->rtt_timer.start - tcb->rtt_timer.count; + rtt *= MSPTICK; + + if(rtt > tcb->srtt && + (tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED)) + tcb->srtt = rtt; + else { + abserr = (rtt > tcb->srtt) ? rtt - tcb->srtt : tcb->srtt - rtt; + tcb->srtt = ((AGAIN-1)*tcb->srtt + rtt) / AGAIN; + tcb->mdev = ((DGAIN-1)*tcb->mdev + abserr) / DGAIN; + DPRINT("tcpout: rtt %d, srtt %d, mdev %d\n", + rtt, tcb->srtt, tcb->mdev); + } + + tcb->backoff = 0; + } + } + + /* If we're waiting for an ack of our SYN, note it and adjust count */ + if(!(tcb->flags & SYNACK)){ + tcb->flags |= SYNACK; + acked--; + tcb->sndcnt--; + } + + /* Acking some oob data if relevant */ + if(tcb->sndoobq && seq_ge(tcb->snd.up,tcb->snd.una) && + seq_gt(seg->ack, tcb->snd.up)) { + oobacked = seg->ack - tcb->snd.up; + acked -= oobacked; + copyupb(&tcb->sndoobq, 0, oobacked); + tcb->sndoobcnt -= oobacked; + DPRINT("update: oobacked = %d\n", oobacked); + } + + copyupb(&tcb->sndq, 0, acked); + + /* This will include the FIN if there is one */ + tcb->sndcnt -= acked; + tcb->snd.una = seg->ack; + /* If ack includes some out-of-band data then update urgent pointer */ + if (seq_gt(seg->ack, tcb->snd.up)) + tcb->snd.up = seg->ack; + + /* Stop retransmission timer, but restart it if there is still + * unacknowledged data. + */ + stop_timer(&tcb->timer); + if(tcb->snd.una != tcb->snd.nxt) + start_timer(&tcb->timer); + + /* If retransmissions have been occurring, make sure the + * send pointer doesn't repeat ancient history + */ + if(seq_lt(tcb->snd.ptr, tcb->snd.una)) + tcb->snd.ptr = tcb->snd.una; + + /* Clear the retransmission flag since the oldest + * unacknowledged segment (the only one that is ever retransmitted) + * has now been acked. + */ + tcb->flags &= ~RETRAN; + tcb->backoff = 0; +} + +int +in_window(Tcpctl *tcb, int seq) +{ + return seq_within(seq, tcb->rcv.nxt, + (int)(tcb->rcv.nxt+tcb->rcv.wnd-1)); +} + +void +proc_syn(Ipconv *s, char tos, Tcp *seg) +{ + Tcpctl *tcb = &s->tcpctl; + ushort mtu; + + + tcb->flags |= FORCE; + + if(PREC(tos) > PREC(tcb->tos)) + tcb->tos = tos; + + tcb->rcv.up = tcb->rcv.nxt = seg->seq + 1; /* p 68 */ + tcb->snd.wl1 = tcb->irs = seg->seq; + tcb->snd.wnd = seg->wnd; + + if(seg->mss != 0) + tcb->mss = seg->mss; + + tcb->max_snd = seg->wnd; + + if((mtu = s->ipinterface->maxmtu) != 0) { + mtu -= TCP_HDRSIZE + TCP_EHSIZE + TCP_PHDRSIZE; + tcb->cwind = tcb->mss = MIN(mtu, tcb->mss); + } +} + +/* Generate an initial sequence number and put a SYN on the send queue */ +void +send_syn(Tcpctl *tcb) +{ + tcb->iss = iss(); + tcb->rttseq = tcb->snd.wl2 = tcb->snd.una = tcb->iss; + tcb->snd.ptr = tcb->snd.nxt = tcb->rttseq; + tcb->sndcnt++; + tcb->flags |= FORCE; +} + +void +add_reseq(Tcpctl *tcb, char tos, Tcp *seg, Block *bp, ushort length) +{ + Reseq *rp, *rp1; + + qlock(&reseqlock); + if(!reseqfree) { + qunlock(&reseqlock); + print("tcp: no resequence descriptors\n"); + freeb(bp); + return; + } + + rp = reseqfree; + reseqfree = rp->next; + qunlock(&reseqlock); + + rp->seg = *seg; + rp->tos = tos; + rp->bp = bp; + rp->length = length; + + /* Place on reassembly list sorting by starting seq number */ + rp1 = tcb->reseq; + if(rp1 == 0 || seq_lt(seg->seq, rp1->seg.seq)) { + rp->next = rp1; + tcb->reseq = rp; + } + else { + for(;;){ + if(rp1->next == 0 || + seq_lt(seg->seq, rp1->next->seg.seq)) { + rp->next = rp1->next; + rp1->next = rp; + break; + } + rp1 = rp1->next; + } + } +} + + +void +get_reseq(Tcpctl *tcb, char *tos, Tcp *seg, Block **bp, ushort *length) +{ + Reseq *rp; + + if((rp = tcb->reseq) == 0) + return; + + tcb->reseq = rp->next; + + *tos = rp->tos; + *seg = rp->seg; + *bp = rp->bp; + *length = rp->length; + + qlock(&reseqlock); + rp->next = reseqfree; + reseqfree = rp; + qunlock(&reseqlock); +} + +int +trim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length) +{ + Block *nbp; + long dupcnt,excess; + ushort len; /* Segment length including flags */ + char accept; + + accept = 0; + len = *length; + if(seg->flags & SYN) + len++; + if(seg->flags & FIN) + len++; + + /* Acceptability tests */ + if(tcb->rcv.wnd == 0) { + if(seg->seq == tcb->rcv.nxt && len == 0) + return 0; + } else { + /* Some part of the segment must be in the window */ + if(in_window(tcb,seg->seq)) { + accept++; + } + else if(len != 0) { + if(in_window(tcb, (int)(seg->seq+len-1)) || + seq_within(tcb->rcv.nxt, seg->seq,(int)(seg->seq+len-1))) + accept++; + } + } + if(!accept) { + freeb(*bp); + return -1; + } + dupcnt = tcb->rcv.nxt - seg->seq; + if(dupcnt > 0){ + tcb->rerecv += dupcnt; + if(seg->flags & SYN){ + seg->flags &= ~SYN; + seg->seq++; + + if (seg->up > 1) + seg->up--; + else + seg->flags &= ~URG; + dupcnt--; + } + if(dupcnt > 0){ + copyupb(bp, 0, (ushort)dupcnt); + seg->seq += dupcnt; + *length -= dupcnt; + + if (seg->up > dupcnt) + seg->up -= dupcnt; + else { + seg->flags &= ~URG; + seg->up = 0; + } + } + } + excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd); + if(excess > 0){ + tcb->rerecv += excess; + *length -= excess; + nbp = copyb(*bp, *length); + freeb(*bp); + *bp = nbp; + seg->flags &= ~FIN; + } + return 0; +} + +void +extract_oob(Block **bp, Block **oobbp, Tcp *seg) + +{ + DPRINT("extract_oob: size = %u\n", seg->up); + + if (*oobbp = allocb(seg->up)) + (*oobbp)->wptr = (*oobbp)->wptr + + copyupb(bp, (*oobbp)->rptr, seg->up); + else + copyupb(bp, 0, seg->up); +} + +int +copyupb(Block **bph, uchar *data, int count) +{ + int n, bytes; + Block *bp; + + bytes = 0; + if(bph == 0) + return 0; + + while(*bph && count != 0) { + bp = *bph; + n = MIN(count, BLEN(bp)); + if(data && n) { + memmove(data, bp->rptr, n); + data += n; + } + bytes += n; + count -= n; + bp->rptr += n; + if(BLEN(bp) == 0) { + *bph = bp->next; + bp->next = 0; + freeb(bp); + } + } + + return bytes; +} + +void +appendb(Block **list, Block *bp) +{ + Block *f; + + if(f = *list) { + while(f->next) + f = f->next; + f->next = bp; + } + else + *list = bp; + + bp->next = 0; +} + +int +dupb(Block **hp, Block *bp, int offset, int count) +{ + int i, blen, bytes = 0; + uchar *addr; + + *hp = allocb(count); + if(*hp == 0) + return 0; + + /* Correct to front of data area */ + while(bp && offset && offset >= BLEN(bp)) { + offset -= BLEN(bp); + bp = bp->next; + } + if(bp == 0) + return 0; + + addr = bp->rptr + offset; + blen = BLEN(bp) - offset; + + while(count) { + i = MIN(count, blen); + memmove((*hp)->wptr, addr, i); + (*hp)->wptr += i; + bytes += i; + count -= i; + bp = bp->next; + if(!bp) + break; + blen = BLEN(bp); + addr = bp->rptr; + } + + return bytes; +} + +Block * +copyb(Block *bp, int count) +{ + Block *nbp; + int i; + + nbp = allocb(count); + if(nbp == 0) + return 0; + + while(bp && count) { + i = MIN(count, BLEN(bp)); + memmove(nbp->wptr, bp->rptr, i); + nbp->wptr += i; + count -= i; + bp = bp->next; + } + + return nbp; +} + +ushort tcp_mss = DEF_MSS; /* Maximum segment size to be sent with SYN */ +int tcp_irtt = DEF_RTT; /* Initial guess at round trip time */ + +void +init_tcpctl(Ipconv *s) +{ + + Tcpctl *tcb = &s->tcpctl; + + memset(tcb, 0, sizeof(Tcpctl)); + + tcb->cwind = tcb->mss = tcp_mss; + tcb->ssthresh = 65535; + tcb->srtt = tcp_irtt; + + /* Initialize timer intervals */ + tcb->timer.start = tcb->srtt / MSPTICK; + tcb->timer.func = (void(*)(void*))tcp_timeout; + tcb->timer.arg = (void *)s; + tcb->rtt_timer.start = MAX_TIME; + + /* Initialise ack timer */ + tcb->acktimer.start = TCP_ACK / MSPTICK; + tcb->acktimer.func = (void(*)(void*))tcp_acktimer; + tcb->acktimer.arg = (void *)s; +} + +void +close_self(Ipconv *s, int reason) +{ + Reseq *rp,*rp1; + Tcpctl *tcb = &s->tcpctl; + + stop_timer(&tcb->timer); + stop_timer(&tcb->rtt_timer); + s->err = reason; + + /* Flush reassembly queue; nothing more can arrive */ + for(rp = tcb->reseq;rp != 0;rp = rp1){ + rp1 = rp->next; + freeb(rp->bp); + + qlock(&reseqlock); + rp->next = reseqfree; + reseqfree = rp; + qunlock(&reseqlock); + } + + tcb->reseq = 0; + s->err = reason; + + setstate(s, CLOSED); +} + +int +iss(void) +{ + static int seq; + + seq += 250000; + return seq; +} + +int +seq_within(int x, int low, int high) +{ + if(low <= high){ + if(low <= x && x <= high) + return 1; + } else { + if(low >= x && x >= high) + return 1; + } + return 0; +} + +int +seq_lt(int x, int y) +{ + return (long)(x-y) < 0; +} + +int +seq_le(int x, int y) +{ + return (long)(x-y) <= 0; +} + +int +seq_gt(int x, int y) +{ + return (long)(x-y) > 0; +} + +int +seq_ge(int x, int y) +{ + return (long)(x-y) >= 0; +} + +void +setstate(Ipconv *s, char newstate) +{ + char oldstate; + Tcpctl *tcb = &s->tcpctl; + + oldstate = tcb->state; + tcb->state = newstate; + + state_upcall(s, oldstate, newstate); +} + +Block * +htontcp(Tcp *tcph, Block *data, Tcphdr *ph) +{ + ushort hdrlen; + int dlen; + ushort csum; + Tcphdr *h; + Block *bp; + + hdrlen = TCP_HDRSIZE; + if(tcph->mss) + hdrlen += MSS_LENGTH; + + if(data) { + dlen = blen(data); + if((data = padb(data, hdrlen + TCP_PKT)) == 0) + return 0; + /* If we collected blocks delimit the end of the chain */ + for(bp = data; bp->next; bp = bp->next) + bp->flags &= ~S_DELIM; + bp->flags |= S_DELIM; + } + else { + dlen = 0; + data = allocb(hdrlen + TCP_PKT); + if(data == 0) + return 0; + data->wptr += hdrlen + TCP_PKT; + data->flags |= S_DELIM; + } + + + memmove(data->rptr, ph, TCP_PKT); + + h = (Tcphdr *)(data->rptr); + h->proto = IP_TCPPROTO; + hnputs(h->tcplen, hdrlen + dlen); + hnputs(h->tcpsport, tcph->source); + hnputs(h->tcpdport, tcph->dest); + hnputl(h->tcpseq, tcph->seq); + hnputl(h->tcpack, tcph->ack); + hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags); + hnputs(h->tcpwin, tcph->wnd); + h->tcpcksum[0] = 0; + h->tcpcksum[1] = 0; + h->Unused = 0; + hnputs(h->tcpurg, tcph->up); + + if(tcph->mss != 0){ + h->tcpopt[0] = MSS_KIND; + h->tcpopt[1] = MSS_LENGTH; + hnputs(h->tcpmss, tcph->mss); + } + csum = ptcl_csum(data, TCP_EHSIZE+TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE); + hnputs(h->tcpcksum, csum); + + return data; +} + +int +ntohtcp(Tcp *tcph, Block **bpp) +{ + ushort hdrlen; + ushort i, optlen; + Block *nbp; + Tcphdr *h; + uchar *optr; + + *bpp = pullup(*bpp, TCP_PKT+TCP_HDRSIZE); + if(*bpp == 0) + return -1; + + h = (Tcphdr *)((*bpp)->rptr); + tcph->source = nhgets(h->tcpsport); + tcph->dest = nhgets(h->tcpdport); + tcph->seq = nhgetl(h->tcpseq); + tcph->ack = nhgetl(h->tcpack); + + hdrlen = (h->tcpflag[0] & 0xf0) >> 2; + if(hdrlen < TCP_HDRSIZE) { + freeb(*bpp); + return -1; + } + + tcph->flags = h->tcpflag[1]; + tcph->wnd = nhgets(h->tcpwin); + tcph->up = nhgets(h->tcpurg); + tcph->mss = 0; + + *bpp = pullup(*bpp, hdrlen+TCP_PKT); + if(!*bpp) + return -1; + + for(optr = h->tcpopt, i = TCP_HDRSIZE; i < hdrlen;) { + switch(*optr++){ + case EOL_KIND: + goto eol; + case NOOP_KIND: + i++; + break; + case MSS_KIND: + optlen = *optr++; + if(optlen == MSS_LENGTH) + tcph->mss = nhgets(optr); + i += optlen; + break; + } + } +eol: + return hdrlen; +} diff --git a/port/tcpoutput.c b/port/tcpoutput.c new file mode 100644 index 0000000000000000000000000000000000000000..c3ead71ffb2da7e512a2981520cc9cfea3ce80c1 --- /dev/null +++ b/port/tcpoutput.c @@ -0,0 +1,283 @@ +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +extern int tcpdbg; +extern ushort tcp_mss; +extern Queue *Tcpoutput; + +#define DPRINT if(tcpdbg) print + +void +tcp_output(Ipconv *s) +{ + Block *hbp,*dbp, *sndq; + ushort ssize, dsize, usable, sent, oobsent; + int qlen; + char doing_oob; + Tcphdr ph; + Tcp seg; + Tcpctl *tcb; + + tcb = &s->tcpctl; + + switch(tcb->state) { + case LISTEN: + case CLOSED: + return; + } + for(;;){ + if (tcb->sndoobq) { + /* We have pending out-of-band data - use it */ + qlen = tcb->sndoobcnt; + oobsent = tcb->snd.ptr - tcb->snd.up; + if (oobsent >= qlen) { + oobsent = qlen; + goto normal; + } + sndq = tcb->sndoobq; + sent = oobsent; + doing_oob = 1; + DPRINT("tcp_out: oob: qlen = %lux sent = %lux\n", + qlen, sent); + } else { + oobsent = 0; + normal: + qlen = tcb->sndcnt; + sent = tcb->snd.ptr - tcb->snd.una - oobsent; + sndq = tcb->sndq; + doing_oob = 0; + DPRINT("tcp_out: norm: qlen = %lux sent = %lux\n", qlen, sent); + } + + /* Don't send anything else until our SYN has been acked */ + if(sent != 0 && !(tcb->flags & SYNACK)) + break; + + if(tcb->snd.wnd == 0){ + /* Allow only one closed-window probe at a time */ + if(sent != 0) + break; + /* Force a closed-window probe */ + usable = 1; + } else { + /* usable window = offered window - unacked bytes in transit + * limited by the congestion window + */ + usable = MIN(tcb->snd.wnd,tcb->cwind) - sent; + if(sent != 0 && qlen - sent < tcb->mss) + usable = 0; + } + + ssize = MIN(qlen - sent, usable); + ssize = MIN(ssize, tcb->mss); + dsize = ssize; + + if (!doing_oob) + seg.up = 0; + else { + seg.up = ssize; + DPRINT("tcp_out: oob seg.up = %d\n", seg.up); + } + + DPRINT("tcp_out: ssize = %lux\n", ssize); + if(ssize == 0 && !(tcb->flags & FORCE)) + break; + + /* Stop ack timer if one will be piggy backed on data */ + stop_timer(&tcb->acktimer); + + tcb->flags &= ~FORCE; + + seg.source = s->psrc; + seg.dest = s->pdst; + /* Every state except SYN_SENT */ + seg.flags = ACK; + seg.mss = 0; + + switch(tcb->state){ + case SYN_SENT: + seg.flags = 0; + /* No break */ + case SYN_RECEIVED: + if(tcb->snd.ptr == tcb->iss){ + seg.flags |= SYN; + dsize--; + /* Also send MSS */ + seg.mss = tcp_mss; + } + break; + } + seg.seq = tcb->snd.ptr; + seg.ack = tcb->last_ack = tcb->rcv.nxt; + seg.wnd = tcb->rcv.wnd; + + if (doing_oob) { + DPRINT("tcp_out: Setting URG (up = %u)\n", seg.up); + seg.flags |= URG; + } + + /* Now try to extract some data from the send queue. + * Since SYN and FIN occupy sequence space and are reflected + * in sndcnt but don't actually sit in the send queue, + * dupb will return one less than dsize if a FIN needs to be sent. + */ + if(dsize != 0){ + if(dupb(&dbp, sndq, sent, dsize) != dsize) { + seg.flags |= FIN; + dsize--; + } + DPRINT("dupb: 1st char = %c\n", dbp->rptr[0]); + } else + dbp = 0; + + /* If the entire send queue will now be in the pipe, set the + * push flag + */ + if((dsize != 0) && + ((sent + ssize) == (tcb->rcvcnt + tcb->rcvoobcnt))) + seg.flags |= PSH; + + /* If this transmission includes previously transmitted data, + * snd.nxt will already be past snd.ptr. In this case, + * compute the amount of retransmitted data and keep score + */ + if(tcb->snd.ptr < tcb->snd.nxt) + tcb->resent += MIN((int)tcb->snd.nxt - (int)tcb->snd.ptr,(int)ssize); + + tcb->snd.ptr += ssize; + + /* If this is the first transmission of a range of sequence + * numbers, record it so we'll accept acknowledgments + * for it later + */ + if(seq_gt(tcb->snd.ptr,tcb->snd.nxt)) + tcb->snd.nxt = tcb->snd.ptr; + + /* Fill in fields of pseudo IP header */ + hnputl(ph.tcpdst, s->dst); + hnputl(ph.tcpsrc, Myip); + hnputs(ph.tcpsport, s->psrc); + hnputs(ph.tcpdport, s->pdst); + + /* Build header, link data and compute cksum */ + if((hbp = htontcp(&seg, dbp, &ph)) == 0) { + freeb(dbp); + return; + } + + /* If we're sending some data or flags, start retransmission + * and round trip timers if they aren't already running. + */ + if(ssize != 0){ + tcb->timer.start = backoff(tcb->backoff) * + (2 * tcb->mdev + tcb->srtt + MSPTICK) / MSPTICK; + if(!run_timer(&tcb->timer)) + start_timer(&tcb->timer); + + /* If round trip timer isn't running, start it */ + if(!run_timer(&tcb->rtt_timer)){ + start_timer(&tcb->rtt_timer); + tcb->rttseq = tcb->snd.ptr; + } + } + DPRINT("tcp_output: ip_send s%lux a%lux w%lux u%lux\n", + seg.seq, seg.ack, seg.wnd, seg.up); + + PUTNEXT(Tcpoutput, hbp); + } +} + +int tcptimertype = 0; + +void +tcp_timeout(void *arg) +{ + Tcpctl *tcb; + Ipconv *s; + + s = (Ipconv *)arg; + tcb = &s->tcpctl; + + DPRINT("Timer %lux state = %d\n", s, tcb->state); + + switch(tcb->state){ + case CLOSED: + panic("tcptimeout"); + case TIME_WAIT: + close_self(s, 0); + break; + case ESTABLISHED: + if(tcb->backoff < MAXBACKOFF) + tcb->backoff++; + goto retran; + default: + tcb->backoff++; + DPRINT("tcp_timeout: retransmit %d %x\n", tcb->backoff, s); + + if (tcb->backoff >= MAXBACKOFF) { + DPRINT("tcp_timeout: timeout\n"); + close_self(s, Etimedout); + } + else { + retran: + qlock(tcb); + tcb->flags |= RETRAN|FORCE; + tcb->snd.ptr = tcb->snd.una; + + /* Reduce slowstart threshold to half current window */ + tcb->ssthresh = tcb->cwind / 2; + tcb->ssthresh = MAX(tcb->ssthresh,tcb->mss); + + /* Shrink congestion window to 1 packet */ + tcb->cwind = tcb->mss; + tcp_output(s); + qunlock(tcb); + } + } +} + +int +backoff(int n) +{ + if(tcptimertype == 1) + return n+1; + else { + if(n <= 4) + return 1 << n; + else + return n * n; + } +} + +void +tcp_acktimer(Ipconv *s) +{ + Tcpctl *tcb = &s->tcpctl; + + qlock(tcb); + tcb->flags |= FORCE; + tcprcvwin(s); + tcp_output(s); + qunlock(tcb); +} + +void +tcprcvwin(Ipconv *s) +{ + Tcpctl *tcb = &s->tcpctl; + + /* Calculate new window */ + if(s->readq) { + tcb->rcv.wnd = Streamhi - s->readq->next->len; + if(tcb->rcv.wnd < 0) + tcb->rcv.wnd = 0; + } + else + tcb->rcv.wnd = Streamhi; +} diff --git a/port/tcptimer.c b/port/tcptimer.c new file mode 100644 index 0000000000000000000000000000000000000000..7ccf07843c6985f34ebd64c85f5c01270cf72886 --- /dev/null +++ b/port/tcptimer.c @@ -0,0 +1,129 @@ + #include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "errno.h" +#include "arp.h" +#include "ipdat.h" + +/* Head of running timer chain */ +Timer *timers; +QLock timerlock; +Rendez Tcpack; +Rendez tcpflowr; + +void +tcpackproc(void *junk) +{ + Timer *t,*tp; + Timer *expired; + + for(;;) { + expired = 0; + + /* Run through the list of running timers, decrementing each one. + * If one has expired, take it off the running list and put it + * on a singly linked list of expired timers + */ + + qlock(&timerlock); + for(t = timers;t != 0; t = tp) { + tp = t->next; + if(tp == t) + panic("Timer loop at %lux\n",(long)tp); + + if(t->state == TIMER_RUN && --(t->count) == 0){ + + /* Delete from active timer list */ + if(timers == t) + timers = t->next; + if(t->next != 0) + t->next->prev = t->prev; + if(t->prev != 0) + t->prev->next = t->next; + + t->state = TIMER_EXPIRE; + /* Put on head of expired timer list */ + t->next = expired; + expired = t; + } + } + qunlock(&timerlock); + + while((t = expired) != 0){ + expired = t->next; + if(t->state == TIMER_EXPIRE && t->func) + (*t->func)(t->arg); + } + + tsleep(&Tcpack, return0, 0, MSPTICK); + } +} + +void +start_timer(Timer *t) +{ + + if(t == 0 || t->start == 0) + return; + + qlock(&timerlock); + + t->count = t->start; + if(t->state != TIMER_RUN){ + t->state = TIMER_RUN; + /* Put on head of active timer list */ + t->prev = 0; + t->next = timers; + if(t->next != 0) + t->next->prev = t; + timers = t; + } + qunlock(&timerlock); +} + +void +stop_timer(Timer *t) +{ + if(t == 0) + return; + + qlock(&timerlock); + + if(t->state == TIMER_RUN){ + /* Delete from active timer list */ + if(timers == t) + timers = t->next; + if(t->next != 0) + t->next->prev = t->prev; + if(t->prev != 0) + t->prev->next = t->next; + } + t->state = TIMER_STOP; + + qunlock(&timerlock); +} + +void +tcpflow(void *conv) +{ + Ipconv *base, *ifc, *etab; + + base = (Ipconv*)conv; + + etab = &base[conf.ip]; + for(;;) { + sleep(&tcpflowr, return0, 0); + + for(ifc = base; ifc < etab; ifc++) { + if(ifc->stproto == &tcpinfo && + ifc->ref != 0 && ifc->readq && + !QFULL(ifc->readq->next)) { + tcprcvwin(ifc); + tcp_acktimer(ifc); + } + } + } +} + diff --git a/power/conf.h b/power/conf.h index 4df01549f968d084cd92bee4ce515128010e75e6..0ec75659080993e5b400fbc4db86ef3b264bc457 100644 --- a/power/conf.h +++ b/power/conf.h @@ -30,6 +30,10 @@ Conftab conftab[] = { {"base0", &conf.base0 }, {"base1", &conf.base1 }, {"copymode", &conf.copymode }, + {"ipif", &conf.ipif }, + {"ip", &conf.ip }, + {"arp", &conf.arp }, + {"frag", &conf.frag }, { 0, 0 }, }; diff --git a/power/dat.h b/power/dat.h index c38ec1f6bbbf36a453018585e3aaf1ea2f9cc0ca..a1cf8bd43ed86ed8503b81729ab1c7d6192e9f35 100644 --- a/power/dat.h +++ b/power/dat.h @@ -170,6 +170,11 @@ struct Conf ulong base0; /* base of bank 0 */ ulong base1; /* base of bank 1 */ ulong copymode; /* 0 is copy on write, 1 is copy on reference */ + ulong ipif; /* Ip protocol interfaces */ + ulong ip; /* Ip conversations per interface */ + ulong arp; /* Arp table size */ + ulong frag; /* Ip fragment assemble queue size */ + }; struct Dev diff --git a/power/errno.h b/power/errno.h index a233cab1fc0dc3b810e7fc1a1f2028bb20e894a1..e42273972351e0d4b423c6b6922491ca511cb170 100644 --- a/power/errno.h +++ b/power/errno.h @@ -53,6 +53,15 @@ enum{ Enovmem, /* virtual memory allocation failed */ Enoasync, /* out of async stream modules */ Enopipe, /* out of pipes */ + Emsgsize, /* message is too big for protocol */ + Enoport, /* network port not available */ + Edevbusy, /* network device is busy or allocated */ + Eaddrnotfound, /* network address not found */ + Enetunreach, /* network unreachable */ + Etimedout, /* connection timed out */ + Econrefused, /* connection refused */ + Enoproto, /* network protocol not supported */ + Eprotonosup, /* operation not supported by network protocol */ Eisstream, /* seek on a stream */ Egreg, /* ken has implemented datakit */ }; diff --git a/power/fns.h b/power/fns.h index ad1105794acbf8413d0a64bfacc1a8596dcc34c1..5164334b1c7d1de180c790f07c9729709817e5bc 100644 --- a/power/fns.h +++ b/power/fns.h @@ -4,6 +4,8 @@ Block *allocb(ulong); int anyready(void); void append(List**, List*); void arginit(void); +int blen(Block *); +int bround(Block *, int); void cancel(Alarm*); int canlock(Lock*); int canqlock(QLock*); @@ -141,6 +143,7 @@ void printinit(void); void printslave(void); void procinit0(void); Proc *proctab(int); +Block *pullup(Block *, int); void purgetlb(int); Queue *pushq(Stream*, Qinfo*); void putmmu(ulong, ulong); diff --git a/power/ipdat.h b/power/ipdat.h new file mode 100644 index 0000000000000000000000000000000000000000..37ecd18825c57dd84a4f614cf8d5555749d0a027 --- /dev/null +++ b/power/ipdat.h @@ -0,0 +1,440 @@ +typedef struct Ipconv Ipconv; +typedef struct Ipifc Ipifc; +typedef struct Fragq Fragq; +typedef struct Ipfrag Ipfrag; +typedef ulong Ipaddr; +typedef struct Arpcache Arpcache; +typedef ushort Port; +typedef struct Udphdr Udphdr; +typedef struct Etherhdr Etherhdr; +typedef struct Reseq Reseq; +typedef struct Tcp Tcp; +typedef struct Tcpctl Tcpctl; +typedef struct Tcphdr Tcphdr; +typedef struct Timer Timer; + +struct Etherhdr { +#define ETHER_HDR 14 + uchar d[6]; + uchar s[6]; + uchar type[2]; + + /* Now we have the ip fields */ +#define ETHER_IPHDR 20 + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + uchar ttl; /* Time to live */ + uchar proto; /* Protocol */ + uchar cksum[2]; /* Header checksum */ + uchar src[4]; /* Ip source */ + uchar dst[4]; /* Ip destination */ +}; + +/* Ethernet packet types */ +#define ET_IP 0x0800 + +/* A userlevel data gram */ +struct Udphdr { +#define UDP_EHSIZE 22 + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + + /* Udp pseudo ip really starts here */ +#define UDP_PHDRSIZE 12 +#define UDP_HDRSIZE 20 + uchar Unused; + uchar udpproto; /* Protocol */ + uchar udpplen[2]; /* Header plus data length */ + uchar udpsrc[4]; /* Ip source */ + uchar udpdst[4]; /* Ip destination */ + uchar udpsport[2]; /* Source port */ + uchar udpdport[2]; /* Destination port */ + uchar udplen[2]; /* data length */ + uchar udpcksum[2]; /* Checksum */ +}; + +#define TCP_PKT (TCP_EHSIZE+TCP_IPLEN+TCP_PHDRSIZE) + +struct Tcphdr { +#define TCP_EHSIZE 14 + uchar d[6]; /* Ethernet destination */ + uchar s[6]; /* Ethernet source */ + uchar type[2]; /* Ethernet packet type */ +#define TCP_IPLEN 8 + uchar vihl; /* Version and header length */ + uchar tos; /* Type of service */ + uchar length[2]; /* packet length */ + uchar id[2]; /* Identification */ + uchar frag[2]; /* Fragment information */ + +#define TCP_PHDRSIZE 12 + uchar Unused; + uchar proto; + uchar tcplen[2]; + uchar tcpsrc[4]; + uchar tcpdst[4]; + +#define TCP_HDRSIZE 20 + uchar tcpsport[2]; + uchar tcpdport[2]; + uchar tcpseq[4]; + uchar tcpack[4]; + uchar tcpflag[2]; + uchar tcpwin[2]; + uchar tcpcksum[2]; + uchar tcpurg[2]; + + /* Options segment */ + uchar tcpopt[2]; + uchar tcpmss[2]; + }; + + + +struct Timer { + Timer *next; + Timer *prev; + int state; + int start; + int count; + void (*func)(void*); + void *arg; + }; + +struct Tcpctl { + QLock; + uchar state; /* Connection state */ + uchar type; /* Listening or active connection */ + uchar code; /* Icmp code */ + struct { + int una; /* Unacked data pointer */ + int nxt; /* Next sequence expected */ + int ptr; /* Data pointer */ + ushort wnd; /* Tcp send window */ + int up; /* Urgent data pointer */ + int wl1; + int wl2; + } snd; + int iss; + ushort cwind; + ushort ssthresh; + int resent; + struct { + int nxt; + ushort wnd; + int up; + } rcv; + int irs; + ushort mss; + int rerecv; + ushort window; + int max_snd; + int last_ack; + char backoff; + char flags; + char tos; + + Block *rcvq; + ushort rcvcnt; + + Block *rcvoobq; + ushort rcvoobcnt; + + Block *sndq; /* List of data going out */ + ushort sndcnt; /* Amount of data in send queue */ + + Block *sndoobq; /* List of blocks going oob */ + ushort sndoobcnt; /* Size of out of band queue */ + ushort oobmark; /* Out of band sequence mark */ + char oobflags; /* Out of band data flags */ + + Reseq *reseq; /* Resequencing queue */ + Timer timer; + Timer acktimer; /* Acknoledge timer */ + Timer rtt_timer; /* Round trip timer */ + int rttseq; /* Round trip sequence */ + int srtt; /* Shortened round trip */ + int mdev; /* Mean deviation of round trip */ +}; + +struct Tcp { + Port source; + Port dest; + int seq; + int ack; + char flags; + ushort wnd; + ushort up; + ushort mss; + }; + +struct Reseq { + Reseq *next; + Tcp seg; + Block *bp; + ushort length; + char tos; + }; + +/* An ip interface used for UDP/TCP/ARP/ICMP */ +struct Ipconv { + QLock; /* Ref count lock */ + int ref; + Qinfo *stproto; /* Stream protocol for this device */ + Ipaddr dst; /* Destination from connect */ + + Port psrc; /* Source port */ + Port pdst; /* Destination port */ + + uchar ptype; /* Source port type */ + Ipifc *ipinterface; /* Ip protocol interface */ + Queue *readq; /* Pointer to upstream read q */ + + QLock listenq; /* List of people waiting incoming cons */ + Rendez listenr; /* Some where to sleep while waiting */ + Ipconv *listen; + + char err; /* Async protocol error */ + int backlog; /* Maximum number of waiting connections */ + int curlog; /* Number of waiting connections */ + int contype; + Tcpctl tcpctl; /* Tcp control block */ +}; + +#define MAX_TIME 100000000 /* Forever */ +#define TCP_ACK 200 /* Timed ack sequence every 200ms */ + +#define URG 0x20 +#define ACK 0x10 +#define PSH 0x08 +#define RST 0x04 +#define SYN 0x02 +#define FIN 0x01 + +#define EOL_KIND 0 +#define NOOP_KIND 1 +#define MSS_KIND 2 + +#define MSS_LENGTH 4 +#define MSL2 10 +#define MSPTICK 200 +#define DEF_MSS 1024 +#define DEF_RTT 1000 +#define TCPOOB_HADDATA 1 +#define TCPOOB_HAVEDATA 2 + +#define TCP_PASSIVE 0 +#define TCP_ACTIVE 1 + +#define MAXBACKOFF 5 +#define FORCE 1 +#define CLONE 2 +#define RETRAN 4 +#define ACTIVE 8 +#define SYNACK 16 +#define AGAIN 8 +#define DGAIN 4 + +#define TIMER_STOP 0 +#define TIMER_RUN 1 +#define TIMER_EXPIRE 2 + +#define set_timer(t,x) (((t)->start) = (x)/MSPTICK) +#define dur_timer(t) ((t)->start) +#define read_timer(t) ((t)->count) +#define run_timer(t) ((t)->state == TIMER_RUN) + +enum { + CLOSED = 0, + LISTEN, + SYN_SENT, + SYN_RECEIVED, + ESTABLISHED, + FINWAIT1, + FINWAIT2, + CLOSE_WAIT, + CLOSING, + LAST_ACK, + TIME_WAIT + }; + +/* + * Ip interface structure. We have one for each active protocol driver + */ +struct Ipifc { + QLock; + int ref; + uchar protocol; /* Ip header protocol number */ + char name[NAMELEN]; /* Protocol name */ + void (*iprcv) (Ipconv *, Block *); /* Receive demultiplexor */ + Ipconv *connections; /* Connection list */ + int maxmtu; /* Maximum transfer unit */ + int minmtu; /* Minumum tranfer unit */ + int hsize; /* Media header size */ + Lock; +}; + +struct Fragq { + QLock; + Block *blist; + Fragq *next; + Ipaddr src; + Ipaddr dst; + ushort id; + }; + +struct Ipfrag { + ushort foff; + ushort flen; + }; + +struct Arpcache { + uchar status; /* Entry status */ + uchar type; /* Entry type */ + Ipaddr ip; /* Host byte order */ + uchar eip[4]; /* Network byte order */ + uchar et[6]; /* Ethernet address for this ip */ + int age; /* Entry timeout */ + Arpcache *hash; + Arpcache **hashhd; + Arpcache *frwd; + Arpcache *prev; +}; +#define ARP_FREE 0 +#define ARP_OK 1 +#define ARP_ASKED 2 +#define ARP_TEMP 0 +#define ARP_PERM 1 +#define Arphashsize 32 +#define ARPHASH(p) arphash[((p[2]^p[3])%Arphashsize)] +#define ARP_WAITMS 2500 /* Wait for arp replys */ + +#define IP_VER 0x40 /* Using IP version 4 */ +#define IP_HLEN 0x05 /* Header length in characters */ +#define IP_DF 0x4000 /* Don't fragment */ +#define IP_MF 0x2000 /* More fragments */ + +#define ICMP_ECHOREPLY 0 /* Echo Reply */ +#define ICMP_UNREACH 3 /* Destination Unreachable */ +#define ICMP_SOURCEQUENCH 4 /* Source Quench */ +#define ICMP_REDIRECT 5 /* Redirect */ +#define ICMP_ECHO 8 /* Echo Request */ +#define ICMP_TIMXCEED 11 /* Time-to-live Exceeded */ +#define ICMP_PARAMPROB 12 /* Parameter Problem */ +#define ICMP_TSTAMP 13 /* Timestamp */ +#define ICMP_TSTAMPREPLY 14 /* Timestamp Reply */ +#define ICMP_IREQ 15 /* Information Request */ +#define ICMP_IREQREPLY 16 /* Information Reply */ + +/* Sizes */ +#define IP_MAX 8192 /* Maximum Internet packet size */ +#define UDP_MAX (IP_MAX-ETHER_IPHDR) /* Maximum UDP datagram size */ +#define UDP_DATMAX (UDP_MAX-UDP_HDRSIZE) /* Maximum amount of udp data */ + +/* Protocol numbers */ +#define IP_UDPPROTO 17 +#define IP_TCPPROTO 6 + +/* Protocol port numbers */ +#define PORTALLOC 5000 /* First automatic allocated port */ +#define PRIVPORTALLOC 600 /* First priveleged port allocated */ +#define PORTMAX 30000 /* Last port to allocte */ + +/* Stuff to go in funs.h someday */ +Ipifc *newipifc(uchar, void (*)(Ipconv *, Block*), Ipconv *, int, int, int, char*); +void closeipifc(Ipifc*); +ushort ip_csum(uchar*); +int arp_lookup(uchar*, uchar*); +Ipaddr ipparse(char*); +void hnputs(uchar*, ushort); +void hnputl(uchar*, ulong); +ulong nhgetl(uchar*); +ushort nhgets(uchar*); +ushort ptcl_csum(Block*bp, int, int); +void ppkt(Block*); +void udprcvmsg(Ipconv *, Block*); +Block *btrim(Block*, int, int); +Block *ip_reassemble(int, Block*, Etherhdr*); +Ipconv *portused(Ipconv *, Port); +Port nextport(Ipconv *, Port); +void arp_enter(Arpentry*, int); +void arp_flush(void); +int arp_delete(char*); +void arplinkhead(Arpcache*); +Fragq *ipfragallo(void); +void ipfragfree(Fragq*); +void iproute(uchar*, uchar*); +void initfrag(int); +Block *copyb(Block*, int); +int ntohtcp(Tcp*, Block**); +void reset(Ipaddr, Ipaddr, char, ushort, Tcp*); +void proc_syn(Ipconv*, char, Tcp*); +void send_syn(Tcpctl*); +void tcp_output(Ipconv*); +int seq_within(int, int, int); +void update(Ipconv *, Tcp *); +int trim(Tcpctl *, Tcp *, Block **, ushort *); +void add_reseq(Tcpctl *, char, Tcp *, Block *, ushort); +void close_self(Ipconv *, int); +int seq_gt(int, int); +void appendb(Block **, Block *); +Ipconv *ip_conn(Ipconv *, Port, Port, Ipaddr dest, char proto); +void ipmkdir(Qinfo *, Dirtab *, Ipconv *); +int inb_window(Tcpctl *, int); +Block *htontcp(Tcp *, Block *, Tcphdr *); +void start_timer(Timer *); +void stop_timer(Timer *); +int copyupb(Block **, uchar *, int); +void init_tcpctl(Ipconv *); +void close_self(Ipconv *, int); +int iss(void); +int seq_within(int, int, int); +int seq_lt(int, int); +int seq_le(int, int); +int seq_gt(int, int); +int seq_ge(int, int); +void setstate(Ipconv *, char); +void tcpackproc(void*); +Block *htontcp(Tcp *, Block *, Tcphdr *); +int ntohtcp(Tcp *, Block **); +void extract_oob(Block **, Block **, Tcp *); +void get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *); +void state_upcall(Ipconv*, char oldstate, char newstate); +int backoff(int); +int dupb(Block **, Block *, int, int); +void tcp_input(Ipconv *, Block *); +void tcprcvwin(Ipconv *); +void open_tcp(Ipconv *, int, ushort, char); +void tcpflow(void*); +void tcp_timeout(void *); +void tcp_acktimer(void *); +Ipconv *ipclonecon(Chan *); +void iplisten(Chan *, Ipconv *, Ipconv *); + +#define fmtaddr(xx) (xx>>24)&0xff,(xx>>16)&0xff,(xx>>8)&0xff,xx&0xff +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define BLKIP(xp) ((Etherhdr *)((xp)->rptr)) +#define BLKFRAG(xp) ((Ipfrag *)((xp)->rptr)) +#define PREC(x) ((x)>>5 & 7) + +#define WORKBUF 64 + +extern Ipaddr Myip; +extern Ipaddr Mymask; +extern Ipaddr classmask[4]; +extern Ipconv *ipconv[]; +extern char *tcpstate[]; +extern Rendez tcpflowr; +extern Qinfo tcpinfo; +extern Qinfo ipinfo; +extern Qinfo udpinfo; diff --git a/power/main.c b/power/main.c index 4f64023c3ba86a5602c01687e04c19cabc4d08c3..434414af48c2bae624e9b7e441984ae4c5defcda 100644 --- a/power/main.c +++ b/power/main.c @@ -634,6 +634,10 @@ confinit(void) conf.nurp = 25; conf.nqueue = 5 * conf.nstream; conf.nblock = 10 * conf.nstream; + conf.ipif = 8; + conf.ip = 64; + conf.arp = 32; + conf.frag = 32; confread();