1 /*-
2 * Copyright (c) 1982, 1986, 1988, 1993
3 * The Regents of the University of California.
4 * Copyright (c) 2006-2007 Robert N. M. Watson
5 * Copyright (c) 2010-2011 Juniper Networks, Inc.
6 * All rights reserved.
7 *
8 * Portions of this software were developed by Robert N. M. Watson under
9 * contract to Juniper Networks, Inc.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94
36 */
37
38 #include <errno.h>
39 #include <string.h>
40 #include "../tcplp.h"
41 #include "../lib/cbuf.h"
42 #include "tcp.h"
43 #include "tcp_fsm.h"
44 #include "tcp_seq.h"
45 #include "tcp_var.h"
46 #include "tcp_timer.h"
47 #include "tcp_fastopen.h"
48 #include "ip6.h"
49
50 #include "tcp_const.h"
51
52 #include <openthread/tcp.h>
53
54 //static void tcp_disconnect(struct tcpcb *);
55 static void tcp_usrclosed(struct tcpcb *);
56
57 /*
58 * samkumar: Removed tcp6_usr_bind, since checking if an address/port is free
59 * needs to be done at the host system (along with other socket management
60 * duties). TCPlp doesn't know what other sockets are in the system, or which
61 * other addresses/ports are busy.
62 */
63
64 /* samkumar: This is based on a function in in6_pcb.c. */
in6_pcbconnect(struct tcpcb * tp,struct sockaddr_in6 * nam)65 static int in6_pcbconnect(struct tcpcb* tp, struct sockaddr_in6* nam) {
66 register struct sockaddr_in6 *sin6 = nam;
67 tp->faddr = sin6->sin6_addr;
68 tp->fport = sin6->sin6_port;
69 return 0;
70 }
71
72 /*
73 * Initiate connection to peer.
74 * Create a template for use in transmissions on this connection.
75 * Enter SYN_SENT state, and mark socket as connecting.
76 * Start keep-alive timer, and seed output sequence space.
77 * Send initial segment on connection.
78 */
79 /*
80 * samkumar: I removed locking, statistics, and inpcb management. The signature
81 * used to be
82 *
83 * static int
84 * tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td)
85 */
86 static int
tcp6_connect(struct tcpcb * tp,struct sockaddr_in6 * nam)87 tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *nam)
88 {
89 int error;
90
91 int sb_max = cbuf_free_space(&tp->recvbuf); // same as sendbuf
92
93 /*
94 * samkumar: For autobind, the original BSD code assigned the port first
95 * (with logic that also looked at the address) and then the address. This
96 * was done by calling into other parts of the FreeBSD network stack,
97 * outside of the TCP stack. Here, we just use the tcplp_sys_autobind
98 * function to do all of that work.
99 */
100 bool autobind_addr = IN6_IS_ADDR_UNSPECIFIED(&tp->laddr);
101 bool autobind_port = (tp->lport == 0);
102 if (autobind_addr || autobind_port) {
103 otSockAddr foreign;
104 otSockAddr local;
105
106 memcpy(&foreign.mAddress, &nam->sin6_addr, sizeof(foreign.mAddress));
107 foreign.mPort = ntohs(nam->sin6_port);
108
109 if (!autobind_addr) {
110 memcpy(&local.mAddress, &tp->laddr, sizeof(local.mAddress));
111 }
112
113 if (!autobind_port) {
114 local.mPort = ntohs(tp->lport);
115 }
116
117 if (!tcplp_sys_autobind(tp->instance, &foreign, &local, autobind_addr, autobind_port)) {
118 // Autobind failed
119 error = EINVAL;
120 goto out;
121 }
122
123 if (autobind_addr) {
124 memcpy(&tp->laddr, &local.mAddress, sizeof(tp->laddr));
125 }
126
127 if (autobind_port) {
128 tp->lport = htons(local.mPort);
129 }
130 }
131 error = in6_pcbconnect(tp, nam);
132 if (error != 0)
133 goto out;
134
135 /* Compute window scaling to request. */
136 while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
137 (TCP_MAXWIN << tp->request_r_scale) < sb_max)
138 tp->request_r_scale++;
139
140 tcp_state_change(tp, TCPS_SYN_SENT);
141 tp->iss = tcp_new_isn(tp);
142 tcp_sendseqinit(tp);
143
144 return 0;
145
146 out:
147 return error;
148 }
149
150 /*
151 * samkumar: I removed locking, statistics, inpcb management, and debug probes.
152 * I also remove codepaths that check for IPv6, since the address is assumed to
153 * be IPv6. The signature used to be
154 *
155 * static int
156 * tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
157 */
158 int
tcp6_usr_connect(struct tcpcb * tp,struct sockaddr_in6 * sin6p)159 tcp6_usr_connect(struct tcpcb* tp, struct sockaddr_in6* sin6p)
160 {
161 int error = 0;
162
163 if (tp->t_state != TCPS_CLOSED) { // samkumar: This is a check that I added
164 return (EISCONN);
165 }
166
167 /* samkumar: I removed the following error check since we receive sin6p
168 * in the function argument and don't need to convert a struct sockaddr to
169 * a struct sockaddr_in6 anymore.
170 *
171 * if (nam->sa_len != sizeof (*sin6p))
172 * return (EINVAL);
173 */
174
175 /*
176 * Must disallow TCP ``connections'' to multicast addresses.
177 */
178 /* samkumar: I commented out the check on sin6p->sin6_family. */
179 if (/*sin6p->sin6_family == AF_INET6
180 && */IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr))
181 return (EAFNOSUPPORT);
182
183 /*
184 * samkumar: There was some code here that obtained the TCB (struct tcpcb*)
185 * by getting the inpcb from the socket and the TCB from the inpcb. I
186 * removed that code.
187 */
188
189 /*
190 * XXXRW: Some confusion: V4/V6 flags relate to binding, and
191 * therefore probably require the hash lock, which isn't held here.
192 * Is this a significant problem?
193 */
194 if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) {
195 tcplp_sys_log("V4-Mapped Address!");
196
197 /*
198 * samkumar: There used to be code that woulf handle the case of
199 * v4-mapped addresses. It would call in6_sin6_2_sin to convert the
200 * struct sockaddr_in6 to a struct sockaddr_in, set the INP_IPV4 flag
201 * and clear the INP_IPV6 flag on inp->inp_vflag, do some other
202 * processing, and finally call tcp_connect and tcp_output. However,
203 * it would first check if the IN6P_IPV6_V6ONLY flag was set in
204 * inp->inp_flags, and if so, it would return with EINVAL. In TCPlp, we
205 * support IPv6 only, so I removed the check for IN6P_IPV6_V6ONLY and
206 * always act as if that flag is set. I kept the code in that if
207 * statement making the check, and removed the other code that actually
208 * handled this case.
209 */
210 error = EINVAL;
211 goto out;
212 }
213
214 /*
215 * samkumar: I removed some code here that set/cleared some flags in the`
216 * inpcb and called prison_remote_ip6.
217 */
218
219 /*
220 * samkumar: Originally, the struct thread *td was passed along to
221 * tcp6_connect.
222 */
223 if ((error = tcp6_connect(tp, sin6p)) != 0)
224 goto out;
225
226 tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp));
227 error = tcp_output(tp);
228
229 out:
230 return (error);
231 }
232
233 /*
234 * Do a send by putting data in output queue and updating urgent
235 * marker if URG set. Possibly send more data. Unlike the other
236 * pru_*() routines, the mbuf chains are our responsibility. We
237 * must either enqueue them or free them. The other pru_* routines
238 * generally are caller-frees.
239 */
240 /*
241 * samkumar: I removed locking, statistics, inpcb management, and debug probes.
242 * I also removed support for the urgent pointer.
243 *
244 * I changed the signature of this function. It used to be
245 * static int
246 * tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
247 * struct sockaddr *nam, struct mbuf *control, struct thread *td)
248 *
249 * The new function signature works as follows. DATA is a new linked buffer to
250 * add to the end of the send buffer. EXTENDBY is the number of bytes by which
251 * to extend the final linked buffer of the send buffer. Either DATA should be
252 * NULL, or EXTENDBY should be 0.
253 */
tcp_usr_send(struct tcpcb * tp,int moretocome,otLinkedBuffer * data,size_t extendby,struct sockaddr_in6 * nam)254 int tcp_usr_send(struct tcpcb* tp, int moretocome, otLinkedBuffer* data, size_t extendby, struct sockaddr_in6* nam)
255 {
256 int error = 0;
257 int do_fastopen_implied_connect = (nam != NULL) && IS_FASTOPEN(tp->t_flags) && tp->t_state < TCPS_SYN_SENT;
258
259 /*
260 * samkumar: This if statement and the next are checks that I added
261 */
262 if (tp->t_state < TCPS_ESTABLISHED && !IS_FASTOPEN(tp->t_flags)) {
263 error = ENOTCONN;
264 goto out;
265 }
266
267 if (tpiscantsend(tp)) {
268 error = EPIPE;
269 goto out;
270 }
271
272 /*
273 * samkumar: There used to be logic here that acquired locks, dealt with
274 * INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and handled the
275 * control mbuf passed as an argument (which would result in an error since
276 * TCP doesn't support control information). I've deleted that code, but
277 * added the following if block based on those checks.
278 */
279 if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED && !do_fastopen_implied_connect)) {
280 error = ECONNRESET;
281 goto out;
282 }
283
284 /*
285 * The following code used to be wrapped in an if statement:
286 * "if (!(flags & PRUS_OOB))", that only executed it if the "out of band"
287 * flag was not set. In TCP, "out of band" data is conveyed via the urgent
288 * pointer, and TCPlp does not support the urgent pointer. Therefore, I
289 * removed the "if" check and put its body below.
290 */
291
292 /*
293 * samkumar: The FreeBSD code calls sbappendstream(&so->so_snd, m, flags);
294 * I've replaced it with the following logic, which appends to the
295 * send buffer according to TCPlp's data structures.
296 */
297 if (data == NULL) {
298 if (extendby == 0) {
299 goto out;
300 }
301 lbuf_extend(&tp->sendbuf, extendby);
302 } else {
303 if (data->mLength == 0) {
304 goto out;
305 }
306 lbuf_append(&tp->sendbuf, data);
307 }
308
309 /*
310 * samkumar: There used to be code here to handle "implied connect,"
311 * which initiates the TCP handshake if sending data on a socket that
312 * isn't yet connected. For now, I've special-cased this code to work
313 * only for TCP Fast Open for IPv6 (since implied connect is the only
314 * way to initiate a connection with TCP Fast Open).
315 */
316 if (do_fastopen_implied_connect) {
317 error = tcp6_connect(tp, nam);
318 if (error)
319 goto out;
320 tcp_fastopen_connect(tp);
321 }
322
323 /*
324 * samkumar: There used to be code here handling the PRUS_EOF flag in
325 * the former flags parameter. I've removed this code.
326 */
327
328 /*
329 * samkumar: The code below was previously wrapped in an if statement
330 * that checked that the INP_DROPPED flag in inp->inp_flags and the
331 * PRUS_NOTREADY flag in the former flags parameter were both clear.
332 * If either one was set, then tcp_output would not be called.
333 *
334 * The "more to come" functionality was previously triggered via the
335 * PRUS_MORETOCOME flag in the flags parameter to this function. Since
336 * that's the only flag that TCPlp uses here, I replaced the flags
337 * parameter with a "moretocome" parameter, which we check instead.
338 */
339 if (moretocome)
340 tp->t_flags |= TF_MORETOCOME;
341 error = tcp_output(tp);
342 if (moretocome)
343 tp->t_flags &= ~TF_MORETOCOME;
344
345 /*
346 * samkumar: This is where the "if (!(flags & PRUS_OOB))" block would end.
347 * There used to be a large "else" block handling out-of-band data, but I
348 * removed that entire block since we do not support the urgent pointer in
349 * TCPlp.
350 */
351 out:
352 return (error);
353 }
354
355 /*
356 * After a receive, possibly send window update to peer.
357 */
358 int
tcp_usr_rcvd(struct tcpcb * tp)359 tcp_usr_rcvd(struct tcpcb* tp)
360 {
361 int error = 0;
362
363 /*
364 * samkumar: There used to be logic here that acquired locks, dealt with
365 * INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and added debug
366 * probes I've deleted that code, but left the following if block.
367 */
368 if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
369 error = ECONNRESET;
370 goto out;
371 }
372
373 /*
374 * For passively-created TFO connections, don't attempt a window
375 * update while still in SYN_RECEIVED as this may trigger an early
376 * SYN|ACK. It is preferable to have the SYN|ACK be sent along with
377 * application response data, or failing that, when the DELACK timer
378 * expires.
379 */
380 if (IS_FASTOPEN(tp->t_flags) &&
381 (tp->t_state == TCPS_SYN_RECEIVED))
382 goto out;
383
384 tcp_output(tp);
385
386 out:
387 return (error);
388 }
389
390 /*
391 * samkumar: Removed the tcp_disconnect function. It is meant to be a
392 * "friendly" disconnect to complement the unceremonious "abort" functionality
393 * that is also provbided. The FreeBSD implementation called it from
394 * tcp_usr_close, which we removed (see the comment below for the reason why).
395 * It's not called from anywhere else, so I'm removing this function entirely.
396 */
397
398 /*
399 * Mark the connection as being incapable of further output.
400 */
401 /*
402 * samkumar: Modified to remove locking, socket/inpcb handling, and debug
403 * probes.
404 */
405 int
tcp_usr_shutdown(struct tcpcb * tp)406 tcp_usr_shutdown(struct tcpcb* tp)
407 {
408 int error = 0;
409
410 /*
411 * samkumar: replaced checks on the INP_TIMEWAIT and INP_DROPPED flags on
412 * inp->inp_flags with these checks on tp->t_state.
413 */
414 if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
415 error = ECONNRESET;
416 goto out;
417 }
418
419 /* samkumar: replaced socantsendmore with tpcantsendmore */
420 tpcantsendmore(tp);
421 tcp_usrclosed(tp);
422
423 /*
424 * samkumar: replaced check on INP_DROPPED flag in inp->inp_flags with
425 * this check on tp->t_state.
426 */
427 if (tp->t_state != TCPS_CLOSED)
428 error = tcp_output(tp);
429
430 out:
431 return (error);
432 }
433
434
435 /*
436 * User issued close, and wish to trail through shutdown states:
437 * if never received SYN, just forget it. If got a SYN from peer,
438 * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
439 * If already got a FIN from peer, then almost done; go to LAST_ACK
440 * state. In all other cases, have already sent FIN to peer (e.g.
441 * after PRU_SHUTDOWN), and just have to play tedious game waiting
442 * for peer to send FIN or not respond to keep-alives, etc.
443 * We can let the user exit from the close as soon as the FIN is acked.
444 */
445 /*
446 * Removed locking, TCP Offload, and socket/inpcb handling.
447 */
448 static void
tcp_usrclosed(struct tcpcb * tp)449 tcp_usrclosed(struct tcpcb *tp)
450 {
451 switch (tp->t_state) {
452 case TCPS_LISTEN:
453 tcp_state_change(tp, TCPS_CLOSED);
454 /* FALLTHROUGH */
455 case TCPS_CLOSED:
456 tp = tcp_close(tp);
457 tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
458 /*
459 * tcp_close() should never return NULL here as the socket is
460 * still open.
461 */
462 KASSERT(tp != NULL,
463 ("tcp_usrclosed: tcp_close() returned NULL"));
464 break;
465
466 case TCPS_SYN_SENT:
467 case TCPS_SYN_RECEIVED:
468 tp->t_flags |= TF_NEEDFIN;
469 break;
470
471 case TCPS_ESTABLISHED:
472 tcp_state_change(tp, TCPS_FIN_WAIT_1);
473 break;
474
475 case TCPS_CLOSE_WAIT:
476 tcp_state_change(tp, TCPS_LAST_ACK);
477 break;
478 }
479 if (tp->t_state >= TCPS_FIN_WAIT_2) {
480 /* samkumar: commented out the following "soisdisconnected" line. */
481 // soisdisconnected(tp->t_inpcb->inp_socket);
482 /* Prevent the connection hanging in FIN_WAIT_2 forever. */
483 if (tp->t_state == TCPS_FIN_WAIT_2) {
484 int timeout;
485
486 timeout = (tcp_fast_finwait2_recycle) ?
487 tcp_finwait2_timeout : TP_MAXIDLE(tp);
488 tcp_timer_activate(tp, TT_2MSL, timeout);
489 }
490 }
491 }
492
493 /*
494 * samkumar: I removed the tcp_usr_close function. It was meant to be called in
495 * case the socket is closed. It calls tcp_disconnect if the underlying TCP
496 * connection is still alive when the socket is closed ("full TCP state").
497 * In TCPlp, we can't handle this because we want to free up the underlying
498 * memory immediately when the user deallocates a TCP connection, making it
499 * unavailable for the somewhat more ceremonious closing that tcp_disconnect
500 * would allow. The host system is expected to simply abort the connection if
501 * the application deallocates it.
502 */
503
504 /*
505 * Abort the TCP. Drop the connection abruptly.
506 */
507 /*
508 * samkumar: Modified to remove locking, socket/inpcb handling, and debug
509 * probes.
510 */
511 void
tcp_usr_abort(struct tcpcb * tp)512 tcp_usr_abort(struct tcpcb* tp)
513 {
514 /*
515 * If we still have full TCP state, and we're not dropped, drop.
516 */
517 /*
518 * I replaced the checks on inp->inp_flags (which tested for the absence of
519 * INP_TIMEWAIT and INP_DROPPED flags), with the following checks on
520 * tp->t_state.
521 */
522 if (tp->t_state != TCP6S_TIME_WAIT &&
523 tp->t_state != TCP6S_CLOSED) {
524 tcp_drop(tp, ECONNABORTED);
525 } else if (tp->t_state == TCPS_TIME_WAIT) { // samkumar: I added this clause
526 tp = tcp_close(tp);
527 tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
528 }
529 }
530