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.

icmpv6.c 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 <string.h>
  25. #include <errno.h>
  26. #include <byteswap.h>
  27. #include <ipxe/in.h>
  28. #include <ipxe/iobuf.h>
  29. #include <ipxe/tcpip.h>
  30. #include <ipxe/ping.h>
  31. #include <ipxe/icmpv6.h>
  32. /** @file
  33. *
  34. * ICMPv6 protocol
  35. *
  36. */
  37. /* Disambiguate the various error causes */
  38. #define EHOSTUNREACH_ROUTE \
  39. __einfo_error ( EINFO_EHOSTUNREACH_ROUTE )
  40. #define EINFO_EHOSTUNREACH_ROUTE \
  41. __einfo_uniqify ( EINFO_EHOSTUNREACH, 0, \
  42. "No route to destination" )
  43. #define EHOSTUNREACH_PROHIBITED \
  44. __einfo_error ( EINFO_EHOSTUNREACH_PROHIBITED )
  45. #define EINFO_EHOSTUNREACH_PROHIBITED \
  46. __einfo_uniqify ( EINFO_EHOSTUNREACH, 1, \
  47. "Communication administratively prohibited" )
  48. #define EHOSTUNREACH_ADDRESS \
  49. __einfo_error ( EINFO_EHOSTUNREACH_ADDRESS )
  50. #define EINFO_EHOSTUNREACH_ADDRESS \
  51. __einfo_uniqify ( EINFO_EHOSTUNREACH, 3, \
  52. "Address unreachable" )
  53. #define EHOSTUNREACH_PORT \
  54. __einfo_error ( EINFO_EHOSTUNREACH_PORT )
  55. #define EINFO_EHOSTUNREACH_PORT \
  56. __einfo_uniqify ( EINFO_EHOSTUNREACH, 4, \
  57. "Port unreachable" )
  58. #define EHOSTUNREACH_CODE( code ) \
  59. EUNIQ ( EINFO_EHOSTUNREACH, ( (code) & 0x1f ), \
  60. EHOSTUNREACH_ROUTE, EHOSTUNREACH_PROHIBITED, \
  61. EHOSTUNREACH_ADDRESS, EHOSTUNREACH_PORT )
  62. #define ETIMEDOUT_HOP \
  63. __einfo_error ( EINFO_ETIMEDOUT_HOP )
  64. #define EINFO_ETIMEDOUT_HOP \
  65. __einfo_uniqify ( EINFO_ETIMEDOUT, 0, \
  66. "Hop limit exceeded in transit" )
  67. #define ETIMEDOUT_REASSEMBLY \
  68. __einfo_error ( EINFO_ETIMEDOUT_REASSEMBLY )
  69. #define EINFO_ETIMEDOUT_REASSEMBLY \
  70. __einfo_uniqify ( EINFO_ETIMEDOUT, 1, \
  71. "Fragment reassembly time exceeded" )
  72. #define ETIMEDOUT_CODE( code ) \
  73. EUNIQ ( EINFO_ETIMEDOUT, ( (code) & 0x1f ), \
  74. ETIMEDOUT_HOP, ETIMEDOUT_REASSEMBLY )
  75. #define EPROTO_BAD_HEADER \
  76. __einfo_error ( EINFO_EPROTO_BAD_HEADER )
  77. #define EINFO_EPROTO_BAD_HEADER \
  78. __einfo_uniqify ( EINFO_EPROTO, 0, \
  79. "Erroneous header field" )
  80. #define EPROTO_NEXT_HEADER \
  81. __einfo_error ( EINFO_EPROTO_NEXT_HEADER )
  82. #define EINFO_EPROTO_NEXT_HEADER \
  83. __einfo_uniqify ( EINFO_EPROTO, 1, \
  84. "Unrecognised next header type" )
  85. #define EPROTO_OPTION \
  86. __einfo_error ( EINFO_EPROTO_OPTION )
  87. #define EINFO_EPROTO_OPTION \
  88. __einfo_uniqify ( EINFO_EPROTO, 2, \
  89. "Unrecognised IPv6 option" )
  90. #define EPROTO_CODE( code ) \
  91. EUNIQ ( EINFO_EPROTO, ( (code) & 0x1f ), \
  92. EPROTO_BAD_HEADER, EPROTO_NEXT_HEADER, EPROTO_OPTION )
  93. struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol;
  94. /**
  95. * Process received ICMPv6 echo request packet
  96. *
  97. * @v iobuf I/O buffer
  98. * @v netdev Network device
  99. * @v sin6_src Source socket address
  100. * @v sin6_dest Destination socket address
  101. * @ret rc Return status code
  102. */
  103. static int icmpv6_rx_echo_request ( struct io_buffer *iobuf,
  104. struct net_device *netdev __unused,
  105. struct sockaddr_in6 *sin6_src,
  106. struct sockaddr_in6 *sin6_dest __unused ) {
  107. struct sockaddr_tcpip *st_src =
  108. ( ( struct sockaddr_tcpip * ) sin6_src );
  109. return icmp_rx_echo_request ( iobuf, st_src, &icmpv6_echo_protocol );
  110. }
  111. /** ICMPv6 echo request handler */
  112. struct icmpv6_handler icmpv6_echo_request_handler __icmpv6_handler = {
  113. .type = ICMPV6_ECHO_REQUEST,
  114. .rx = icmpv6_rx_echo_request,
  115. };
  116. /**
  117. * Process received ICMPv6 echo reply packet
  118. *
  119. * @v iobuf I/O buffer
  120. * @v netdev Network device
  121. * @v sin6_src Source socket address
  122. * @v sin6_dest Destination socket address
  123. * @ret rc Return status code
  124. */
  125. static int icmpv6_rx_echo_reply ( struct io_buffer *iobuf,
  126. struct net_device *netdev __unused,
  127. struct sockaddr_in6 *sin6_src,
  128. struct sockaddr_in6 *sin6_dest __unused ) {
  129. struct sockaddr_tcpip *st_src =
  130. ( ( struct sockaddr_tcpip * ) sin6_src );
  131. return icmp_rx_echo_reply ( iobuf, st_src );
  132. }
  133. /** ICMPv6 echo reply handler */
  134. struct icmpv6_handler icmpv6_echo_reply_handler __icmpv6_handler = {
  135. .type = ICMPV6_ECHO_REPLY,
  136. .rx = icmpv6_rx_echo_reply,
  137. };
  138. /**
  139. * Identify ICMPv6 handler
  140. *
  141. * @v type ICMPv6 type
  142. * @ret handler ICMPv6 handler, or NULL if not found
  143. */
  144. static struct icmpv6_handler * icmpv6_handler ( unsigned int type ) {
  145. struct icmpv6_handler *handler;
  146. for_each_table_entry ( handler, ICMPV6_HANDLERS ) {
  147. if ( handler->type == type )
  148. return handler;
  149. }
  150. return NULL;
  151. }
  152. /**
  153. * Process a received packet
  154. *
  155. * @v iobuf I/O buffer
  156. * @v netdev Network device
  157. * @v st_src Partially-filled source address
  158. * @v st_dest Partially-filled destination address
  159. * @v pshdr_csum Pseudo-header checksum
  160. * @ret rc Return status code
  161. */
  162. static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
  163. struct sockaddr_tcpip *st_src,
  164. struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) {
  165. struct sockaddr_in6 *sin6_src = ( ( struct sockaddr_in6 * ) st_src );
  166. struct sockaddr_in6 *sin6_dest = ( ( struct sockaddr_in6 * ) st_dest );
  167. struct icmp_header *icmp = iobuf->data;
  168. size_t len = iob_len ( iobuf );
  169. struct icmpv6_handler *handler;
  170. unsigned int csum;
  171. int rc;
  172. /* Sanity check */
  173. if ( len < sizeof ( *icmp ) ) {
  174. DBGC ( netdev, "ICMPv6 packet too short at %zd bytes (min %zd "
  175. "bytes)\n", len, sizeof ( *icmp ) );
  176. rc = -EINVAL;
  177. goto done;
  178. }
  179. /* Verify checksum */
  180. csum = tcpip_continue_chksum ( pshdr_csum, icmp, len );
  181. if ( csum != 0 ) {
  182. DBGC ( netdev, "ICMPv6 checksum incorrect (is %04x, should be "
  183. "0000)\n", csum );
  184. DBGC_HDA ( netdev, 0, icmp, len );
  185. rc = -EINVAL;
  186. goto done;
  187. }
  188. /* Identify handler */
  189. handler = icmpv6_handler ( icmp->type );
  190. if ( ! handler ) {
  191. switch ( icmp->type ) {
  192. case ICMPV6_DESTINATION_UNREACHABLE:
  193. rc = -EHOSTUNREACH_CODE ( icmp->code );
  194. break;
  195. case ICMPV6_PACKET_TOO_BIG:
  196. rc = -ERANGE;
  197. break;
  198. case ICMPV6_TIME_EXCEEDED:
  199. rc = -ETIMEDOUT_CODE ( icmp->code );
  200. break;
  201. case ICMPV6_PARAMETER_PROBLEM:
  202. rc = -EPROTO_CODE ( icmp->code );
  203. break;
  204. default:
  205. DBGC ( netdev, "ICMPv6 unrecognised type %d code %d\n",
  206. icmp->type, icmp->code );
  207. rc = -ENOTSUP;
  208. break;
  209. };
  210. goto done;
  211. }
  212. /* Pass to handler */
  213. if ( ( rc = handler->rx ( iob_disown ( iobuf ), netdev, sin6_src,
  214. sin6_dest ) ) != 0 ) {
  215. DBGC ( netdev, "ICMPv6 could not handle type %d: %s\n",
  216. icmp->type, strerror ( rc ) );
  217. goto done;
  218. }
  219. done:
  220. free_iob ( iobuf );
  221. return rc;
  222. }
  223. /** ICMPv6 TCP/IP protocol */
  224. struct tcpip_protocol icmpv6_protocol __tcpip_protocol = {
  225. .name = "ICMPv6",
  226. .rx = icmpv6_rx,
  227. .tcpip_proto = IP_ICMP6,
  228. };
  229. /** ICMPv6 echo protocol */
  230. struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol = {
  231. .family = AF_INET6,
  232. .request = ICMPV6_ECHO_REQUEST,
  233. .reply = ICMPV6_ECHO_REPLY,
  234. .tcpip_protocol = &icmpv6_protocol,
  235. .net_checksum = 1,
  236. };