Commit | Line | Data |
---|---|---|
4ad99bae | 1 | /* tcp_output.c 4.15 81/11/20 */ |
76ee76df BJ |
2 | |
3 | #include "../h/param.h" | |
4 | #include "../h/systm.h" | |
5 | #include "../h/mbuf.h" | |
6 | #include "../h/socket.h" | |
d52566dd | 7 | #include "../h/socketvar.h" |
d52566dd | 8 | #include "../net/inet.h" |
53a5409e | 9 | #include "../net/inet_pcb.h" |
d52566dd BJ |
10 | #include "../net/inet_systm.h" |
11 | #include "../net/imp.h" | |
12 | #include "../net/ip.h" | |
eb44bfb2 | 13 | #include "../net/ip_var.h" |
d52566dd BJ |
14 | #include "../net/tcp.h" |
15 | #include "../net/tcp_var.h" | |
16 | #include "../net/tcp_fsm.h" | |
17 | #include "/usr/include/errno.h" | |
76ee76df | 18 | |
ea727f86 BJ |
19 | /* |
20 | * Special routines to send control messages. | |
21 | */ | |
22 | tcp_sndctl(tp) | |
53a5409e | 23 | struct tcpcb *tp; |
ea727f86 BJ |
24 | { |
25 | COUNT(TCP_SNDCTL); | |
26 | ||
27 | if (tcp_send(tp)) | |
28 | return (1); | |
29 | tcp_sndnull(tp); | |
d52566dd | 30 | return (0); |
ea727f86 BJ |
31 | } |
32 | ||
33 | tcp_sndwin(tp) | |
53a5409e | 34 | struct tcpcb *tp; |
ea727f86 BJ |
35 | { |
36 | int ihave, hehas; | |
37 | COUNT(TCP_SNDWIN); | |
38 | ||
39 | if (tp->rcv_adv) { | |
53a5409e BJ |
40 | register struct socket *so = tp->t_inpcb->inp_socket; |
41 | ||
d52566dd BJ |
42 | ihave = so->so_rcv.sb_hiwat - |
43 | (so->so_rcv.sb_cc + tp->seqcnt); | |
ea727f86 | 44 | hehas = tp->rcv_adv - tp->rcv_nxt; |
d52566dd | 45 | if ((100*(ihave-hehas)/so->so_rcv.sb_hiwat) < 35) |
ea727f86 BJ |
46 | return; |
47 | } | |
48 | if (tcp_send(tp)) | |
cdad2eb1 | 49 | return; |
ea727f86 | 50 | tcp_sndnull(tp); |
ea727f86 BJ |
51 | } |
52 | ||
53 | tcp_sndnull(tp) | |
53a5409e | 54 | register struct tcpcb *tp; |
ea727f86 BJ |
55 | { |
56 | COUNT(TCP_SNDNULL); | |
57 | ||
cdad2eb1 | 58 | (void) tcp_output(tp, 0, 0, (struct mbuf *)0); |
ea727f86 BJ |
59 | tp->tc_flags &= ~TC_ACK_DUE; |
60 | } | |
61 | ||
62 | tcp_sndrst(tp, n) | |
53a5409e BJ |
63 | register struct tcpcb *tp; |
64 | register struct tcpiphdr *n; | |
ea727f86 BJ |
65 | { |
66 | COUNT(TCP_SNDRST); | |
67 | ||
68 | /* don't send a reset in response to a reset */ | |
eb44bfb2 | 69 | if (n->ti_flags&TH_RST) |
ea727f86 BJ |
70 | return; |
71 | tp->tc_flags |= TC_SND_RST; | |
eb44bfb2 BJ |
72 | if (n->ti_flags&TH_ACK) |
73 | tp->snd_nxt = n->ti_ackno; | |
ea727f86 BJ |
74 | tp->tc_flags &= ~TC_SYN_RCVD; |
75 | tcp_sndnull(tp); | |
76 | tp->tc_flags &= ~TC_SND_RST; | |
77 | } | |
78 | ||
79 | /* | |
80 | * Tcp segment output routine. | |
81 | */ | |
82 | tcp_send(tp) | |
53a5409e | 83 | register struct tcpcb *tp; |
76ee76df | 84 | { |
76ee76df | 85 | register unsigned long last, wind; |
53a5409e | 86 | register struct socket *so = tp->t_inpcb->inp_socket; |
76ee76df | 87 | struct mbuf *m; |
53a5409e | 88 | int flags = 0, forced, sent, len; |
76ee76df | 89 | |
ea727f86 | 90 | COUNT(TCP_SEND); |
76ee76df BJ |
91 | tp->snd_lst = tp->snd_nxt; |
92 | forced = 0; | |
93 | m = NULL; | |
ea727f86 | 94 | if (tp->snd_nxt == tp->iss) { |
76ee76df BJ |
95 | flags |= TH_SYN; |
96 | tp->snd_lst++; | |
97 | } | |
76ee76df | 98 | last = tp->snd_off; |
d52566dd | 99 | for (m = so->so_snd.sb_mb; m != NULL; m = m->m_next) |
76ee76df | 100 | last += m->m_len; |
76ee76df | 101 | if (tp->snd_nxt > last) { |
76ee76df BJ |
102 | if ((tp->tc_flags&TC_SND_FIN) && |
103 | (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) { | |
104 | ||
105 | flags |= TH_FIN; | |
106 | tp->seq_fin = tp->snd_lst++; | |
107 | } | |
ea727f86 | 108 | } else { |
76ee76df | 109 | if (tp->tc_flags&TC_SYN_ACKED) { |
76ee76df | 110 | wind = tp->snd_una + tp->snd_wnd; |
cdad2eb1 | 111 | tp->snd_lst = MIN(last, wind); |
76ee76df BJ |
112 | if ((len = tp->snd_lst - tp->snd_nxt) > 1024) |
113 | tp->snd_lst -= len - 1024; | |
76ee76df BJ |
114 | if (tp->snd_lst >= wind) |
115 | tp->t_persist = T_PERS; | |
116 | } | |
76ee76df BJ |
117 | if ((tp->tc_flags&TC_FORCE_ONE) && (tp->snd_lst == wind)) { |
118 | tp->snd_lst = tp->snd_nxt + 1; | |
119 | forced = 1; | |
ddfd844e | 120 | } else if (tp->snd_nxt >= tp->snd_lst && (tp->tc_flags&TC_SND_FIN) == 0) |
7be5ba08 | 121 | return (0); |
4ad99bae | 122 | m = m_copy(so->so_snd.sb_mb, |
cdad2eb1 BJ |
123 | (int)(MAX(tp->iss+1,tp->snd_nxt) - tp->snd_off), |
124 | (int)(tp->snd_lst - tp->snd_off)); | |
76ee76df BJ |
125 | if (tp->snd_end > tp->iss && tp->snd_end <= tp->snd_lst) |
126 | flags |= TH_EOL; | |
76ee76df BJ |
127 | if ((tp->tc_flags&TC_SND_FIN) && !forced && |
128 | tp->snd_lst == last && | |
129 | (tp->seq_fin == tp->iss || tp->snd_nxt <= tp->seq_fin)) { | |
76ee76df BJ |
130 | flags |= TH_FIN; |
131 | tp->seq_fin = tp->snd_lst++; | |
132 | } | |
133 | } | |
ddfd844e BJ |
134 | if (tp->snd_nxt >= tp->snd_lst) |
135 | return (0); | |
ea727f86 BJ |
136 | if (tp->tc_flags & TC_SND_URG) |
137 | flags |= TH_URG; | |
cdad2eb1 | 138 | sent = tcp_output(tp, flags, (int)(tp->snd_lst - tp->snd_nxt), m); |
ea727f86 BJ |
139 | if (!forced) { |
140 | tp->t_rexmt = tp->t_xmtime; | |
141 | tp->t_rexmt_val = tp->snd_lst; | |
142 | if ((tp->tc_flags&TC_REXMT) == 0) { | |
143 | tp->t_rexmttl = T_REXMTTL; | |
144 | tp->t_rtl_val = tp->snd_lst; | |
76ee76df | 145 | } |
76ee76df | 146 | } |
ea727f86 BJ |
147 | if (sent) |
148 | tp->snd_nxt = tp->snd_lst; | |
149 | if ((tp->tc_flags&TC_SYN_ACKED) && | |
150 | tp->snd_una > tp->t_xmt_val) { | |
151 | tp->t_xmt = 0; | |
152 | tp->t_xmt_val = tp->snd_lst; | |
76ee76df | 153 | } |
ea727f86 | 154 | tp->tc_flags &= ~(TC_ACK_DUE|TC_REXMT|TC_FORCE_ONE); |
7be5ba08 | 155 | tp->snd_hi = MAX(tp->snd_nxt, tp->snd_hi); |
ea727f86 | 156 | return (1); |
76ee76df BJ |
157 | } |
158 | ||
159 | /* | |
160 | * Create template to be used to send tcp packets on a connection. | |
161 | * Call after host entry created, allocates an mbuf and fills | |
162 | * in a skeletal tcp/ip header, minimizing the amount of work | |
163 | * necessary when the connection is used. | |
164 | */ | |
53a5409e | 165 | struct tcpiphdr * |
76ee76df | 166 | tcp_template(tp) |
53a5409e | 167 | struct tcpcb *tp; |
76ee76df | 168 | { |
53a5409e | 169 | register struct inpcb *inp = tp->t_inpcb; |
76ee76df | 170 | register struct mbuf *m; |
53a5409e | 171 | register struct tcpiphdr *n; |
76ee76df | 172 | |
4ad99bae | 173 | COUNT(TCP_TEMPLATE); |
76ee76df BJ |
174 | m = m_get(1); |
175 | if (m == 0) | |
176 | return (0); | |
53a5409e BJ |
177 | m->m_off = MMAXOFF - sizeof (struct tcpiphdr); |
178 | m->m_len = sizeof (struct tcpiphdr); | |
179 | n = mtod(m, struct tcpiphdr *); | |
eb44bfb2 BJ |
180 | n->ti_next = n->ti_prev = 0; |
181 | n->ti_x1 = 0; | |
182 | n->ti_pr = IPPROTO_TCP; | |
183 | n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip)); | |
4ad99bae BJ |
184 | n->ti_src = inp->inp_laddr; |
185 | n->ti_dst = inp->inp_faddr; | |
eb44bfb2 BJ |
186 | n->ti_sport = htons(inp->inp_lport); |
187 | n->ti_dport = htons(inp->inp_fport); | |
188 | n->ti_seq = 0; | |
189 | n->ti_ackno = 0; | |
190 | n->ti_x2 = 0; | |
191 | n->ti_off = 5; | |
192 | n->ti_flags = 0; | |
193 | n->ti_win = 0; | |
194 | n->ti_sum = 0; | |
195 | n->ti_urp = 0; | |
76ee76df BJ |
196 | return (n); |
197 | } | |
198 | ||
199 | tcp_output(tp, flags, len, dat) | |
53a5409e | 200 | register struct tcpcb *tp; |
76ee76df BJ |
201 | register int flags; |
202 | int len; | |
203 | struct mbuf *dat; | |
204 | { | |
53a5409e | 205 | register struct tcpiphdr *t; /* known to be r9 */ |
76ee76df | 206 | register struct mbuf *m; |
53a5409e | 207 | struct socket *so = tp->t_inpcb->inp_socket; |
76ee76df | 208 | register struct ip *ip; |
76ee76df BJ |
209 | #ifdef TCPDEBUG |
210 | struct tcp_debug tdb; | |
211 | #endif | |
3552a746 | 212 | COUNT(TCP_OUTPUT); |
76ee76df | 213 | |
e1506033 | 214 | if ((t = tp->t_template) == 0) |
76ee76df BJ |
215 | return (0); |
216 | MGET(m, 0); | |
217 | if (m == 0) | |
218 | return (0); | |
53a5409e BJ |
219 | m->m_off = MMAXOFF - sizeof(struct tcpiphdr); |
220 | m->m_len = sizeof (struct tcpiphdr); | |
76ee76df BJ |
221 | m->m_next = dat; |
222 | if (flags & TH_SYN) | |
223 | len--; | |
224 | if (flags & TH_FIN) | |
225 | len--; | |
cdad2eb1 BJ |
226 | if (len < 0) |
227 | panic("tcp_output"); | |
53a5409e BJ |
228 | bcopy((caddr_t)t, mtod(m, caddr_t), sizeof (struct tcpiphdr)); |
229 | t = mtod(m, struct tcpiphdr *); | |
76ee76df BJ |
230 | if (tp->tc_flags&TC_SND_RST) { |
231 | flags &= ~TH_SYN; | |
232 | flags |= TH_RST; | |
233 | } | |
234 | if (tp->tc_flags&TC_SYN_RCVD) | |
235 | flags |= TH_ACK; | |
eb44bfb2 | 236 | t->ti_flags = flags; |
76ee76df | 237 | if (flags & TH_URG) |
cdad2eb1 | 238 | t->ti_urp = htons((u_short)tp->snd_urp); /*XXX */ |
eb44bfb2 | 239 | t->ti_win = |
53a5409e BJ |
240 | so->so_rcv.sb_hiwat - |
241 | (so->so_rcv.sb_cc + tp->seqcnt); | |
eb44bfb2 BJ |
242 | if (tp->rcv_nxt + t->ti_win > tp->rcv_adv) |
243 | tp->rcv_adv = tp->rcv_nxt + t->ti_win; | |
76ee76df | 244 | if (len) |
cdad2eb1 | 245 | t->ti_len = htons((u_short)(len + TCPSIZE)); |
eb44bfb2 | 246 | t->ti_win = htons(t->ti_win); |
76ee76df | 247 | #ifdef TCPDEBUG |
53a5409e | 248 | if ((so->so_options & SO_DEBUG) || tcpconsdebug) { |
eb44bfb2 BJ |
249 | t->ti_seq = tp->snd_nxt; |
250 | t->ti_ackno = tp->rcv_nxt; | |
76ee76df BJ |
251 | tdb_setup(tp, t, INSEND, &tdb); |
252 | tdb_stuff(&tdb, -2); | |
253 | } | |
254 | #endif | |
eb44bfb2 BJ |
255 | t->ti_seq = htonl(tp->snd_nxt); |
256 | t->ti_ackno = htonl(tp->rcv_nxt); | |
257 | t->ti_sum = 0; /* gratuitous? */ | |
2b4b57cd | 258 | t->ti_sum = inet_cksum(m, sizeof (struct tcpiphdr) + len); |
76ee76df BJ |
259 | ip = (struct ip *)t; |
260 | ip->ip_v = IPVERSION; | |
261 | ip->ip_hl = 5; | |
262 | ip->ip_tos = 0; | |
53a5409e | 263 | ip->ip_len = len + sizeof(struct tcpiphdr); |
76ee76df BJ |
264 | ip->ip_id = ip_id++; |
265 | ip->ip_off = 0; | |
266 | ip->ip_ttl = MAXTTL; | |
2b4b57cd BJ |
267 | ip_send(ip); |
268 | return (1); | |
76ee76df BJ |
269 | } |
270 | ||
d52566dd BJ |
271 | tcp_fasttimo() |
272 | { | |
273 | ||
4ad99bae BJ |
274 | COUNT(TCP_FASTTIMO); |
275 | /* someday do delayed ack processing here */ | |
d52566dd | 276 | } |