/* * Copyright (C) 2015 Martine Lenders * * This file is subject to the terms and conditions of the GNU Lesser General * Public License v2.1. See the file LICENSE in the top level directory for * more details. */ /** * @ingroup net_gnrc_icmpv6 * @{ * * @file * * @author Martine Lenders */ #include #include #include #include "byteorder.h" #include "kernel_types.h" #include "net/ipv6/hdr.h" #include "net/gnrc.h" #include "net/gnrc/ndp.h" #include "net/protnum.h" #include "od.h" #include "utlist.h" #include "net/gnrc/icmpv6.h" #include "net/gnrc/icmpv6/echo.h" #define ENABLE_DEBUG (0) #include "debug.h" static inline uint16_t _calc_csum(gnrc_pktsnip_t *hdr, gnrc_pktsnip_t *pseudo_hdr, gnrc_pktsnip_t *payload) { uint16_t csum = 0; uint16_t len = (uint16_t)hdr->size; while (payload && (payload != hdr)) { csum = inet_csum_slice(csum, payload->data, payload->size, len); len += (uint16_t)payload->size; payload = payload->next; } csum = inet_csum(csum, hdr->data, hdr->size); csum = ipv6_hdr_inet_csum(csum, pseudo_hdr->data, PROTNUM_ICMPV6, len); return ~csum; } void gnrc_icmpv6_demux(kernel_pid_t iface, gnrc_pktsnip_t *pkt) { gnrc_pktsnip_t *icmpv6, *ipv6; icmpv6_hdr_t *hdr; icmpv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_ICMPV6); assert(icmpv6 != NULL); /* there can be extension headers between IPv6 and ICMPv6 header so we have * to search it */ ipv6 = gnrc_pktsnip_search_type(icmpv6, GNRC_NETTYPE_IPV6); assert(ipv6 != NULL); if (icmpv6->size < sizeof(icmpv6_hdr_t)) { DEBUG("icmpv6: packet too short.\n"); return; } /* Note: size will be checked again in gnrc_icmpv6_echo_req_handle, gnrc_ndp_rtr_sol_handle, and others */ hdr = (icmpv6_hdr_t *)icmpv6->data; if (_calc_csum(icmpv6, ipv6, pkt)) { DEBUG("icmpv6: wrong checksum.\n"); /* don't release: IPv6 does this */ return; } switch (hdr->type) { /* TODO: handle ICMPv6 errors */ #ifdef MODULE_GNRC_ICMPV6_ECHO case ICMPV6_ECHO_REQ: DEBUG("icmpv6: handle echo request.\n"); gnrc_icmpv6_echo_req_handle(iface, (ipv6_hdr_t *)ipv6->data, (icmpv6_echo_t *)hdr, icmpv6->size); break; #endif #if (defined(MODULE_GNRC_NDP_ROUTER) || defined(MODULE_GNRC_SIXLOWPAN_ND_ROUTER)) case ICMPV6_RTR_SOL: DEBUG("icmpv6: router solicitation received\n"); gnrc_ndp_rtr_sol_handle(iface, pkt, ipv6->data, (ndp_rtr_sol_t *)hdr, icmpv6->size); break; #endif #ifdef MODULE_GNRC_NDP case ICMPV6_RTR_ADV: DEBUG("icmpv6: router advertisement received\n"); gnrc_ndp_rtr_adv_handle(iface, pkt, ipv6->data, (ndp_rtr_adv_t *)hdr, icmpv6->size); break; case ICMPV6_NBR_SOL: DEBUG("icmpv6: neighbor solicitation received\n"); gnrc_ndp_nbr_sol_handle(iface, pkt, ipv6->data, (ndp_nbr_sol_t *)hdr, icmpv6->size); break; case ICMPV6_NBR_ADV: DEBUG("icmpv6: neighbor advertisement received\n"); gnrc_ndp_nbr_adv_handle(iface, pkt, ipv6->data, (ndp_nbr_adv_t *)hdr, icmpv6->size); break; #endif case ICMPV6_REDIRECT: DEBUG("icmpv6: redirect message received\n"); /* TODO */ break; default: DEBUG("icmpv6: unknown type field %u\n", hdr->type); break; } /* ICMPv6-all will be send in gnrc_ipv6.c so only dispatch of subtypes is * needed */ if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_ICMPV6, hdr->type, pkt)) { DEBUG("icmpv6: no one interested in type %d\n", hdr->type); gnrc_pktbuf_release(pkt); } } gnrc_pktsnip_t *gnrc_icmpv6_build(gnrc_pktsnip_t *next, uint8_t type, uint8_t code, size_t size) { gnrc_pktsnip_t *pkt; icmpv6_hdr_t *icmpv6; pkt = gnrc_pktbuf_add(next, NULL, size, GNRC_NETTYPE_ICMPV6); if (pkt == NULL) { DEBUG("icmpv6_echo: no space left in packet buffer\n"); return NULL; } DEBUG("icmpv6: Building ICMPv6 message with type=%u, code=%u\n", type, code); icmpv6 = (icmpv6_hdr_t *)pkt->data; icmpv6->type = type; icmpv6->code = code; icmpv6->csum.u16 = 0; return pkt; } int gnrc_icmpv6_calc_csum(gnrc_pktsnip_t *hdr, gnrc_pktsnip_t *pseudo_hdr) { uint32_t csum = 0; if (hdr == NULL) { return -EFAULT; } if (hdr->type != GNRC_NETTYPE_ICMPV6) { return -EBADMSG; } csum = _calc_csum(hdr, pseudo_hdr, hdr->next); if (csum == 0) { return -ENOENT; } ((icmpv6_hdr_t *)hdr->data)->csum = byteorder_htons(csum); return 0; } /** * @} */