/src/openthread/third_party/tcplp/bsdtcp/tcp_subr.c
Line  | Count  | Source  | 
1  |  | /*-  | 
2  |  |  * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995  | 
3  |  |  *  The Regents of the University of California.  All rights reserved.  | 
4  |  |  *  | 
5  |  |  * Redistribution and use in source and binary forms, with or without  | 
6  |  |  * modification, are permitted provided that the following conditions  | 
7  |  |  * are met:  | 
8  |  |  * 1. Redistributions of source code must retain the above copyright  | 
9  |  |  *    notice, this list of conditions and the following disclaimer.  | 
10  |  |  * 2. Redistributions in binary form must reproduce the above copyright  | 
11  |  |  *    notice, this list of conditions and the following disclaimer in the  | 
12  |  |  *    documentation and/or other materials provided with the distribution.  | 
13  |  |  * 4. Neither the name of the University nor the names of its contributors  | 
14  |  |  *    may be used to endorse or promote products derived from this software  | 
15  |  |  *    without specific prior written permission.  | 
16  |  |  *  | 
17  |  |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  | 
18  |  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  | 
19  |  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  | 
20  |  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  | 
21  |  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  | 
22  |  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  | 
23  |  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  | 
24  |  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  | 
25  |  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  | 
26  |  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  | 
27  |  |  * SUCH DAMAGE.  | 
28  |  |  *  | 
29  |  |  *  @(#)tcp_subr.c  8.2 (Berkeley) 5/24/95  | 
30  |  |  */  | 
31  |  |  | 
32  |  | #include <errno.h>  | 
33  |  | #include <stddef.h>  | 
34  |  | #include <string.h>  | 
35  |  |  | 
36  |  | #include "../tcplp.h"  | 
37  |  | #include "ip.h"  | 
38  |  | #include "ip6.h"  | 
39  |  | #include "tcp.h"  | 
40  |  | #include "tcp_fsm.h"  | 
41  |  | #include "tcp_var.h"  | 
42  |  | #include "tcp_seq.h"  | 
43  |  | #include "tcp_timer.h"  | 
44  |  | #include "sys/queue.h"  | 
45  |  | #include "../lib/bitmap.h"  | 
46  |  | #include "../lib/cbuf.h"  | 
47  |  | #include "cc.h"  | 
48  |  | #include "tcp_fastopen.h"  | 
49  |  |  | 
50  |  | #include "tcp_const.h"  | 
51  |  |  | 
52  |  | static void reinitialize_tcb(struct tcpcb* tp);  | 
53  |  |  | 
54  |  | /*  | 
55  |  |  * samkumar: This is rewritten to have the host network stack to generate the  | 
56  |  |  * ISN with appropriate randomness.  | 
57  |  |  */  | 
58  | 0  | tcp_seq tcp_new_isn(struct tcpcb* tp) { | 
59  | 0  |   return (uint32_t) tcplp_sys_generate_isn();  | 
60  | 0  | }  | 
61  |  |  | 
62  |  | /*  | 
63  |  |  * samkumar: There used to be a function, void tcp_init(void), that would  | 
64  |  |  * initialize global state for TCP, including a hash table to store TCBs,  | 
65  |  |  * allocating memory zones for sockets, and setting global configurable state.  | 
66  |  |  * In FreeBSD 12.0, it also makes a call to the function tcp_fastopen_init.  | 
67  |  |  * None of that is needed for TCPlp: TCB allocation and matching is done by  | 
68  |  |  * the host system and global configurable state is removed with hardcoded  | 
69  |  |  * values in order to save memory, for example. Thus, I've removed the function  | 
70  |  |  * entirely.  | 
71  |  |  */  | 
72  |  |  | 
73  |  | /*  | 
74  |  |  * A subroutine which makes it easy to track TCP state changes with DTrace.  | 
75  |  |  * This function shouldn't be called for t_state initializations that don't  | 
76  |  |  * correspond to actual TCP state transitions.  | 
77  |  |  */  | 
78  |  | void  | 
79  |  | tcp_state_change(struct tcpcb *tp, int newstate)  | 
80  | 0  | { | 
81  |  | #if 0  | 
82  |  | #if defined(KDTRACE_HOOKS)  | 
83  |  |   int pstate = tp->t_state;  | 
84  |  | #endif  | 
85  |  | #endif  | 
86  | 0  |   tcplp_sys_log("Socket %p: %s --> %s", tp, tcpstates[tp->t_state], tcpstates[newstate]); | 
87  | 0  |   tp->t_state = newstate;  | 
88  |  |  | 
89  |  |   // samkumar: may need to do other actions too, so call into the host  | 
90  | 0  |   tcplp_sys_on_state_change(tp, newstate);  | 
91  |  | #if 0  | 
92  |  |   TCP_PROBE6(state__change, NULL, tp, NULL, tp, NULL, pstate);  | 
93  |  | #endif  | 
94  | 0  | }  | 
95  |  |  | 
96  |  |  /* samkumar: Based on tcp_newtcb in tcp_subr.c, and tcp_usr_attach in tcp_usrreq.c. */  | 
97  | 1  | void initialize_tcb(struct tcpcb* tp) { | 
98  | 1  |   uint32_t ticks = tcplp_sys_get_ticks();  | 
99  |  |  | 
100  |  |   /* samkumar: Clear all fields starting laddr; rest are initialized by the host. */  | 
101  | 1  |   memset(((uint8_t*) tp) + offsetof(struct tcpcb, laddr), 0x00, sizeof(struct tcpcb) - offsetof(struct tcpcb, laddr));  | 
102  | 1  |   tp->reass_fin_index = -1;  | 
103  |  |  | 
104  |  |   /*  | 
105  |  |    * samkumar: Only New Reno congestion control is implemented at the moment,  | 
106  |  |    * so there's no need to record the congestion control algorithm used for  | 
107  |  |    * each TCB.  | 
108  |  |    */  | 
109  |  |   // CC_ALGO(tp) = CC_DEFAULT();  | 
110  |  |   // tp->ccv->type = IPPROTO_TCP;  | 
111  | 1  |   tp->ccv->ccvc.tcp = tp;  | 
112  |  |  | 
113  |  |   /*  | 
114  |  |    * samkumar: The original code used to choose a different constant  | 
115  |  |    * depending on whether it's an IPv4 or IPv6 connection. In TCPlp, we  | 
116  |  |    * unconditionally choose the IPv6 branch.  | 
117  |  |    */  | 
118  | 1  |   tp->t_maxseg = tp->t_maxopd =  | 
119  |  | //#ifdef INET6  | 
120  | 1  |     /*isipv6 ? */V_tcp_v6mssdflt /*:*/  | 
121  |  | //#endif /* INET6 */  | 
122  | 1  |     /*V_tcp_mssdflt*/;  | 
123  |  |  | 
124  | 1  |   if (V_tcp_do_rfc1323)  | 
125  | 1  |     tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);  | 
126  | 1  |   if (V_tcp_do_sack)  | 
127  | 1  |     tp->t_flags |= TF_SACK_PERMIT;  | 
128  | 1  |   TAILQ_INIT(&tp->snd_holes);  | 
129  |  |  | 
130  |  |   /*  | 
131  |  |    * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no  | 
132  |  |    * rtt estimate.  Set rttvar so that srtt + 4 * rttvar gives  | 
133  |  |    * reasonable initial retransmit time.  | 
134  |  |    */  | 
135  | 1  |   tp->t_srtt = TCPTV_SRTTBASE;  | 
136  | 1  |   tp->t_rttvar = ((TCPTV_RTOBASE - TCPTV_SRTTBASE) << TCP_RTTVAR_SHIFT) / 4;  | 
137  | 1  |   tp->t_rttmin = TCPTV_MIN < 1 ? 1 : TCPTV_MIN; /* samkumar: used to be tcp_rexmit_min, which was set in tcp_init */  | 
138  | 1  |   tp->t_rxtcur = TCPTV_RTOBASE;  | 
139  | 1  |   tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;  | 
140  | 1  |   tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;  | 
141  | 1  |   tp->t_rcvtime = ticks;  | 
142  |  |  | 
143  |  |   /* samkumar: Taken from tcp_usr_attach in tcp_usrreq.c. */  | 
144  | 1  |   tp->t_state = TCP6S_CLOSED;  | 
145  |  |  | 
146  |  |   /* samkumar: Added to initialize the per-TCP sackhole pool. */  | 
147  | 1  |   tcp_sack_init(tp);  | 
148  | 1  | }  | 
149  |  |  | 
150  |  | /* Re-initialize the TCB. */  | 
151  |  | static void reinitialize_tcb(struct tcpcb* tp)  | 
152  | 0  | { | 
153  | 0  |   uint32_t ntraversed;  | 
154  | 0  |   lbuf_pop(&tp->sendbuf, lbuf_used_space(&tp->sendbuf), &ntraversed);  | 
155  | 0  |   cbuf_pop(&tp->recvbuf, cbuf_used_space(&tp->recvbuf));  | 
156  | 0  |   tp->accepted_from = NULL;  | 
157  | 0  |   initialize_tcb(tp);  | 
158  | 0  | }  | 
159  |  |  | 
160  |  | /*  | 
161  |  |  * samkumar: Most of this function was no longer needed. It did things like  | 
162  |  |  * reference-counting for the TCB, updating host cache stats for better  | 
163  |  |  * starting values of, e.g., ssthresh, for new connections, freeing resources  | 
164  |  |  * for TCP offloading, etc. There's no host cache in TCPlp and the host system  | 
165  |  |  * is responsible for managing TCB memory, so much of this isn't needed. I just  | 
166  |  |  * kept (and adpated) the few parts of the function that appeared to be needed  | 
167  |  |  * for TCPlp.  | 
168  |  |  */  | 
169  |  | void  | 
170  |  | tcp_discardcb(struct tcpcb *tp)  | 
171  | 0  | { | 
172  | 0  |   tcp_cancel_timers(tp);  | 
173  |  |  | 
174  |  |   /* Allow the CC algorithm to clean up after itself. */  | 
175  | 0  |   if (CC_ALGO(tp)->cb_destroy != NULL)  | 
176  | 0  |     CC_ALGO(tp)->cb_destroy(tp->ccv);  | 
177  |  | 
  | 
178  | 0  |   tcp_free_sackholes(tp);  | 
179  |  | 
  | 
180  | 0  |   reinitialize_tcb(tp);  | 
181  | 0  | }  | 
182  |  |  | 
183  |  |  | 
184  |  |  /*  | 
185  |  |  * Attempt to close a TCP control block, marking it as dropped, and freeing  | 
186  |  |  * the socket if we hold the only reference.  | 
187  |  |  */  | 
188  |  | /*  | 
189  |  |  * samkumar: Most of this function has to do with dropping the reference to  | 
190  |  |  * the inpcb, freeing resources at the socket layer and marking it as  | 
191  |  |  * disconnected, and miscellaneous cleanup. I've rewritten this to do what is  | 
192  |  |  * needed for TCP.  | 
193  |  |  */  | 
194  |  | struct tcpcb *  | 
195  |  | tcp_close_tcb(struct tcpcb *tp)  | 
196  | 0  | { | 
197  |  |   /* samkumar: Eliminate the TFO pending counter. */  | 
198  |  |   /*  | 
199  |  |   if (tp->t_tfo_pending) { | 
200  |  |     tcp_fastopen_decrement_counter(tp->t_tfo_pending);  | 
201  |  |     tp->t_tfo_pending = NULL;  | 
202  |  |   }  | 
203  |  |   */  | 
204  | 0  |   tcp_state_change(tp, TCP6S_CLOSED); // for the print statement  | 
205  | 0  |   tcp_discardcb(tp);  | 
206  |  |   // Don't reset the TCB by calling initialize_tcb, since that overwrites the buffer contents.  | 
207  | 0  |   return tp;  | 
208  | 0  | }  | 
209  |  |  | 
210  |  | /*  | 
211  |  |  * Create template to be used to send tcp packets on a connection.  | 
212  |  |  * Allocates an mbuf and fills in a skeletal tcp/ip header.  The only  | 
213  |  |  * use for this function is in keepalives, which use tcp_respond.  | 
214  |  |  */  | 
215  |  | /*  | 
216  |  |  * samkumar: I changed the signature of this function. Instead of allocating  | 
217  |  |  * the struct tcptemp using malloc, populating it, and then returning it, I  | 
218  |  |  * have the caller allocate it. This function merely populates it now.  | 
219  |  |  */  | 
220  |  | void  | 
221  |  | tcpip_maketemplate(struct tcpcb* tp, struct tcptemp* t)  | 
222  | 0  | { | 
223  | 0  |   tcpip_fillheaders(tp, (void *)&t->tt_ipgen, (void *)&t->tt_t);  | 
224  | 0  | }  | 
225  |  |  | 
226  |  | /*  | 
227  |  |  * Fill in the IP and TCP headers for an outgoing packet, given the tcpcb.  | 
228  |  |  * tcp_template used to store this data in mbufs, but we now recopy it out  | 
229  |  |  * of the tcpcb each time to conserve mbufs.  | 
230  |  |  */  | 
231  |  | /*  | 
232  |  |  * samkumar: This has a different signature from the original function in  | 
233  |  |  * tcp_subr.c. In particular, IP header information is filled into an  | 
234  |  |  * otMessageInfo rather than into a struct representing the on-wire header  | 
235  |  |  * format. Additionally, I have changed it to always assume IPv6; I removed the  | 
236  |  |  * code for IPv4.  | 
237  |  |  */  | 
238  |  | void  | 
239  |  | tcpip_fillheaders(struct tcpcb* tp, otMessageInfo* ip_ptr, void *tcp_ptr)  | 
240  | 0  | { | 
241  | 0  |   struct tcphdr *th = (struct tcphdr *)tcp_ptr;  | 
242  |  |  | 
243  |  |   /* Fill in the IP header */  | 
244  |  |  | 
245  |  |     /* samkumar: The old IPv6 code, for reference. */  | 
246  |  |   // ip6 = (struct ip6_hdr *)ip_ptr;  | 
247  |  |   // ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) |  | 
248  |  |   //     (inp->inp_flow & IPV6_FLOWINFO_MASK);  | 
249  |  |   // ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |  | 
250  |  |   //     (IPV6_VERSION & IPV6_VERSION_MASK);  | 
251  |  |   // ip6->ip6_nxt = IPPROTO_TCP;  | 
252  |  |   // ip6->ip6_plen = htons(sizeof(struct tcphdr));  | 
253  |  |   // ip6->ip6_src = inp->in6p_laddr;  | 
254  |  |   // ip6->ip6_dst = inp->in6p_faddr;  | 
255  |  | 
  | 
256  | 0  |   memset(ip_ptr, 0x00, sizeof(otMessageInfo));  | 
257  | 0  |   memcpy(&ip_ptr->mSockAddr, &tp->laddr, sizeof(ip_ptr->mSockAddr));  | 
258  | 0  |   memcpy(&ip_ptr->mPeerAddr, &tp->faddr, sizeof(ip_ptr->mPeerAddr));  | 
259  |  |  | 
260  |  |   /* Fill in the TCP header */  | 
261  |  |   /* samkumar: I kept the old code commented out, for reference. */  | 
262  |  |   //th->th_sport = inp->inp_lport;  | 
263  |  |   //th->th_dport = inp->inp_fport;  | 
264  | 0  |   th->th_sport = tp->lport;  | 
265  | 0  |   th->th_dport = tp->fport;  | 
266  | 0  |   th->th_seq = 0;  | 
267  | 0  |   th->th_ack = 0;  | 
268  |  |   // th->th_x2 = 0;  | 
269  |  |   // th->th_off = 5;  | 
270  | 0  |   th->th_off_x2 = (5 << TH_OFF_SHIFT);  | 
271  | 0  |   th->th_flags = 0;  | 
272  | 0  |   th->th_win = 0;  | 
273  | 0  |   th->th_urp = 0;  | 
274  | 0  |   th->th_sum = 0;   /* in_pseudo() is called later for ipv4 */  | 
275  | 0  | }  | 
276  |  |  | 
277  |  | /*  | 
278  |  |  * Send a single message to the TCP at address specified by  | 
279  |  |  * the given TCP/IP header.  If m == NULL, then we make a copy  | 
280  |  |  * of the tcpiphdr at th and send directly to the addressed host.  | 
281  |  |  * This is used to force keep alive messages out using the TCP  | 
282  |  |  * template for a connection.  If flags are given then we send  | 
283  |  |  * a message back to the TCP which originated the segment th,  | 
284  |  |  * and discard the mbuf containing it and any other attached mbufs.  | 
285  |  |  *  | 
286  |  |  * In any case the ack and sequence number of the transmitted  | 
287  |  |  * segment are as specified by the parameters.  | 
288  |  |  *  | 
289  |  |  * NOTE: If m != NULL, then th must point to *inside* the mbuf.  | 
290  |  |  */  | 
291  |  | /* samkumar: Original signature was  | 
292  |  | void  | 
293  |  | tcp_respond(struct tcpcb *tp, void *ipgen, struct tcphdr *th, struct mbuf *m,  | 
294  |  |   tcp_seq ack, tcp_seq seq, int flags)  | 
295  |  | */  | 
296  |  | /*  | 
297  |  |  * samkumar: Essentially all of the code had to be discarded/rewritten since I  | 
298  |  |  * have to send out packets by allocating buffers from the host system,  | 
299  |  |  * populating them, and passing them back to the host system to send out. I  | 
300  |  |  * simplified the code by only using the logic that was fully necessary,  | 
301  |  |  * eliminating the code for IPv4 packets and keeping only the code for IPv6  | 
302  |  |  * packets. I also removed all of the mbuf logic, instead using the logic for  | 
303  |  |  * using the host system's buffering (in particular, the code to reuse the  | 
304  |  |  * provided mbuf is no longer there).  | 
305  |  |  */  | 
306  |  | void  | 
307  |  | tcp_respond(struct tcpcb *tp, otInstance* instance, struct ip6_hdr* ip6gen, struct tcphdr *thgen,  | 
308  |  |   tcp_seq ack, tcp_seq seq, int flags)  | 
309  | 42  | { | 
310  | 42  |   otMessage* message = tcplp_sys_new_message(instance);  | 
311  | 42  |   if (message == NULL) { | 
312  | 0  |     return;  | 
313  | 0  |   }  | 
314  | 42  |   if (otMessageSetLength(message, sizeof(struct tcphdr)) != OT_ERROR_NONE) { | 
315  | 0  |     tcplp_sys_free_message(instance, message);  | 
316  | 0  |     return;  | 
317  | 0  |   }  | 
318  |  |  | 
319  | 42  |   struct tcphdr th;  | 
320  | 42  |   struct tcphdr* nth = &th;  | 
321  | 42  |   otMessageInfo ip6info;  | 
322  | 42  |   int win = 0;  | 
323  | 42  |   if (tp != NULL) { | 
324  | 0  |     if (!(flags & TH_RST)) { | 
325  | 0  |       win = cbuf_free_space(&tp->recvbuf);  | 
326  | 0  |       if (win > (long)TCP_MAXWIN << tp->rcv_scale)  | 
327  | 0  |         win = (long)TCP_MAXWIN << tp->rcv_scale;  | 
328  | 0  |     }  | 
329  | 0  |   }  | 
330  | 42  |   memset(&ip6info, 0x00, sizeof(otMessageInfo));  | 
331  | 42  |   memcpy(&ip6info.mSockAddr, &ip6gen->ip6_dst, sizeof(ip6info.mSockAddr));  | 
332  | 42  |   memcpy(&ip6info.mPeerAddr, &ip6gen->ip6_src, sizeof(ip6info.mPeerAddr));  | 
333  | 42  |   nth->th_sport = thgen->th_dport;  | 
334  | 42  |   nth->th_dport = thgen->th_sport;  | 
335  | 42  |   nth->th_seq = htonl(seq);  | 
336  | 42  |   nth->th_ack = htonl(ack);  | 
337  |  |   /* samkumar: original code for setting th_x2 and th_off, for reference. */  | 
338  |  |   // nth->th_x2 = 0;  | 
339  |  |   // nth->th_off = (sizeof (struct tcphdr) + optlen) >> 2;  | 
340  | 42  |   nth->th_off_x2 = (sizeof(struct tcphdr) >> 2) << TH_OFF_SHIFT;  | 
341  | 42  |   nth->th_flags = flags;  | 
342  | 42  |   if (tp != NULL)  | 
343  | 0  |     nth->th_win = htons((uint16_t) (win >> tp->rcv_scale));  | 
344  | 42  |   else  | 
345  | 42  |     nth->th_win = htons((uint16_t)win);  | 
346  | 42  |   nth->th_urp = 0;  | 
347  | 42  |   nth->th_sum = 0;  | 
348  |  |  | 
349  | 42  |   otMessageWrite(message, 0, &th, sizeof(struct tcphdr));  | 
350  |  |  | 
351  | 42  |   tcplp_sys_send_message(instance, message, &ip6info);  | 
352  | 42  | }  | 
353  |  |  | 
354  |  | /*  | 
355  |  |  * Drop a TCP connection, reporting  | 
356  |  |  * the specified error.  If connection is synchronized,  | 
357  |  |  * then send a RST to peer.  | 
358  |  |  */  | 
359  |  | /*  | 
360  |  |  * samkumar: I changed the parameter "errno" to "errnum" since it caused  | 
361  |  |  * problems during compilation. I also the code for asserting locks,  | 
362  |  |  * incermenting stats, and managing the sockets layer.  | 
363  |  |  */  | 
364  |  | struct tcpcb *  | 
365  |  | tcp_drop(struct tcpcb *tp, int errnum)  | 
366  | 0  | { | 
367  | 0  |   if (TCPS_HAVERCVDSYN(tp->t_state)) { | 
368  | 0  |     tcp_state_change(tp, TCP6S_CLOSED);  | 
369  | 0  |     (void) tcplp_output(tp);  | 
370  | 0  |   }  | 
371  | 0  |   if (errnum == ETIMEDOUT && tp->t_softerror)  | 
372  | 0  |     errnum = tp->t_softerror;  | 
373  | 0  |   tp = tcp_close_tcb(tp);  | 
374  | 0  |   tcplp_sys_connection_lost(tp, errnum);  | 
375  | 0  |   return tp;  | 
376  | 0  | }  | 
377  |  |  | 
378  |  | /*  | 
379  |  |  * Look-up the routing entry to the peer of this inpcb.  If no route  | 
380  |  |  * is found and it cannot be allocated, then return 0.  This routine  | 
381  |  |  * is called by TCP routines that access the rmx structure and by  | 
382  |  |  * tcp_mss_update to get the peer/interface MTU.  | 
383  |  |  */  | 
384  |  | /*  | 
385  |  |  * samkumar: In TCPlp, we don't bother with keeping track of the MTU for each  | 
386  |  |  * route. The MSS we choose for the 6LoWPAN/802.15.4 network is probably the  | 
387  |  |  * bottleneck, so we just use that. (I also removed the struct in_conninfo *  | 
388  |  |  * that was formerly the first argument).  | 
389  |  |  */  | 
390  |  | uint64_t  | 
391  |  | tcp_maxmtu6(struct tcpcb* tp, struct tcp_ifcap *cap)  | 
392  | 0  | { | 
393  | 0  |   uint64_t maxmtu = 0;  | 
394  |  | 
  | 
395  | 0  |   KASSERT (tp != NULL, ("tcp_maxmtu6 with NULL tcpcb pointer")); | 
396  | 0  |   if (!IN6_IS_ADDR_UNSPECIFIED(&tp->faddr)) { | 
397  | 0  |     maxmtu = FRAMES_PER_SEG * FRAMECAP_6LOWPAN;  | 
398  | 0  |   }  | 
399  |  | 
  | 
400  | 0  |   return (maxmtu);  | 
401  | 0  | }  |