# #include "util.h" #include "pkts.h" #include "buf.h" /* tcp_init store away tcp info in tcb and tcb send pkt - much of tcp hdr info remains constant and will not need to be refilled later. Note that local skt and foreign skt are saved away in byte swab form. */ tcp_init(ptcb, lskt, fskt) register struct tcb *ptcb; short lskt, fskt; { register struct tcp_hdr *tcp; int seqno[3]; long *lseqno; tcp = &ptcb->t_tcp; tcp->tc_sskt = swab(lskt); tcp->tc_dskt = swab(fskt); tcp->tc_flags = 0; ptcb->t_flags = 0; qtime(&seqno); lseqno = &seqno[1]; ptcb->t_iss = *lseqno; ptcb->t_suna = ptcb->t_iss; ptcb->t_snxt = ptcb->t_iss; ptcb->t_swnd = 1; ptcb->t_rwnd = RWINDOW; ptcb->t_ntrans = 0; ptcb->t_retrans = RETRANSTIME; ptcb->t_ttime.tt_begin = &ptcb->t_ttime.tt_list[0]; ptcb->t_ttime.tt_end = &ptcb->t_ttime.tt_list[NTT]; ptcb->t_ttime.tt_first = ptcb->t_ttime.tt_begin; ptcb->t_ttime.tt_next = ptcb->t_ttime.tt_begin; } /* orig_syn process original received SYN */ orig_syn(ptcb, tcp) register struct tcb *ptcb; register struct tcp_hdr *tcp; { register struct tcp_hdr *tcp2; short maxseg; tcp2 = &ptcb->t_tcp; ptcb->t_state |= SYN_RCVD; ptcb->t_irs = tcp->tc_seqno; ptcb->t_rnxt = tcp->tc_seqno + 1; ptcb->t_rwnd = RWINDOW; ptcb->t_rlast = ptcb->t_rnxt + RWINDOW; ptcb->t_swnd = tcp->tc_window; ptcb->t_swseq = tcp->tc_seqno; ptcb->t_swack = ptcb->t_iss; tcp2->tc_flags |= ACK; ptcb->t_acktime = curtime; tcp2->tc_dskt = tcp->tc_sskt; if (tcp_opt_ssiz(tcp, &maxseg)) { if (maxseg < ptcb->t_maxsdata) ptcb->t_maxsdata = maxseg; } } tcp_opt_ssiz(tcp, maxseg) struct tcp_hdr *tcp; char maxseg[2]; { register char *tcpo, *tcpoe; int hdlen; if ((hdlen = ((tcp->tc_off & 0360) >> 2)) <= TCPHSIZ) return(FALSE); tcpo = (char *) tcp + TCPHSIZ; tcpoe = (char *) tcp + hdlen; while (tcpo < tcpoe) { switch(*tcpo) { case 0: return(FALSE); case 1: tcpo++; break; case TCPOPT_MAXSEG: tcpo += 2; maxseg[1] = *tcpo++; maxseg[0] = *tcpo; return(TRUE); default: return(FALSE); } } } /* ch_tcp Oversee tcp chk of packet just received. Return FALSE if packet can be returned to frq. Major issue in checking is that before we know fhost's sequence numbers, we must use less obvious strategies to chk that packet is really destined for this connection. Also note that we stay in state SYN_SENT until we receive the fhost's SYN (even if our SYN is ACKed). */ ch_tcp(ptcb, ppkt) register struct tcb *ptcb; struct rcvpkt *ppkt; { register struct tcp_hdr *tcp, *tcp2; struct inet_hdr *inp; int tcplen; int finflag; inp = &ppkt->rp_inet; tcp = (char *) inp + ((inp->ip_ihlver & 017) << 2); tcplen = inp->ip_len - ((inp->ip_ihlver & 017) << 2); tcp2 = &ptcb->t_tcp; if ((tcp->tc_dskt != tcp2->tc_sskt) || ((ptcb->t_state & SYN_RCVD) && (tcp->tc_sskt != tcp2->tc_dskt)) ) { logerr("ch_tcp bad socket fhost %X fskt %d lskt %d\n", inp->ip_src, tcp->tc_sskt, tcp->tc_dskt); return(FALSE); } if (~cksum_tcp(&ptcb->t_phdr, tcp, tcplen) != 0) { logerr("ch_tcp bad chksum fhost %X\n", inp->ip_src); return(FALSE); } tcp_swab(tcp); if (prflag & PF_RCV) { dmp_state(); dmp_pkt("Packet received\n", &ppkt->rp_net, inp->ip_len); } switch( ptcb->t_state ) { case LISTEN: if (tcp->tc_flags & RST) return(FALSE); if (tcp->tc_flags & ACK) { tcp_s_reset(ptcb, ppkt, tcp); return(FALSE); } if (!(tcp->tc_flags & SYN)) return(FALSE); if (!ch_security(ptcb, inp)) { tcp_s_reset(ptcb, ppkt, tcp); return(FALSE); } orig_syn(ptcb, tcp); if (!ch_seq(ptcb, ppkt, tcp, tcplen)) return(FALSE); break; case SYN_SENT: if (tcp->tc_flags & ACK) { if (!ch_ack(ptcb, tcp)) { tcp_s_reset(ptcb, ppkt, tcp); return(FALSE); } if (tcp->tc_flags & RST) { tcp_r_reset(ptcb, ppkt); return(FALSE); } } else if (tcp->tc_flags & RST) return(FALSE); if (!ch_security(ptcb, inp)) { tcp_s_reset(ptcb, ppkt, tcp); return(FALSE); } if (!(tcp->tc_flags & SYN)) return(FALSE); orig_syn(ptcb, tcp); if (tcp->tc_flags & ACK) { ptcb->t_suna = tcp->tc_ackno; ptcb->t_ntrans = 0; ptcb->t_sndtime = curtime; if ((ptcb->t_suna - ptcb->t_iss) > 0) ptcb->t_state |= SYN_ACK; sb_ack(ptcb); } if (!ch_seq(ptcb, ppkt, tcp, tcplen)) return(FALSE); break; case CLOSED2: return(FALSE); default: if (!ch_seq(ptcb, ppkt, tcp, tcplen)) return(FALSE); if (tcp->tc_flags & RST) { tcp_r_reset(ptcb, ppkt); return(FALSE); } if (!ch_security(ptcb, inp) || (tcp->tc_flags & SYN)) { tcp_reset(ptcb, ppkt); return(FALSE); } if (!ch_ack(ptcb, tcp)) return(FALSE); break; } /* Pkt ok - now process data, URG, FIN */ finflag = (tcp->tc_flags & FIN); if (seq_data(ptcb, ppkt, tcp) && finflag) tcp_fin(ptcb); return(TRUE); } /* ch_ack check and process ack Ack is acceptable for states in which our SYN has not been acked if snd.una < seg.ack <= snd.nxt. Ack is acceptable for other states, if snd.una <= seg.ack <= snd.nxt. Ack is checked early for state SYN_SENT so don't update ack world - errors might be found in packet later. If ack is acceptable, for all states except SYN_SENT, update send unack and window info and notify send buffering manager that some new data has been acked. Also update round trip xmit time records, and state info if looking for ack of our SYN or FIN. */ ch_ack(ptcb, tcp) register struct tcb *ptcb; register struct tcp_hdr *tcp; { long diff; register struct ttseq *ttp; if (!(tcp->tc_flags & ACK)) return(FALSE); if (!(ptcb->t_state & SYN_ACK)) { if (((ptcb->t_iss - tcp->tc_ackno) >= 0) || ((tcp->tc_ackno - ptcb->t_snxt) > 0) ) { logerr("ch_ack rcv bad ack to our syn fhost %X\n", ptcb->t_inet.ip_dest); return(FALSE); } } else { if ((ptcb->t_suna - tcp->tc_ackno) > 0) return(TRUE); /* Dupe ack */ if ((tcp->tc_ackno - ptcb->t_snxt) > 0) { logerr("ch_ack rcv ack for unsent seqno fhost %X\n", ptcb->t_inet.ip_dest); if (ptcb->t_acktime == 0) ptcb->t_acktime = curtime; return(FALSE); } } if (ptcb->t_state == SYN_SENT) return(TRUE); diff = tcp->tc_seqno - ptcb->t_swseq; if ( (diff > 0) || ((diff == 0) && ((tcp->tc_ackno - ptcb->t_swack) >= 0)) ) { ptcb->t_swnd = tcp->tc_window; ptcb->t_swseq = tcp->tc_seqno; ptcb->t_swack = tcp->tc_ackno; } if (ptcb->t_suna == tcp->tc_ackno) return(TRUE); ptcb->t_suna = tcp->tc_ackno; ptcb->t_sndtime = curtime; ptcb->t_ntrans = 0; if ( (ptcb->t_state & (SYN_SENT|SYN_ACK)) == SYN_SENT) { if ((ptcb->t_suna - ptcb->t_iss) > 0) ptcb->t_state |= SYN_ACK; } else if ( (ptcb->t_state & (FIN_SENT|FIN_ACK)) == FIN_SENT) { if ((ptcb->t_suna - ptcb->t_snxt) >= 0) { #ifdef TCPTEST qtime(&stats.s_fintime); #endif ptcb->t_state |= FIN_ACK; } } sb_ack(ptcb); for (ttp = ptcb->t_ttime.tt_first; ttp < ptcb->t_ttime.tt_next; ttp++) if ( (ptcb->t_suna - ttp->tt_seqno) > 0) ttp->tt_acktime = curtime; else break; ptcb->t_ttime.tt_first = ttp; return(TRUE); } /* tcp_fin invoked when rcv fin from fhost */ tcp_fin(ptcb) register struct tcb *ptcb; { register struct rcvpkt *ppkt; ptcb->t_state |= FIN_RCVD; ptcb->t_rnxt++; ptcb->t_rlast = ptcb->t_rnxt; ptcb->t_rwnd = 0; ptcb->t_fintime = curtime; if (ptcb->t_acktime == 0) ptcb->t_acktime = curtime; for (ppkt = gtque(&rwaitq); ppkt != NULL; ppkt = gtque(&rwaitq)) enque(&frq, ppkt); } short cksum_tcp(phd, tcp, tcplen) register struct tcp_phdr *phd; register char *tcp; int tcplen; { short chksum; short cksum(), fcksum(); phd->pt_len = swab(tcplen); chksum = cksum(phd, (PTCPHSIZ >> 1), 0); if (tcplen & 01) { *(tcp + tcplen) = 0; tcplen++; } if (tcplen > 40) chksum = fcksum(tcp, (tcplen >> 1), chksum); else chksum = cksum(tcp, (tcplen >> 1), chksum); return(chksum); } tcp_swab(tcp) register struct tcp_hdr *tcp; { /* Displacement of fields onto word boundaries in tcp header - fields to be byte swabbed are NOT commented out */ static int tcp_swabads[6] = { /* 0, short sskt */ /* 2, short dskt */ 4, 6, /* long seqno */ 8, 10, /* long ackno */ /* 12, char offset, char flags */ 14, /* short window */ /* 16, short chksum */ 18 /* short urgp */ }; swaba(tcp, tcp_swabads, 6); } /* tcp_reset send reset packet and shut down world */ tcp_reset(ptcb, ppkt) struct tcb *ptcb; struct rcvpkt *ppkt; { snd_reset(ptcb, ppkt); logerr("tcp_reset connection to %X reset\n", ptcb->t_inet.ip_dest); usr_reset(ptcb); ptcb->t_state = CLOSED2; net_close(); } /* tcp_r_reset we received reset packet shut down world */ tcp_r_reset(ptcb, ppkt) struct tcb *ptcb; struct rcvpkt *ppkt; { logerr("tcp_r_reset connection reset by fhost %X\n", ptcb->t_inet.ip_dest); usr_reset(ptcb); ptcb->t_state = CLOSED2; net_close(); } /* tcp_s_reset send reset packet unless RST set on incoming packet */ tcp_s_reset(ptcb, ppkt, tcp) struct rcvpkt *ppkt; struct tcp_hdr *tcp; { if (tcp->tc_flags & RST) return; snd_reset(ptcb, ppkt); }