X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/6703c41f8c7e40310a32d41cdae2a788271a7f12..4ab1a5c3d51e670b73ca6e3477d72344edf9b224:/usr/src/sys/netinet/tcp_input.c?ds=inline diff --git a/usr/src/sys/netinet/tcp_input.c b/usr/src/sys/netinet/tcp_input.c index 18579e900d..d00ead02e7 100644 --- a/usr/src/sys/netinet/tcp_input.c +++ b/usr/src/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* tcp_input.c 1.45 81/12/23 */ +/* tcp_input.c 1.71 82/06/30 */ #include "../h/param.h" #include "../h/systm.h" @@ -7,6 +7,7 @@ #include "../h/socket.h" #include "../h/socketvar.h" #include "../net/in.h" +#include "../net/route.h" #include "../net/in_pcb.h" #include "../net/in_systm.h" #include "../net/if.h" @@ -19,12 +20,13 @@ #include "../net/tcp_var.h" #include "../net/tcpip.h" #include "../net/tcp_debug.h" -#include "../errno.h" +#include int tcpprintfs = 0; int tcpcksum = 1; struct sockaddr_in tcp_in = { AF_INET }; struct tcpiphdr tcp_saveti; +extern tcpnodelack; struct tcpcb *tcp_newtcpcb(); /* @@ -37,14 +39,15 @@ tcp_input(m0) register struct tcpiphdr *ti; struct inpcb *inp; register struct mbuf *m; + struct mbuf *om = 0; int len, tlen, off; - register struct tcpcb *tp; + register struct tcpcb *tp = 0; register int tiflags; struct socket *so; int todrop, acked; short ostate; + struct in_addr laddr; -COUNT(TCP_INPUT); /* * Get IP and TCP header together in first mbuf. * Note: IP leaves IP header in first mbuf. @@ -70,33 +73,56 @@ COUNT(TCP_INPUT); ti->ti_next = ti->ti_prev = 0; ti->ti_x1 = 0; ti->ti_len = (u_short)tlen; -#if vax - ti->ti_len = htons(ti->ti_len); +#if vax || pdp11 + ti->ti_len = htons((u_short)ti->ti_len); #endif if (ti->ti_sum = in_cksum(m, len)) { tcpstat.tcps_badsum++; - printf("tcp cksum %x\n", ti->ti_sum); + if (tcpprintfs) + printf("tcp cksum %x\n", ti->ti_sum); goto drop; } } /* * Check that TCP offset makes sense, - * process TCP options and adjust length. + * pull out TCP options and adjust length. */ off = ti->ti_off << 2; if (off < sizeof (struct tcphdr) || off > tlen) { tcpstat.tcps_badoff++; goto drop; } - ti->ti_len = tlen - off; -#if 0 - if (off > sizeof (struct tcphdr)) - tcp_options(ti); -#endif + tlen -= off; + ti->ti_len = tlen; + if (off > sizeof (struct tcphdr)) { + if ((m = m_pullup(m, sizeof (struct ip) + off)) == 0) { + tcpstat.tcps_hdrops++; + goto drop; + } + ti = mtod(m, struct tcpiphdr *); + om = m_get(M_DONTWAIT); + if (om == 0) + goto drop; + om->m_off = MMINOFF; + om->m_len = off - sizeof (struct tcphdr); + { caddr_t op = mtod(m, caddr_t) + sizeof (struct tcpiphdr); + bcopy(op, mtod(om, caddr_t), (unsigned)om->m_len); + m->m_len -= om->m_len; + bcopy(op+om->m_len, op, + (unsigned)(m->m_len-sizeof (struct tcpiphdr))); + } + } tiflags = ti->ti_flags; -#if vax + /* + * Drop TCP and IP headers. + */ + off += sizeof (struct ip); + m->m_off += off; + m->m_len -= off; + +#if vax || pdp11 /* * Convert TCP protocol specific fields to host format. */ @@ -107,10 +133,12 @@ COUNT(TCP_INPUT); #endif /* - * Locate pcb for segment. + * Locate pcb for segment. On match, update the local + * address stored in the block to reflect anchoring. */ inp = in_pcblookup - (&tcb, ti->ti_src, ti->ti_sport, ti->ti_dst, ti->ti_dport); + (&tcb, ti->ti_src, ti->ti_sport, ti->ti_dst, ti->ti_dport, + INPLOOKUP_WILDCARD); /* * If the state is CLOSED (i.e., TCB does not exist) then @@ -134,6 +162,14 @@ COUNT(TCP_INPUT); tp->t_idle = 0; tp->t_timer[TCPT_KEEP] = TCPTV_KEEP; + /* + * Process options. + */ + if (om) { + tcp_dooptions(tp, om); + om = 0; + } + /* * Calculate amount of space in receive window, * and then do TCP input processing. @@ -165,11 +201,18 @@ COUNT(TCP_INPUT); goto drop; tcp_in.sin_addr = ti->ti_src; tcp_in.sin_port = ti->ti_sport; - if (in_pcbconnect(inp, (struct sockaddr *)&tcp_in)) + laddr = inp->inp_laddr; + if (inp->inp_laddr.s_addr == 0) + inp->inp_laddr = ti->ti_dst; + if (in_pcbconnect(inp, (struct sockaddr_in *)&tcp_in)) { + inp->inp_laddr = laddr; goto drop; + } tp->t_template = tcp_template(tp); if (tp->t_template == 0) { in_pcbdisconnect(inp); + inp->inp_laddr = laddr; + tp = 0; goto drop; } tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; @@ -199,19 +242,24 @@ COUNT(TCP_INPUT); SEQ_GT(ti->ti_ack, tp->snd_max))) goto dropwithreset; if (tiflags & TH_RST) { - if (tiflags & TH_ACK) + if (tiflags & TH_ACK) { tcp_drop(tp, ECONNREFUSED); + tp = 0; + } goto drop; } if ((tiflags & TH_SYN) == 0) goto drop; tp->snd_una = ti->ti_ack; + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; tp->t_timer[TCPT_REXMT] = 0; tp->irs = ti->ti_seq; tcp_rcvseqinit(tp); tp->t_flags |= TF_ACKNOW; if (SEQ_GT(tp->snd_una, tp->iss)) { - so->so_state |= SS_CONNAWAITING; + if (so->so_options & SO_ACCEPTCONN) + so->so_state |= SS_CONNAWAITING; soisconnected(so); tp->t_state = TCPS_ESTABLISHED; (void) tcp_reass(tp, (struct tcpiphdr *)0); @@ -250,6 +298,7 @@ trimthenstep6: if (tp->rcv_nxt != ti->ti_seq) goto dropafterack; if (ti->ti_len > 0) { + m_adj(m, ti->ti_len); ti->ti_len = 0; ti->ti_flags &= ~(TH_PUSH|TH_FIN); } @@ -258,10 +307,11 @@ trimthenstep6: * If segment begins before rcv_nxt, drop leading * data (and SYN); if nothing left, just ack. */ - if (SEQ_GT(tp->rcv_nxt, ti->ti_seq)) { - todrop = tp->rcv_nxt - ti->ti_seq; + todrop = tp->rcv_nxt - ti->ti_seq; + if (todrop > 0) { if (tiflags & TH_SYN) { tiflags &= ~TH_SYN; + ti->ti_flags &= ~TH_SYN; ti->ti_seq++; if (ti->ti_urp > 1) ti->ti_urp--; @@ -269,7 +319,8 @@ trimthenstep6: tiflags &= ~TH_URG; todrop--; } - if (todrop > ti->ti_len) + if (todrop > ti->ti_len || + todrop == ti->ti_len && (tiflags&TH_FIN) == 0) goto dropafterack; m_adj(m, todrop); ti->ti_seq += todrop; @@ -278,20 +329,17 @@ trimthenstep6: ti->ti_urp -= todrop; else { tiflags &= ~TH_URG; - /* ti->ti_flags &= ~TH_URG; */ - /* ti->ti_urp = 0; */ + ti->ti_flags &= ~TH_URG; + ti->ti_urp = 0; } - /* tiflags &= ~TH_SYN; */ - /* ti->ti_flags &= ~TH_SYN; */ } /* * If segment ends after window, drop trailing data * (and PUSH and FIN); if nothing left, just ACK. */ - if (SEQ_GT(ti->ti_seq+ti->ti_len, tp->rcv_nxt+tp->rcv_wnd)) { - todrop = - ti->ti_seq+ti->ti_len - (tp->rcv_nxt+tp->rcv_wnd); - if (todrop > ti->ti_len) + todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd); + if (todrop > 0) { + if (todrop >= ti->ti_len) goto dropafterack; m_adj(m, -todrop); ti->ti_len -= todrop; @@ -299,6 +347,16 @@ trimthenstep6: } } + /* + * If a segment is received on a connection after the + * user processes are gone, then RST the other end. + */ + if (so->so_state & SS_USERGONE) { + tcp_close(tp); + tp = 0; + goto dropwithreset; + } + /* * If the RST bit is set examine the state: * SYN_RECEIVED STATE: @@ -319,9 +377,14 @@ trimthenstep6: inp->inp_ppcb = 0; tp = tcp_newtcpcb(inp); tp->t_state = TCPS_LISTEN; + inp->inp_faddr.s_addr = 0; + inp->inp_fport = 0; + inp->inp_laddr.s_addr = 0; /* not quite right */ + tp = 0; goto drop; } tcp_drop(tp, ECONNREFUSED); + tp = 0; goto drop; case TCPS_ESTABLISHED: @@ -329,12 +392,14 @@ trimthenstep6: case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: tcp_drop(tp, ECONNRESET); + tp = 0; goto drop; case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: tcp_close(tp); + tp = 0; goto drop; } @@ -344,6 +409,7 @@ trimthenstep6: */ if (tiflags & TH_SYN) { tcp_drop(tp, ECONNRESET); + tp = 0; goto dropwithreset; } @@ -368,8 +434,11 @@ trimthenstep6: SEQ_GT(ti->ti_ack, tp->snd_max)) goto dropwithreset; tp->snd_una++; /* SYN acked */ + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; tp->t_timer[TCPT_REXMT] = 0; - so->so_state |= SS_CONNAWAITING; + if (so->so_options & SO_ACCEPTCONN) + so->so_state |= SS_CONNAWAITING; soisconnected(so); tp->t_state = TCPS_ESTABLISHED; (void) tcp_reass(tp, (struct tcpiphdr *)0); @@ -398,39 +467,43 @@ trimthenstep6: if (SEQ_GT(ti->ti_ack, tp->snd_max)) goto dropafterack; acked = ti->ti_ack - tp->snd_una; + + /* + * If transmit timer is running and timed sequence + * number was acked, update smoothed round trip time. + */ + if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) { + if (tp->t_srtt == 0) + tp->t_srtt = tp->t_rtt; + else + tp->t_srtt = + tcp_alpha * tp->t_srtt + + (1 - tcp_alpha) * tp->t_rtt; +/* printf("rtt %d srtt*100 now %d\n", tp->t_rtt, (int)(tp->t_srtt*100)); */ + tp->t_rtt = 0; + } + if (ti->ti_ack == tp->snd_max) tp->t_timer[TCPT_REXMT] = 0; else { TCPT_RANGESET(tp->t_timer[TCPT_REXMT], tcp_beta * tp->t_srtt, TCPTV_MIN, TCPTV_MAX); - tp->t_rtt = 0; + tp->t_rtt = 1; tp->t_rxtshift = 0; } if (acked > so->so_snd.sb_cc) { sbdrop(&so->so_snd, so->so_snd.sb_cc); tp->snd_wnd -= so->so_snd.sb_cc; } else { - sbdrop(&so->so_snd.sb_cc, acked); + sbdrop(&so->so_snd, acked); tp->snd_wnd -= acked; acked = 0; } - if (so->so_snd.sb_flags & SB_WAIT) + if ((so->so_snd.sb_flags & SB_WAIT) || so->so_snd.sb_sel) sowwakeup(so); tp->snd_una = ti->ti_ack; - - /* - * If transmit timer is running and timed sequence - * number was acked, update smoothed round trip time. - */ - if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) { - if (tp->t_srtt == 0) - tp->t_srtt = tp->t_rtt; - else - tp->t_srtt = - tcp_alpha * tp->t_srtt + - (1 - tcp_alpha) * tp->t_rtt; - tp->t_rtt = 0; - } + if (SEQ_LT(tp->snd_nxt, tp->snd_una)) + tp->snd_nxt = tp->snd_una; switch (tp->t_state) { @@ -440,8 +513,15 @@ trimthenstep6: * then enter FIN_WAIT_2. */ case TCPS_FIN_WAIT_1: - if (ourfinisacked) + if (ourfinisacked) { + /* + * If we can't receive any more + * data, then closing user can proceed. + */ + if (so->so_state & SS_CANTRCVMORE) + soisdisconnected(so); tp->t_state = TCPS_FIN_WAIT_2; + } break; /* @@ -466,8 +546,10 @@ trimthenstep6: * and return. */ case TCPS_LAST_ACK: - if (ourfinisacked) + if (ourfinisacked) { tcp_close(tp); + tp = 0; + } goto drop; /* @@ -487,7 +569,7 @@ step6: * Update window information. */ if (SEQ_LT(tp->snd_wl1, ti->ti_seq) || tp->snd_wl1 == ti->ti_seq && - (SEQ_LEQ(tp->snd_wl2, ti->ti_ack) || + (SEQ_LT(tp->snd_wl2, ti->ti_ack) || tp->snd_wl2 == ti->ti_ack && ti->ti_win > tp->snd_wnd)) { tp->snd_wnd = ti->ti_win; tp->snd_wl1 = ti->ti_seq; @@ -497,20 +579,39 @@ step6: } /* - * If an URG bit is set in the segment and is greater than the - * current known urgent pointer, then signal the user that the - * remote side has out of band data. This should not happen - * in CLOSE_WAIT, CLOSING, LAST-ACK or TIME_WAIT STATES since - * a FIN has been received from the remote side. In these states - * we ignore the URG. + * Process segments with URG. */ - if ((tiflags & TH_URG) == 0 && TCPS_HAVERCVDFIN(tp->t_state) == 0) - if (SEQ_GT(ti->ti_urp, tp->rcv_up)) { - tp->rcv_up = ti->ti_urp; -#if 0 - sohasoutofband(so); /* XXX */ + if ((tiflags & TH_URG) && ti->ti_urp && + TCPS_HAVERCVDFIN(tp->t_state) == 0) { + /* + * If this segment advances the known urgent pointer, + * then mark the data stream. This should not happen + * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since + * a FIN has been received from the remote side. + * In these states we ignore the URG. + */ + if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) { + tp->rcv_up = ti->ti_seq + ti->ti_urp; + so->so_oobmark = so->so_rcv.sb_cc + + (tp->rcv_up - tp->rcv_nxt) - 1; + if (so->so_oobmark == 0) + so->so_state |= SS_RCVATMARK; +#ifdef TCPTRUEOOB + if ((tp->t_flags & TF_DOOOB) == 0) #endif + sohasoutofband(so); + tp->t_oobflags &= ~TCPOOB_HAVEDATA; } + /* + * Remove out of band data so doesn't get presented to user. + * This can happen independent of advancing the URG pointer, + * but if two URG's are pending at once, some out-of-band + * data may creep in... ick. + */ + if (ti->ti_urp <= ti->ti_len) { + tcp_pulloutofband(so, ti); + } + } /* * Process the segment text, merging it into the TCP sequencing queue, @@ -522,14 +623,11 @@ step6: */ if ((ti->ti_len || (tiflags&TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { - off += sizeof (struct ip); /* drop IP header */ - m->m_off += off; - m->m_len -= off; tiflags = tcp_reass(tp, ti); -{ extern tcpdelack; -if (tcpdelack) tp->t_flags |= TF_DELACK; else - tp->t_flags |= TF_ACKNOW; /* XXX TF_DELACK */ -} + if (tcpnodelack == 0) + tp->t_flags |= TF_DELACK; + else + tp->t_flags |= TF_ACKNOW; } else { m_freem(m); tiflags &= ~TH_FIN; @@ -590,20 +688,25 @@ if (tcpdelack) tp->t_flags |= TF_DELACK; else /* * Return any desired output. */ - tcp_output(tp); + (void) tcp_output(tp); return; dropafterack: /* - * Generate an ACK dropping incoming segment. - * Make ACK reflect our state. + * Generate an ACK dropping incoming segment if it occupies + * sequence space, where the ACK reflects our state. */ - if (tiflags & TH_RST) + if ((tiflags&TH_RST) || + tlen == 0 && (tiflags&(TH_SYN|TH_FIN)) == 0) goto drop; - tcp_respond(ti, tp->rcv_nxt, tp->snd_nxt, TH_ACK); + if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG) + tcp_trace(TA_RESPOND, ostate, tp, &tcp_saveti, 0); + tcp_respond(tp, ti, tp->rcv_nxt, tp->snd_nxt, TH_ACK); return; dropwithreset: + if (om) + (void) m_free(om); /* * Generate a RST, dropping incoming segment. * Make ACK acceptable to originator of segment. @@ -611,11 +714,12 @@ dropwithreset: if (tiflags & TH_RST) goto drop; if (tiflags & TH_ACK) - tcp_respond(ti, (tcp_seq)0, ti->ti_ack, TH_RST); + tcp_respond(tp, ti, (tcp_seq)0, ti->ti_ack, TH_RST); else { if (tiflags & TH_SYN) ti->ti_len++; - tcp_respond(ti, ti->ti_seq+ti->ti_len, (tcp_seq)0, TH_RST|TH_ACK); + tcp_respond(tp, ti, ti->ti_seq+ti->ti_len, (tcp_seq)0, + TH_RST|TH_ACK); } return; @@ -623,10 +727,132 @@ drop: /* * Drop space held by incoming segment and return. */ + if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)) + tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0); m_freem(m); return; } +tcp_dooptions(tp, om) + struct tcpcb *tp; + struct mbuf *om; +{ + register u_char *cp; + int opt, optlen, cnt; + + cp = mtod(om, u_char *); + cnt = om->m_len; + for (; cnt > 0; cnt -= optlen, cp += optlen) { + opt = cp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else + optlen = cp[1]; + switch (opt) { + + default: + break; + + case TCPOPT_MAXSEG: + if (optlen != 4) + continue; + tp->t_maxseg = *(u_short *)(cp + 2); +#if vax || pdp11 + tp->t_maxseg = ntohs((u_short)tp->t_maxseg); +#endif + break; + +#ifdef TCPTRUEOOB + case TCPOPT_WILLOOB: + tp->t_flags |= TF_DOOOB; +printf("tp %x dooob\n", tp); + break; + + case TCPOPT_OOBDATA: { + int seq; + register struct socket *so = tp->t_inpcb->inp_socket; + tcp_seq mark; + + if (optlen != 8) + continue; + seq = cp[2]; + if (seq < tp->t_iobseq) + seq += 256; +printf("oobdata cp[2] %d iobseq %d seq %d\n", cp[2], tp->t_iobseq, seq); + if (seq - tp->t_iobseq > 128) { +printf("bad seq\n"); + tp->t_oobflags |= TCPOOB_OWEACK; + break; + } + tp->t_iobseq = cp[2]; + tp->t_iobc = cp[3]; + mark = *(tcp_seq *)(cp + 4); +#if vax || pdp11 + mark = ntohl(mark); +#endif + so->so_oobmark = so->so_rcv.sb_cc + (mark-tp->rcv_nxt); + if (so->so_oobmark == 0) + so->so_state |= SS_RCVATMARK; +printf("take oob data %x input iobseq now %x\n", tp->t_iobc, tp->t_iobseq); + sohasoutofband(so); + break; + } + + case TCPOPT_OOBACK: { + int seq; + + if (optlen != 4) + continue; + if (tp->t_oobseq != cp[2]) { +printf("wrong ack\n"); + break; + } +printf("take oob ack %x and cancel rexmt\n", cp[2]); + tp->t_oobflags &= ~TCPOOB_NEEDACK; + tp->t_timer[TCPT_OOBREXMT] = 0; + break; + } +#endif TCPTRUEOOB + } + } + (void) m_free(om); +} + +/* + * Pull out of band byte out of a segment so + * it doesn't appear in the user's data queue. + * It is still reflected in the segment length for + * sequencing purposes. + */ +tcp_pulloutofband(so, ti) + struct socket *so; + struct tcpiphdr *ti; +{ + register struct mbuf *m; + int cnt = ti->ti_urp - 1; + + m = dtom(ti); + while (cnt >= 0) { + if (m->m_len > cnt) { + char *cp = mtod(m, caddr_t) + cnt; + struct tcpcb *tp = sototcpcb(so); + + tp->t_iobc = *cp; + tp->t_oobflags |= TCPOOB_HAVEDATA; + bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1)); + m->m_len--; + return; + } + cnt -= m->m_len; + m = m->m_next; + if (m == 0) + break; + } + panic("tcp_pulloutofband"); +} + /* * Insert segment ti into reassembly queue of tcp with * control block tp. Return TH_FIN if reassembly now includes @@ -640,7 +866,6 @@ tcp_reass(tp, ti) struct socket *so = tp->t_inpcb->inp_socket; struct mbuf *m; int flags; -COUNT(TCP_REASS); /* * Call with ti==0 after become established to @@ -664,13 +889,13 @@ COUNT(TCP_REASS); */ if ((struct tcpiphdr *)q->ti_prev != (struct tcpiphdr *)tp) { register int i; - q = (struct tcpiphdr *)(q->ti_prev); + q = (struct tcpiphdr *)q->ti_prev; /* conversion to int (in i) handles seq wraparound */ i = q->ti_seq + q->ti_len - ti->ti_seq; if (i > 0) { if (i >= ti->ti_len) goto drop; - m_adj(dtom(tp), i); + m_adj(dtom(ti), i); ti->ti_len -= i; ti->ti_seq += i; } @@ -681,17 +906,20 @@ COUNT(TCP_REASS); * While we overlap succeeding segments trim them or, * if they are completely covered, dequeue them. */ - while (q != (struct tcpiphdr *)tp && - SEQ_GT(ti->ti_seq + ti->ti_len, q->ti_seq)) { + while (q != (struct tcpiphdr *)tp) { register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; + if (i <= 0) + break; if (i < q->ti_len) { + q->ti_seq += i; q->ti_len -= i; m_adj(dtom(q), i); break; } q = (struct tcpiphdr *)q->ti_next; - m_freem(dtom(q->ti_prev)); + m = dtom(q->ti_prev); remque(q->ti_prev); + m_freem(m); } /* @@ -718,7 +946,7 @@ present: m = dtom(ti); ti = (struct tcpiphdr *)ti->ti_next; if (so->so_state & SS_CANTRCVMORE) - (void) m_freem(m); + m_freem(m); else sbappend(&so->so_rcv, m); } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt);