Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. */
  19. FILE_LICENCE ( GPL2_OR_LATER );
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <byteswap.h>
  24. #include <ipxe/refcnt.h>
  25. #include <ipxe/list.h>
  26. #include <ipxe/iobuf.h>
  27. #include <ipxe/tcpip.h>
  28. #include <ipxe/icmp.h>
  29. #include <ipxe/interface.h>
  30. #include <ipxe/xfer.h>
  31. #include <ipxe/open.h>
  32. #include <ipxe/netdevice.h>
  33. #include <ipxe/ping.h>
  34. /** @file
  35. *
  36. * ICMP ping protocol
  37. *
  38. */
  39. /**
  40. * A ping connection
  41. *
  42. */
  43. struct ping_connection {
  44. /** Reference counter */
  45. struct refcnt refcnt;
  46. /** List of ping connections */
  47. struct list_head list;
  48. /** Remote socket address */
  49. struct sockaddr_tcpip peer;
  50. /** Local port number */
  51. uint16_t port;
  52. /** Data transfer interface */
  53. struct interface xfer;
  54. };
  55. /** List of registered ping connections */
  56. static LIST_HEAD ( ping_conns );
  57. /**
  58. * Identify ping connection by local port number
  59. *
  60. * @v port Local port number
  61. * @ret ping Ping connection, or NULL
  62. */
  63. static struct ping_connection * ping_demux ( unsigned int port ) {
  64. struct ping_connection *ping;
  65. list_for_each_entry ( ping, &ping_conns, list ) {
  66. if ( ping->port == port )
  67. return ping;
  68. }
  69. return NULL;
  70. }
  71. /**
  72. * Check if local port number is available
  73. *
  74. * @v port Local port number
  75. * @ret port Local port number, or negative error
  76. */
  77. static int ping_port_available ( int port ) {
  78. return ( ping_demux ( port ) ? -EADDRINUSE : port );
  79. }
  80. /**
  81. * Process ICMP ping reply
  82. *
  83. * @v iobuf I/O buffer
  84. * @v st_src Source address
  85. * @ret rc Return status code
  86. */
  87. int ping_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src ) {
  88. struct icmp_echo *echo = iobuf->data;
  89. struct ping_connection *ping;
  90. struct xfer_metadata meta;
  91. int rc;
  92. /* Sanity check: should already have been checked by ICMP layer */
  93. assert ( iob_len ( iobuf ) >= sizeof ( *echo ) );
  94. /* Identify connection */
  95. ping = ping_demux ( ntohs ( echo->ident ) );
  96. DBGC ( ping, "PING %p reply id %#04x seq %#04x\n",
  97. ping, ntohs ( echo->ident ), ntohs ( echo->sequence ) );
  98. if ( ! ping ) {
  99. rc = -ENOTCONN;
  100. goto discard;
  101. }
  102. /* Strip header, construct metadata, and pass data to upper layer */
  103. iob_pull ( iobuf, sizeof ( *echo ) );
  104. memset ( &meta, 0, sizeof ( meta ) );
  105. meta.src = ( ( struct sockaddr * ) st_src );
  106. meta.flags = XFER_FL_ABS_OFFSET;
  107. meta.offset = ntohs ( echo->sequence );
  108. return xfer_deliver ( &ping->xfer, iob_disown ( iobuf ), &meta );
  109. discard:
  110. free_iob ( iobuf );
  111. return rc;
  112. }
  113. /**
  114. * Allocate I/O buffer for ping
  115. *
  116. * @v ping Ping connection
  117. * @v len Payload size
  118. * @ret iobuf I/O buffer, or NULL
  119. */
  120. static struct io_buffer *
  121. ping_alloc_iob ( struct ping_connection *ping __unused, size_t len ) {
  122. size_t header_len;
  123. struct io_buffer *iobuf;
  124. header_len = ( MAX_LL_NET_HEADER_LEN + sizeof ( struct icmp_echo ) );
  125. iobuf = alloc_iob ( header_len + len );
  126. if ( iobuf )
  127. iob_reserve ( iobuf, header_len );
  128. return iobuf;
  129. }
  130. /**
  131. * Deliver datagram as I/O buffer
  132. *
  133. * @v ping Ping connection
  134. * @v iobuf I/O buffer
  135. * @v meta Data transfer metadata
  136. * @ret rc Return status code
  137. */
  138. static int ping_deliver ( struct ping_connection *ping, struct io_buffer *iobuf,
  139. struct xfer_metadata *meta ) {
  140. struct icmp_echo *echo = iob_push ( iobuf, sizeof ( *echo ) );
  141. int rc;
  142. /* Construct header */
  143. memset ( echo, 0, sizeof ( *echo ) );
  144. echo->ident = htons ( ping->port );
  145. echo->sequence = htons ( meta->offset );
  146. /* Transmit echo request */
  147. if ( ( rc = icmp_tx_echo_request ( iob_disown ( iobuf ),
  148. &ping->peer ) ) != 0 ) {
  149. DBGC ( ping, "PING %p could not transmit: %s\n",
  150. ping, strerror ( rc ) );
  151. return rc;
  152. }
  153. return 0;
  154. }
  155. /**
  156. * Close ping connection
  157. *
  158. * @v ping Ping connection
  159. * @v rc Reason for close
  160. */
  161. static void ping_close ( struct ping_connection *ping, int rc ) {
  162. /* Close data transfer interface */
  163. intf_shutdown ( &ping->xfer, rc );
  164. /* Remove from list of connections and drop list's reference */
  165. list_del ( &ping->list );
  166. ref_put ( &ping->refcnt );
  167. DBGC ( ping, "PING %p closed\n", ping );
  168. }
  169. /** Ping data transfer interface operations */
  170. static struct interface_operation ping_xfer_operations[] = {
  171. INTF_OP ( xfer_deliver, struct ping_connection *, ping_deliver ),
  172. INTF_OP ( xfer_alloc_iob, struct ping_connection *, ping_alloc_iob ),
  173. INTF_OP ( intf_close, struct ping_connection *, ping_close ),
  174. };
  175. /** Ping data transfer interface descriptor */
  176. static struct interface_descriptor ping_xfer_desc =
  177. INTF_DESC ( struct ping_connection, xfer, ping_xfer_operations );
  178. /**
  179. * Open a ping connection
  180. *
  181. * @v xfer Data transfer interface
  182. * @v peer Peer socket address
  183. * @v local Local socket address, or NULL
  184. * @ret rc Return status code
  185. */
  186. static int ping_open ( struct interface *xfer, struct sockaddr *peer,
  187. struct sockaddr *local ) {
  188. struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer;
  189. struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local;
  190. struct ping_connection *ping;
  191. int port;
  192. int rc;
  193. /* Allocate and initialise structure */
  194. ping = zalloc ( sizeof ( *ping ) );
  195. if ( ! ping ) {
  196. rc = -ENOMEM;
  197. goto err_alloc;
  198. }
  199. DBGC ( ping, "PING %p allocated\n", ping );
  200. ref_init ( &ping->refcnt, NULL );
  201. intf_init ( &ping->xfer, &ping_xfer_desc, &ping->refcnt );
  202. memcpy ( &ping->peer, st_peer, sizeof ( ping->peer ) );
  203. /* Bind to local port */
  204. port = tcpip_bind ( st_local, ping_port_available );
  205. if ( port < 0 ) {
  206. rc = port;
  207. DBGC ( ping, "PING %p could not bind: %s\n",
  208. ping, strerror ( rc ) );
  209. goto err_bind;
  210. }
  211. ping->port = port;
  212. DBGC ( ping, "PING %p bound to id %#04x\n", ping, port );
  213. /* Attach parent interface, transfer reference to connection
  214. * list, and return
  215. */
  216. intf_plug_plug ( &ping->xfer, xfer );
  217. list_add ( &ping->list, &ping_conns );
  218. return 0;
  219. err_bind:
  220. ref_put ( &ping->refcnt );
  221. err_alloc:
  222. return rc;
  223. }
  224. /** Ping IPv4 socket opener */
  225. struct socket_opener ping_ipv4_socket_opener __socket_opener = {
  226. .semantics = PING_SOCK_ECHO,
  227. .family = AF_INET,
  228. .open = ping_open,
  229. };
  230. /** Ping IPv6 socket opener */
  231. struct socket_opener ping_ipv6_socket_opener __socket_opener = {
  232. .semantics = PING_SOCK_ECHO,
  233. .family = AF_INET6,
  234. .open = ping_open,
  235. };
  236. /** Linkage hack */
  237. int ping_sock_echo = PING_SOCK_ECHO;