home *** CD-ROM | disk | FTP | other *** search
- /* TCP header conversion routines
- * Copyright 1991 Phil Karn, KA9Q
- */
- #include "global.h"
- #include "mbuf.h"
- #include "tcp.h"
- #include "ip.h"
- #include "internet.h"
-
- /* Convert TCP header in host format into mbuf ready for transmission,
- * link in data (if any). If ph != NULL, compute checksum, otherwise
- * take checksum from tcph->checksum
- */
- struct mbuf *
- htontcp(tcph,bp,ph)
- register struct tcp *tcph;
- struct mbuf *bp;
- struct pseudo_header *ph;
- {
- int16 hdrlen;
- register char *cp;
-
- hdrlen = TCPLEN;
- if(tcph->optlen > 0 && tcph->optlen <= TCP_MAXOPT){
- hdrlen += tcph->optlen;
- } else if(tcph->mss != 0){
- hdrlen += MSS_LENGTH;
- }
- bp = pushdown(bp,hdrlen);
- cp = bp->data;
- cp = put16(cp,tcph->source);
- cp = put16(cp,tcph->dest);
- cp = put32(cp,tcph->seq);
- cp = put32(cp,tcph->ack);
- *cp++ = hdrlen << 2; /* Offset field */
- *cp = 0;
- if(tcph->flags.congest)
- *cp |= 64;
- if(tcph->flags.urg)
- *cp |= 32;
- if(tcph->flags.ack)
- *cp |= 16;
- if(tcph->flags.psh)
- *cp |= 8;
- if(tcph->flags.rst)
- *cp |= 4;
- if(tcph->flags.syn)
- *cp |= 2;
- if(tcph->flags.fin)
- *cp |= 1;
- cp++;
- cp = put16(cp,tcph->wnd);
- if(ph == NULLHEADER){
- /* Use user-supplied checksum */
- cp = put16(cp,tcph->checksum);
- } else {
- /* Zero out checksum field for later recalculation */
- *cp++ = 0;
- *cp++ = 0;
- }
- cp = put16(cp,tcph->up);
-
- /* Write options, if any */
- if(hdrlen > TCPLEN){
- if(tcph->mss != 0){
- *cp++ = MSS_KIND;
- *cp++ = MSS_LENGTH;
- cp = put16(cp,tcph->mss);
- } else
- memcpy(cp,tcph->options,tcph->optlen);
- }
- /* Recompute checksum, if requested */
- if(ph != NULLHEADER)
- put16(&bp->data[16],cksum(ph,bp,ph->length));
-
- return bp;
- }
- /* Pull TCP header off mbuf */
- int
- ntohtcp(tcph,bpp)
- register struct tcp *tcph;
- struct mbuf **bpp;
- {
- int hdrlen,i,optlen,kind;
- register int flags;
- char hdrbuf[TCPLEN],*cp;
-
- i = pullup(bpp,hdrbuf,TCPLEN);
- /* Note that the results will be garbage if the header is too short.
- * We don't check for this because returned ICMP messages will be
- * truncated, and we at least want to get the port numbers.
- */
- tcph->source = get16(&hdrbuf[0]);
- tcph->dest = get16(&hdrbuf[2]);
- tcph->seq = get32(&hdrbuf[4]);
- tcph->ack = get32(&hdrbuf[8]);
- hdrlen = (hdrbuf[12] & 0xf0) >> 2;
- flags = hdrbuf[13];
- tcph->flags.congest = flags & 64;
- tcph->flags.urg = flags & 32;
- tcph->flags.ack = flags & 16;
- tcph->flags.psh = flags & 8;
- tcph->flags.rst = flags & 4;
- tcph->flags.syn = flags & 2;
- tcph->flags.fin = flags & 1;
- tcph->wnd = get16(&hdrbuf[14]);
- tcph->checksum = get16(&hdrbuf[16]);
- tcph->up = get16(&hdrbuf[18]);
- tcph->mss = 0;
- tcph->optlen = hdrlen - TCPLEN;
-
- /* Check for option field. Only space for one is allowed, but
- * since there's only one TCP option (MSS) this isn't a problem
- */
- if(i < TCPLEN || hdrlen < TCPLEN)
- return -1; /* Header smaller than legal minimum */
- if(tcph->optlen == 0)
- return (int)hdrlen; /* No options, all done */
-
- if(tcph->optlen > len_p(*bpp)){
- /* Remainder too short for options length specified */
- return -1;
- }
- pullup(bpp,tcph->options,tcph->optlen); /* "Can't fail" */
- /* Process options */
- for(cp=tcph->options,i=tcph->optlen; i > 0;){
- kind = *cp++;
- /* Process single-byte options */
- switch(kind){
- case EOL_KIND:
- i--;
- cp++;
- return (int)hdrlen; /* End of options list */
- case NOOP_KIND:
- i--;
- cp++;
- continue; /* Go look for next option */
- }
- /* All other options have a length field */
- optlen = uchar(*cp++);
-
- /* Process valid multi-byte options */
- switch(kind){
- case MSS_KIND:
- if(optlen == MSS_LENGTH){
- tcph->mss = get16(cp);
- }
- break;
- }
- optlen = max(2,optlen); /* Enforce legal minimum */
- i -= optlen;
- cp += optlen - 2;
- }
- return (int)hdrlen;
- }
-