Coverage Report

Created: 2026-01-17 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openthread/third_party/tcplp/bsdtcp/tcp_usrreq.c
Line
Count
Source
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. */
65
0
static int in6_pcbconnect(struct tcpcb* tp, struct sockaddr_in6* nam) {
66
0
  register struct sockaddr_in6 *sin6 = nam;
67
0
  tp->faddr = sin6->sin6_addr;
68
0
  tp->fport = sin6->sin6_port;
69
0
  return 0;
70
0
}
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
87
tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *nam)
88
0
{
89
0
  int error;
90
91
0
  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
0
  bool autobind_addr = IN6_IS_ADDR_UNSPECIFIED(&tp->laddr);
101
0
  bool autobind_port = (tp->lport == 0);
102
0
  if (autobind_addr || autobind_port) {
103
0
    otSockAddr foreign;
104
0
    otSockAddr local;
105
106
0
    memcpy(&foreign.mAddress, &nam->sin6_addr, sizeof(foreign.mAddress));
107
0
    foreign.mPort = ntohs(nam->sin6_port);
108
109
0
    if (!autobind_addr) {
110
0
      memcpy(&local.mAddress, &tp->laddr, sizeof(local.mAddress));
111
0
    }
112
113
0
    if (!autobind_port) {
114
0
      local.mPort = ntohs(tp->lport);
115
0
    }
116
117
0
    if (!tcplp_sys_autobind(tp->instance, &foreign, &local, autobind_addr, autobind_port)) {
118
      // Autobind failed
119
0
      error = EINVAL;
120
0
      goto out;
121
0
    }
122
123
0
    if (autobind_addr) {
124
0
      memcpy(&tp->laddr, &local.mAddress, sizeof(tp->laddr));
125
0
    }
126
127
0
    if (autobind_port) {
128
0
      tp->lport = htons(local.mPort);
129
0
    }
130
0
  }
131
0
  error = in6_pcbconnect(tp, nam);
132
0
  if (error != 0)
133
0
    goto out;
134
135
  /* Compute window scaling to request.  */
136
0
  while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
137
0
      (TCP_MAXWIN << tp->request_r_scale) < sb_max)
138
0
    tp->request_r_scale++;
139
140
0
  tcp_state_change(tp, TCPS_SYN_SENT);
141
0
  tp->iss = tcp_new_isn(tp);
142
0
  tcp_sendseqinit(tp);
143
144
0
  return 0;
145
146
0
out:
147
0
  return error;
148
0
}
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
159
tcp6_usr_connect(struct tcpcb* tp, struct sockaddr_in6* sin6p)
160
0
{
161
0
  int error = 0;
162
163
0
  if (tp->t_state != TCPS_CLOSED) { // samkumar: This is a check that I added
164
0
    return (EISCONN);
165
0
  }
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
0
  if (/*sin6p->sin6_family == AF_INET6
180
0
      && */IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr))
181
0
    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
0
  if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) {
195
0
    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 tcplp_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
0
    error = EINVAL;
211
0
    goto out;
212
0
  }
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
0
  if ((error = tcp6_connect(tp, sin6p)) != 0)
224
0
    goto out;
225
226
0
  tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp));
227
0
  error = tcplp_output(tp);
228
229
0
out:
230
0
  return (error);
231
0
}
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
 */
254
int tcp_usr_send(struct tcpcb* tp, int moretocome, otLinkedBuffer* data, size_t extendby, struct sockaddr_in6* nam)
255
0
{
256
0
  int error = 0;
257
0
  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
0
  if (tp->t_state < TCPS_ESTABLISHED && !IS_FASTOPEN(tp->t_flags)) {
263
0
    error = ENOTCONN;
264
0
    goto out;
265
0
  }
266
267
0
  if (tpiscantsend(tp)) {
268
0
    error = EPIPE;
269
0
    goto out;
270
0
  }
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
0
   if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED && !do_fastopen_implied_connect)) {
280
0
    error = ECONNRESET;
281
0
    goto out;
282
0
  }
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
0
  if (data == NULL) {
298
0
    if (extendby == 0) {
299
0
      goto out;
300
0
    }
301
0
    lbuf_extend(&tp->sendbuf, extendby);
302
0
  } else {
303
0
    if (data->mLength == 0) {
304
0
      goto out;
305
0
    }
306
0
    lbuf_append(&tp->sendbuf, data);
307
0
  }
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
0
  if (do_fastopen_implied_connect) {
317
0
    error = tcp6_connect(tp, nam);
318
0
    if (error)
319
0
      goto out;
320
0
    tcp_fastopen_connect(tp);
321
0
  }
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 tcplp_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
0
  if (moretocome)
