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