• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/socket.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)254 int tcp_usr_send(struct tcpcb* tp, int moretocome, otLinkedBuffer* data, size_t extendby)
255 {
256 	int error = 0;
257 
258 	/*
259 	 * samkumar: This if statement and the next are checks that I added
260 	 */
261 	if (tp->t_state < TCPS_ESTABLISHED) {
262 		error = ENOTCONN;
263 		goto out;
264 	}
265 
266 	if (tpiscantsend(tp)) {
267 		error = EPIPE;
268 		goto out;
269 	}
270 
271 	/*
272 	 * samkumar: There used to be logic here that acquired locks, dealt with
273 	 * INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and handled the
274 	 * control mbuf passed as an argument (which would result in an error since
275 	 * TCP doesn't support control information). I've deleted that code, but
276 	 * left the following if block.
277 	 */
278 	 if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
279 		error = ECONNRESET;
280 		goto out;
281 	}
282 
283 	/*
284 	 * The following code used to be wrapped in an if statement:
285 	 * "if (!(flags & PRUS_OOB))", that only executed it if the "out of band"
286 	 * flag was not set. In TCB, "out of band" data is conveyed via the urgent
287 	 * pointer, and TCPlp does not support the urgent pointer. Therefore, I
288 	 * removed the "if" check and put its body below.
289 	 */
290 
291 	/*
292 	 * samkumar; The FreeBSD code calls sbappendstream(&so->so_snd, m, flags);
293 	 * I've replaced it with the following logic, which appends to the
294 	 * send buffer according to TCPlp's data structures.
295 	 */
296 	if (data == NULL) {
297 		if (extendby == 0) {
298 			goto out;
299 		}
300 		lbuf_extend(&tp->sendbuf, extendby);
301 	} else {
302 		if (data->mLength == 0) {
303 			goto out;
304 		}
305 		lbuf_append(&tp->sendbuf, data);
306 	}
307 
308 	/*
309 	 * samkumar: There used to be code here to handle "implied connect,"
310 	 * which initiates the TCP handshake if sending data on a socket that
311 	 * isn't yet connected. TCPlp doesn't support this at the moment, but
312 	 * it might be worth revisiting  when implementing TCP Fast Open.
313 	 */
314 
315 	/*
316 	 * samkumar: There used to be code here handling the PRUS_EOF flag in
317 	 * the former flags parameter. I've removed this code.
318 	 */
319 
320 	/*
321 	 * samkumar: The code below was previously wrapped in an if statement
322 	 * that checked that the INP_DROPPED flag in inp->inp_flags and the
323 	 * PRUS_NOTREADY flag in the former flags parameter were both clear.
324 	 * If either one was set, then tcp_output would not be called.
325 	 *
326 	 * The "more to come" functionality was previously triggered via the
327 	 * PRUS_MORETOCOME flag in the flags parameter to this function. Since
328 	 * that's the only flag that TCPlp uses here, I replaced the flags
329 	 * parameter with a "moretocome" parameter, which we check instead.
330 	 */
331 	if (moretocome)
332 		tp->t_flags |= TF_MORETOCOME;
333 	error = tcp_output(tp);
334 	if (moretocome)
335 		tp->t_flags &= ~TF_MORETOCOME;
336 
337 	/*
338 	 * samkumar: This is where the "if (!(flags & PRUS_OOB))" block would end.
339 	 * There used to be a large "else" block handling out-of-band data, but I
340 	 * removed that entire block since we do not support the urgent pointer in
341 	 * TCPlp.
342 	 */
343 out:
344 	return (error);
345 }
346 
347 /*
348  * After a receive, possibly send window update to peer.
349  */
350 int
tcp_usr_rcvd(struct tcpcb * tp)351 tcp_usr_rcvd(struct tcpcb* tp)
352 {
353 	int error = 0;
354 
355 	/*
356 	 * samkumar: There used to be logic here that acquired locks, dealt with
357 	 * INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and added debug
358 	 * probes I've deleted that code, but left the following if block.
359 	 */
360 	if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
361 		error = ECONNRESET;
362 		goto out;
363 	}
364 
365 	tcp_output(tp);
366 
367 out:
368 	return (error);
369 }
370 
371 /*
372  * samkumar: Removed the tcp_disconnect function. It is meant to be a
373  * "friendly" disconnect to complement the unceremonious "abort" functionality
374  * that is also provbided. The FreeBSD implementation called it from
375  * tcp_usr_close, which we removed (see the comment below for the reason why).
376  * It's not called from anywhere else, so I'm removing this function entirely.
377  */
378 
379 /*
380  * Mark the connection as being incapable of further output.
381  */
382 /*
383  * samkumar: Modified to remove locking, socket/inpcb handling, and debug
384  * probes.
385  */
386 int
tcp_usr_shutdown(struct tcpcb * tp)387 tcp_usr_shutdown(struct tcpcb* tp)
388 {
389 	int error = 0;
390 
391 	/*
392 	 * samkumar: replaced checks on the INP_TIMEWAIT and INP_DROPPED flags on
393 	 * inp->inp_flags with these checks on tp->t_state.
394 	 */
395 	if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
396 		error = ECONNRESET;
397 		goto out;
398 	}
399 
400 	/* samkumar: replaced socantsendmore with tpcantsendmore */
401 	tpcantsendmore(tp);
402 	tcp_usrclosed(tp);
403 
404 	/*
405 	 * samkumar: replaced check on INP_DROPPED flag in inp->inp_flags with
406 	 * this check on tp->t_state.
407 	 */
408 	if (tp->t_state != TCPS_CLOSED)
409 		error = tcp_output(tp);
410 
411 out:
412 	return (error);
413 }
414 
415 
416 /*
417  * User issued close, and wish to trail through shutdown states:
418  * if never received SYN, just forget it.  If got a SYN from peer,
419  * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
420  * If already got a FIN from peer, then almost done; go to LAST_ACK
421  * state.  In all other cases, have already sent FIN to peer (e.g.
422  * after PRU_SHUTDOWN), and just have to play tedious game waiting
423  * for peer to send FIN or not respond to keep-alives, etc.
424  * We can let the user exit from the close as soon as the FIN is acked.
425  */
426 /*
427  * Removed locking, TCP Offload, and socket/inpcb handling.
428  */
429 static void
tcp_usrclosed(struct tcpcb * tp)430 tcp_usrclosed(struct tcpcb *tp)
431 {
432 	switch (tp->t_state) {
433 	case TCPS_LISTEN:
434 		tcp_state_change(tp, TCPS_CLOSED);
435 		/* FALLTHROUGH */
436 	case TCPS_CLOSED:
437 		tp = tcp_close(tp);
438 		tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
439 		/*
440 		 * tcp_close() should never return NULL here as the socket is
441 		 * still open.
442 		 */
443 		KASSERT(tp != NULL,
444 		    ("tcp_usrclosed: tcp_close() returned NULL"));
445 		break;
446 
447 	case TCPS_SYN_SENT:
448 	case TCPS_SYN_RECEIVED:
449 		tp->t_flags |= TF_NEEDFIN;
450 		break;
451 
452 	case TCPS_ESTABLISHED:
453 		tcp_state_change(tp, TCPS_FIN_WAIT_1);
454 		break;
455 
456 	case TCPS_CLOSE_WAIT:
457 		tcp_state_change(tp, TCPS_LAST_ACK);
458 		break;
459 	}
460 	if (tp->t_state >= TCPS_FIN_WAIT_2) {
461 		/* samkumar: commented out the following "soisdisconnected" line. */
462 		// soisdisconnected(tp->t_inpcb->inp_socket);
463 		/* Prevent the connection hanging in FIN_WAIT_2 forever. */
464 		if (tp->t_state == TCPS_FIN_WAIT_2) {
465 			int timeout;
466 
467 			timeout = (tcp_fast_finwait2_recycle) ?
468 			    tcp_finwait2_timeout : TP_MAXIDLE(tp);
469 			tcp_timer_activate(tp, TT_2MSL, timeout);
470 		}
471 	}
472 }
473 
474 /*
475  * samkumar: I removed the tcp_usr_close function. It was meant to be called in
476  * case the socket is closed. It calls tcp_disconnect if the underlying TCP
477  * connection is still alive when the socket is closed ("full TCP state").
478  * In TCPlp, we can't handle this because we want to free up the underlying
479  * memory immediately when the user deallocates a TCP connection, making it
480  * unavailable for the somewhat more ceremonious closing that tcp_disconnect
481  * would allow. The host system is expected to simply abort the connection if
482  * the application deallocates it.
483  */
484 
485 /*
486  * Abort the TCP.  Drop the connection abruptly.
487  */
488 /*
489  * samkumar: Modified to remove locking, socket/inpcb handling, and debug
490  * probes.
491  */
492 void
tcp_usr_abort(struct tcpcb * tp)493 tcp_usr_abort(struct tcpcb* tp)
494 {
495 	/*
496 	 * If we still have full TCP state, and we're not dropped, drop.
497 	 */
498 	/*
499 	 * I replaced the checks on inp->inp_flags (which tested for the absence of
500 	 * INP_TIMEWAIT and INP_DROPPED flags), with the following checks on
501 	 * tp->t_state.
502 	 */
503 	if (tp->t_state != TCP6S_TIME_WAIT &&
504 		tp->t_state != TCP6S_CLOSED) {
505 		tcp_drop(tp, ECONNABORTED);
506 	} else if (tp->t_state == TCPS_TIME_WAIT) { // samkumar: I added this clause
507 		tp = tcp_close(tp);
508 		tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
509 	}
510 }
511