340
0
    tp->t_flags |= TF_MORETOCOME;
341
0
  error = tcplp_output(tp);
342
0
  if (moretocome)
343
0
    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
0
out:
352
0
  return (error);
353
0
}
354
355
/*
356
 * After a receive, possibly send window update to peer.
357
 */
358
int
359
tcp_usr_rcvd(struct tcpcb* tp)
360
0
{
361
0
  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
0
  if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
369
0
    error = ECONNRESET;
370
0
    goto out;
371
0
  }
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
0
  if (IS_FASTOPEN(tp->t_flags) &&
381
0
      (tp->t_state == TCPS_SYN_RECEIVED))
382
0
    goto out;
383
384
0
  tcplp_output(tp);
385
386
0
out:
387
0
  return (error);
388
0
}
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
406
tcp_usr_shutdown(struct tcpcb* tp)
407
0
{
408
0
  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
0
  if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
415
0
    error = ECONNRESET;
416
0
    goto out;
417
0
  }
418
419
  /* samkumar: replaced socantsendmore with tpcantsendmore */
420
0
  tpcantsendmore(tp);
421
0
  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
0
  if (tp->t_state != TCPS_CLOSED)
428
0
    error = tcplp_output(tp);
429
430
0
out:
431
0
  return (error);
432
0
}
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
449
tcp_usrclosed(struct tcpcb *tp)
450
0
{
451
0
  switch (tp->t_state) {
452
0
  case TCPS_LISTEN:
453
0
    tcp_state_change(tp, TCPS_CLOSED);
454
    /* FALLTHROUGH */
455
0
  case TCPS_CLOSED:
456
0
    tp = tcp_close_tcb(tp);
457
0
    tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
458
    /*
459
     * tcp_close_tcb() should never return NULL here as the socket is
460
     * still open.
461
     */
462
0
    KASSERT(tp != NULL,
463
0
        ("tcp_usrclosed: tcp_close_tcb() returned NULL"));
464
0
    break;
465
466
0
  case TCPS_SYN_SENT:
467
0
  case TCPS_SYN_RECEIVED:
468
0
    tp->t_flags |= TF_NEEDFIN;
469
0
    break;
470
471
0
  case TCPS_ESTABLISHED:
472
0
    tcp_state_change(tp, TCPS_FIN_WAIT_1);
473
0
    break;
474
475
0
  case TCPS_CLOSE_WAIT:
476
0
    tcp_state_change(tp, TCPS_LAST_ACK);
477
0
    break;
478
0
  }
479
0
  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
0
    if (tp->t_state == TCPS_FIN_WAIT_2) {
484
0
      int timeout;
485
486
0
      timeout = (tcp_fast_finwait2_recycle) ?
487
0
          tcp_finwait2_timeout : TP_MAXIDLE(tp);
488
0
      tcp_timer_activate(tp, TT_2MSL, timeout);
489
0
    }
490
0
  }
491
0
}
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
512
tcp_usr_abort(struct tcpcb* tp)
513
0
{
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
0
  if (tp->t_state != TCP6S_TIME_WAIT &&
523
0
    tp->t_state != TCP6S_CLOSED) {
524
0
    tcp_drop(tp, ECONNABORTED);
525
0
  } else if (tp->t_state == TCP6S_TIME_WAIT) { // samkumar: I added this clause
526
0
    tp = tcp_close_tcb(tp);
527
0
    tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
528
0
  }
529
0
}