123193Smckusick /* 229152Smckusick * Copyright (c) 1982, 1986 Regents of the University of California. 332789Sbostic * All rights reserved. 423193Smckusick * 532789Sbostic * Redistribution and use in source and binary forms are permitted 6*34855Sbostic * provided that the above copyright notice and this paragraph are 7*34855Sbostic * duplicated in all such forms and that any documentation, 8*34855Sbostic * advertising materials, and other materials related to such 9*34855Sbostic * distribution and use acknowledge that the software was developed 10*34855Sbostic * by the University of California, Berkeley. The name of the 11*34855Sbostic * University may not be used to endorse or promote products derived 12*34855Sbostic * from this software without specific prior written permission. 13*34855Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34855Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34855Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1632789Sbostic * 17*34855Sbostic * @(#)tcp_subr.c 7.14 (Berkeley) 06/29/88 1823193Smckusick */ 195068Swnj 2017064Sbloom #include "param.h" 2117064Sbloom #include "systm.h" 2217064Sbloom #include "mbuf.h" 2317064Sbloom #include "socket.h" 2417064Sbloom #include "socketvar.h" 2517064Sbloom #include "protosw.h" 2617064Sbloom #include "errno.h" 2710896Ssam 2810896Ssam #include "../net/route.h" 2910896Ssam #include "../net/if.h" 3010896Ssam 3117064Sbloom #include "in.h" 3217064Sbloom #include "in_pcb.h" 3317064Sbloom #include "in_systm.h" 3417064Sbloom #include "ip.h" 3517064Sbloom #include "ip_var.h" 3617064Sbloom #include "ip_icmp.h" 3717064Sbloom #include "tcp.h" 3817064Sbloom #include "tcp_fsm.h" 3917064Sbloom #include "tcp_seq.h" 4017064Sbloom #include "tcp_timer.h" 4117064Sbloom #include "tcp_var.h" 4217064Sbloom #include "tcpip.h" 435068Swnj 4431395Skarels int tcp_ttl = TCP_TTL; 4531395Skarels 465068Swnj /* 475068Swnj * Tcp initialization 485068Swnj */ 495068Swnj tcp_init() 505068Swnj { 515068Swnj 525068Swnj tcp_iss = 1; /* wrong */ 535068Swnj tcb.inp_next = tcb.inp_prev = &tcb; 545068Swnj } 555068Swnj 565068Swnj /* 575068Swnj * Create template to be used to send tcp packets on a connection. 585068Swnj * Call after host entry created, allocates an mbuf and fills 595068Swnj * in a skeletal tcp/ip header, minimizing the amount of work 605068Swnj * necessary when the connection is used. 615068Swnj */ 625068Swnj struct tcpiphdr * 635068Swnj tcp_template(tp) 645068Swnj struct tcpcb *tp; 655068Swnj { 665068Swnj register struct inpcb *inp = tp->t_inpcb; 675068Swnj register struct mbuf *m; 685068Swnj register struct tcpiphdr *n; 695068Swnj 7026815Skarels if ((n = tp->t_template) == 0) { 7132101Skarels m = m_get(M_DONTWAIT, MT_HEADER); 7226815Skarels if (m == NULL) 7326815Skarels return (0); 7426815Skarels m->m_off = MMAXOFF - sizeof (struct tcpiphdr); 7526815Skarels m->m_len = sizeof (struct tcpiphdr); 7626815Skarels n = mtod(m, struct tcpiphdr *); 7726815Skarels } 785068Swnj n->ti_next = n->ti_prev = 0; 795068Swnj n->ti_x1 = 0; 805068Swnj n->ti_pr = IPPROTO_TCP; 815068Swnj n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); 825068Swnj n->ti_src = inp->inp_laddr; 835068Swnj n->ti_dst = inp->inp_faddr; 845068Swnj n->ti_sport = inp->inp_lport; 855068Swnj n->ti_dport = inp->inp_fport; 865068Swnj n->ti_seq = 0; 875089Swnj n->ti_ack = 0; 885068Swnj n->ti_x2 = 0; 895068Swnj n->ti_off = 5; 905068Swnj n->ti_flags = 0; 915068Swnj n->ti_win = 0; 925068Swnj n->ti_sum = 0; 935068Swnj n->ti_urp = 0; 945068Swnj return (n); 955068Swnj } 965068Swnj 975068Swnj /* 985164Swnj * Send a single message to the TCP at address specified by 995164Swnj * the given TCP/IP header. If flags==0, then we make a copy 1005164Swnj * of the tcpiphdr at ti and send directly to the addressed host. 1015164Swnj * This is used to force keep alive messages out using the TCP 1025164Swnj * template for a connection tp->t_template. If flags are given 1035164Swnj * then we send a message back to the TCP which originated the 1045164Swnj * segment ti, and discard the mbuf containing it and any other 1055164Swnj * attached mbufs. 1065164Swnj * 1075164Swnj * In any case the ack and sequence number of the transmitted 1085164Swnj * segment are as specified by the parameters. 1095068Swnj */ 1105392Swnj tcp_respond(tp, ti, ack, seq, flags) 1115392Swnj struct tcpcb *tp; 1125068Swnj register struct tcpiphdr *ti; 1135089Swnj tcp_seq ack, seq; 1145068Swnj int flags; 1155068Swnj { 11630762Skarels register struct mbuf *m; 1176212Swnj int win = 0, tlen; 1186353Ssam struct route *ro = 0; 1195068Swnj 1206353Ssam if (tp) { 1215392Swnj win = sbspace(&tp->t_inpcb->inp_socket->so_rcv); 1226353Ssam ro = &tp->t_inpcb->inp_route; 1236353Ssam } 1245164Swnj if (flags == 0) { 1259644Ssam m = m_get(M_DONTWAIT, MT_HEADER); 12610144Ssam if (m == NULL) 1275164Swnj return; 12831727Skarels #ifdef TCP_COMPAT_42 12931727Skarels tlen = 1; 13031727Skarels #else 13131727Skarels tlen = 0; 13231727Skarels #endif 13330762Skarels m->m_len = sizeof (struct tcpiphdr) + tlen; 1345164Swnj *mtod(m, struct tcpiphdr *) = *ti; 1355164Swnj ti = mtod(m, struct tcpiphdr *); 1365164Swnj flags = TH_ACK; 1375164Swnj } else { 1385245Sroot m = dtom(ti); 1395164Swnj m_freem(m->m_next); 1405164Swnj m->m_next = 0; 1416117Swnj m->m_off = (int)ti - (int)m; 14230762Skarels tlen = 0; 1435164Swnj m->m_len = sizeof (struct tcpiphdr); 1445089Swnj #define xchg(a,b,type) { type t; t=a; a=b; b=t; } 1455164Swnj xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long); 1465164Swnj xchg(ti->ti_dport, ti->ti_sport, u_short); 1475068Swnj #undef xchg 1485164Swnj } 1495089Swnj ti->ti_next = ti->ti_prev = 0; 1505089Swnj ti->ti_x1 = 0; 1519185Ssam ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); 1528942Sroot ti->ti_seq = htonl(seq); 1538942Sroot ti->ti_ack = htonl(ack); 1545089Swnj ti->ti_x2 = 0; 1555089Swnj ti->ti_off = sizeof (struct tcphdr) >> 2; 1565068Swnj ti->ti_flags = flags; 1579185Ssam ti->ti_win = htons((u_short)win); 1585392Swnj ti->ti_urp = 0; 1596304Sroot ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + tlen); 1606212Swnj ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + tlen; 16131395Skarels ((struct ip *)ti)->ip_ttl = tcp_ttl; 1626353Ssam (void) ip_output(m, (struct mbuf *)0, ro, 0); 1635068Swnj } 1645075Swnj 1655089Swnj /* 1665089Swnj * Create a new TCP control block, making an 1675089Swnj * empty reassembly queue and hooking it to the argument 1685089Swnj * protocol control block. 1695089Swnj */ 1705075Swnj struct tcpcb * 1715075Swnj tcp_newtcpcb(inp) 1725075Swnj struct inpcb *inp; 1735075Swnj { 1749644Ssam struct mbuf *m = m_getclr(M_DONTWAIT, MT_PCB); 1755075Swnj register struct tcpcb *tp; 1765075Swnj 17710144Ssam if (m == NULL) 17810144Ssam return ((struct tcpcb *)0); 1795075Swnj tp = mtod(m, struct tcpcb *); 1805075Swnj tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp; 18117317Skarels tp->t_maxseg = TCP_MSS; 1826470Sroot tp->t_flags = 0; /* sends options! */ 1835075Swnj tp->t_inpcb = inp; 18431726Skarels /* 18531757Skarels * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no 18631757Skarels * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives 18731757Skarels * reasonable initial retransmit time. 18831726Skarels */ 18931757Skarels tp->t_srtt = TCPTV_SRTTBASE; 19031757Skarels tp->t_rttvar = TCPTV_SRTTDFLT << 2; 19132374Skarels TCPT_RANGESET(tp->t_rxtcur, 19232374Skarels ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1, 19332374Skarels TCPTV_MIN, TCPTV_REXMTMAX); 19417359Skarels tp->snd_cwnd = sbspace(&inp->inp_socket->so_snd); 19532101Skarels tp->snd_ssthresh = 65535; /* XXX */ 1965075Swnj inp->inp_ppcb = (caddr_t)tp; 1975075Swnj return (tp); 1985075Swnj } 1995075Swnj 2005089Swnj /* 2015089Swnj * Drop a TCP connection, reporting 2025089Swnj * the specified error. If connection is synchronized, 2035089Swnj * then send a RST to peer. 2045089Swnj */ 20510395Ssam struct tcpcb * 2065075Swnj tcp_drop(tp, errno) 20710395Ssam register struct tcpcb *tp; 2085075Swnj int errno; 2095075Swnj { 2105075Swnj struct socket *so = tp->t_inpcb->inp_socket; 2115075Swnj 2125286Sroot if (TCPS_HAVERCVDSYN(tp->t_state)) { 2135075Swnj tp->t_state = TCPS_CLOSED; 2148776Sroot (void) tcp_output(tp); 21530524Skarels tcpstat.tcps_drops++; 21630524Skarels } else 21730524Skarels tcpstat.tcps_conndrops++; 2185075Swnj so->so_error = errno; 21910395Ssam return (tcp_close(tp)); 2205075Swnj } 2215075Swnj 2225089Swnj /* 2235089Swnj * Close a TCP control block: 2245089Swnj * discard all space held by the tcp 2255089Swnj * discard internet protocol block 2265089Swnj * wake up any sleepers 2275089Swnj */ 22810395Ssam struct tcpcb * 2295075Swnj tcp_close(tp) 2305075Swnj register struct tcpcb *tp; 2315075Swnj { 2325075Swnj register struct tcpiphdr *t; 2335261Swnj struct inpcb *inp = tp->t_inpcb; 2345261Swnj struct socket *so = inp->inp_socket; 23512422Ssam register struct mbuf *m; 2365075Swnj 2375075Swnj t = tp->seg_next; 23812422Ssam while (t != (struct tcpiphdr *)tp) { 23912422Ssam t = (struct tcpiphdr *)t->ti_next; 24012422Ssam m = dtom(t->ti_prev); 24112422Ssam remque(t->ti_prev); 24212422Ssam m_freem(m); 24312422Ssam } 2445089Swnj if (tp->t_template) 2455075Swnj (void) m_free(dtom(tp->t_template)); 2465075Swnj (void) m_free(dtom(tp)); 2475261Swnj inp->inp_ppcb = 0; 2486472Sroot soisdisconnected(so); 2495269Sroot in_pcbdetach(inp); 25030524Skarels tcpstat.tcps_closed++; 25110395Ssam return ((struct tcpcb *)0); 2525075Swnj } 2535075Swnj 2545075Swnj tcp_drain() 2555075Swnj { 2565075Swnj 2575075Swnj } 2585075Swnj 25930233Skarels /* 26030233Skarels * Notify a tcp user of an asynchronous error; 26130233Skarels * just wake up so that he can collect error status. 26230233Skarels */ 26330233Skarels tcp_notify(inp) 26430233Skarels register struct inpcb *inp; 26530233Skarels { 26630233Skarels 26730233Skarels wakeup((caddr_t) &inp->inp_socket->so_timeo); 26830233Skarels sorwakeup(inp->inp_socket); 26930233Skarels sowwakeup(inp->inp_socket); 27030233Skarels } 27124818Skarels tcp_ctlinput(cmd, sa) 2726584Ssam int cmd; 27324818Skarels struct sockaddr *sa; 2745075Swnj { 2756591Ssam extern u_char inetctlerrmap[]; 27624818Skarels struct sockaddr_in *sin; 27721119Skarels int tcp_quench(), in_rtchange(); 2786591Ssam 27924818Skarels if ((unsigned)cmd > PRC_NCMDS) 2806591Ssam return; 28124818Skarels if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) 28224818Skarels return; 28324818Skarels sin = (struct sockaddr_in *)sa; 28424818Skarels if (sin->sin_addr.s_addr == INADDR_ANY) 28524818Skarels return; 28624818Skarels 2876591Ssam switch (cmd) { 2886591Ssam 2896591Ssam case PRC_QUENCH: 29024818Skarels in_pcbnotify(&tcb, &sin->sin_addr, 0, tcp_quench); 2916591Ssam break; 2926591Ssam 29324818Skarels case PRC_ROUTEDEAD: 29421119Skarels case PRC_REDIRECT_NET: 29521119Skarels case PRC_REDIRECT_HOST: 29624818Skarels case PRC_REDIRECT_TOSNET: 29724818Skarels case PRC_REDIRECT_TOSHOST: 29824818Skarels in_pcbnotify(&tcb, &sin->sin_addr, 0, in_rtchange); 29921119Skarels break; 30021119Skarels 3016591Ssam default: 30221119Skarels if (inetctlerrmap[cmd] == 0) 30321119Skarels return; /* XXX */ 30424818Skarels in_pcbnotify(&tcb, &sin->sin_addr, (int)inetctlerrmap[cmd], 30530233Skarels tcp_notify); 3066591Ssam } 3075075Swnj } 30817359Skarels 30917359Skarels /* 31017359Skarels * When a source quench is received, close congestion window 31131442Skarels * to one segment. We will gradually open it again as we proceed. 31217359Skarels */ 31317359Skarels tcp_quench(inp) 31417359Skarels struct inpcb *inp; 31517359Skarels { 31617359Skarels struct tcpcb *tp = intotcpcb(inp); 31717359Skarels 31824818Skarels if (tp) 31931442Skarels tp->snd_cwnd = tp->t_maxseg; 32017359Skarels } 321