Commit | Line | Data |
---|---|---|
62a291b2 | 1 | /* tcp_subr.c 6.5 85/05/27 */ |
ecaa4e6f | 2 | |
20666ad3 JB |
3 | #include "param.h" |
4 | #include "systm.h" | |
5 | #include "mbuf.h" | |
6 | #include "socket.h" | |
7 | #include "socketvar.h" | |
8 | #include "protosw.h" | |
9 | #include "errno.h" | |
f4d55810 | 10 | |
c124e997 | 11 | #include "../net/route.h" |
f4d55810 SL |
12 | #include "../net/if.h" |
13 | ||
20666ad3 JB |
14 | #include "in.h" |
15 | #include "in_pcb.h" | |
16 | #include "in_systm.h" | |
17 | #include "ip.h" | |
18 | #include "ip_var.h" | |
19 | #include "ip_icmp.h" | |
20 | #include "tcp.h" | |
21 | #include "tcp_fsm.h" | |
22 | #include "tcp_seq.h" | |
23 | #include "tcp_timer.h" | |
24 | #include "tcp_var.h" | |
25 | #include "tcpip.h" | |
ecaa4e6f BJ |
26 | |
27 | /* | |
28 | * Tcp initialization | |
29 | */ | |
30 | tcp_init() | |
31 | { | |
32 | ||
33 | tcp_iss = 1; /* wrong */ | |
34 | tcb.inp_next = tcb.inp_prev = &tcb; | |
405c9168 BJ |
35 | tcp_alpha = TCP_ALPHA; |
36 | tcp_beta = TCP_BETA; | |
ecaa4e6f BJ |
37 | } |
38 | ||
39 | /* | |
40 | * Create template to be used to send tcp packets on a connection. | |
41 | * Call after host entry created, allocates an mbuf and fills | |
42 | * in a skeletal tcp/ip header, minimizing the amount of work | |
43 | * necessary when the connection is used. | |
44 | */ | |
45 | struct tcpiphdr * | |
46 | tcp_template(tp) | |
47 | struct tcpcb *tp; | |
48 | { | |
49 | register struct inpcb *inp = tp->t_inpcb; | |
50 | register struct mbuf *m; | |
51 | register struct tcpiphdr *n; | |
52 | ||
cce93e4b | 53 | m = m_get(M_WAIT, MT_HEADER); |
5cdc4d65 | 54 | if (m == NULL) |
ecaa4e6f BJ |
55 | return (0); |
56 | m->m_off = MMAXOFF - sizeof (struct tcpiphdr); | |
57 | m->m_len = sizeof (struct tcpiphdr); | |
58 | n = mtod(m, struct tcpiphdr *); | |
59 | n->ti_next = n->ti_prev = 0; | |
60 | n->ti_x1 = 0; | |
61 | n->ti_pr = IPPROTO_TCP; | |
62 | n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); | |
63 | n->ti_src = inp->inp_laddr; | |
64 | n->ti_dst = inp->inp_faddr; | |
65 | n->ti_sport = inp->inp_lport; | |
66 | n->ti_dport = inp->inp_fport; | |
67 | n->ti_seq = 0; | |
0974b45c | 68 | n->ti_ack = 0; |
ecaa4e6f BJ |
69 | n->ti_x2 = 0; |
70 | n->ti_off = 5; | |
71 | n->ti_flags = 0; | |
72 | n->ti_win = 0; | |
73 | n->ti_sum = 0; | |
74 | n->ti_urp = 0; | |
75 | return (n); | |
76 | } | |
77 | ||
78 | /* | |
405c9168 BJ |
79 | * Send a single message to the TCP at address specified by |
80 | * the given TCP/IP header. If flags==0, then we make a copy | |
81 | * of the tcpiphdr at ti and send directly to the addressed host. | |
82 | * This is used to force keep alive messages out using the TCP | |
83 | * template for a connection tp->t_template. If flags are given | |
84 | * then we send a message back to the TCP which originated the | |
85 | * segment ti, and discard the mbuf containing it and any other | |
86 | * attached mbufs. | |
87 | * | |
88 | * In any case the ack and sequence number of the transmitted | |
89 | * segment are as specified by the parameters. | |
ecaa4e6f | 90 | */ |
8e65fd66 BJ |
91 | tcp_respond(tp, ti, ack, seq, flags) |
92 | struct tcpcb *tp; | |
ecaa4e6f | 93 | register struct tcpiphdr *ti; |
0974b45c | 94 | tcp_seq ack, seq; |
ecaa4e6f BJ |
95 | int flags; |
96 | { | |
405c9168 | 97 | struct mbuf *m; |
1e977657 | 98 | int win = 0, tlen; |
c124e997 | 99 | struct route *ro = 0; |
ecaa4e6f | 100 | |
c124e997 | 101 | if (tp) { |
8e65fd66 | 102 | win = sbspace(&tp->t_inpcb->inp_socket->so_rcv); |
c124e997 SL |
103 | ro = &tp->t_inpcb->inp_route; |
104 | } | |
405c9168 | 105 | if (flags == 0) { |
cce93e4b | 106 | m = m_get(M_DONTWAIT, MT_HEADER); |
5cdc4d65 | 107 | if (m == NULL) |
405c9168 | 108 | return; |
1e977657 | 109 | m->m_len = sizeof (struct tcpiphdr) + 1; |
405c9168 BJ |
110 | *mtod(m, struct tcpiphdr *) = *ti; |
111 | ti = mtod(m, struct tcpiphdr *); | |
112 | flags = TH_ACK; | |
1e977657 | 113 | tlen = 1; |
405c9168 | 114 | } else { |
4aed14e3 | 115 | m = dtom(ti); |
405c9168 BJ |
116 | m_freem(m->m_next); |
117 | m->m_next = 0; | |
1acff8ec | 118 | m->m_off = (int)ti - (int)m; |
405c9168 | 119 | m->m_len = sizeof (struct tcpiphdr); |
0974b45c | 120 | #define xchg(a,b,type) { type t; t=a; a=b; b=t; } |
405c9168 BJ |
121 | xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long); |
122 | xchg(ti->ti_dport, ti->ti_sport, u_short); | |
ecaa4e6f | 123 | #undef xchg |
1e977657 | 124 | tlen = 0; |
405c9168 | 125 | } |
0974b45c BJ |
126 | ti->ti_next = ti->ti_prev = 0; |
127 | ti->ti_x1 = 0; | |
af8f6a21 | 128 | ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); |
2c48b3f8 BJ |
129 | ti->ti_seq = htonl(seq); |
130 | ti->ti_ack = htonl(ack); | |
0974b45c BJ |
131 | ti->ti_x2 = 0; |
132 | ti->ti_off = sizeof (struct tcphdr) >> 2; | |
ecaa4e6f | 133 | ti->ti_flags = flags; |
af8f6a21 | 134 | ti->ti_win = htons((u_short)win); |
8e65fd66 | 135 | ti->ti_urp = 0; |
f3cdd721 | 136 | ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + tlen); |
1e977657 | 137 | ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + tlen; |
0974b45c | 138 | ((struct ip *)ti)->ip_ttl = TCP_TTL; |
c124e997 | 139 | (void) ip_output(m, (struct mbuf *)0, ro, 0); |
ecaa4e6f | 140 | } |
a6503abf | 141 | |
0974b45c BJ |
142 | /* |
143 | * Create a new TCP control block, making an | |
144 | * empty reassembly queue and hooking it to the argument | |
145 | * protocol control block. | |
146 | */ | |
a6503abf BJ |
147 | struct tcpcb * |
148 | tcp_newtcpcb(inp) | |
149 | struct inpcb *inp; | |
150 | { | |
cce93e4b | 151 | struct mbuf *m = m_getclr(M_DONTWAIT, MT_PCB); |
a6503abf | 152 | register struct tcpcb *tp; |
a6503abf | 153 | |
5cdc4d65 SL |
154 | if (m == NULL) |
155 | return ((struct tcpcb *)0); | |
a6503abf | 156 | tp = mtod(m, struct tcpcb *); |
a6503abf | 157 | tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp; |
247c6a81 | 158 | tp->t_maxseg = TCP_MSS; |
3e60e1e6 | 159 | tp->t_flags = 0; /* sends options! */ |
a6503abf | 160 | tp->t_inpcb = inp; |
247c6a81 | 161 | tp->t_srtt = TCPTV_SRTTBASE; |
05586739 | 162 | tp->snd_cwnd = sbspace(&inp->inp_socket->so_snd); |
a6503abf BJ |
163 | inp->inp_ppcb = (caddr_t)tp; |
164 | return (tp); | |
165 | } | |
166 | ||
0974b45c BJ |
167 | /* |
168 | * Drop a TCP connection, reporting | |
169 | * the specified error. If connection is synchronized, | |
170 | * then send a RST to peer. | |
171 | */ | |
0e3936fa | 172 | struct tcpcb * |
a6503abf | 173 | tcp_drop(tp, errno) |
0e3936fa | 174 | register struct tcpcb *tp; |
a6503abf BJ |
175 | int errno; |
176 | { | |
177 | struct socket *so = tp->t_inpcb->inp_socket; | |
178 | ||
d3504cc0 | 179 | if (TCPS_HAVERCVDSYN(tp->t_state)) { |
a6503abf | 180 | tp->t_state = TCPS_CLOSED; |
39d536e6 | 181 | (void) tcp_output(tp); |
a6503abf BJ |
182 | } |
183 | so->so_error = errno; | |
0e3936fa | 184 | return (tcp_close(tp)); |
a6503abf BJ |
185 | } |
186 | ||
72e4f44e SL |
187 | tcp_abort(inp) |
188 | struct inpcb *inp; | |
189 | { | |
a1edc12b | 190 | |
0e3936fa | 191 | (void) tcp_close((struct tcpcb *)inp->inp_ppcb); |
72e4f44e SL |
192 | } |
193 | ||
0974b45c BJ |
194 | /* |
195 | * Close a TCP control block: | |
196 | * discard all space held by the tcp | |
197 | * discard internet protocol block | |
198 | * wake up any sleepers | |
199 | */ | |
0e3936fa | 200 | struct tcpcb * |
a6503abf BJ |
201 | tcp_close(tp) |
202 | register struct tcpcb *tp; | |
203 | { | |
204 | register struct tcpiphdr *t; | |
364801f5 BJ |
205 | struct inpcb *inp = tp->t_inpcb; |
206 | struct socket *so = inp->inp_socket; | |
13e2480b | 207 | register struct mbuf *m; |
a6503abf | 208 | |
a6503abf | 209 | t = tp->seg_next; |
13e2480b SL |
210 | while (t != (struct tcpiphdr *)tp) { |
211 | t = (struct tcpiphdr *)t->ti_next; | |
212 | m = dtom(t->ti_prev); | |
213 | remque(t->ti_prev); | |
214 | m_freem(m); | |
215 | } | |
0974b45c | 216 | if (tp->t_template) |
a6503abf | 217 | (void) m_free(dtom(tp->t_template)); |
0974b45c BJ |
218 | if (tp->t_tcpopt) |
219 | (void) m_free(dtom(tp->t_tcpopt)); | |
220 | if (tp->t_ipopt) | |
221 | (void) m_free(dtom(tp->t_ipopt)); | |
a6503abf | 222 | (void) m_free(dtom(tp)); |
364801f5 | 223 | inp->inp_ppcb = 0; |
4aed14e3 | 224 | soisdisconnected(so); |
86676257 | 225 | in_pcbdetach(inp); |
0e3936fa | 226 | return ((struct tcpcb *)0); |
a6503abf BJ |
227 | } |
228 | ||
a6503abf BJ |
229 | tcp_drain() |
230 | { | |
a6503abf | 231 | |
a6503abf BJ |
232 | } |
233 | ||
72e4f44e SL |
234 | tcp_ctlinput(cmd, arg) |
235 | int cmd; | |
236 | caddr_t arg; | |
a6503abf | 237 | { |
62a291b2 | 238 | struct in_addr *in; |
39674d5f | 239 | extern u_char inetctlerrmap[]; |
62a291b2 | 240 | int tcp_quench(), in_rtchange(); |
39674d5f SL |
241 | |
242 | if (cmd < 0 || cmd > PRC_NCMDS) | |
243 | return; | |
244 | switch (cmd) { | |
245 | ||
246 | case PRC_ROUTEDEAD: | |
247 | break; | |
248 | ||
249 | case PRC_QUENCH: | |
62a291b2 MK |
250 | in = &((struct icmp *)arg)->icmp_ip.ip_dst; |
251 | in_pcbnotify(&tcb, in, 0, tcp_quench); | |
252 | break; | |
253 | ||
254 | case PRC_REDIRECT_NET: | |
255 | case PRC_REDIRECT_HOST: | |
256 | in = &((struct icmp *)arg)->icmp_ip.ip_dst; | |
257 | in_pcbnotify(&tcb, in, 0, in_rtchange); | |
39674d5f SL |
258 | break; |
259 | ||
39674d5f | 260 | case PRC_IFDOWN: |
62a291b2 MK |
261 | in = &((struct sockaddr_in *)arg)->sin_addr; |
262 | goto notify; | |
263 | ||
39674d5f SL |
264 | case PRC_HOSTDEAD: |
265 | case PRC_HOSTUNREACH: | |
62a291b2 MK |
266 | in = (struct in_addr *)arg; |
267 | goto notify; | |
39674d5f SL |
268 | |
269 | default: | |
62a291b2 MK |
270 | if (inetctlerrmap[cmd] == 0) |
271 | return; /* XXX */ | |
272 | in = &((struct icmp *)arg)->icmp_ip.ip_dst; | |
273 | notify: | |
274 | in_pcbnotify(&tcb, in, (int)inetctlerrmap[cmd], tcp_abort); | |
39674d5f | 275 | } |
a6503abf | 276 | } |
05586739 MK |
277 | |
278 | /* | |
279 | * When a source quench is received, close congestion window | |
280 | * to 80% of the outstanding data (but not less than one segment). | |
281 | */ | |
282 | tcp_quench(inp) | |
283 | struct inpcb *inp; | |
284 | { | |
285 | struct tcpcb *tp = intotcpcb(inp); | |
286 | ||
287 | tp->snd_cwnd = MAX(8 * (tp->snd_nxt - tp->snd_una) / 10, tp->t_maxseg); | |
288 | } |