You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ping.c 7.1KB

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