A port/arp.h => port/arp.h +42 -0
@@ 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
A port/devarp.c => port/devarp.c +233 -0
@@ 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;
+}
+
A port/devip.c => port/devip.c +882 -0
@@ 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;
+}
+
A port/ipdat.h => port/ipdat.h +440 -0
@@ 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;
M port/lib.h => port/lib.h +1 -1
@@ 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
*/
A port/stip.c => port/stip.c +631 -0
@@ 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");
+
+}
A port/tcpif.c => port/tcpif.c +84 -0
@@ 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;
+ }
+}
A port/tcpinput.c => port/tcpinput.c +1126 -0
@@ 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;
+}
A port/tcpoutput.c => port/tcpoutput.c +283 -0
@@ 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;
+}
A port/tcptimer.c => port/tcptimer.c +129 -0
@@ 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);
+ }
+ }
+ }
+}
+
M power/conf.h => power/conf.h +4 -0
@@ 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 },
};
M power/dat.h => power/dat.h +5 -0
@@ 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
M power/errno.h => power/errno.h +9 -0
@@ 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 */
};
M power/fns.h => power/fns.h +3 -0
@@ 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);
A power/ipdat.h => power/ipdat.h +440 -0
@@ 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;
M power/main.c => power/main.c +4 -0
@@ 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();