|
- /**************************************************************************
- *
- * Copyright 2008-2018 by Andrey Butok. FNET Community
- *
- ***************************************************************************
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ***************************************************************************
- *
- * ICMPv6 protocol implementation.
- *
- ***************************************************************************/
- #include "fnet.h"
-
- #if FNET_CFG_IP6
-
- #include "fnet_icmp6.h"
- #include "fnet_loop.h"
- #include "fnet_timer.h"
- #include "fnet_prot.h"
- #include "fnet_socket.h"
- #include "fnet_socket_prv.h"
- #include "fnet_checksum_prv.h"
- #include "fnet_mld.h"
-
- /************************************************************************
- * Function Prototypes
- *************************************************************************/
- 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);
-
- /************************************************************************
- * Protocol API structure.
- ************************************************************************/
- fnet_prot_if_t fnet_icmp6_prot_if =
- {
- 0, /* Pointer to the head of the protocol's socket list.*/
- AF_INET6, /* Address domain family.*/
- SOCK_UNSPEC, /* Socket type used for.*/
- FNET_PROT_ICMP6, /* Protocol number.*/
- 0, /* Protocol initialization function.*/
- 0, /* Protocol release function.*/
- _fnet_icmp6_input, /* Protocol input function,.*/
- 0, /* Protocol input control function.*/
- 0, /* protocol drain function.*/
- 0 /* Socket API */
- };
-
- /************************************************************************
- * DESCRIPTION: ICMPv6 input function.
- *************************************************************************/
- 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)
- {
- fnet_icmp6_header_t *hdr;
- fnet_uint16_t sum;
- fnet_ip6_addr_t *src_ip_rx;
- fnet_ip6_addr_t *dest_ip_rx;
- fnet_ip6_addr_t *src_ip_tx;
- fnet_ip6_addr_t *dest_ip_tx;
- fnet_prot_notify_t prot_cmd;
- fnet_bool_t discard_flag = FNET_FALSE;
-
- if((netif != 0) && (nb != 0))
- {
- /* The header must reside in contiguous area of memory. */
- if(_fnet_netbuf_pullup(&nb, sizeof(fnet_icmp6_header_t)) == FNET_ERR)
- {
- goto DISCARD;
- }
-
- hdr = (fnet_icmp6_header_t *)nb->data_ptr;
-
- dest_ip_rx = &((struct fnet_sockaddr_in6 *)(dest_addr))->sin6_addr.s6_addr;
- src_ip_rx = &((struct fnet_sockaddr_in6 *)(src_addr))->sin6_addr.s6_addr;
-
- /* Swap source and destination addresses.*/
- dest_ip_tx = src_ip_rx;
- src_ip_tx = dest_ip_rx;
-
- /* Drop Multicast loopback.*/
- #if FNET_CFG_LOOPBACK
- if ((netif == FNET_LOOP_IF) && FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx))
- {
- goto DISCARD;
- }
- #endif /* FNET_CFG_LOOPBACK */
-
- /* Verify the checksum. */
- sum = _fnet_checksum_pseudo_netbuf_start( nb, FNET_HTONS((fnet_uint16_t)FNET_PROT_ICMP6), (fnet_uint16_t)nb->total_length );
- sum = _fnet_checksum_pseudo_netbuf_end( sum, (fnet_uint8_t *)src_ip_rx, (fnet_uint8_t *)dest_ip_rx, sizeof(fnet_ip6_addr_t) );
- if(sum)
- {
- goto DISCARD;
- }
-
- /************************************************************
- * Process incoming ICMPv6 packets.
- *************************************************************/
- switch (hdr->type)
- {
- /**************************
- * Neighbor Solicitation.
- **************************/
- case FNET_ICMP6_TYPE_NEIGHBOR_SOLICITATION:
- _fnet_nd6_neighbor_solicitation_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
- break;
- /**************************
- * Neighbor Advertisemnt.
- **************************/
- case FNET_ICMP6_TYPE_NEIGHBOR_ADVERTISEMENT:
- _fnet_nd6_neighbor_advertisement_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
- break;
- /**************************
- * Router Advertisemnt.
- **************************/
- case FNET_ICMP6_TYPE_ROUTER_ADVERTISEMENT:
- _fnet_nd6_router_advertisement_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
- break;
- /**************************
- * Router Advertisemnt.
- **************************/
- case FNET_ICMP6_TYPE_REDIRECT:
- _fnet_nd6_redirect_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
- break;
- #if FNET_CFG_MLD
- /**************************
- * Multicast Listener Query.
- **************************/
- case FNET_ICMP6_TYPE_MULTICAST_LISTENER_QUERY:
- _fnet_mld_query_receive(netif, src_ip_rx, dest_ip_rx, nb, ip6_nb);
- break;
- #endif
- /**************************
- * Echo Request.
- * RFC4443 4.1: Every node MUST implement an ICMPv6 Echo responder function that
- * receives Echo Requests and originates corresponding Echo Replies.
- **************************/
- case FNET_ICMP6_TYPE_ECHO_REQ:
- hdr->type = FNET_ICMP6_TYPE_ECHO_REPLY;
-
- /* RFC4443: the source address of the reply MUST be a unicast
- * address belonging to the interface on which
- * the Echo Request message was received.*/
- if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx))
- {
- src_ip_tx = FNET_NULL;
- }
-
- /* Swap source and destination addresses.*/
- _fnet_icmp6_output(netif, src_ip_tx, dest_ip_tx, 0u, nb);
- _fnet_netbuf_free_chain(ip6_nb);
- break;
- #if FNET_CFG_IP6_PMTU_DISCOVERY
- /**************************
- * Packet Too Big Message.
- **************************/
- case FNET_ICMP6_TYPE_PACKET_TOOBIG:
- if(netif->pmtu) /* If PMTU is enabled for the interface.*/
- {
- fnet_uint32_t pmtu;
- fnet_icmp6_err_header_t *icmp6_err = (fnet_icmp6_err_header_t *)nb->data_ptr;
-
- /* The header must reside in contiguous area of memory. */
- if(_fnet_netbuf_pullup(&nb, sizeof(fnet_icmp6_err_header_t)) == FNET_ERR)
- {
- goto DISCARD;
- }
-
- /* RFC 1981.Upon receipt of such a
- * message, the source node reduces its assumed PMTU for the path based
- * on the MTU of the constricting hop as reported in the Packet Too Big
- * message.*/
- pmtu = fnet_ntohl(icmp6_err->data);
-
- /* A node MUST NOT increase its estimate of the Path MTU in response to
- * the contents of a Packet Too Big message. */
- if(netif->pmtu > pmtu)
- {
- _fnet_netif_set_pmtu(netif, pmtu);
- }
- }
-
- discard_flag = FNET_TRUE;
- break;
- #endif
- /**************************
- * Destination Unreachable.
- **************************/
- case FNET_ICMP6_TYPE_DEST_UNREACH:
- switch(hdr->code)
- {
- case FNET_ICMP6_CODE_DU_NO_ROUTE: /* No route to destination. */
- case FNET_ICMP6_CODE_DU_BEYOND_SCOPE: /* Beyond scope of source address.*/
- prot_cmd = FNET_PROT_NOTIFY_UNREACH_NET;
- break;
- case FNET_ICMP6_CODE_DU_ADMIN_PROHIBITED: /* Communication with destination administratively prohibited. */
- case FNET_ICMP6_CODE_DU_ADDR_UNREACH: /* Address unreachable.*/
- prot_cmd = FNET_PROT_NOTIFY_UNREACH_HOST;
- break;
- case FNET_ICMP6_CODE_DU_PORT_UNREACH: /* Port unreachable.*/
- prot_cmd = FNET_PROT_NOTIFY_UNREACH_PORT;
- break;
- default:
- goto DISCARD;
- }
-
- /* Protocol notification.*/
- {
- fnet_ip6_header_t *ip_header;
- fnet_prot_if_t *protocol;
- fnet_size_t hdr_err_length = sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t);
- fnet_size_t hdr_err_data_length = hdr_err_length + 8u; /* 8 bytes is enough for transport protocol (port numbers).*/
-
-
- if(nb->total_length < hdr_err_data_length)
- {
- goto DISCARD;
- }
- if(nb->total_length > hdr_err_data_length)
- {
- _fnet_netbuf_trim(&nb, (fnet_int32_t)(hdr_err_data_length - nb->total_length));
- }
- if(_fnet_netbuf_pullup(&nb, nb->total_length) == FNET_ERR) /* The header must reside in contiguous area of memory.*/
- {
- goto DISCARD;
- }
-
- ip_header = (fnet_ip6_header_t *)((fnet_uint8_t *)nb->data_ptr + sizeof(fnet_icmp6_err_header_t));
-
- if((protocol = _fnet_prot_find(AF_INET6, SOCK_UNSPEC, (fnet_uint32_t)ip_header->next_header)) != 0)
- {
- if(protocol->prot_control_input)
- {
- struct fnet_sockaddr err_src_addr;
- struct fnet_sockaddr err_dest_addr;
-
- /* Prepare addreses for upper protocol.*/
- _fnet_ip6_set_socket_addr(netif, ip_header, &err_src_addr, &err_dest_addr );
-
- _fnet_netbuf_trim(&nb, (fnet_int32_t)(hdr_err_length)); /* Cut the ICMP error header.*/
-
- protocol->prot_control_input(prot_cmd, &err_src_addr, &err_dest_addr, nb);
- }
- }
- }
- discard_flag = FNET_TRUE;
- break;
- default:
- discard_flag = FNET_TRUE;
- break;
- }
- }
- else
- {
- discard_flag = FNET_TRUE;
- }
-
- if(discard_flag == FNET_TRUE)
- {
- DISCARD:
- _fnet_netbuf_free_chain(ip6_nb);
- _fnet_netbuf_free_chain(nb);
- }
- }
-
- /************************************************************************
- * DESCRIPTION: ICMPv6 output function.
- *************************************************************************/
- 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 )
- {
- fnet_icmp6_header_t *hdr = (fnet_icmp6_header_t *)nb->data_ptr;
- FNET_COMP_PACKED_VAR fnet_uint16_t *checksum_p;
-
- /* Checksum calculation.*/
- hdr->checksum = 0u;
-
- hdr->checksum = _fnet_checksum_pseudo_netbuf_start(nb, FNET_HTONS((fnet_uint16_t)FNET_PROT_ICMP6), (fnet_uint16_t)nb->total_length);
- checksum_p = &hdr->checksum;
-
- _fnet_ip6_output(netif, src_ip, dest_ip, FNET_PROT_ICMP6, hop_limit, nb, checksum_p);
- }
-
- /************************************************************************
- * DESCRIPTION: Sends ICMPv6 error message.
- *************************************************************************/
- 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 )
- {
- fnet_ip6_header_t *ip6_header;
- fnet_icmp6_err_header_t *icmp6_err_header;
- const fnet_ip6_addr_t *src_ip_rx;
- const fnet_ip6_addr_t *dest_ip_rx;
- const fnet_ip6_addr_t *src_ip_tx;
- const fnet_ip6_addr_t *dest_ip_tx;
- fnet_netbuf_t *nb_header;
-
- if(origin_nb)
- {
- /* Limit to FNET_IP6_DEFAULT_MTU. */
- if(origin_nb->total_length > (FNET_IP6_DEFAULT_MTU - (sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t)) ))
- {
- _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));
- }
-
- ip6_header = (fnet_ip6_header_t *)origin_nb->data_ptr;
-
- src_ip_rx = &ip6_header->source_addr;
- dest_ip_rx = &ip6_header->destination_addr;
-
- /* Swap source and destination addresses.*/
- src_ip_tx = dest_ip_rx;
- dest_ip_tx = src_ip_rx;
-
- /*******************************************************************
- * RFC 4443:
- * (e) An ICMPv6 error message MUST NOT be originated as a result of
- * receiving the following:
- *******************************************************************/
- /* (e.1) An ICMPv6 error message. */
- /* (e.2) An ICMPv6 REDIRECT message [IPv6-DISC].*/
- if (ip6_header->next_header == FNET_PROT_ICMP6) /* TBD Extension header case.*/
- {
- /* Make sure the packet has at least a 'TYPE' field */
- if (ip6_header->length == 0u)
- {
- goto FREE_NB;
- }
-
- icmp6_err_header = (fnet_icmp6_err_header_t *)((fnet_uint8_t *)ip6_header + sizeof(fnet_ip6_header_t));
- if (FNET_ICMP6_TYPE_IS_ERROR(icmp6_err_header->icmp6_header.type) || (icmp6_err_header->icmp6_header.type == FNET_ICMP6_TYPE_REDIRECT ) )
- {
- goto FREE_NB;
- }
- }
-
- /*
- * (e.3) A packet destined to an IPv6 multicast address. (There are
- * two exceptions to this rule: (1) the Packet Too Big Message
- * (Section 3.2) to allow Path MTU discovery to work for IPv6
- * multicast, and (2) the Parameter Problem Message, Code 2
- * (Section 3.4) reporting an unrecognized IPv6 option (see
- * Section 4.2 of [IPv6]) that has the Option Type highestorder
- * two bits set to 10).
- * (e.4) A packet sent as a link-layer multicast (the exceptions
- * from e.3 apply to this case, too).
- */
- if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx)
- && (!( (type == FNET_ICMP6_TYPE_PACKET_TOOBIG)
- || ((type == FNET_ICMP6_TYPE_PARAM_PROB) && (code == FNET_ICMP6_CODE_PP_OPTION)))) )
- {
- goto FREE_NB;
- }
- else
- {
- if(FNET_IP6_ADDR_IS_MULTICAST(dest_ip_rx))
- {
- /* We may not use multicast address as source. Get real source address. */
- src_ip_tx = _fnet_ip6_select_src_addr(netif, src_ip_rx /*dest*/);
- }
- }
-
- /*
- * (e.5) A packet sent as a link-layer broadcast (the exceptions
- * from e.3 apply to this case, too). TBD
- */
-
-
- /*
- * (e.6) A packet whose source address does not uniquely identify a
- * single node -- e.g., the IPv6 Unspecified Address, an IPv6
- * multicast address, or an address known by the ICMP message
- * originator to be an IPv6 anycast address.
- */
- if(FNET_IP6_ADDR_IS_MULTICAST(src_ip_rx) || FNET_IP6_ADDR_EQUAL(&fnet_ip6_addr_any, src_ip_rx))
- {
- goto FREE_NB;
- }
-
- /* Construct ICMPv6 error header.*/
- if((nb_header = _fnet_netbuf_new((sizeof(fnet_icmp6_err_header_t)), FNET_FALSE)) == 0)
- {
- goto FREE_NB;
- }
-
- icmp6_err_header = (fnet_icmp6_err_header_t *)nb_header->data_ptr;
-
- icmp6_err_header->icmp6_header.type = type;
- icmp6_err_header->icmp6_header.code = code;
- icmp6_err_header->data = fnet_htonl(param);
-
- origin_nb = _fnet_netbuf_concat(nb_header, origin_nb);
-
- /* Swap source and destination addresses.*/
- _fnet_icmp6_output( netif, src_ip_tx, dest_ip_tx, 0u, origin_nb);
-
- return;
-
- FREE_NB:
- _fnet_netbuf_free_chain(origin_nb);
- }
-
- }
- #endif /* FNET_CFG_IP6 */
|