Commit | Line | Data |
---|---|---|
8ae0e4b4 | 1 | /* |
9d91b170 | 2 | * Copyright (c) 1982, 1986, 1988 Regents of the University of California. |
2b6b6284 | 3 | * All rights reserved. |
8ae0e4b4 | 4 | * |
2b6b6284 | 5 | * Redistribution and use in source and binary forms are permitted |
616d42db KB |
6 | * provided that the above copyright notice and this paragraph are |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
2b6b6284 | 16 | * |
6d4c15f5 | 17 | * @(#)tcp_subr.c 7.13.1.2 (Berkeley) %G% |
8ae0e4b4 | 18 | */ |
ecaa4e6f | 19 | |
20666ad3 JB |
20 | #include "param.h" |
21 | #include "systm.h" | |
9d91b170 | 22 | #include "malloc.h" |
20666ad3 JB |
23 | #include "mbuf.h" |
24 | #include "socket.h" | |
25 | #include "socketvar.h" | |
26 | #include "protosw.h" | |
27 | #include "errno.h" | |
f4d55810 | 28 | |
c124e997 | 29 | #include "../net/route.h" |
f4d55810 SL |
30 | #include "../net/if.h" |
31 | ||
20666ad3 JB |
32 | #include "in.h" |
33 | #include "in_pcb.h" | |
34 | #include "in_systm.h" | |
35 | #include "ip.h" | |
36 | #include "ip_var.h" | |
37 | #include "ip_icmp.h" | |
38 | #include "tcp.h" | |
39 | #include "tcp_fsm.h" | |
40 | #include "tcp_seq.h" | |
41 | #include "tcp_timer.h" | |
42 | #include "tcp_var.h" | |
43 | #include "tcpip.h" | |
ecaa4e6f | 44 | |
10604dba | 45 | int tcp_ttl = TCP_TTL; |
10604dba | 46 | |
ecaa4e6f BJ |
47 | /* |
48 | * Tcp initialization | |
49 | */ | |
50 | tcp_init() | |
51 | { | |
52 | ||
53 | tcp_iss = 1; /* wrong */ | |
54 | tcb.inp_next = tcb.inp_prev = &tcb; | |
9d91b170 MK |
55 | if (max_protohdr < sizeof(struct tcpiphdr)) |
56 | max_protohdr = sizeof(struct tcpiphdr); | |
57 | if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN) | |
58 | panic("tcp_init"); | |
ecaa4e6f BJ |
59 | } |
60 | ||
61 | /* | |
62 | * Create template to be used to send tcp packets on a connection. | |
63 | * Call after host entry created, allocates an mbuf and fills | |
64 | * in a skeletal tcp/ip header, minimizing the amount of work | |
65 | * necessary when the connection is used. | |
66 | */ | |
67 | struct tcpiphdr * | |
68 | tcp_template(tp) | |
69 | struct tcpcb *tp; | |
70 | { | |
71 | register struct inpcb *inp = tp->t_inpcb; | |
72 | register struct mbuf *m; | |
73 | register struct tcpiphdr *n; | |
74 | ||
ece01391 | 75 | if ((n = tp->t_template) == 0) { |
9f5105e3 | 76 | m = m_get(M_DONTWAIT, MT_HEADER); |
ece01391 MK |
77 | if (m == NULL) |
78 | return (0); | |
ece01391 MK |
79 | m->m_len = sizeof (struct tcpiphdr); |
80 | n = mtod(m, struct tcpiphdr *); | |
81 | } | |
ecaa4e6f BJ |
82 | n->ti_next = n->ti_prev = 0; |
83 | n->ti_x1 = 0; | |
84 | n->ti_pr = IPPROTO_TCP; | |
85 | n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); | |
86 | n->ti_src = inp->inp_laddr; | |
87 | n->ti_dst = inp->inp_faddr; | |
88 | n->ti_sport = inp->inp_lport; | |
89 | n->ti_dport = inp->inp_fport; | |
90 | n->ti_seq = 0; | |
0974b45c | 91 | n->ti_ack = 0; |
ecaa4e6f BJ |
92 | n->ti_x2 = 0; |
93 | n->ti_off = 5; | |
94 | n->ti_flags = 0; | |
95 | n->ti_win = 0; | |
96 | n->ti_sum = 0; | |
97 | n->ti_urp = 0; | |
98 | return (n); | |
99 | } | |
100 | ||
101 | /* | |
405c9168 BJ |
102 | * Send a single message to the TCP at address specified by |
103 | * the given TCP/IP header. If flags==0, then we make a copy | |
104 | * of the tcpiphdr at ti and send directly to the addressed host. | |
105 | * This is used to force keep alive messages out using the TCP | |
106 | * template for a connection tp->t_template. If flags are given | |
107 | * then we send a message back to the TCP which originated the | |
108 | * segment ti, and discard the mbuf containing it and any other | |
109 | * attached mbufs. | |
110 | * | |
111 | * In any case the ack and sequence number of the transmitted | |
112 | * segment are as specified by the parameters. | |
ecaa4e6f | 113 | */ |
9d91b170 | 114 | tcp_respond(tp, ti, m, ack, seq, flags) |
8e65fd66 | 115 | struct tcpcb *tp; |
ecaa4e6f | 116 | register struct tcpiphdr *ti; |
9d91b170 | 117 | register struct mbuf *m; |
0974b45c | 118 | tcp_seq ack, seq; |
ecaa4e6f BJ |
119 | int flags; |
120 | { | |
1e977657 | 121 | int win = 0, tlen; |
c124e997 | 122 | struct route *ro = 0; |
ecaa4e6f | 123 | |
c124e997 | 124 | if (tp) { |
8e65fd66 | 125 | win = sbspace(&tp->t_inpcb->inp_socket->so_rcv); |
c124e997 SL |
126 | ro = &tp->t_inpcb->inp_route; |
127 | } | |
9d91b170 MK |
128 | if (m == 0) { |
129 | m = m_gethdr(M_DONTWAIT, MT_HEADER); | |
5cdc4d65 | 130 | if (m == NULL) |
405c9168 | 131 | return; |
eeef4ac3 MK |
132 | #ifdef TCP_COMPAT_42 |
133 | tlen = 1; | |
134 | #else | |
135 | tlen = 0; | |
136 | #endif | |
8b6ad229 | 137 | m->m_len = sizeof (struct tcpiphdr) + tlen; |
9d91b170 | 138 | m->m_data += max_linkhdr; |
405c9168 BJ |
139 | *mtod(m, struct tcpiphdr *) = *ti; |
140 | ti = mtod(m, struct tcpiphdr *); | |
141 | flags = TH_ACK; | |
142 | } else { | |
143 | m_freem(m->m_next); | |
144 | m->m_next = 0; | |
9d91b170 | 145 | m->m_data = (caddr_t)ti; |
8b6ad229 | 146 | tlen = 0; |
405c9168 | 147 | m->m_len = sizeof (struct tcpiphdr); |
0974b45c | 148 | #define xchg(a,b,type) { type t; t=a; a=b; b=t; } |
405c9168 BJ |
149 | xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_long); |
150 | xchg(ti->ti_dport, ti->ti_sport, u_short); | |
ecaa4e6f | 151 | #undef xchg |
405c9168 | 152 | } |
0974b45c BJ |
153 | ti->ti_next = ti->ti_prev = 0; |
154 | ti->ti_x1 = 0; | |
af8f6a21 | 155 | ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); |
2c48b3f8 BJ |
156 | ti->ti_seq = htonl(seq); |
157 | ti->ti_ack = htonl(ack); | |
0974b45c BJ |
158 | ti->ti_x2 = 0; |
159 | ti->ti_off = sizeof (struct tcphdr) >> 2; | |
ecaa4e6f | 160 | ti->ti_flags = flags; |
af8f6a21 | 161 | ti->ti_win = htons((u_short)win); |
8e65fd66 | 162 | ti->ti_urp = 0; |
f3cdd721 | 163 | ti->ti_sum = in_cksum(m, sizeof (struct tcpiphdr) + tlen); |
1e977657 | 164 | ((struct ip *)ti)->ip_len = sizeof (struct tcpiphdr) + tlen; |
10604dba | 165 | ((struct ip *)ti)->ip_ttl = tcp_ttl; |
c124e997 | 166 | (void) ip_output(m, (struct mbuf *)0, ro, 0); |
ecaa4e6f | 167 | } |
a6503abf | 168 | |
0974b45c BJ |
169 | /* |
170 | * Create a new TCP control block, making an | |
171 | * empty reassembly queue and hooking it to the argument | |
172 | * protocol control block. | |
173 | */ | |
a6503abf BJ |
174 | struct tcpcb * |
175 | tcp_newtcpcb(inp) | |
176 | struct inpcb *inp; | |
177 | { | |
cce93e4b | 178 | struct mbuf *m = m_getclr(M_DONTWAIT, MT_PCB); |
a6503abf | 179 | register struct tcpcb *tp; |
a6503abf | 180 | |
5cdc4d65 SL |
181 | if (m == NULL) |
182 | return ((struct tcpcb *)0); | |
a6503abf | 183 | tp = mtod(m, struct tcpcb *); |
a6503abf | 184 | tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp; |
247c6a81 | 185 | tp->t_maxseg = TCP_MSS; |
3e60e1e6 | 186 | tp->t_flags = 0; /* sends options! */ |
a6503abf | 187 | tp->t_inpcb = inp; |
7cc62c26 | 188 | /* |
5ca0b868 MK |
189 | * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no |
190 | * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives | |
191 | * reasonable initial retransmit time. | |
7cc62c26 | 192 | */ |
5ca0b868 MK |
193 | tp->t_srtt = TCPTV_SRTTBASE; |
194 | tp->t_rttvar = TCPTV_SRTTDFLT << 2; | |
dabb0e53 MK |
195 | TCPT_RANGESET(tp->t_rxtcur, |
196 | ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1, | |
197 | TCPTV_MIN, TCPTV_REXMTMAX); | |
05586739 | 198 | tp->snd_cwnd = sbspace(&inp->inp_socket->so_snd); |
9f5105e3 | 199 | tp->snd_ssthresh = 65535; /* XXX */ |
a6503abf BJ |
200 | inp->inp_ppcb = (caddr_t)tp; |
201 | return (tp); | |
202 | } | |
203 | ||
0974b45c BJ |
204 | /* |
205 | * Drop a TCP connection, reporting | |
206 | * the specified error. If connection is synchronized, | |
207 | * then send a RST to peer. | |
208 | */ | |
0e3936fa | 209 | struct tcpcb * |
a6503abf | 210 | tcp_drop(tp, errno) |
0e3936fa | 211 | register struct tcpcb *tp; |
a6503abf BJ |
212 | int errno; |
213 | { | |
214 | struct socket *so = tp->t_inpcb->inp_socket; | |
215 | ||
d3504cc0 | 216 | if (TCPS_HAVERCVDSYN(tp->t_state)) { |
a6503abf | 217 | tp->t_state = TCPS_CLOSED; |
39d536e6 | 218 | (void) tcp_output(tp); |
35f3fc10 MK |
219 | tcpstat.tcps_drops++; |
220 | } else | |
221 | tcpstat.tcps_conndrops++; | |
a6503abf | 222 | so->so_error = errno; |
0e3936fa | 223 | return (tcp_close(tp)); |
a6503abf BJ |
224 | } |
225 | ||
0974b45c BJ |
226 | /* |
227 | * Close a TCP control block: | |
228 | * discard all space held by the tcp | |
229 | * discard internet protocol block | |
230 | * wake up any sleepers | |
231 | */ | |
0e3936fa | 232 | struct tcpcb * |
a6503abf BJ |
233 | tcp_close(tp) |
234 | register struct tcpcb *tp; | |
235 | { | |
236 | register struct tcpiphdr *t; | |
364801f5 BJ |
237 | struct inpcb *inp = tp->t_inpcb; |
238 | struct socket *so = inp->inp_socket; | |
13e2480b | 239 | register struct mbuf *m; |
a6503abf | 240 | |
a6503abf | 241 | t = tp->seg_next; |
13e2480b SL |
242 | while (t != (struct tcpiphdr *)tp) { |
243 | t = (struct tcpiphdr *)t->ti_next; | |
244 | m = dtom(t->ti_prev); | |
245 | remque(t->ti_prev); | |
246 | m_freem(m); | |
247 | } | |
0974b45c | 248 | if (tp->t_template) |
a6503abf | 249 | (void) m_free(dtom(tp->t_template)); |
a6503abf | 250 | (void) m_free(dtom(tp)); |
364801f5 | 251 | inp->inp_ppcb = 0; |
4aed14e3 | 252 | soisdisconnected(so); |
86676257 | 253 | in_pcbdetach(inp); |
35f3fc10 | 254 | tcpstat.tcps_closed++; |
0e3936fa | 255 | return ((struct tcpcb *)0); |
a6503abf BJ |
256 | } |
257 | ||
a6503abf BJ |
258 | tcp_drain() |
259 | { | |
a6503abf | 260 | |
a6503abf BJ |
261 | } |
262 | ||
be841dc3 MK |
263 | /* |
264 | * Notify a tcp user of an asynchronous error; | |
265 | * just wake up so that he can collect error status. | |
266 | */ | |
267 | tcp_notify(inp) | |
268 | register struct inpcb *inp; | |
269 | { | |
270 | ||
271 | wakeup((caddr_t) &inp->inp_socket->so_timeo); | |
272 | sorwakeup(inp->inp_socket); | |
273 | sowwakeup(inp->inp_socket); | |
274 | } | |
7c626d4d | 275 | tcp_ctlinput(cmd, sa) |
72e4f44e | 276 | int cmd; |
7c626d4d | 277 | struct sockaddr *sa; |
a6503abf | 278 | { |
39674d5f | 279 | extern u_char inetctlerrmap[]; |
7c626d4d | 280 | struct sockaddr_in *sin; |
62a291b2 | 281 | int tcp_quench(), in_rtchange(); |
39674d5f | 282 | |
7c626d4d MK |
283 | if ((unsigned)cmd > PRC_NCMDS) |
284 | return; | |
285 | if (sa->sa_family != AF_INET && sa->sa_family != AF_IMPLINK) | |
286 | return; | |
287 | sin = (struct sockaddr_in *)sa; | |
288 | if (sin->sin_addr.s_addr == INADDR_ANY) | |
39674d5f | 289 | return; |
39674d5f | 290 | |
7c626d4d | 291 | switch (cmd) { |
39674d5f SL |
292 | |
293 | case PRC_QUENCH: | |
7c626d4d | 294 | in_pcbnotify(&tcb, &sin->sin_addr, 0, tcp_quench); |
62a291b2 MK |
295 | break; |
296 | ||
7c626d4d | 297 | case PRC_ROUTEDEAD: |
62a291b2 MK |
298 | case PRC_REDIRECT_NET: |
299 | case PRC_REDIRECT_HOST: | |
7c626d4d MK |
300 | case PRC_REDIRECT_TOSNET: |
301 | case PRC_REDIRECT_TOSHOST: | |
9d866d2f | 302 | #if BSD>=43 |
7c626d4d | 303 | in_pcbnotify(&tcb, &sin->sin_addr, 0, in_rtchange); |
9d866d2f | 304 | #endif |
39674d5f SL |
305 | break; |
306 | ||
39674d5f | 307 | default: |
62a291b2 MK |
308 | if (inetctlerrmap[cmd] == 0) |
309 | return; /* XXX */ | |
7c626d4d | 310 | in_pcbnotify(&tcb, &sin->sin_addr, (int)inetctlerrmap[cmd], |
be841dc3 | 311 | tcp_notify); |
39674d5f | 312 | } |
a6503abf | 313 | } |
05586739 | 314 | |
9d866d2f MK |
315 | #if BSD<43 |
316 | /* XXX fake routine */ | |
317 | tcp_abort(inp) | |
318 | struct inpcb *inp; | |
319 | { | |
320 | return; | |
321 | } | |
322 | #endif | |
323 | ||
05586739 MK |
324 | /* |
325 | * When a source quench is received, close congestion window | |
2e5a76f2 | 326 | * to one segment. We will gradually open it again as we proceed. |
05586739 MK |
327 | */ |
328 | tcp_quench(inp) | |
329 | struct inpcb *inp; | |
330 | { | |
331 | struct tcpcb *tp = intotcpcb(inp); | |
332 | ||
7c626d4d | 333 | if (tp) |
2e5a76f2 | 334 | tp->snd_cwnd = tp->t_maxseg; |
05586739 | 335 | } |