tcp_monitor.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /****************************************************************************
  2. * net/tcp/tcp_monitor.c
  3. *
  4. * Copyright (C) 2007-2013, 2017-2018 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <stdint.h>
  40. #include <assert.h>
  41. #include <debug.h>
  42. #include <nuttx/net/tcp.h>
  43. #include "devif/devif.h"
  44. #include "socket/socket.h"
  45. #include "tcp/tcp.h"
  46. #ifdef NET_TCP_HAVE_STACK
  47. /****************************************************************************
  48. * Private Function Prototypes
  49. ****************************************************************************/
  50. static void tcp_close_connection(FAR struct socket *psock, uint16_t flags);
  51. static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
  52. FAR void *pvconn, FAR void *pvpriv,
  53. uint16_t flags);
  54. /****************************************************************************
  55. * Private Functions
  56. ****************************************************************************/
  57. /****************************************************************************
  58. * Name: tcp_close_connection
  59. *
  60. * Description:
  61. * Called when a loss-of-connection event has occurred.
  62. *
  63. * Input Parameters:
  64. * psock The TCP socket structure associated.
  65. * flags Set of connection events events
  66. *
  67. * Returned Value:
  68. * None
  69. *
  70. * Assumptions:
  71. * The caller holds the network lock.
  72. *
  73. ****************************************************************************/
  74. static void tcp_close_connection(FAR struct socket *psock, uint16_t flags)
  75. {
  76. /* These loss-of-connection events may be reported:
  77. *
  78. * TCP_CLOSE: The remote host has closed the connection
  79. * TCP_ABORT: The remote host has aborted the connection
  80. * TCP_TIMEDOUT: Connection aborted due to too many retransmissions.
  81. * NETDEV_DOWN: The network device went down
  82. *
  83. * And we need to set these two socket status bits appropriately:
  84. *
  85. * _SF_CONNECTED==1 && _SF_CLOSED==0 - the socket is connected
  86. * _SF_CONNECTED==0 && _SF_CLOSED==1 - the socket was gracefully
  87. * disconnected
  88. * _SF_CONNECTED==0 && _SF_CLOSED==0 - the socket was rudely disconnected
  89. */
  90. if ((flags & TCP_CLOSE) != 0)
  91. {
  92. /* The peer gracefully closed the connection. Marking the
  93. * connection as disconnected will suppress some subsequent
  94. * ENOTCONN errors from receive. A graceful disconnection is
  95. * not handle as an error but as an "end-of-file"
  96. */
  97. psock->s_flags &= ~_SF_CONNECTED;
  98. psock->s_flags |= _SF_CLOSED;
  99. }
  100. else if ((flags & (TCP_ABORT | TCP_TIMEDOUT | NETDEV_DOWN)) != 0)
  101. {
  102. /* The loss of connection was less than graceful. This will
  103. * (eventually) be reported as an ENOTCONN error.
  104. */
  105. psock->s_flags &= ~(_SF_CONNECTED | _SF_CLOSED);
  106. }
  107. }
  108. /****************************************************************************
  109. * Name: tcp_disconnect_event
  110. *
  111. * Description:
  112. * Some connection related event has occurred
  113. *
  114. * Input Parameters:
  115. * dev The device which as active when the event was detected.
  116. * conn The connection structure associated with the socket
  117. * flags Set of events describing why the callback was invoked
  118. *
  119. * Returned Value:
  120. * None
  121. *
  122. * Assumptions:
  123. * The network is locked.
  124. *
  125. ****************************************************************************/
  126. static uint16_t tcp_disconnect_event(FAR struct net_driver_s *dev,
  127. FAR void *pvconn, FAR void *pvpriv,
  128. uint16_t flags)
  129. {
  130. FAR struct socket *psock = (FAR struct socket *)pvpriv;
  131. if (psock != NULL)
  132. {
  133. ninfo("flags: %04x s_flags: %02x\n", flags, psock->s_flags);
  134. /* TCP_DISCONN_EVENTS: TCP_CLOSE, TCP_ABORT, TCP_TIMEDOUT, or
  135. * NETDEV_DOWN. All loss-of-connection events.
  136. */
  137. if ((flags & TCP_DISCONN_EVENTS) != 0)
  138. {
  139. tcp_close_connection(psock, flags);
  140. }
  141. /* TCP_CONNECTED: The socket is successfully connected */
  142. else if ((flags & TCP_CONNECTED) != 0)
  143. {
  144. #if 0 /* REVISIT: Assertion fires. Why? */
  145. FAR struct tcp_conn_s *conn =
  146. (FAR struct tcp_conn_s *)psock->s_conn;
  147. /* Make sure that this is the device bound to the connection */
  148. DEBUGASSERT(conn->dev == NULL || conn->dev == dev);
  149. conn->dev = dev;
  150. #endif
  151. /* If there is no local address assigned to the socket (perhaps
  152. * because it was INADDR_ANY), then assign it the address of the
  153. * connecting device.
  154. *
  155. * TODO: Implement this.
  156. */
  157. /* Indicate that the socket is now connected */
  158. psock->s_flags |= _SF_CONNECTED;
  159. psock->s_flags &= ~_SF_CLOSED;
  160. }
  161. }
  162. return flags;
  163. }
  164. /****************************************************************************
  165. * Name: tcp_shutdown_monitor
  166. *
  167. * Description:
  168. * Stop monitoring TCP connection changes for a given socket.
  169. *
  170. * Input Parameters:
  171. * conn - The TCP connection of interest
  172. * flags - Indicates the type of shutdown. TCP_CLOSE or TCP_ABORT
  173. *
  174. * Returned Value:
  175. * None
  176. *
  177. * Assumptions:
  178. * The caller holds the network lock (if not, it will be locked momentarily
  179. * by this function).
  180. *
  181. ****************************************************************************/
  182. static void tcp_shutdown_monitor(FAR struct tcp_conn_s *conn, uint16_t flags)
  183. {
  184. DEBUGASSERT(conn);
  185. /* Perform callbacks to assure that all sockets, including dup'ed copies,
  186. * are informed of the loss of connection event.
  187. */
  188. net_lock();
  189. (void)tcp_callback(conn->dev, conn, flags);
  190. /* Free all allocated connection event callback structures */
  191. while (conn->connevents != NULL)
  192. {
  193. devif_conn_callback_free(conn->dev, conn->connevents,
  194. &conn->connevents);
  195. }
  196. net_unlock();
  197. }
  198. /****************************************************************************
  199. * Public Functions
  200. ****************************************************************************/
  201. /****************************************************************************
  202. * Name: tcp_start_monitor
  203. *
  204. * Description:
  205. * Set up to receive TCP connection state changes for a given socket
  206. *
  207. * Input Parameters:
  208. * psock - The socket of interest
  209. *
  210. * Returned Value:
  211. * On success, tcp_start_monitor returns OK; On any failure,
  212. * tcp_start_monitor will return a negated errno value. The only failure
  213. * that can occur is if the socket has already been closed and, in this
  214. * case, -ENOTCONN is returned.
  215. *
  216. * Assumptions:
  217. * The caller holds the network lock (if not, it will be locked momentarily
  218. * by this function).
  219. *
  220. ****************************************************************************/
  221. int tcp_start_monitor(FAR struct socket *psock)
  222. {
  223. FAR struct tcp_conn_s *conn;
  224. FAR struct devif_callback_s *cb;
  225. DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
  226. conn = (FAR struct tcp_conn_s *)psock->s_conn;
  227. /* Check if the connection has already been closed before any callbacks
  228. * have been registered. (Maybe the connection is lost before accept has
  229. * registered the monitoring callback.)
  230. */
  231. net_lock();
  232. if (!(conn->tcpstateflags == TCP_ESTABLISHED ||
  233. conn->tcpstateflags == TCP_SYN_RCVD))
  234. {
  235. /* Invoke the TCP_CLOSE connection event now */
  236. tcp_shutdown_monitor(conn, TCP_CLOSE);
  237. /* And return -ENOTCONN to indicate the monitor was not started
  238. * because the socket was already disconnected.
  239. */
  240. net_unlock();
  241. return -ENOTCONN;
  242. }
  243. /* Allocate a callback structure that we will use to get callbacks if
  244. * the network goes down.
  245. */
  246. cb = devif_callback_alloc(conn->dev, &conn->connevents);
  247. if (cb != NULL)
  248. {
  249. cb->event = tcp_disconnect_event;
  250. cb->priv = (FAR void *)psock;
  251. cb->flags = TCP_DISCONN_EVENTS;
  252. }
  253. net_unlock();
  254. return OK;
  255. }
  256. /****************************************************************************
  257. * Name: tcp_stop_monitor
  258. *
  259. * Description:
  260. * Stop monitoring TCP connection changes for a sockets associated with
  261. * a given TCP connection stucture.
  262. *
  263. * Input Parameters:
  264. * conn - The TCP connection of interest
  265. * flags Set of disconnection events
  266. *
  267. * Returned Value:
  268. * None
  269. *
  270. * Assumptions:
  271. * The caller holds the network lock (if not, it will be locked momentarily
  272. * by this function).
  273. *
  274. ****************************************************************************/
  275. void tcp_stop_monitor(FAR struct tcp_conn_s *conn, uint16_t flags)
  276. {
  277. DEBUGASSERT(conn != NULL);
  278. /* Stop the network monitor */
  279. tcp_shutdown_monitor(conn, flags);
  280. }
  281. /****************************************************************************
  282. * Name: tcp_close_monitor
  283. *
  284. * Description:
  285. * One socket in a group of dup'ed sockets has been closed. We need to
  286. * selectively terminate just those things that are waiting of events
  287. * from this specific socket. And also recover any resources that are
  288. * committed to monitoring this socket.
  289. *
  290. * Input Parameters:
  291. * psock - The TCP socket structure that is closed
  292. *
  293. * Returned Value:
  294. * None
  295. *
  296. * Assumptions:
  297. * The caller holds the network lock (if not, it will be locked momentarily
  298. * by this function).
  299. *
  300. ****************************************************************************/
  301. void tcp_close_monitor(FAR struct socket *psock)
  302. {
  303. FAR struct tcp_conn_s *conn;
  304. FAR struct devif_callback_s *cb;
  305. DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
  306. conn = (FAR struct tcp_conn_s *)psock->s_conn;
  307. /* Find and free the the connection event callback */
  308. net_lock();
  309. for (cb = conn->connevents;
  310. cb != NULL && cb->priv != (FAR void *)psock;
  311. cb = cb->nxtconn)
  312. {
  313. }
  314. if (cb != NULL)
  315. {
  316. devif_conn_callback_free(conn->dev, cb, &conn->connevents);
  317. }
  318. /* Make sure that this socket is explicitly marked as closed */
  319. tcp_close_connection(psock, TCP_CLOSE);
  320. /* Now notify any sockets waiting for events from this particular sockets.
  321. * Other dup'ed sockets sharing the same connection must not be effected.
  322. */
  323. /* REVISIT: The following logic won't work: There is no way to compare
  324. * psocks to check for a match. This missing logic could only be an issue
  325. * if the same socket were being used on one thread, but then closed on
  326. * another. Some redesign would be required to find only those event
  327. * handlers that are waiting specifically for this socket (vs. a dup of
  328. * this socket)
  329. */
  330. #if 0
  331. for (cb = conn->list; cb != NULL; cb = cb->nxtconn)
  332. {
  333. if (cb->event != NULL && (cb->flags & TCP_CLOSE) != 0)
  334. {
  335. (void)cb->event(conn->dev, conn, cb->priv, TCP_CLOSE);
  336. }
  337. }
  338. #endif
  339. net_unlock();
  340. }
  341. /****************************************************************************
  342. * Name: tcp_lost_connection
  343. *
  344. * Description:
  345. * Called when a loss-of-connection event has been detected by network
  346. * event handling logic. Perform operations like tcp_stop_monitor but (1)
  347. * explicitly mark this socket and (2) disable further callbacks the to the
  348. * event handler.
  349. *
  350. * Input Parameters:
  351. * psock - The TCP socket structure whose connection was lost.
  352. * cb - devif callback structure
  353. * flags - Set of connection events events
  354. *
  355. * Returned Value:
  356. * None
  357. *
  358. * Assumptions:
  359. * The caller holds the network lock (if not, it will be locked momentarily
  360. * by this function).
  361. *
  362. ****************************************************************************/
  363. void tcp_lost_connection(FAR struct socket *psock,
  364. FAR struct devif_callback_s *cb, uint16_t flags)
  365. {
  366. DEBUGASSERT(psock != NULL && psock->s_conn != NULL);
  367. /* Nullify the callback structure so that recursive callbacks are not
  368. * received by the event handler due to disconnection processing.
  369. *
  370. * NOTE: In a configuration with CONFIG_NET_TCP_WRITE_BUFFERS=y,
  371. * the "semi-permanent" callback structure may have already been
  372. * nullified.
  373. */
  374. if (cb != NULL)
  375. {
  376. cb->flags = 0;
  377. cb->priv = NULL;
  378. cb->event = NULL;
  379. }
  380. /* Make sure that this socket is explicitly marked. It may not get a
  381. * callback due to the above nullification.
  382. */
  383. tcp_close_connection(psock, flags);
  384. /* Then stop the network monitor for all sockets. */
  385. tcp_shutdown_monitor((FAR struct tcp_conn_s *)psock->s_conn, flags);
  386. }
  387. #endif /* NET_TCP_HAVE_STACK */