PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

413 lines
17KB

  1. /**************************************************************************
  2. *
  3. * Copyright 2008-2018 by Andrey Butok. FNET Community
  4. *
  5. ***************************************************************************
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License"); you may
  8. * not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. *
  19. ***************************************************************************
  20. *
  21. * ICMPv6 protocol implementation.
  22. *
  23. ***************************************************************************/
  24. #include "fnet.h"
  25. #if FNET_CFG_IP6
  26. #include "fnet_icmp6.h"
  27. #include "fnet_loop.h"
  28. #include "fnet_timer.h"
  29. #include "fnet_prot.h"
  30. #include "fnet_socket.h"
  31. #include "fnet_socket_prv.h"
  32. #include "fnet_checksum_prv.h"
  33. #include "fnet_mld.h"
  34. /************************************************************************
  35. * Function Prototypes
  36. *************************************************************************/
  37. static void _fnet_icmp6_input(fnet_netif_t *netif, struct fnet_sockaddr *src_addr, struct fnet_sockaddr *dest_addr, fnet_netbuf_t *nb, fnet_netbuf_t *ip6_nb);
  38. /************************************************************************
  39. * Protocol API structure.
  40. ************************************************************************/
  41. fnet_prot_if_t fnet_icmp6_prot_if =
  42. {
  43. 0, /* Pointer to the head of the protocol's socket list.*/
  44. AF_INET6, /* Address domain family.*/
  45. SOCK_UNSPEC, /* Socket type used for.*/
  46. FNET_PROT_ICMP6, /* Protocol number.*/
  47. 0, /* Protocol initialization function.*/
  48. 0, /* Protocol release function.*/
  49. _fnet_icmp6_input, /* Protocol input function,.*/
  50. 0, /* Protocol input control function.*/
  51. 0, /* protocol drain function.*/
  52. 0 /* Socket API */
  53. };
  54. /************************************************************************
  55. * DESCRIPTION: ICMPv6 input function.
  56. *************************************************************************/
  57. static void _fnet_icmp6_input(fnet_netif_t *netif, struct fnet_sockaddr *src_addr, struct fnet_sockaddr *dest_addr, fnet_netbuf_t *nb, fnet_netbuf_t *ip6_nb)
  58. {
  59. fnet_icmp6_header_t *hdr;
  60. fnet_uint16_t sum;
  61. fnet_ip6_addr_t *src_ip_rx;
  62. fnet_ip6_addr_t *dest_ip_rx;
  63. fnet_ip6_addr_t *src_ip_tx;
  64. fnet_ip6_addr_t *dest_ip_tx;
  65. fnet_prot_notify_t prot_cmd;
  66. fnet_bool_t discard_flag = FNET_FALSE;
  67. if((netif != 0) && (nb != 0))
  68. {
  69. /* The header must reside in contiguous area of memory. */
  70. if(_fnet_netbuf_pullup(&nb, sizeof(fnet_icmp6_header_t)) == FNET_ERR)
  71. {
  72. goto DISCARD;
  73. }
  74. hdr = (fnet_icmp6_header_t *)nb->data_ptr;
  75. dest_ip_rx = &((struct fnet_sockaddr_in6 *)(dest_addr))->sin6_addr.s6_addr;
  76. src_ip_rx = &((struct fnet_sockaddr_in6 *)(src_addr))->sin6_addr.s6_addr;
  77. /* Swap source and destination addresses.*/
  78. dest_ip_tx = src_ip_rx;
  79. src_ip_tx = dest_ip_rx;
  80. /* Drop Multicast loopback.*/
  81. #if FNET_CFG_LOOPBACK
  82. if ((netif == FNET_LOOP_IF) && FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx))
  83. {
  84. goto DISCARD;
  85. }
  86. #endif /* FNET_CFG_LOOPBACK */
  87. /* Verify the checksum. */
  88. sum = _fnet_checksum_pseudo_netbuf_start( nb, FNET_HTONS((fnet_uint16_t)FNET_PROT_ICMP6), (fnet_uint16_t)nb->total_length );
  89. sum = _fnet_checksum_pseudo_netbuf_end( sum, (fnet_uint8_t *)src_ip_rx, (fnet_uint8_t *)dest_ip_rx, sizeof(fnet_ip6_addr_t) );
  90. if(sum)
  91. {
  92. goto DISCARD;
  93. }
  94. /************************************************************
  95. * Process incoming ICMPv6 packets.
  96. *************************************************************/
  97. switch (hdr->type)
  98. {
  99. /**************************
  100. * Neighbor Solicitation.
  101. **************************/
  102. case FNET_ICMP6_TYPE_NEIGHBOR_SOLICITATION:
  103. _fnet_nd6_neighbor_solicitation_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
  104. break;
  105. /**************************
  106. * Neighbor Advertisemnt.
  107. **************************/
  108. case FNET_ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT:
  109. _fnet_nd6_neighbor_advertisement_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
  110. break;
  111. /**************************
  112. * Router Advertisemnt.
  113. **************************/
  114. case FNET_ICMP6_TYPE_ROUTER_ADVERTISEMENT:
  115. _fnet_nd6_router_advertisement_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
  116. break;
  117. /**************************
  118. * Router Advertisemnt.
  119. **************************/
  120. case FNET_ICMP6_TYPE_REDIRECT:
  121. _fnet_nd6_redirect_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
  122. break;
  123. #if FNET_CFG_MLD
  124. /**************************
  125. * Multicast Listener Query.
  126. **************************/
  127. case FNET_ICMP6_TYPE_MULTICAST_LISTENER_QUERY:
  128. _fnet_mld_query_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
  129. break;
  130. #endif
  131. /**************************
  132. * Echo Request.
  133. * RFC4443 4.1: Every node MUST implement an ICMPv6 Echo responder function that
  134. * receives Echo Requests and originates corresponding Echo Replies.
  135. **************************/
  136. case FNET_ICMP6_TYPE_ECHO_REQ:
  137. hdr->type = FNET_ICMP6_TYPE_ECHO_REPLY;
  138. /* RFC4443: the source address of the reply MUST be a unicast
  139. * address belonging to the interface on which
  140. * the Echo Request message was received.*/
  141. if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx))
  142. {
  143. src_ip_tx = FNET_NULL;
  144. }
  145. /* Swap source and destination addresses.*/
  146. _fnet_icmp6_output(netif, src_ip_tx, dest_ip_tx, 0u, nb);
  147. _fnet_netbuf_free_chain(ip6_nb);
  148. break;
  149. #if FNET_CFG_IP6_PMTU_DISCOVERY
  150. /**************************
  151. * Packet Too Big Message.
  152. **************************/
  153. case FNET_ICMP6_TYPE_PACKET_TOOBIG:
  154. if(netif->pmtu) /* If PMTU is enabled for the interface.*/
  155. {
  156. fnet_uint32_t pmtu;
  157. fnet_icmp6_err_header_t *icmp6_err = (fnet_icmp6_err_header_t *)nb->data_ptr;
  158. /* The header must reside in contiguous area of memory. */
  159. if(_fnet_netbuf_pullup(&nb, sizeof(fnet_icmp6_err_header_t)) == FNET_ERR)
  160. {
  161. goto DISCARD;
  162. }
  163. /* RFC 1981.Upon receipt of such a
  164. * message, the source node reduces its assumed PMTU for the path based
  165. * on the MTU of the constricting hop as reported in the Packet Too Big
  166. * message.*/
  167. pmtu = fnet_ntohl(icmp6_err->data);
  168. /* A node MUST NOT increase its estimate of the Path MTU in response to
  169. * the contents of a Packet Too Big message. */
  170. if(netif->pmtu > pmtu)
  171. {
  172. _fnet_netif_set_pmtu(netif, pmtu);
  173. }
  174. }
  175. discard_flag = FNET_TRUE;
  176. break;
  177. #endif
  178. /**************************
  179. * Destination Unreachable.
  180. **************************/
  181. case FNET_ICMP6_TYPE_DEST_UNREACH:
  182. switch(hdr->code)
  183. {
  184. case FNET_ICMP6_CODE_DU_NO_ROUTE: /* No route to destination. */
  185. case FNET_ICMP6_CODE_DU_BEYOND_SCOPE: /* Beyond scope of source address.*/
  186. prot_cmd = FNET_PROT_NOTIFY_UNREACH_NET;
  187. break;
  188. case FNET_ICMP6_CODE_DU_ADMIN_PROHIBITED: /* Communication with destination administratively prohibited. */
  189. case FNET_ICMP6_CODE_DU_ADDR_UNREACH: /* Address unreachable.*/
  190. prot_cmd = FNET_PROT_NOTIFY_UNREACH_HOST;
  191. break;
  192. case FNET_ICMP6_CODE_DU_PORT_UNREACH: /* Port unreachable.*/
  193. prot_cmd = FNET_PROT_NOTIFY_UNREACH_PORT;
  194. break;
  195. default:
  196. goto DISCARD;
  197. }
  198. /* Protocol notification.*/
  199. {
  200. fnet_ip6_header_t *ip_header;
  201. fnet_prot_if_t *protocol;
  202. fnet_size_t hdr_err_length = sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t);
  203. fnet_size_t hdr_err_data_length = hdr_err_length + 8u; /* 8 bytes is enough for transport protocol (port numbers).*/
  204. if(nb->total_length < hdr_err_data_length)
  205. {
  206. goto DISCARD;
  207. }
  208. if(nb->total_length > hdr_err_data_length)
  209. {
  210. _fnet_netbuf_trim(&nb, (fnet_int32_t)(hdr_err_data_length - nb->total_length));
  211. }
  212. if(_fnet_netbuf_pullup(&nb, nb->total_length) == FNET_ERR) /* The header must reside in contiguous area of memory.*/
  213. {
  214. goto DISCARD;
  215. }
  216. ip_header = (fnet_ip6_header_t *)((fnet_uint8_t *)nb->data_ptr + sizeof(fnet_icmp6_err_header_t));
  217. if((protocol = _fnet_prot_find(AF_INET6, SOCK_UNSPEC, (fnet_uint32_t)ip_header->next_header)) != 0)
  218. {
  219. if(protocol->prot_control_input)
  220. {
  221. struct fnet_sockaddr err_src_addr;
  222. struct fnet_sockaddr err_dest_addr;
  223. /* Prepare addreses for upper protocol.*/
  224. _fnet_ip6_set_socket_addr(netif, ip_header, &err_src_addr, &err_dest_addr );
  225. _fnet_netbuf_trim(&nb, (fnet_int32_t)(hdr_err_length)); /* Cut the ICMP error header.*/
  226. protocol->prot_control_input(prot_cmd, &err_src_addr, &err_dest_addr, nb);
  227. }
  228. }
  229. }
  230. discard_flag = FNET_TRUE;
  231. break;
  232. default:
  233. discard_flag = FNET_TRUE;
  234. break;
  235. }
  236. }
  237. else
  238. {
  239. discard_flag = FNET_TRUE;
  240. }
  241. if(discard_flag == FNET_TRUE)
  242. {
  243. DISCARD:
  244. _fnet_netbuf_free_chain(ip6_nb);
  245. _fnet_netbuf_free_chain(nb);
  246. }
  247. }
  248. /************************************************************************
  249. * DESCRIPTION: ICMPv6 output function.
  250. *************************************************************************/
  251. void _fnet_icmp6_output( struct fnet_netif *netif, const fnet_ip6_addr_t *src_ip, const fnet_ip6_addr_t *dest_ip, fnet_uint8_t hop_limit, fnet_netbuf_t *nb )
  252. {
  253. fnet_icmp6_header_t *hdr = (fnet_icmp6_header_t *)nb->data_ptr;
  254. FNET_COMP_PACKED_VAR fnet_uint16_t *checksum_p;
  255. /* Checksum calculation.*/
  256. hdr->checksum = 0u;
  257. hdr->checksum = _fnet_checksum_pseudo_netbuf_start(nb, FNET_HTONS((fnet_uint16_t)FNET_PROT_ICMP6), (fnet_uint16_t)nb->total_length);
  258. checksum_p = &hdr->checksum;
  259. _fnet_ip6_output(netif, src_ip, dest_ip, FNET_PROT_ICMP6, hop_limit, nb, checksum_p);
  260. }
  261. /************************************************************************
  262. * DESCRIPTION: Sends ICMPv6 error message.
  263. *************************************************************************/
  264. void _fnet_icmp6_error( struct fnet_netif *netif, fnet_uint8_t type, fnet_uint8_t code, fnet_uint32_t param, fnet_netbuf_t *origin_nb )
  265. {
  266. fnet_ip6_header_t *ip6_header;
  267. fnet_icmp6_err_header_t *icmp6_err_header;
  268. const fnet_ip6_addr_t *src_ip_rx;
  269. const fnet_ip6_addr_t *dest_ip_rx;
  270. const fnet_ip6_addr_t *src_ip_tx;
  271. const fnet_ip6_addr_t *dest_ip_tx;
  272. fnet_netbuf_t *nb_header;
  273. if(origin_nb)
  274. {
  275. /* Limit to FNET_IP6_DEFAULT_MTU. */
  276. if(origin_nb->total_length > (FNET_IP6_DEFAULT_MTU - (sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t)) ))
  277. {
  278. _fnet_netbuf_trim(&origin_nb, (fnet_int32_t)(FNET_IP6_DEFAULT_MTU - (sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t)) - origin_nb->total_length));
  279. }
  280. ip6_header = (fnet_ip6_header_t *)origin_nb->data_ptr;
  281. src_ip_rx = &ip6_header->source_addr;
  282. dest_ip_rx = &ip6_header->destination_addr;
  283. /* Swap source and destination addresses.*/
  284. src_ip_tx = dest_ip_rx;
  285. dest_ip_tx = src_ip_rx;
  286. /*******************************************************************
  287. * RFC 4443:
  288. * (e) An ICMPv6 error message MUST NOT be originated as a result of
  289. * receiving the following:
  290. *******************************************************************/
  291. /* (e.1) An ICMPv6 error message. */
  292. /* (e.2) An ICMPv6 REDIRECT message [IPv6-DISC].*/
  293. if (ip6_header->next_header == FNET_PROT_ICMP6) /* TBD Extension header case.*/
  294. {
  295. /* Make sure the packet has at least a 'TYPE' field */
  296. if (ip6_header->length == 0u)
  297. {
  298. goto FREE_NB;
  299. }
  300. icmp6_err_header = (fnet_icmp6_err_header_t *)((fnet_uint8_t *)ip6_header + sizeof(fnet_ip6_header_t));
  301. if (FNET_ICMP6_TYPE_IS_ERROR(icmp6_err_header->icmp6_header.type) || (icmp6_err_header->icmp6_header.type == FNET_ICMP6_TYPE_REDIRECT ) )
  302. {
  303. goto FREE_NB;
  304. }
  305. }
  306. /*
  307. * (e.3) A packet destined to an IPv6 multicast address. (There are
  308. * two exceptions to this rule: (1) the Packet Too Big Message
  309. * (Section 3.2) to allow Path MTU discovery to work for IPv6
  310. * multicast, and (2) the Parameter Problem Message, Code 2
  311. * (Section 3.4) reporting an unrecognized IPv6 option (see
  312. * Section 4.2 of [IPv6]) that has the Option Type highestorder
  313. * two bits set to 10).
  314. * (e.4) A packet sent as a link-layer multicast (the exceptions
  315. * from e.3 apply to this case, too).
  316. */
  317. if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx)
  318. && (!( (type == FNET_ICMP6_TYPE_PACKET_TOOBIG)
  319. || ((type == FNET_ICMP6_TYPE_PARAM_PROB) && (code == FNET_ICMP6_CODE_PP_OPTION)))) )
  320. {
  321. goto FREE_NB;
  322. }
  323. else
  324. {
  325. if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx))
  326. {
  327. /* We may not use multicast address as source. Get real source address. */
  328. src_ip_tx = _fnet_ip6_select_src_addr(netif, src_ip_rx /*dest*/);
  329. }
  330. }
  331. /*
  332. * (e.5) A packet sent as a link-layer broadcast (the exceptions
  333. * from e.3 apply to this case, too). TBD
  334. */
  335. /*
  336. * (e.6) A packet whose source address does not uniquely identify a
  337. * single node -- e.g., the IPv6 Unspecified Address, an IPv6
  338. * multicast address, or an address known by the ICMP message
  339. * originator to be an IPv6 anycast address.
  340. */
  341. if(FNET_IP6_ADDR_IS_MULTICAST(src_ip_rx) || FNET_IP6_ADDR_EQUAL(&fnet_ip6_addr_any, src_ip_rx))
  342. {
  343. goto FREE_NB;
  344. }
  345. /* Construct ICMPv6 error header.*/
  346. if((nb_header = _fnet_netbuf_new((sizeof(fnet_icmp6_err_header_t)), FNET_FALSE)) == 0)
  347. {
  348. goto FREE_NB;
  349. }
  350. icmp6_err_header = (fnet_icmp6_err_header_t *)nb_header->data_ptr;
  351. icmp6_err_header->icmp6_header.type = type;
  352. icmp6_err_header->icmp6_header.code = code;
  353. icmp6_err_header->data = fnet_htonl(param);
  354. origin_nb = _fnet_netbuf_concat(nb_header, origin_nb);
  355. /* Swap source and destination addresses.*/
  356. _fnet_icmp6_output( netif, src_ip_tx, dest_ip_tx, 0u, origin_nb);
  357. return;
  358. FREE_NB:
  359. _fnet_netbuf_free_chain(origin_nb);
  360. }
  361. }
  362. #endif /* FNET_CFG_IP6 */