/************************************************************************ * NAME: fnet_icmp6_error * * DESCRIPTION: Sends ICMPv6 error message. *************************************************************************/ void fnet_icmp6_error( fnet_netif_t *netif, unsigned char type, unsigned char code, unsigned long param, fnet_netbuf_t *origin_nb ) { fnet_ip6_header_t *ip6_header; fnet_icmp6_err_header_t *icmp6_err_header; fnet_ip6_addr_t *src_ip; const fnet_ip6_addr_t *dest_ip; 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, (int)(FNET_IP6_DEFAULT_MTU - (sizeof(fnet_icmp6_err_header_t) + sizeof(fnet_ip6_header_t)) - origin_nb->total_length)); ip6_header = origin_nb->data_ptr; src_ip = &ip6_header->source_addr; dest_ip = &ip6_header->destination_addr; /******************************************************************* * 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_IP_PROTOCOL_ICMP6) /* TBD Extension header case.*/ { /* Make sure the packet has at least a 'TYPE' field */ if (ip6_header->length == 0) { goto FREE_NB; } icmp6_err_header = (fnet_icmp6_err_header_t *)((unsigned char *)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) && !( (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)) { /* We may not use multicast address as source. Get real source address. */ dest_ip = fnet_ip6_select_src_addr(netif, src_ip /*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) || FNET_IP6_ADDR_EQUAL(&fnet_ip6_addr_any, src_ip)) { 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 = 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); fnet_icmp6_output( netif, dest_ip/*ipsrc*/, src_ip/*ipdest*/, 0, origin_nb); return; FREE_NB: fnet_netbuf_free_chain(origin_nb); } }
/************************************************************************ * NAME: fnet_ping_state_machine * * DESCRIPTION: PING service state machine. ************************************************************************/ static void fnet_ping_state_machine(void *fnet_ping_if_p) { fnet_int32_t received; fnet_icmp_echo_header_t *hdr; fnet_ping_if_t *ping_if = (fnet_ping_if_t *)fnet_ping_if_p; struct sockaddr addr; fnet_size_t addr_len = sizeof(addr); switch(ping_if->state) { /*===================================*/ case FNET_PING_STATE_SENDING_REQUEST: /* Build message.*/ hdr = (fnet_icmp_echo_header_t *)&fnet_ping_if.buffer[0]; /* Fill ICMP Echo request header.*/ fnet_memset_zero(hdr, sizeof(*hdr)); hdr->header.type = (fnet_uint8_t)((fnet_ping_if.family == AF_INET) ? FNET_ICMP_ECHO: FNET_ICMP6_TYPE_ECHO_REQ); hdr->identifier = FNET_CFG_PING_IDENTIFIER; fnet_ping_if.sequence_number++; hdr->sequence_number = fnet_htons(fnet_ping_if.sequence_number); /* Fill payload data by pattern.*/ fnet_memset(&fnet_ping_if.buffer[sizeof(*hdr)], ping_if->pattern, ping_if->packet_size); /* Checksum.*/ #if FNET_CFG_IP4 if(ping_if->family == AF_INET) { hdr->header.checksum = fnet_checksum_buf(&fnet_ping_if.buffer[0], (sizeof(*hdr) + ping_if->packet_size)); } else #endif #if FNET_CFG_IP6 if(ping_if->family == AF_INET6) { const fnet_ip6_addr_t *src_ip = fnet_ip6_select_src_addr(FNET_NULL, (fnet_ip6_addr_t *)ping_if->target_addr.sa_data); /*TBD Check result.*/ hdr->header.checksum = fnet_checksum_pseudo_buf(&fnet_ping_if.buffer[0], (fnet_uint16_t)(sizeof(*hdr) + ping_if->packet_size), FNET_HTONS((fnet_uint16_t)IPPROTO_ICMPV6), (const fnet_uint8_t *)src_ip, ping_if->target_addr.sa_data, sizeof(fnet_ip6_addr_t)); } else #endif {} /* Send request.*/ fnet_socket_sendto(fnet_ping_if.socket_foreign, (fnet_uint8_t*)(&fnet_ping_if.buffer[0]), (sizeof(*hdr) + ping_if->packet_size), 0u, &ping_if->target_addr, sizeof(ping_if->target_addr)); ping_if->packet_count--; fnet_ping_if.send_time = fnet_timer_ticks(); ping_if->state = FNET_PING_STATE_WAITING_REPLY; break; /*===================================*/ case FNET_PING_STATE_WAITING_REPLY: /* Receive data */ received = fnet_socket_recvfrom(ping_if->socket_foreign, (fnet_uint8_t*)(&ping_if->buffer[0]), FNET_PING_BUFFER_SIZE, 0u, &addr, &addr_len ); if(received > 0 ) { fnet_uint16_t checksum = 0u; hdr = (fnet_icmp_echo_header_t *)(ping_if->buffer); /* Check checksum.*/ #if FNET_CFG_IP4 if(ping_if->family == AF_INET) { checksum = fnet_checksum_buf(&fnet_ping_if.buffer[0], (fnet_size_t)received); } else #endif #if 0 /* #if FNET_CFG_IP6 */ /* TBD case to receive from multicast address ff02::1*/ if(ping_if->family == AF_INET6) { checksum = fnet_checksum_pseudo_buf(&fnet_ping_if.buffer[0], (fnet_uint16_t)(received), IPPROTO_ICMPV6, ping_if->local_addr.sa_data, ping_if->target_addr.sa_data, sizeof(fnet_ip6_addr_t)); } else #endif {} /* Check header.*/ if( checksum ||(hdr->header.type != ((addr.sa_family == AF_INET) ? FNET_ICMP_ECHOREPLY: FNET_ICMP6_TYPE_ECHO_REPLY)) ||(hdr->identifier != FNET_CFG_PING_IDENTIFIER) ||(hdr->sequence_number != fnet_htons(ping_if->sequence_number)) ) { goto NO_DATA; } /* Call handler.*/ if(ping_if->handler) { ping_if->handler(FNET_ERR_OK, ping_if->packet_count, &addr, ping_if->handler_cookie); } if(ping_if->packet_count) { ping_if->state = FNET_PING_STATE_WAITING_TIMEOUT; } else { fnet_ping_release(); } } else if(received == FNET_ERR) { /* Call handler.*/ if(ping_if->handler) { fnet_error_t sock_err ; fnet_size_t option_len; /* Get socket error.*/ option_len = sizeof(sock_err); fnet_socket_getopt(ping_if->socket_foreign, SOL_SOCKET, SO_ERROR, (fnet_uint8_t*)&sock_err, &option_len); ping_if->handler(sock_err, ping_if->packet_count, FNET_NULL, ping_if->handler_cookie); } if(ping_if->packet_count) { ping_if->state = FNET_PING_STATE_WAITING_TIMEOUT; } else { fnet_ping_release(); } } else /* No data. Check timeout */ { NO_DATA: if(fnet_timer_get_interval(fnet_ping_if.send_time, fnet_timer_ticks()) > fnet_ping_if.timeout_clk) { /* Call handler.*/ if(ping_if->handler) { ping_if->handler(FNET_ERR_TIMEDOUT, ping_if->packet_count, FNET_NULL, ping_if->handler_cookie); } if(ping_if->packet_count) { ping_if->state = FNET_PING_STATE_SENDING_REQUEST; } else { fnet_ping_release(); } } } break; /*===================================*/ case FNET_PING_STATE_WAITING_TIMEOUT: if(fnet_timer_get_interval(fnet_ping_if.send_time, fnet_timer_ticks()) > fnet_ping_if.timeout_clk) { ping_if->state = FNET_PING_STATE_SENDING_REQUEST; } break; default: break; /* do nothing, avoid compiler warning "enumeration value not handled in switch" */ } }