1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Component */
16 /** */
17 /** Transmission Control Protocol (TCP) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22
23
24 #define NX_SOURCE_CODE
25
26
27 /* Include necessary system files. */
28
29 #include "nx_api.h"
30 #include "nx_ip.h"
31 #include "nx_packet.h"
32 #include "nx_tcp.h"
33 #include "tx_thread.h"
34
35
36 /**************************************************************************/
37 /* */
38 /* FUNCTION RELEASE */
39 /* */
40 /* _nx_tcp_socket_receive PORTABLE C */
41 /* 6.1 */
42 /* AUTHOR */
43 /* */
44 /* Yuxin Zhou, Microsoft Corporation */
45 /* */
46 /* DESCRIPTION */
47 /* */
48 /* This function attempts to receive one or more TCP packets from the */
49 /* specified socket. */
50 /* */
51 /* INPUT */
52 /* */
53 /* socket_ptr Pointer to socket */
54 /* packet_ptr Pointer to packet pointer */
55 /* wait_option Suspension option */
56 /* */
57 /* OUTPUT */
58 /* */
59 /* status Completion status */
60 /* */
61 /* CALLS */
62 /* */
63 /* _nx_tcp_packet_send_ack Send ACK message */
64 /* _nx_tcp_socket_thread_suspend Suspend calling thread */
65 /* tx_mutex_get Get protection mutex */
66 /* tx_mutex_put Put protection mutex */
67 /* */
68 /* CALLED BY */
69 /* */
70 /* Application */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
77 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
78 /* resulting in version 6.1 */
79 /* */
80 /**************************************************************************/
_nx_tcp_socket_receive(NX_TCP_SOCKET * socket_ptr,NX_PACKET ** packet_ptr,ULONG wait_option)81 UINT _nx_tcp_socket_receive(NX_TCP_SOCKET *socket_ptr, NX_PACKET **packet_ptr, ULONG wait_option)
82 {
83
84 NX_IP *ip_ptr;
85 NX_TCP_HEADER *header_ptr;
86 NX_PACKET *head_packet_ptr;
87 ULONG header_length;
88
89 #ifdef TX_ENABLE_EVENT_TRACE
90 TX_TRACE_BUFFER_ENTRY *trace_event;
91 ULONG trace_timestamp;
92 #endif
93
94 /* Setup the pointer to the associated IP instance. */
95 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
96
97 /* Set the return pointer to NULL initially. */
98 *packet_ptr = NX_NULL;
99
100 /* If trace is enabled, insert this event into the trace buffer. */
101 NX_TRACE_IN_LINE_INSERT(NX_TRACE_TCP_SOCKET_RECEIVE, socket_ptr, 0, 0, 0, NX_TRACE_TCP_EVENTS, &trace_event, &trace_timestamp);
102
103 /* Get protection while we look at this socket. */
104 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
105
106 /* Determine if the socket is currently bound. */
107 if (!socket_ptr -> nx_tcp_socket_bound_next)
108 {
109
110 /* Release protection. */
111 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
112
113 /* Socket is not bound, return an error message. */
114 return(NX_NOT_BOUND);
115 }
116
117 /* Do not return without data if there is data on the queue. */
118 if (!socket_ptr -> nx_tcp_socket_receive_queue_head)
119 {
120 /* There is no data on the queue. */
121
122 /* Determine if the socket is still in an active state, but also allow
123 a receive socket operation if there are still more queued receive
124 packets for this socket. */
125 if ((socket_ptr -> nx_tcp_socket_state < NX_TCP_SYN_SENT) ||
126 (socket_ptr -> nx_tcp_socket_state == NX_TCP_CLOSE_WAIT) ||
127 (socket_ptr -> nx_tcp_socket_state >= NX_TCP_CLOSING))
128 {
129
130 /* Release the IP protection. */
131 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
132
133 /* Return an error code. */
134 return(NX_NOT_CONNECTED);
135 }
136 }
137
138 /* Pickup the important information from the socket. */
139
140 /* Attempt to build a pointer to the first packet in the socket's
141 receive queue. */
142 if (socket_ptr -> nx_tcp_socket_receive_queue_head)
143 {
144
145 /* Yes, there is a packet on the receive queue. Setup a pointer to it and
146 its header. */
147 head_packet_ptr = socket_ptr -> nx_tcp_socket_receive_queue_head;
148 }
149 else
150 {
151
152 /* Just set the pointers to NULL. */
153 head_packet_ptr = NX_NULL;
154 }
155
156 /* Determine if there is a receive packet available. */
157 /*lint -e{923} suppress cast of ULONT to pointer. */
158 if ((head_packet_ptr) && (head_packet_ptr -> nx_packet_queue_next == ((NX_PACKET *)NX_PACKET_READY)))
159 {
160
161
162 /* Yes, the first packet in the queue is available and has been ACKed. Remove it
163 from the queue and return it to the caller. */
164 if (head_packet_ptr == socket_ptr -> nx_tcp_socket_receive_queue_tail)
165 {
166
167 /* Only item in the queue. Set the head and tail pointers to NULL. */
168 socket_ptr -> nx_tcp_socket_receive_queue_head = NX_NULL;
169 socket_ptr -> nx_tcp_socket_receive_queue_tail = NX_NULL;
170 }
171 else
172 {
173
174 /* Simply update the head pointer to the packet after the current. The tail pointer does not
175 need update. */
176 socket_ptr -> nx_tcp_socket_receive_queue_head = head_packet_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next;
177 }
178
179 /* Decrease the number of received packets. */
180 socket_ptr -> nx_tcp_socket_receive_queue_count--;
181
182 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
183 header_ptr = (NX_TCP_HEADER *)head_packet_ptr -> nx_packet_prepend_ptr;
184
185 /* Calculate the header size for this packet. */
186 header_length = (header_ptr -> nx_tcp_header_word_3 >> NX_TCP_HEADER_SHIFT) * (ULONG)sizeof(ULONG);
187
188 /* Adjust the packet prepend pointer and length to position past the TCP header. */
189 head_packet_ptr -> nx_packet_prepend_ptr = head_packet_ptr -> nx_packet_prepend_ptr + header_length;
190 head_packet_ptr -> nx_packet_length = head_packet_ptr -> nx_packet_length - header_length;
191
192 /* Indicate that this TCP packet is no longer enqueued by marking it again as allocated. This is what
193 it was prior to being part of the TCP receive queue. */
194 /*lint -e{923} suppress cast of ULONT to pointer. */
195 head_packet_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ALLOCATED;
196
197 /* Clear the queue next pointer. */
198 head_packet_ptr -> nx_packet_queue_next = NX_NULL;
199
200 /* Place the packet pointer in the return pointer. */
201 *packet_ptr = head_packet_ptr;
202
203 /* Check the receive queue count. */
204 if (socket_ptr -> nx_tcp_socket_receive_queue_count == 0)
205 {
206
207 /* Make sure the current receive window is the default window! */
208 socket_ptr -> nx_tcp_socket_rx_window_current = socket_ptr -> nx_tcp_socket_rx_window_default;
209 }
210 else
211 {
212
213 /* Increase the receive window size. */
214 socket_ptr -> nx_tcp_socket_rx_window_current += (*packet_ptr) -> nx_packet_length;
215 }
216
217 /* Determine if an ACK should be forced out for window update, SWS avoidance algorithm.
218 RFC1122, Section4.2.3.3, Page97-98. */
219 if (((socket_ptr -> nx_tcp_socket_rx_window_current - socket_ptr -> nx_tcp_socket_rx_window_last_sent) >= (socket_ptr -> nx_tcp_socket_rx_window_default / 2)) &&
220 ((socket_ptr -> nx_tcp_socket_state == NX_TCP_ESTABLISHED) || (socket_ptr -> nx_tcp_socket_state == NX_TCP_FIN_WAIT_1) || (socket_ptr -> nx_tcp_socket_state == NX_TCP_FIN_WAIT_2)))
221 {
222
223 /* Send a Window Update. */
224 _nx_tcp_packet_send_ack(socket_ptr, socket_ptr -> nx_tcp_socket_tx_sequence);
225 }
226
227 #ifdef TX_ENABLE_EVENT_TRACE
228 /* Update the trace event with the status. */
229 NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_TCP_SOCKET_RECEIVE, 0, *packet_ptr, (*packet_ptr) -> nx_packet_length, socket_ptr -> nx_tcp_socket_rx_sequence);
230 #endif /* TX_ENABLE_EVENT_TRACE */
231
232 /* Release protection. */
233 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
234
235 /* Return a successful status. */
236 return(NX_SUCCESS);
237 }
238 else if ((wait_option) && (_tx_thread_current_ptr != &(ip_ptr -> nx_ip_thread)))
239 {
240
241 /* Suspend the thread on this socket's receive queue. */
242
243 /* Save the return packet pointer address as well. */
244 _tx_thread_current_ptr -> tx_thread_additional_suspend_info = (void *)packet_ptr;
245
246 /* Increment the suspended thread count. */
247 socket_ptr -> nx_tcp_socket_receive_suspended_count++;
248
249 /* Suspend the thread on the receive queue. */
250 /* Note that the mutex is released inside _nx_tcp_socket_thread_suspend(). */
251 _nx_tcp_socket_thread_suspend(&(socket_ptr -> nx_tcp_socket_receive_suspension_list), _nx_tcp_receive_cleanup, socket_ptr, &(ip_ptr -> nx_ip_protection), wait_option);
252 #ifdef TX_ENABLE_EVENT_TRACE
253 if (*packet_ptr)
254 {
255
256 /* Update the trace event with the status. */
257 NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_TCP_SOCKET_RECEIVE, 0, *packet_ptr, (*packet_ptr) -> nx_packet_length, socket_ptr -> nx_tcp_socket_rx_sequence);
258 }
259 #endif /* TX_ENABLE_EVENT_TRACE */
260 /* If not, just return the error code. */
261 return(_tx_thread_current_ptr -> tx_thread_suspend_status);
262 }
263 else
264 {
265
266 /* Release protection. */
267 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
268
269 /* Return an empty receive queue error message. */
270 return(NX_NO_PACKET);
271 }
272 }
273
274