page@swan.ulowell.edu (Bob Page) (03/18/89)
Submitted-by: rminnich@super.org (Ronald G. Minnich) Posting-number: Volume 89, Issue 82 Archive-name: comm/amigatcp.3 # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # ftpserv.c # tcpsubr.c # ip.c # udp.c # This archive created: Fri Mar 17 17:57:41 1989 cat << \SHAR_EOF > ftpserv.c /* FTP Server state machine - see RFC 959 */ #define LINELEN 128 /* Length of command buffer */ #include <stdio.h> #include "machdep.h" #include "mbuf.h" #include "netuser.h" #include "timer.h" #include "tcp.h" #include "ftp.h" /* Command table */ static char *commands[] = { "user", #define USER_CMD 0 "acct", #define ACCT_CMD 1 "pass", #define PASS_CMD 2 "type", #define TYPE_CMD 3 "list", #define LIST_CMD 4 "cwd", #define CWD_CMD 5 "dele", #define DELE_CMD 6 "name", #define NAME_CMD 7 "quit", #define QUIT_CMD 8 "retr", #define RETR_CMD 9 "stor", #define STOR_CMD 10 "port", #define PORT_CMD 11 "nlst", #define NLST_CMD 12 "pwd", #define PWD_CMD 13 "xpwd", /* For compatibility with 4.2BSD */ #define XPWD_CMD 14 NULLCHAR }; /* Response messages */ static char banner[] = "220 %s FTP Ready\r\n"; static char badcmd[] = "500 Unknown command\r\n"; static char nopass[] = "202 Password not needed\r\n"; static char logged[] = "230 Logged in\r\n"; static char typeok[] = "200 Type OK\r\n"; static char cwdok[] = "250 CWD OK\r\n"; static char pwdmsg[] = "257 \"%s\" is current directory\r\n"; static char badtype[] = "501 Unknown type\r\n"; static char badport[] = "501 Bad port syntax\r\n"; static char unimp[] = "502 Command not yet implemented\r\n"; static char bye[] = "221 Goodbye!\r\n"; static char nodir[] = "553 Can't read directory\r\n"; static char cantopen[] = "550 Can't open file\r\n"; static char sending[] = "150 Opening data connection for %s %s\r\n"; static char cantmake[] = "553 Can't create file\r\n"; static char portok[] = "200 Port command okay\r\n"; static char rxok[] = "226 File received OK\r\n"; static char txok[] = "226 File sent OK\r\n"; static struct tcb *ftp_tcb; /* Start up FTP service */ ftp_start(argc,argv) int argc; char *argv[]; { struct socket lsocket; void r_ftp(),s_ftp(); lsocket.address = ip_addr; if(argc < 2) lsocket.port = FTP_PORT; else lsocket.port = atoi(argv[1]); ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_ftp,NULLVFP,s_ftp,0,(int *)NULL); } ftp_stop() { if(ftp_tcb != NULLTCB) close_tcp(ftp_tcb); } /* FTP server control channel connection state change upcall handler */ static void s_ftp(tcb,old,new) struct tcb *tcb; char old,new; { extern char hostname[]; struct ftp *ftp,*ftp_create(); void ftp_delete(); char *inet_ntoa(),*pwd(); switch(new){ #ifdef QUICKSTART case SYN_RECEIVED: #else case ESTABLISHED: #endif if((ftp = ftp_create(LINELEN)) == NULLFTP){ /* No space, kill connection */ close_tcp(tcb); return; } ftp->control = tcb; /* Downward link */ tcb->user = (int *)ftp; /* Upward link */ /* Set default data port */ ftp->port.address = tcb->conn.remote.address; ftp->port.port = FTPD_PORT; /* Note current directory */ #ifndef AMIGA ftp->cd = pwd(); #endif log(tcb,"open FTP"); tprintf(ftp->control,banner,hostname); break; case CLOSE_WAIT: close_tcp(tcb); break; case CLOSED: log(tcb,"close FTP"); if((ftp = (struct ftp *)tcb->user) != NULLFTP) ftp_delete(ftp); /* Check if server is being shut down */ if(tcb == ftp_tcb) ftp_tcb = NULLTCB; del_tcp(tcb); break; } } /* FTP control channel receiver upcall handler */ static void r_ftp(tcb,cnt) struct tcb *tcb; int16 cnt; { register struct ftp *ftp; char *index(),c; struct mbuf *bp; void docommand(); if((ftp = (struct ftp *)tcb->user) == NULLFTP){ /* Unknown connection, just kill it */ close_tcp(tcb); return; } switch(ftp->state){ case COMMAND_STATE: /* Assemble an input line in the session buffer. Return if incomplete */ recv_tcp(tcb,&bp,0); while(pullup(&bp,&c,1) == 1){ switch(c){ case '\r': /* Strip cr's */ continue; case '\n': /* Complete line; process it */ ftp->buf[ftp->cnt] = '\0'; docommand(ftp); ftp->cnt = 0; break; default: /* Assemble line */ if(ftp->cnt != LINELEN-1) ftp->buf[ftp->cnt++] = c; break; } } /* else no linefeed present yet to terminate command */ break; case SENDING_STATE: case RECEIVING_STATE: /* Leave commands pending on receive queue until * present command is done */ break; } } /* FTP server data channel connection state change upcall handler */ void s_ftpd(tcb,old,new) struct tcb *tcb; char old,new; { struct ftp *ftp; #ifndef CPM #ifndef AMIGA char *cdsave; #endif #endif if((ftp = (struct ftp *)tcb->user) == NULLFTP){ /* Unknown connection, kill it */ close_tcp(tcb); return; } switch(new){ case FINWAIT2: case TIME_WAIT: if(ftp != NULLFTP && ftp->state == SENDING_STATE){ /* We've received an ack of our FIN, so * send a completion message on the control channel */ ftp->state = COMMAND_STATE; tprintf(ftp->control,txok); /* Kick command parser if something is waiting */ if(ftp->control->rcvcnt != 0) r_ftp(ftp->control,ftp->control->rcvcnt); } break; case CLOSE_WAIT: close_tcp(tcb); if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){ /* End of file received on incoming file */ #ifdef CPM if(ftp->type == ASCII_TYPE) putc(CTLZ,ftp->fp); #endif #ifndef CPM #ifndef AMIGA cdsave = pwd(); /* Save current directory */ chdir(ftp->cd); /* Switch to user's directory*/ #endif #endif fclose(ftp->fp); #ifndef CPM #ifndef AMIGA if(cdsave != NULLCHAR){ chdir(cdsave); /* And back */ free(cdsave); } #endif #endif ftp->fp = NULLFILE; ftp->state = COMMAND_STATE; tprintf(ftp->control,rxok); /* Kick command parser if something is waiting */ if(ftp->control->rcvcnt != 0) r_ftp(ftp->control,ftp->control->rcvcnt); } break; case CLOSED: if(ftp != NULLFTP) ftp->data = NULLTCB; del_tcp(tcb); break; } } /* Parse and execute ftp commands */ static void docommand(ftp) register struct ftp *ftp; { void r_ftpd(),t_ftpd(),s_ftpd(); char *cmd,*arg,*cp,**cmdp; char *index(),*malloc(),*strcpy(); struct socket dport; #ifndef CPM #ifndef AMIGA FILE *dir(); char *cdsave; #endif #endif cmd = ftp->buf; if(ftp->cnt == 0){ /* Can't be a legal FTP command */ tprintf(ftp->control,badcmd); return; } cmd = ftp->buf; /* Translate entire buffer to lower case */ for(cp = cmd;*cp != '\0';cp++) *cp = tolower(*cp); /* Find command in table; if not present, return syntax error */ for(cmdp = commands;*cmdp != NULLCHAR;cmdp++) if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0) break; if(*cmdp == NULLCHAR){ tprintf(ftp->control,badcmd); return; } arg = &cmd[strlen(*cmdp)]; while(*arg == ' ') arg++; /* Execute specific command */ switch(cmdp-commands){ case USER_CMD: if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){ close_tcp(ftp->control); break; } strcpy(ftp->username,arg); tprintf(ftp->control,logged); break; case TYPE_CMD: switch(*arg){ case 'a': /* Ascii */ ftp->type = ASCII_TYPE; tprintf(ftp->control,typeok); break; case 'b': /* Binary */ case 'i': /* Image */ ftp->type = IMAGE_TYPE; tprintf(ftp->control,typeok); break; default: /* Invalid */ tprintf(ftp->control,badtype); break; } break; case QUIT_CMD: tprintf(ftp->control,bye); close_tcp(ftp->control); break; case RETR_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); #ifndef CPM #ifndef AMIGA cdsave = pwd(); /* Save current directory */ chdir(ftp->cd); /* Switch to user's directory*/ #endif #endif ftp->fp = fopen(arg,"r"); #ifndef CPM #ifndef AMIGA chdir(cdsave); /* And back */ free(cdsave); #endif #endif if(ftp->fp == NULLFILE){ tprintf(ftp->control,cantopen); } else { log(ftp->control,"RETR %s/%s",ftp->cd,arg); dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = SENDING_STATE; tprintf(ftp->control,sending,"RETR",arg); /* This hack is just so we can talk to ourselves */ ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE, 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp); } break; case STOR_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); #ifndef CPM #ifndef AMIGA cdsave = pwd(); /* Save current directory */ chdir(ftp->cd); /* Switch to user's directory */ #endif #endif ftp->fp = fopen(arg,"w"); #ifndef CPM #ifndef AMIGA chdir(cdsave); /* And back */ free(cdsave); #endif #endif if(ftp->fp == NULLFILE){ tprintf(ftp->control,cantmake); } else { log(ftp->control,"STOR %s/%s",ftp->cd,arg); dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = RECEIVING_STATE; tprintf(ftp->control,sending,"STOR",arg); /* This hack is just so we can talk to ourselves */ ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE, 0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp); } break; case PORT_CMD: if(pport(&ftp->port,arg) == -1){ tprintf(ftp->control,badport); } else { tprintf(ftp->control,portok); } break; /* #ifndef CPM */ #ifndef AMIGA case LIST_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); cdsave = pwd(); /* Save current directory */ chdir(ftp->cd); /* Switch to user's directory */ ftp->fp = dir(arg,1); chdir(cdsave); /* And back */ free(cdsave); if(ftp->fp == NULLFILE){ tprintf(ftp->control,nodir); break; } dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = SENDING_STATE; tprintf(ftp->control,sending,"LIST",arg); /* This hack is just so we can talk to ourselves */ ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE, 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp); break; case NLST_CMD: /* Disk operation; return ACK now */ tcp_output(ftp->control); cdsave = pwd(); /* Save current directory */ chdir(ftp->cd); /* Switch to user's directory */ ftp->fp = dir(arg,0); chdir(cdsave); /* And back */ free(cdsave); if(ftp->fp == NULLFILE){ tprintf(ftp->control,nodir); break; } dport.address = ip_addr; dport.port = FTPD_PORT; ftp->state = SENDING_STATE; tprintf(ftp->control,sending,"NLST",arg); /* This hack is just so we can talk to ourselves */ ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE, 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp); ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE, 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp); break; case CWD_CMD: tcp_output(ftp->control); /* Disk operation; return ACK now */ cdsave = pwd(); /* Save current directory */ chdir(ftp->cd); /* Go to user's context */ if(chdir(arg) == 0){ /* Attempt switch */ /* Succeeded, record in control block */ free(ftp->cd); ftp->cd = pwd(); tprintf(ftp->control,cwdok); } else { /* Failed, don't change anything */ tprintf(ftp->control,nodir); } chdir(cdsave); /* Go back */ free(cdsave); break; case XPWD_CMD: case PWD_CMD: tprintf(ftp->control,pwdmsg,ftp->cd); break; #else case LIST_CMD: case NLST_CMD: case CWD_CMD: case XPWD_CMD: case PWD_CMD: #endif case ACCT_CMD: case DELE_CMD: tprintf(ftp->control,unimp); break; case PASS_CMD: tprintf(ftp->control,nopass); break; } } static int pport(sock,arg) struct socket *sock; char *arg; { int32 n; int atoi(),i; n = 0; for(i=0;i<4;i++){ n = atoi(arg) + (n << 8); if((arg = index(arg,',')) == NULLCHAR) return -1; arg++; } sock->address = n; n = atoi(arg); if((arg = index(arg,',')) == NULLCHAR) return -1; arg++; n = atoi(arg) + (n << 8); sock->port = n; return 0; } SHAR_EOF cat << \SHAR_EOF > tcpsubr.c #ifdef TRACE #include <stdio.h> #endif #include "machdep.h" #include "timer.h" #include "mbuf.h" #include "netuser.h" #include "internet.h" #include "tcp.h" struct tcb *tcbs[NTCB]; /* Lookup connection, return TCB pointer or NULLTCB if nonexistant */ struct tcb * lookup_tcb(conn) struct connection *conn; { register struct tcb *tcb; int16 hash_tcb(); tcb = tcbs[hash_tcb(conn)]; while(tcb != NULLTCB){ /* Yet another structure compatibility hack */ if(conn->local.address == tcb->conn.local.address && conn->remote.address == tcb->conn.remote.address && conn->local.port == tcb->conn.local.port && conn->remote.port == tcb->conn.remote.port) break; tcb = tcb->next; } return tcb; } /* Create a TCB, return pointer. Return pointer if TCB already exists. */ struct tcb * create_tcb(conn) struct connection *conn; { char *calloc(); register struct tcb *tcb; void tcp_timeout(),tcp_msl(); void link_tcb(); if((tcb = lookup_tcb(conn)) != NULLTCB) return tcb; if((tcb = (struct tcb *)calloc(1,sizeof (struct tcb))) == NULLTCB) return NULLTCB; bcopy((char *)conn,(char *)&tcb->conn,sizeof(struct connection)); tcb->mss = DEF_MSS; tcb->srtt = DEF_RTT * MSPTICK; /* Initialize retransmission timeout */ tcb->timer.start = (BETA * tcb->srtt)/MSPTICK; tcb->timer.func = tcp_timeout; tcb->timer.arg = (int *)tcb; link_tcb(tcb); return tcb; } /* Close our TCB */ void close_self(tcb,reason) register struct tcb *tcb; char reason; { struct reseq *rp,*rp1; stop_timer(&tcb->timer); tcb->reason = reason; /* Flush reassembly queue; nothing more can arrive */ for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){ rp1 = rp->next; free_p(rp->bp); free((char *)rp); } tcb->reseq = NULLRESEQ; setstate(tcb,CLOSED); } /* Determine initial sequence number */ #ifdef AMIGA /* * routine called at startup time with inital value of iss for system. This * is probably based on the time or something */ static int32 seq; void setiss(initval) int32 initval; { seq = initval; } #endif int32 iss() { #ifndef AMIGA static int32 seq; #endif seq += 250000; return seq; } /* Sequence number comparisons * Return true if x is between low and high inclusive, * false otherwise */ int seq_within(x,low,high) register int32 x,low,high; { if(low <= high){ if(low <= x && x <= high) return 1; } else { if(low >= x && x >= high) return 1; } return 0; } int seq_lt(x,y) register int32 x,y; { return (long)(x-y) < 0; } int seq_le(x,y) register int32 x,y; { return (long)(x-y) <= 0; } int seq_gt(x,y) register int32 x,y; { return (long)(x-y) > 0; } int seq_ge(x,y) register int32 x,y; { return (long)(x-y) >= 0; } /* Hash a connect structure into the hash chain header array */ static int16 hash_tcb(conn) struct connection *conn; { register int16 hval; /* Compute hash function on connection structure */ hval = hiword(conn->remote.address); hval ^= loword(conn->remote.address); hval ^= hiword(conn->local.address); hval ^= loword(conn->local.address); hval ^= conn->remote.port; hval ^= conn->local.port; hval %= NTCB; return hval; } /* Insert TCB at head of proper hash chain */ void link_tcb(tcb) register struct tcb *tcb; { register struct tcb **tcbhead; int16 hash_tcb(); char i_state; tcb->prev = NULLTCB; i_state = disable(); tcbhead = &tcbs[hash_tcb(&tcb->conn)]; tcb->next = *tcbhead; if(tcb->next != NULLTCB){ tcb->next->prev = tcb; } *tcbhead = tcb; restore(i_state); } /* Remove TCB from whatever hash chain it may be on */ void unlink_tcb(tcb) register struct tcb *tcb; { register struct tcb **tcbhead; int16 hash_tcb(); char i_state; i_state = disable(); tcbhead = &tcbs[hash_tcb(&tcb->conn)]; if(*tcbhead == tcb) *tcbhead = tcb->next; /* We're the first one on the chain */ if(tcb->prev != NULLTCB) tcb->prev->next = tcb->next; if(tcb->next != NULLTCB) tcb->next->prev = tcb->prev; restore(i_state); } void setstate(tcb,newstate) register struct tcb *tcb; register char newstate; { register char oldstate; oldstate = tcb->state; tcb->state = newstate; if(tcb->s_upcall){ (*tcb->s_upcall)(tcb,oldstate,newstate); } /* Notify the user that he can begin sending data */ if(tcb->t_upcall && newstate == ESTABLISHED){ (*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt); } } #ifdef TRACE /* TCP connection states */ char *tcpstates[] = { "Closed", "Listen", "SYN sent", "SYN received", "Established", "FIN wait 1", "FIN wait 2", "Close wait", "Closing", "Last ACK", "Time wait" }; /* TCP segment header flags */ char *tcpflags[] = { "FIN", /* 0x01 */ "SYN", /* 0x02 */ "RST", /* 0x04 */ "PSH", /* 0x08 */ "ACK", /* 0x10 */ "URG" /* 0x20 */ }; /* TCP closing reasons */ char *reasons[] = { "Normal", "Reset", "Timeout", "ICMP" }; /* Return 1 if arg is a valid TCB, 0 otherwise */ int tcpval(tcb) struct tcb *tcb; { register int i; register struct tcb *tcb1; for(i=0;i<NTCB;i++){ for(tcb1=tcbs[i];tcb1 != NULLTCB;tcb1 = tcb1->next){ if(tcb1 == tcb) return 1; } } return 0; } /* Dump TCP stats and summary of all TCBs /* &TCB Rcv-Q Snd-Q Local socket Remote socket State * 1234 0 0 xxx.xxx.xxx.xxx:xxxxx xxx.xxx.xxx.xxx:xxxxx Established */ int tcpstat() { register int i; register struct tcb *tcb; char *psocket(); printf("conout %u conin %u reset out %u runt %u chksum err %u bdcsts %u\r\n", tcp_stat.conout,tcp_stat.conin,tcp_stat.resets,tcp_stat.runt, tcp_stat.checksum,tcp_stat.bdcsts); #ifdef AMIGA printf("&TCB Rcv-Q Snd-Q Local socket Remote socket State\r\n"); #else printf("&TCB Rcv-Q Snd-Q Local socket Remote socket State\r\n"); #endif for(i=0;i<NTCB;i++){ for(tcb=tcbs[i];tcb != NULLTCB;tcb = tcb->next){ #ifdef AMIGA printf("%6lx%6u%6u ",(unsigned long)tcb, tcb->rcvcnt,tcb->sndcnt); #else printf("%4x%6u%6u ",(int)tcb,tcb->rcvcnt,tcb->sndcnt); #endif printf("%-23s",psocket(&tcb->conn.local)); printf("%-23s",psocket(&tcb->conn.remote)); printf("%-s\r\n",tcpstates[tcb->state]); } } fflush(stdout); return 0; } /* Dump a TCP control block */ void state_tcp(tcb) struct tcb *tcb; { int32 sent,recvd; if(tcb == NULLTCB) return; /* Compute total data sent and received; take out SYN and FIN */ sent = tcb->snd.una - tcb->iss; /* Acknowledged data only */ recvd = tcb->rcv.nxt - tcb->irs; switch(tcb->state){ case LISTEN: case SYN_SENT: /* Nothing received or acked yet */ sent = recvd = 0; break; case SYN_RECEIVED: recvd--; /* Got SYN, no data acked yet */ sent = 0; break; case ESTABLISHED: /* Got and sent SYN */ case FINWAIT1: /* FIN not acked yet */ sent--; recvd--; break; case FINWAIT2: /* Our SYN and FIN both acked */ sent -= 2; recvd--; break; case CLOSE_WAIT: /* Got SYN and FIN, our FIN not yet acked */ case CLOSING: case LAST_ACK: sent--; recvd -= 2; break; case TIME_WAIT: /* Sent and received SYN/FIN, all acked */ sent -= 2; recvd -= 2; break; } printf("Local: %s",psocket(&tcb->conn.local)); printf(" Remote: %s",psocket(&tcb->conn.remote)); printf(" State: %s\r\n",tcpstates[tcb->state]); printf(" Init seq Unack Next WL1 WL2 Wind MSS Queue Total\r\n"); printf("Send:"); printf("%9lx",tcb->iss); printf("%9lx",tcb->snd.una); printf("%9lx",tcb->snd.nxt); printf("%9lx",tcb->snd.wl1); printf("%9lx",tcb->snd.wl2); printf("%6u",tcb->snd.wnd); printf("%6u",tcb->mss); printf("%6u",tcb->sndcnt); printf("%11lu\r\n",sent); printf("Recv:"); printf("%9lx",tcb->irs); printf(" "); printf("%9lx",tcb->rcv.nxt); printf(" "); printf(" "); printf("%6u",tcb->rcv.wnd); printf(" "); printf("%6u",tcb->rcvcnt); printf("%11lu\r\n",recvd); if(tcb->reseq != (struct reseq *)NULL){ register struct reseq *rp; printf("Reassembly queue:\r\n"); for(rp = tcb->reseq;rp != (struct reseq *)NULL; rp = rp->next){ printf(" seq x%lx %u bytes\r\n",rp->seg.seq,rp->length); } } printf("Retry %u",tcb->retry); switch(tcb->timer.state){ case TIMER_STOP: printf(" Timer stopped"); break; case TIMER_RUN: printf(" Timer running (%ld/%ld mS)", (long)MSPTICK * (tcb->timer.start - tcb->timer.count), (long)MSPTICK * tcb->timer.start); break; case TIMER_EXPIRE: printf(" Timer expired"); } printf(" Smoothed round trip time %ld mS\r\n",tcb->srtt); fflush(stdout); } /* Dump a TCP segment header. Assumed to be in network byte order */ void tcp_dump(bp,source,dest,check) struct mbuf *bp; int32 source,dest; /* IP source and dest addresses */ int check; /* 0 if checksum test is to be bypassed */ { int hdr_len,i; register struct tcp_header *tcph; struct pseudo_header ph; char tmpbuf; if(bp == NULLBUF) return; /* If packet isn't in a single buffer, make a temporary copy and * note the fact so we free it later */ if(bp->next != NULLBUF){ bp = copy_p(bp,len_mbuf(bp)); tmpbuf = 1; } else tmpbuf = 0; tcph = (struct tcp_header *)bp->data; hdr_len = hinibble(tcph->offset) * sizeof(int32); printf("TCP: %u->%u Seq x%lx", ntohs(tcph->source),ntohs(tcph->dest), ntohl(tcph->seq),ntohl(tcph->ack)); if(tcph->flags & ACK) printf(" Ack x%lx",ntohl(tcph->ack)); for(i=0;i<6;i++){ if(tcph->flags & 1 << i){ printf(" %s",tcpflags[i]); } } printf(" Wnd %u",ntohs(tcph->wnd)); if(tcph->flags & URG) printf(" UP x%x",ntohs(tcph->up)); if(hdr_len > sizeof(struct tcp_header)){ struct mss *mssp; mssp = (struct mss *)(tcph + 1); if(mssp->kind == MSS_KIND && mssp->length == MSS_LENGTH){ printf(" MSS %u",ntohs(mssp->mss)); } } /* Verify checksum */ if(check){ ph.source = source; ph.dest = dest; ph.protocol = TCP_PTCL; ph.length = len_mbuf(bp); ph.zero = 0; if((i = cksum(&ph,bp,ph.length)) != 0) printf(" CHECKSUM ERROR (%u)",i); } printf("\r\n"); if(tmpbuf) free_p(bp); } #endif SHAR_EOF cat << \SHAR_EOF > ip.c /* Upper half of IP, consisting of send/receive primitives, including * fragment reassembly, for higher level protocols. * Not needed when running as a standalone gateway. */ #define TLB 30 /* Reassembly limit time */ #include <stdio.h> #include "machdep.h" #include "mbuf.h" #include "timer.h" #include "internet.h" #include "ip.h" #include "icmp.h" #include "iface.h" int ip_recv(); /* Should be void, but C complains */ char *calloc(),*malloc(); char ip_ttl = MAXTTL; /* Default time-to-live for IP datagrams */ struct reasm *reasmq; #define INSERT 0 #define APPEND 1 #define PREPEND 2 /* Send an IP datagram. Modeled after the example interface on p 32 of * RFC 791 */ void ip_send(source,dest,protocol,tos,ttl,bp,length,id,df) int32 source; /* source address */ int32 dest; /* Destination address */ char protocol; /* Protocol */ char tos; /* Type of service */ char ttl; /* Time-to-live */ struct mbuf *bp; /* Data portion of datagram */ int16 length; /* Optional length of data portion */ int16 id; /* Optional identification */ char df; /* Don't-fragment flag */ { struct mbuf *hbp; /* mbuf containing IP header */ struct ip_header *iph; /* Pointer to IP header */ static int16 id_cntr; /* Datagram serial number */ int16 hdr_len; /* IP header length, bytes */ void ip_route(); /* Datagram router */ if(length == 0 && bp != NULLBUF) length = len_mbuf(bp); if(id == 0) id = id_cntr++; if(ttl == 0) ttl = ip_ttl; /* Allocate an mbuf for the IP header */ hdr_len = sizeof(struct ip_header); if((hbp = alloc_mbuf(hdr_len)) == NULLBUF){ /* We really ought to source-quench the sender, but that would * probably fail too. */ free_p(bp); return; } hbp->cnt = hdr_len; /* and fill it in */ iph = (struct ip_header *)hbp->data; iph->v_ihl = (IPVERSION << 4) | (hdr_len/sizeof(int32)); iph->tos = tos; iph->length = htons(hdr_len + length); iph->id = htons(id); if(df) iph->fl_offs = htons(DF); else iph->fl_offs = 0; iph->ttl = ttl; iph->protocol = protocol; iph->checksum = 0; iph->source = htonl(source); iph->dest = htonl(dest); iph->checksum = cksum(NULLHEADER,hbp,hdr_len); hbp->next = bp; ip_route(hbp,0); /* Toss it to the router */ } /* Reassemble incoming IP fragments and dispatch completed datagrams * to the proper transport module */ int /* Should really be void */ ip_recv(bp,rxbroadcast) struct mbuf *bp; char rxbroadcast; { register struct ip_header *ipp; /* Pointer to original IP header */ struct ip_header ip; /* Extracted copy of header */ int16 ip_len; /* Length of IP header */ struct mbuf *fraghandle(); void (*recv)(); /* Function to call with completed datagram */ void tcp_input(),udp_input(),icmp_input(); ipp = (struct ip_header *)bp->data; /* Initial check for protocols we can't handle */ switch(ipp->protocol & 0xff){ case TCP_PTCL: recv = tcp_input; break; case UDP_PTCL: recv = udp_input; break; case ICMP_PTCL: recv = icmp_input; break; default: /* Send an ICMP Protocol Unknown response... */ ip_stats.badproto++; /* ...unless it's a broadcast */ if(!rxbroadcast) icmp_output(bp,DEST_UNREACH,PROT_UNREACH,(union icmp_args *)NULL); free_p(bp); return; } ip_len = lonibble(ipp->v_ihl) * sizeof(int32); /* Extract IP header */ pullup(&bp,(char *)&ip,ip_len); /* Convert to host byte order */ ip.length = ntohs(ip.length) - ip_len; /* Length of data portion */ ip.id = ntohs(ip.id); ip.fl_offs = ntohs(ip.fl_offs); ip.source = ntohl(ip.source); ip.dest = ntohl(ip.dest); /* If we have a complete packet, call the next layer * to handle the result */ if((bp = fraghandle(&ip,bp)) != NULLBUF) (*recv)(bp,ip.protocol,ip.source,ip.dest,ip.tos,ip.length,rxbroadcast); } /* Process IP datagram fragments * If datagram is complete, return it with ip->length containing its * entire length; otherwise return NULLBUF */ static struct mbuf * fraghandle(ip,bp) struct ip_header *ip; /* IP header, host byte order */ struct mbuf *bp; /* The fragment itself */ { void ip_timeout(),freefrag(),free_reasm(); struct reasm *lookup_reasm(),*creat_reasm(); register struct reasm *rp; /* Pointer to reassembly descriptor */ struct frag *lastfrag,*nextfrag,*tfp,*newfrag(); struct mbuf *tbp; int16 i; int16 offset; /* Index of first byte in fragment */ int16 last; /* Index of first byte beyond fragment */ char mf; /* 1 if not last fragment, 0 otherwise */ offset = (ip->fl_offs & F_OFFSET) << 3; /* Convert to bytes */ last = offset + ip->length; mf = (ip->fl_offs & MF) ? 1 : 0; rp = lookup_reasm(ip); if(offset == 0 && !mf){ /* Complete datagram received. Discard any earlier fragments */ if(rp != NULLREASM) free_reasm(rp); return bp; } if(rp == NULLREASM){ /* First fragment; create new reassembly descriptor */ if((rp = creat_reasm(ip)) == NULLREASM){ /* No space for descriptor, drop fragment */ free_p(bp); return NULLBUF; } } /* Keep restarting timer as long as we keep getting fragments */ stop_timer(&rp->timer); start_timer(&rp->timer); /* If this is the last fragment, we now know how long the * entire datagram is; record it */ if(!mf) rp->length = last; /* Set nextfrag to the first fragment which begins after us, * and lastfrag to the last fragment which begins before us */ lastfrag = NULLFRAG; for(nextfrag = rp->fraglist;nextfrag != NULLFRAG;nextfrag = nextfrag->next){ if(nextfrag->offset > offset) break; lastfrag = nextfrag; } /* Check for overlap with preceeding fragment */ if(lastfrag != NULLFRAG && offset < lastfrag->last){ /* Strip overlap from new fragment */ i = lastfrag->last - offset; pullup(&bp,NULLCHAR,i); if(bp == NULLBUF) return NULLBUF; /* Nothing left */ offset += i; } /* Look for overlap with succeeding segments */ for(; nextfrag != NULLFRAG; nextfrag = tfp){ tfp = nextfrag->next; /* save in case we delete fp */ if(nextfrag->offset >= last) break; /* Past our end */ /* Trim the front of this entry; if nothing is * left, remove it. */ i = last - nextfrag->offset; pullup(&nextfrag->buf,NULLCHAR,i); if(nextfrag->buf == NULLBUF){ /* superseded; delete from list */ if(nextfrag->prev != NULLFRAG) nextfrag->prev->next = nextfrag->next; else rp->fraglist = nextfrag->next; if(tfp->next != NULLFRAG) nextfrag->next->prev = nextfrag->prev; freefrag(nextfrag); } else nextfrag->offset = last; } /* Lastfrag now points, as before, to the fragment before us; * nextfrag points at the next fragment. Check to see if we can * join to either or both fragments. */ i = INSERT; if(lastfrag != NULLFRAG && lastfrag->last == offset) i |= APPEND; if(nextfrag != NULLFRAG && nextfrag->offset == last) i |= PREPEND; switch(i){ case INSERT: /* Insert new desc between lastfrag and nextfrag */ tfp = newfrag(offset,last,bp); tfp->prev = lastfrag; tfp->next = nextfrag; if(lastfrag != NULLFRAG) lastfrag->next = tfp; /* Middle of list */ else rp->fraglist = tfp; /* First on list */ if(nextfrag != NULLFRAG) nextfrag->prev = tfp; break; case APPEND: /* Append to lastfrag */ append(&lastfrag->buf,bp); lastfrag->last = last; /* Extend forward */ break; case PREPEND: /* Prepend to nextfrag */ tbp = nextfrag->buf; nextfrag->buf = bp; append(&nextfrag->buf,tbp); nextfrag->offset = offset; /* Extend backward */ break; case (APPEND|PREPEND): /* Consolidate by appending this fragment and nextfrag * to lastfrag and removing the nextfrag descriptor */ append(&lastfrag->buf,bp); append(&lastfrag->buf,nextfrag->buf); nextfrag->buf = NULLBUF; lastfrag->last = nextfrag->last; /* Finally unlink and delete the now unneeded nextfrag */ lastfrag->next = nextfrag->next; if(nextfrag->next != NULLFRAG) nextfrag->next->prev = lastfrag; freefrag(nextfrag); break; } if(rp->fraglist->offset == 0 && rp->fraglist->next == NULLFRAG && rp->length != 0){ /* We've gotten a complete datagram, so extract it from the * reassembly buffer and pass it on. */ bp = rp->fraglist->buf; rp->fraglist->buf = NULLBUF; ip->length = rp->length; /* Tell IP the entire length */ free_reasm(rp); return bp; } else return NULLBUF; } static struct reasm * lookup_reasm(ip) struct ip_header *ip; { register struct reasm *rp; for(rp = reasmq;rp != NULLREASM;rp = rp->next){ if(ip->source == rp->source && ip->dest == rp->dest && ip->protocol == rp->protocol && ip->id == rp->id) return rp; } return NULLREASM; } #ifdef FOO static int16 hash_reasm(source,dest,protocol,id) int32 source; int32 dest, char protocol; int16 id; { register int16 hval; hval = loword(source); hval ^= hiword(source); hval ^= loword(dest); hval ^= hiword(dest); hval ^= protocol & 0xff; hval ^= id; hval %= RHASH; return hval; } #endif /* Create a reassembly descriptor, * put at head of reassembly list */ static struct reasm * creat_reasm(ip) register struct ip_header *ip; { register struct reasm *rp; if((rp = (struct reasm *)calloc(1,sizeof(struct reasm))) == NULLREASM) return rp; /* No space for descriptor */ rp->source = ip->source; rp->dest = ip->dest; rp->id = ip->id; rp->protocol = ip->protocol; rp->timer.start = TLB; rp->timer.func = ip_timeout; rp->timer.arg = (int *)rp; rp->next = reasmq; if(rp->next != NULLREASM) rp->next->prev = rp; reasmq = rp; return rp; } /* Free all resources associated with a reassembly descriptor */ static void free_reasm(rp) register struct reasm *rp; { register struct frag *fp; stop_timer(&rp->timer); /* Remove from list of reassembly descriptors */ if(rp->prev != NULLREASM) rp->prev->next = rp->next; else reasmq = rp->next; if(rp->next != NULLREASM) rp->next->prev = rp->prev; /* Free any fragments on list, starting at beginning */ while((fp = rp->fraglist) != NULLFRAG){ rp->fraglist = fp->next; free_p(fp->buf); free((char *)fp); } free((char *)rp); } /* Handle reassembly timeouts by deleting all reassembly resources */ static void ip_timeout(arg) int *arg; { register struct reasm *rp; rp = (struct reasm *)arg; free_reasm(rp); } /* Create a fragment */ static struct frag * newfrag(offset,last,bp) int16 offset,last; struct mbuf *bp; { struct frag *fp; if((fp = (struct frag *)calloc(1,sizeof(struct frag))) == NULLFRAG){ /* Drop fragment */ free_p(bp); return NULLFRAG; } fp->buf = bp; fp->offset = offset; fp->last = last; return fp; } /* Delete a fragment, return next one on queue */ static void freefrag(fp) struct frag *fp; { free_p(fp->buf); free((char *)fp); } #ifdef TRACE int doipstat(argc,argv) int argc; char *argv[]; { extern struct ip_stats ip_stats; extern struct reasm *reasmq; register struct reasm *rp; register struct frag *fp; char *inet_ntoa(); printf("total %ld runt %u len err %u vers err %u", ip_stats.total,ip_stats.runt,ip_stats.length,ip_stats.version); printf(" chksum err %u badproto %u\r\n", ip_stats.checksum,ip_stats.badproto); if(reasmq != NULLREASM) printf("Reassembly fragments:\r\n"); for(rp = reasmq;rp != NULLREASM;rp = rp->next){ printf("src %s",inet_ntoa(rp->source)); printf(" dest %s",inet_ntoa(rp->dest)); printf(" id %u pctl %u time %u len %u\r\n", rp->id,rp->protocol,rp->timer.count,rp->length); for(fp = rp->fraglist;fp != NULLFRAG;fp = fp->next){ printf(" offset %u last %u\r\n",fp->offset,fp->last); } } return 0; } #endif SHAR_EOF cat << \SHAR_EOF > udp.c /* Send and receive User Datagram Protocol packets */ #include "machdep.h" #include "mbuf.h" #include "netuser.h" #include "udp.h" #include "internet.h" struct udp_cb *udps[NUDP]; /* Hash table for UDP structures */ struct udp_stat udp_stat; /* Statistics */ /* Create a UDP control block for lsocket, so that we can queue * incoming datagrams. */ int open_udp(lsocket,r_upcall) struct socket *lsocket; void (*r_upcall)(); { char *malloc(); register struct udp_cb *up; struct udp_cb *lookup_udp(); int16 hval,hash_udp(); if((up = lookup_udp(lsocket)) != NULLUDP) return 0; /* Already exists */ if((up = (struct udp_cb *)malloc(sizeof (struct udp_cb))) == NULLUDP){ net_error = NO_SPACE; return -1; } up->rcvq = NULLBUF; up->rcvcnt = 0; up->socket.address = lsocket->address; up->socket.port = lsocket->port; up->r_upcall = r_upcall; hval = hash_udp(lsocket); up->next = udps[hval]; up->prev = NULLUDP; up->next->prev = up; udps[hval] = up; return 0; } /* Send a UDP datagram */ int send_udp(lsocket,fsocket,tos,ttl,bp,length,id,df) struct socket *lsocket; /* Source socket */ struct socket *fsocket; /* Destination socket */ char tos; /* Type-of-service for IP */ char ttl; /* Time-to-live for IP */ struct mbuf *bp; /* Data field, if any */ int16 length; /* Length of data field */ int16 id; /* Optional ID field for IP */ char df; /* Don't Fragment flag for IP */ { struct mbuf *hbp; int16 hdr_len; struct pseudo_header ph; struct udp_header *uhdr; if(length == 0 && bp != NULLBUF) length = len_mbuf(bp); hdr_len = sizeof(struct udp_header); length += hdr_len; /* Allocate UDP protocol header and fill it in */ if((hbp = alloc_mbuf(hdr_len)) == NULLBUF){ net_error = NO_SPACE; return -1; } hbp->cnt = hdr_len; uhdr = (struct udp_header *)hbp->data; uhdr->source = htons(lsocket->port); uhdr->dest = htons(fsocket->port); uhdr->length = htons(length); uhdr->checksum = 0; /* Link in the user data */ hbp->next = bp; /* Create IP pseudo-header, compute checksum and send it */ ph.length = length; ph.source = lsocket->address; ph.dest = fsocket->address; ph.protocol = UDP_PTCL; ph.zero = 0; /* All zeros and all ones is equivalent in one's complement arithmetic; * the spec requires us to change zeros into ones to distinguish an * all-zero checksum from no checksum at all */ if((uhdr->checksum = cksum(&ph,hbp,length)) == 0) uhdr->checksum = 0xffffffff; udp_stat.sent++; ip_send(lsocket->address,fsocket->address,UDP_PTCL,tos,ttl,hbp,length,id,df); return length; } /* Accept a waiting datagram, if available. Returns length of datagram */ int recv_udp(lsocket,fsocket,bp) struct socket *lsocket; /* Local socket to receive on */ struct socket *fsocket; /* Place to stash incoming socket */ struct mbuf **bp; /* Place to stash data packet */ { struct udp_cb *lookup_udp(); register struct udp_cb *up; struct socket *sp; struct mbuf *buf; int16 length; up = lookup_udp(lsocket); if(up == NULLUDP){ net_error = NO_CONN; return -1; } if(up->rcvcnt == 0){ net_error = WOULDBLK; return -1; } buf = dequeue(&up->rcvq); up->rcvcnt--; sp = (struct socket *)buf->data; /* Fill in the user's foreign socket structure, if given */ if(fsocket != NULLSOCK){ fsocket->address = sp->address; fsocket->port = sp->port; } /* Strip socket header and hand data to user */ pullup(&buf,NULLCHAR,sizeof(struct socket)); length = len_mbuf(buf); if(bp != (struct mbuf **)NULL) *bp = buf; else free_p(buf); return length; } /* Delete a UDP control block */ int del_udp(lsocket) struct socket *lsocket; { register struct udp_cb *up; struct udp_cb *lookup_udp(); struct mbuf *bp; int16 hval; if((up = lookup_udp(lsocket)) == NULLUDP){ net_error = INVALID; return -1; } /* Get rid of any pending packets */ while(up->rcvcnt != 0){ bp = up->rcvq; up->rcvq = up->rcvq->anext; free_p(bp); up->rcvcnt--; } hval = hash_udp(&up->socket); if(udps[hval] == up){ /* First on list */ udps[hval] = up->next; up->next->prev = NULLUDP; } else { up->prev->next = up->next; up->next->prev = up->prev; } free((char *)up); return 0; } /* Process an incoming UDP datagram */ void udp_input(bp,protocol,source,dest,tos,length,rxbroadcast) struct mbuf *bp; char protocol; int32 source; /* Source IP address */ int32 dest; /* Dest IP address */ char tos; int16 length; char rxbroadcast; /* The only protocol that accepts 'em */ { struct pseudo_header ph; struct udp_header udp; struct udp_cb *up,*lookup_udp(); struct socket lsocket; struct socket *fsocket; struct mbuf *sp; int ckfail = 0; if(bp == NULLBUF) return; udp_stat.rcvd++; /* Create pseudo-header and verify checksum */ ph.source = source; ph.dest = dest; ph.protocol = protocol; ph.length = length; ph.zero = 0; if(cksum(&ph,bp,length) != 0) /* Checksum apparently failed, note for later */ ckfail++; /* Extract UDP header in host order */ pullup(&bp,(char *)&udp,sizeof(struct udp_header)); /* If the checksum field is zero, then ignore a checksum error. * I think this is dangerously wrong, but it is in the spec. */ if(ckfail && udp.checksum != 0){ udp_stat.cksum++; free_p(bp); return; } udp.dest = ntohs(udp.dest); udp.source = ntohs(udp.source); /* If this was a broadcast packet, pretend it was sent to us */ if(rxbroadcast){ lsocket.address = ip_addr; udp_stat.bdcsts++; } else lsocket.address = dest; lsocket.port = udp.dest; /* See if there's somebody around to read it */ if((up = lookup_udp(&lsocket)) == NULLUDP){ /* Nope, toss it on the floor */ udp_stat.unknown++; free_p(bp); return; } /* Create a buffer which will contain the foreign socket info */ if((sp = alloc_mbuf(sizeof(struct socket))) == NULLBUF){ /* No space, drop whole packet */ free_p(bp); return; } sp->cnt = sizeof(struct socket); fsocket = (struct socket *)sp->data; fsocket->address = source; fsocket->port = udp.source; /* Yes, remove the now redundant UDP header, chain the foreign socket * info in front of it and queue it */ sp->next = bp; enqueue(&up->rcvq,sp); up->rcvcnt++; if(up->r_upcall) (*up->r_upcall)(&lsocket,up->rcvcnt); } /* Look up UDP socket, return control block pointer or NULLUDP if nonexistant */ static struct udp_cb * lookup_udp(socket) struct socket *socket; { register struct udp_cb *up; int16 hash_udp(); up = udps[hash_udp(socket)]; while(up != NULLUDP){ if(bcmp((char *)socket,(char *)&up->socket,sizeof(struct socket)) == 0) break; up = up->next; } return up; } /* Hash a UDP socket (address and port) structure */ static int16 hash_udp(socket) struct socket *socket; { int16 hval; /* Compute hash function on socket structure */ hval = hiword(socket->address); hval ^= loword(socket->address); hval ^= socket->port; hval %= NUDP; return hval; } #ifdef TRACE /* Dump UDP statistics and control blocks */ doudpstat() { extern struct udp_stat udp_stat; char *psocket(); register struct udp_cb *udp; register int i; printf("sent %u rcvd %u bdcsts %u cksum err %u unknown socket %u\r\n", udp_stat.sent,udp_stat.rcvd,udp_stat.bdcsts,udp_stat.cksum,udp_stat.unknown); #ifdef AMIGA printf("&UCB Rcv-Q Local socket\r\n"); #else printf("&UCB Rcv-Q Local socket\r\n"); #endif for(i=0;i<NUDP;i++){ for(udp = udps[i];udp != NULLUDP; udp = udp->next){ #ifdef AMIGA printf("%6lx%6u %s\r\n",(unsigned long)udp,udp->rcvcnt, #else printf("%x%6u %s\r\n",(int)udp,udp->rcvcnt, #endif psocket(&udp->socket)); } } } /* Dump a UDP header */ void udp_dump(bp,source,dest,check) struct mbuf *bp; int32 source,dest; int check; /* If 0, bypass checksum verify */ { register struct udp_header *udph; struct pseudo_header ph; int i; char tmpbuf; if(bp == NULLBUF) return; /* If packet isn't in a single buffer, make a temporary copy and * note the fact so we free it later */ if(bp->next != NULLBUF){ bp = copy_p(bp,len_mbuf(bp)); tmpbuf = 1; } else tmpbuf = 0; udph = (struct udp_header *)bp->data; printf("UDP:"); printf(" %u->%u",ntohs(udph->source), ntohs(udph->dest)); printf(" len %u",ntohs(udph->length)); if(check){ /* Verify checksum */ ph.source = source; ph.dest = dest; ph.zero = 0; ph.protocol = UDP_PTCL; ph.length = len_mbuf(bp); if(udph->checksum != 0 && (i = cksum(&ph,bp,ph.length)) != 0) printf(" CHECKSUM ERROR (%u)",i); } printf("\r\n"); if(tmpbuf) free_p(bp); } #endif SHAR_EOF # End of shell archive exit 0 -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.