2016-05-19 11:42:01 +02:00
|
|
|
/** @file
|
|
|
|
* @brief ICMPv6 related functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation
|
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-05-19 11:42:01 +02:00
|
|
|
*/
|
|
|
|
|
2018-11-30 11:54:56 +01:00
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(net_icmpv6, CONFIG_NET_ICMPV6_LOG_LEVEL);
|
2016-05-19 11:42:01 +02:00
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <misc/slist.h>
|
2016-10-24 12:17:31 +02:00
|
|
|
#include <misc/byteorder.h>
|
2016-05-19 11:42:01 +02:00
|
|
|
#include <net/net_core.h>
|
2017-04-03 17:14:35 +02:00
|
|
|
#include <net/net_pkt.h>
|
2016-05-19 11:42:01 +02:00
|
|
|
#include <net/net_if.h>
|
|
|
|
#include "net_private.h"
|
|
|
|
#include "icmpv6.h"
|
2016-11-03 13:44:44 +01:00
|
|
|
#include "ipv6.h"
|
2016-12-13 14:50:31 +01:00
|
|
|
#include "net_stats.h"
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2017-03-03 15:42:29 +01:00
|
|
|
#if defined(CONFIG_NET_RPL)
|
|
|
|
#include "rpl.h"
|
|
|
|
#endif
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
#define PKT_WAIT_TIME K_SECONDS(1)
|
2017-03-15 09:15:45 +01:00
|
|
|
|
2016-05-19 11:42:01 +02:00
|
|
|
static sys_slist_t handlers;
|
|
|
|
|
2017-03-29 14:53:59 +02:00
|
|
|
const char *net_icmpv6_type2str(int icmpv6_type)
|
|
|
|
{
|
|
|
|
switch (icmpv6_type) {
|
|
|
|
case NET_ICMPV6_DST_UNREACH:
|
|
|
|
return "Destination Unreachable";
|
|
|
|
case NET_ICMPV6_PACKET_TOO_BIG:
|
|
|
|
return "Packet Too Big";
|
|
|
|
case NET_ICMPV6_TIME_EXCEEDED:
|
|
|
|
return "Time Exceeded";
|
|
|
|
case NET_ICMPV6_PARAM_PROBLEM:
|
|
|
|
return "IPv6 Bad Header";
|
|
|
|
case NET_ICMPV6_ECHO_REQUEST:
|
|
|
|
return "Echo Request";
|
|
|
|
case NET_ICMPV6_ECHO_REPLY:
|
|
|
|
return "Echo Reply";
|
|
|
|
case NET_ICMPV6_MLD_QUERY:
|
|
|
|
return "Multicast Listener Query";
|
|
|
|
case NET_ICMPV6_RS:
|
|
|
|
return "Router Solicitation";
|
|
|
|
case NET_ICMPV6_RA:
|
|
|
|
return "Router Advertisement";
|
|
|
|
case NET_ICMPV6_NS:
|
|
|
|
return "Neighbor Solicitation";
|
|
|
|
case NET_ICMPV6_NA:
|
|
|
|
return "Neighbor Advertisement";
|
|
|
|
case NET_ICMPV6_MLDv2:
|
|
|
|
return "Multicast Listener Report v2";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "?";
|
|
|
|
}
|
|
|
|
|
2016-05-19 11:42:01 +02:00
|
|
|
void net_icmpv6_register_handler(struct net_icmpv6_handler *handler)
|
|
|
|
{
|
|
|
|
sys_slist_prepend(&handlers, &handler->node);
|
|
|
|
}
|
|
|
|
|
2017-03-03 13:15:43 +01:00
|
|
|
void net_icmpv6_unregister_handler(struct net_icmpv6_handler *handler)
|
|
|
|
{
|
|
|
|
sys_slist_find_and_remove(&handlers, &handler->node);
|
|
|
|
}
|
|
|
|
|
2017-04-21 16:27:50 +02:00
|
|
|
static inline void setup_ipv6_header(struct net_pkt *pkt, u16_t extra_len,
|
|
|
|
u8_t hop_limit, u8_t icmp_type,
|
|
|
|
u8_t icmp_code)
|
2016-05-19 11:42:01 +02:00
|
|
|
{
|
2017-06-28 14:34:02 +02:00
|
|
|
struct net_buf *frag = pkt->frags;
|
2018-11-29 20:23:03 +01:00
|
|
|
const u32_t unused = 0U;
|
2017-06-28 14:34:02 +02:00
|
|
|
u16_t pos;
|
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
NET_IPV6_HDR(pkt)->vtc = 0x60;
|
|
|
|
NET_IPV6_HDR(pkt)->tcflow = 0;
|
|
|
|
NET_IPV6_HDR(pkt)->flow = 0;
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2018-08-13 08:57:00 +02:00
|
|
|
NET_IPV6_HDR(pkt)->len = htons(NET_ICMPH_LEN + extra_len +
|
|
|
|
NET_ICMPV6_UNUSED_LEN);
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
NET_IPV6_HDR(pkt)->nexthdr = IPPROTO_ICMPV6;
|
|
|
|
NET_IPV6_HDR(pkt)->hop_limit = hop_limit;
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2017-06-28 14:34:02 +02:00
|
|
|
frag = net_pkt_write(pkt, frag, net_pkt_ip_hdr_len(pkt), &pos,
|
|
|
|
sizeof(icmp_type), &icmp_type, PKT_WAIT_TIME);
|
|
|
|
frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(icmp_code),
|
|
|
|
&icmp_code, PKT_WAIT_TIME);
|
2016-11-24 14:25:50 +01:00
|
|
|
|
|
|
|
/* ICMPv6 header has 4 unused bytes that must be zero, RFC 4443 ch 3.1
|
2016-05-19 11:42:01 +02:00
|
|
|
*/
|
2017-07-10 15:24:52 +02:00
|
|
|
net_pkt_write(pkt, frag, pos, &pos, 4, (u8_t *)&unused, PKT_WAIT_TIME);
|
2016-11-24 14:25:50 +01:00
|
|
|
}
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_set_chksum(struct net_pkt *pkt)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
2018-11-29 20:23:03 +01:00
|
|
|
u16_t chksum = 0U;
|
2018-08-09 10:12:09 +02:00
|
|
|
struct net_buf *frag;
|
|
|
|
struct net_buf *temp_frag;
|
|
|
|
u16_t temp_pos;
|
2017-06-28 14:34:02 +02:00
|
|
|
u16_t pos;
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
/* Skip to the position of checksum */
|
|
|
|
frag = net_frag_skip(pkt->frags, 0, &pos,
|
|
|
|
net_pkt_ip_hdr_len(pkt) +
|
2017-06-28 14:34:02 +02:00
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
2018-08-09 10:12:09 +02:00
|
|
|
1 + 1 /* type + code */);
|
|
|
|
if (pos > 0 && !frag) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-06-28 14:34:02 +02:00
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
/* Cache checksum fragment and postion, to be safe side first
|
|
|
|
* write 0's in checksum position and calculate checksum and
|
|
|
|
* write checksum in the packet.
|
|
|
|
*/
|
|
|
|
temp_frag = frag;
|
|
|
|
temp_pos = pos;
|
2017-06-28 14:34:02 +02:00
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
frag = net_pkt_write(pkt, frag, pos, &pos, sizeof(chksum),
|
2017-06-28 14:34:02 +02:00
|
|
|
(u8_t *)&chksum, PKT_WAIT_TIME);
|
2018-08-09 10:12:09 +02:00
|
|
|
if (pos > 0 && !frag) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-06-28 14:34:02 +02:00
|
|
|
|
2018-12-04 09:55:31 +01:00
|
|
|
chksum = net_calc_chksum_icmpv6(pkt);
|
2017-06-28 14:34:02 +02:00
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
temp_frag = net_pkt_write(pkt, temp_frag, temp_pos, &temp_pos,
|
|
|
|
sizeof(chksum), (u8_t *)&chksum,
|
|
|
|
PKT_WAIT_TIME);
|
|
|
|
if (temp_pos > 0 && !temp_frag) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_get_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
frag = net_frag_read(pkt->frags,
|
|
|
|
net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt),
|
|
|
|
&pos, sizeof(*hdr), (u8_t *)hdr);
|
2018-05-17 15:00:54 +02:00
|
|
|
if (pos > 0 && !frag) {
|
2018-08-09 10:12:09 +02:00
|
|
|
NET_ERR("Cannot get the ICMPv6 header");;
|
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_set_hdr(struct net_pkt *pkt, struct net_icmp_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
|
|
|
frag = net_pkt_write(pkt, pkt->frags, net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt), &pos,
|
2018-08-09 10:12:09 +02:00
|
|
|
sizeof(*hdr), (u8_t *)hdr, PKT_WAIT_TIME);
|
|
|
|
if (pos > 0 && !frag) {
|
2018-05-18 19:08:28 +02:00
|
|
|
NET_ERR("Cannot set the ICMPv6 header");
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_get_ns_hdr(struct net_pkt *pkt, struct net_icmpv6_ns_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
|
|
|
frag = net_frag_read(pkt->frags,
|
|
|
|
net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
2018-08-09 10:12:09 +02:00
|
|
|
sizeof(struct net_icmp_hdr),
|
|
|
|
&pos, sizeof(*hdr), (u8_t *)hdr);
|
2018-05-17 15:00:54 +02:00
|
|
|
if (pos > 0 && !frag) {
|
2018-05-18 19:08:28 +02:00
|
|
|
NET_ERR("Cannot get the ICMPv6 NS header");;
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_set_ns_hdr(struct net_pkt *pkt, struct net_icmpv6_ns_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
2018-11-29 20:23:03 +01:00
|
|
|
hdr->reserved = 0U;
|
2017-06-28 14:34:02 +02:00
|
|
|
|
|
|
|
frag = net_pkt_write(pkt, pkt->frags, net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
|
|
|
sizeof(struct net_icmp_hdr), &pos,
|
2018-08-09 10:12:09 +02:00
|
|
|
sizeof(*hdr), (u8_t *)hdr, PKT_WAIT_TIME);
|
|
|
|
if (pos > 0 && !frag) {
|
2018-05-18 19:08:28 +02:00
|
|
|
NET_ERR("Cannot set the ICMPv6 NS header");
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_get_nd_opt_hdr(struct net_pkt *pkt,
|
|
|
|
struct net_icmpv6_nd_opt_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
frag = net_frag_read(pkt->frags,
|
|
|
|
net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
|
|
|
sizeof(struct net_icmp_hdr) +
|
|
|
|
net_pkt_ipv6_ext_opt_len(pkt),
|
|
|
|
&pos, sizeof(*hdr), (u8_t *)hdr);
|
2018-05-17 15:00:54 +02:00
|
|
|
if (pos > 0 && !frag) {
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_get_na_hdr(struct net_pkt *pkt, struct net_icmpv6_na_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
|
|
|
sizeof(struct net_icmp_hdr),
|
|
|
|
&pos, sizeof(*hdr), (u8_t *)hdr);
|
2017-06-28 14:34:02 +02:00
|
|
|
|
2018-05-17 15:00:54 +02:00
|
|
|
if (pos > 0 && !frag) {
|
2018-05-18 19:08:28 +02:00
|
|
|
NET_ERR("Cannot get the ICMPv6 NA header");
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_set_na_hdr(struct net_pkt *pkt, struct net_icmpv6_na_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
2018-09-12 04:09:03 +02:00
|
|
|
(void)memset(hdr->reserved, 0, sizeof(hdr->reserved));
|
2017-06-28 14:34:02 +02:00
|
|
|
|
|
|
|
frag = net_pkt_write(pkt, pkt->frags,
|
|
|
|
net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
|
|
|
sizeof(struct net_icmp_hdr),
|
2018-08-09 10:12:09 +02:00
|
|
|
&pos, sizeof(*hdr), (u8_t *)hdr,
|
2017-06-28 14:34:02 +02:00
|
|
|
PKT_WAIT_TIME);
|
|
|
|
if (!frag) {
|
2018-05-18 19:08:28 +02:00
|
|
|
NET_ERR("Cannot set the ICMPv6 NA header");
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
int net_icmpv6_get_ra_hdr(struct net_pkt *pkt, struct net_icmpv6_ra_hdr *hdr)
|
2017-06-28 14:34:02 +02:00
|
|
|
{
|
|
|
|
struct net_buf *frag;
|
|
|
|
u16_t pos;
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
frag = net_frag_read(pkt->frags, net_pkt_ip_hdr_len(pkt) +
|
|
|
|
net_pkt_ipv6_ext_len(pkt) +
|
|
|
|
sizeof(struct net_icmp_hdr),
|
|
|
|
&pos, sizeof(*hdr), (u8_t *)hdr);
|
2018-05-17 15:00:54 +02:00
|
|
|
if (pos > 0 && !frag) {
|
2018-05-18 19:08:28 +02:00
|
|
|
NET_ERR("Cannot get the ICMPv6 RA header");
|
2018-08-09 10:12:09 +02:00
|
|
|
return -EINVAL;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:12:09 +02:00
|
|
|
return 0;
|
2017-06-28 14:34:02 +02:00
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
static enum net_verdict handle_echo_request(struct net_pkt *orig)
|
2016-11-24 14:25:50 +01:00
|
|
|
{
|
2018-08-09 10:12:09 +02:00
|
|
|
struct net_icmp_hdr icmp_hdr;
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt;
|
2017-03-03 09:03:33 +01:00
|
|
|
struct net_buf *frag;
|
2016-11-24 14:25:50 +01:00
|
|
|
struct net_if *iface;
|
2018-11-01 15:06:39 +01:00
|
|
|
s16_t payload_len;
|
2018-08-09 10:12:09 +02:00
|
|
|
int ret;
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2018-08-16 13:32:31 +02:00
|
|
|
NET_DBG("Received Echo Request from %s to %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(orig)->src)),
|
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(orig)->dst)));
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2017-04-03 17:14:35 +02:00
|
|
|
iface = net_pkt_iface(orig);
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2018-11-01 15:06:39 +01:00
|
|
|
payload_len = ntohs(NET_IPV6_HDR(orig)->len) -
|
|
|
|
net_pkt_ipv6_ext_len(orig) - NET_ICMPH_LEN;
|
|
|
|
if (payload_len < NET_ICMPV6_UNUSED_LEN) {
|
|
|
|
/* No identifier or sequence number present */
|
|
|
|
goto drop_no_pkt;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
pkt = net_pkt_get_reserve_tx(0, PKT_WAIT_TIME);
|
|
|
|
if (!pkt) {
|
|
|
|
goto drop_no_pkt;
|
2017-03-15 09:15:45 +01:00
|
|
|
}
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
frag = net_pkt_copy_all(orig, 0, PKT_WAIT_TIME);
|
2016-11-24 14:25:50 +01:00
|
|
|
if (!frag) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_frag_add(pkt, frag);
|
|
|
|
net_pkt_set_family(pkt, AF_INET6);
|
|
|
|
net_pkt_set_iface(pkt, iface);
|
|
|
|
net_pkt_set_ll_reserve(pkt, net_buf_headroom(frag));
|
|
|
|
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2017-04-12 11:29:19 +02:00
|
|
|
if (net_pkt_ipv6_ext_len(orig)) {
|
|
|
|
net_pkt_set_ipv6_ext_len(pkt, net_pkt_ipv6_ext_len(orig));
|
2017-03-03 15:42:29 +01:00
|
|
|
} else {
|
2017-04-12 11:29:19 +02:00
|
|
|
net_pkt_set_ipv6_ext_len(pkt, 0);
|
2017-03-03 15:42:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up IPv6 Header fields */
|
2017-04-10 13:03:41 +02:00
|
|
|
NET_IPV6_HDR(pkt)->vtc = 0x60;
|
|
|
|
NET_IPV6_HDR(pkt)->tcflow = 0;
|
|
|
|
NET_IPV6_HDR(pkt)->flow = 0;
|
|
|
|
NET_IPV6_HDR(pkt)->hop_limit = net_if_ipv6_get_hop_limit(iface);
|
2017-03-03 15:42:29 +01:00
|
|
|
|
2018-11-02 15:05:58 +01:00
|
|
|
if (net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) {
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst,
|
|
|
|
&NET_IPV6_HDR(orig)->src);
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src,
|
2017-03-03 09:03:33 +01:00
|
|
|
net_if_ipv6_select_src_addr(iface,
|
2017-04-10 13:03:41 +02:00
|
|
|
&NET_IPV6_HDR(orig)->dst));
|
2016-05-19 11:42:01 +02:00
|
|
|
} else {
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&addr, &NET_IPV6_HDR(orig)->src);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src,
|
|
|
|
&NET_IPV6_HDR(orig)->dst);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, &addr);
|
2016-05-19 11:42:01 +02:00
|
|
|
}
|
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
if (NET_IPV6_HDR(pkt)->nexthdr == NET_IPV6_NEXTHDR_HBHO) {
|
2017-03-03 15:42:29 +01:00
|
|
|
#if defined(CONFIG_NET_RPL)
|
2017-04-21 16:27:50 +02:00
|
|
|
u16_t offset = NET_IPV6H_LEN;
|
2017-03-03 15:42:29 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_rpl_revert_header(pkt, offset, &offset) < 0) {
|
2017-03-03 15:42:29 +01:00
|
|
|
/* TODO: Handle error cases */
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-09-11 09:16:03 +02:00
|
|
|
net_pkt_lladdr_src(pkt)->addr = net_pkt_lladdr_dst(orig)->addr;
|
|
|
|
net_pkt_lladdr_src(pkt)->len = net_pkt_lladdr_dst(orig)->len;
|
2016-11-29 14:32:33 +01:00
|
|
|
|
|
|
|
/* We must not set the destination ll address here but trust
|
|
|
|
* that it is set properly using a value from neighbor cache.
|
|
|
|
*/
|
2018-09-11 09:16:03 +02:00
|
|
|
net_pkt_lladdr_dst(pkt)->addr = NULL;
|
2016-11-24 14:25:50 +01:00
|
|
|
|
2017-06-28 14:34:02 +02:00
|
|
|
/* ICMPv6 fields */
|
2018-08-09 10:12:09 +02:00
|
|
|
ret = net_icmpv6_get_hdr(pkt, &icmp_hdr);
|
|
|
|
if (ret < 0) {
|
2018-03-16 09:35:11 +01:00
|
|
|
goto drop;
|
|
|
|
}
|
2018-08-09 10:12:09 +02:00
|
|
|
|
|
|
|
icmp_hdr.type = NET_ICMPV6_ECHO_REPLY;
|
|
|
|
icmp_hdr.code = 0;
|
|
|
|
icmp_hdr.chksum = 0;
|
|
|
|
net_icmpv6_set_hdr(pkt, &icmp_hdr);
|
|
|
|
net_icmpv6_set_chksum(pkt);
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2018-08-16 13:32:31 +02:00
|
|
|
NET_DBG("Sending Echo Reply from %s to %s",
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src)),
|
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst)));
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_send_data(pkt) < 0) {
|
2016-11-24 14:25:50 +01:00
|
|
|
goto drop;
|
2016-05-19 11:42:01 +02:00
|
|
|
}
|
|
|
|
|
2017-04-03 17:14:35 +02:00
|
|
|
net_pkt_unref(orig);
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_sent(iface);
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2017-03-03 09:13:36 +01:00
|
|
|
return NET_OK;
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2016-11-24 14:25:50 +01:00
|
|
|
drop:
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2017-03-15 09:15:45 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
drop_no_pkt:
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_drop(iface);
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2016-11-24 14:25:50 +01:00
|
|
|
return NET_DROP;
|
2016-06-14 08:42:01 +02:00
|
|
|
}
|
|
|
|
|
2017-04-21 16:27:50 +02:00
|
|
|
int net_icmpv6_send_error(struct net_pkt *orig, u8_t type, u8_t code,
|
|
|
|
u32_t param)
|
2016-06-14 08:42:01 +02:00
|
|
|
{
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt;
|
|
|
|
struct net_buf *frag;
|
2018-03-27 10:31:31 +02:00
|
|
|
struct net_if *iface = net_pkt_iface(orig);
|
2016-06-14 08:42:01 +02:00
|
|
|
size_t extra_len, reserve;
|
2017-03-15 09:15:45 +01:00
|
|
|
int err = -EIO;
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
if (NET_IPV6_HDR(orig)->nexthdr == IPPROTO_ICMPV6) {
|
2017-06-28 14:34:02 +02:00
|
|
|
struct net_icmp_hdr icmp_hdr[1];
|
|
|
|
|
|
|
|
if (!net_icmpv6_get_hdr(orig, icmp_hdr) ||
|
|
|
|
icmp_hdr->code < 128) {
|
2016-06-14 08:42:01 +02:00
|
|
|
/* We must not send ICMP errors back */
|
2017-03-15 09:15:45 +01:00
|
|
|
err = -EINVAL;
|
2017-04-05 08:37:44 +02:00
|
|
|
goto drop_no_pkt;
|
2016-06-14 08:42:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
pkt = net_pkt_get_reserve_tx(0, PKT_WAIT_TIME);
|
|
|
|
if (!pkt) {
|
2017-03-15 09:15:45 +01:00
|
|
|
err = -ENOMEM;
|
2017-04-05 08:37:44 +02:00
|
|
|
goto drop_no_pkt;
|
2017-03-15 09:15:45 +01:00
|
|
|
}
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2016-10-24 12:17:31 +02:00
|
|
|
/* There is unsed part in ICMPv6 error msg header what we might need
|
|
|
|
* to store the param variable.
|
|
|
|
*/
|
2016-06-14 08:42:01 +02:00
|
|
|
reserve = sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr) +
|
|
|
|
NET_ICMPV6_UNUSED_LEN;
|
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
if (NET_IPV6_HDR(orig)->nexthdr == IPPROTO_UDP) {
|
2016-06-14 08:42:01 +02:00
|
|
|
extra_len = sizeof(struct net_ipv6_hdr) +
|
|
|
|
sizeof(struct net_udp_hdr);
|
2017-04-10 13:03:41 +02:00
|
|
|
} else if (NET_IPV6_HDR(orig)->nexthdr == IPPROTO_TCP) {
|
2017-02-22 12:51:21 +01:00
|
|
|
extra_len = sizeof(struct net_ipv6_hdr) +
|
|
|
|
sizeof(struct net_tcp_hdr);
|
2018-05-02 16:14:28 +02:00
|
|
|
} else if (NET_IPV6_HDR(orig)->nexthdr == NET_IPV6_NEXTHDR_FRAG) {
|
|
|
|
extra_len = net_pkt_get_len(orig);
|
2016-06-14 08:42:01 +02:00
|
|
|
} else {
|
2017-04-03 17:14:35 +02:00
|
|
|
size_t space = CONFIG_NET_BUF_DATA_SIZE -
|
2017-03-20 15:14:50 +01:00
|
|
|
net_if_get_ll_reserve(iface,
|
2017-04-10 13:03:41 +02:00
|
|
|
&NET_IPV6_HDR(orig)->dst);
|
2016-06-14 08:42:01 +02:00
|
|
|
|
|
|
|
if (reserve > space) {
|
|
|
|
extra_len = 0;
|
|
|
|
} else {
|
|
|
|
extra_len = space - reserve;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We only copy minimal IPv6 + next header from original message.
|
|
|
|
* This is so that the memory pressure is minimized.
|
|
|
|
*/
|
2017-04-05 08:37:44 +02:00
|
|
|
frag = net_pkt_copy(orig, extra_len, reserve, PKT_WAIT_TIME);
|
2016-06-14 08:42:01 +02:00
|
|
|
if (!frag) {
|
2017-03-15 09:15:45 +01:00
|
|
|
err = -ENOMEM;
|
2016-06-14 08:42:01 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_frag_add(pkt, frag);
|
|
|
|
net_pkt_set_family(pkt, AF_INET6);
|
|
|
|
net_pkt_set_iface(pkt, iface);
|
|
|
|
net_pkt_set_ll_reserve(pkt, net_buf_headroom(frag));
|
2017-04-12 11:29:19 +02:00
|
|
|
net_pkt_set_ipv6_ext_len(pkt, 0);
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
setup_ipv6_header(pkt, extra_len, net_if_ipv6_get_hop_limit(iface),
|
2016-06-14 08:42:01 +02:00
|
|
|
type, code);
|
|
|
|
|
2016-10-24 12:17:31 +02:00
|
|
|
/* Depending on error option, we store the param into the ICMP message.
|
|
|
|
*/
|
|
|
|
if (type == NET_ICMPV6_PARAM_PROBLEM) {
|
2017-06-28 14:34:02 +02:00
|
|
|
sys_put_be32(param, (u8_t *)net_pkt_icmp_data(pkt) +
|
2016-10-24 12:17:31 +02:00
|
|
|
sizeof(struct net_icmp_hdr));
|
|
|
|
}
|
|
|
|
|
2018-11-02 15:05:58 +01:00
|
|
|
if (net_ipv6_is_addr_mcast(&NET_IPV6_HDR(orig)->dst)) {
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst,
|
|
|
|
&NET_IPV6_HDR(orig)->src);
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src,
|
2017-03-20 15:14:50 +01:00
|
|
|
net_if_ipv6_select_src_addr(iface,
|
2017-04-10 13:03:41 +02:00
|
|
|
&NET_IPV6_HDR(orig)->dst));
|
2016-06-14 08:42:01 +02:00
|
|
|
} else {
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&addr, &NET_IPV6_HDR(orig)->src);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src,
|
|
|
|
&NET_IPV6_HDR(orig)->dst);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, &addr);
|
2016-06-14 08:42:01 +02:00
|
|
|
}
|
|
|
|
|
2018-09-11 09:16:03 +02:00
|
|
|
net_pkt_lladdr_src(pkt)->addr = net_pkt_lladdr_dst(orig)->addr;
|
|
|
|
net_pkt_lladdr_src(pkt)->len = net_pkt_lladdr_dst(orig)->len;
|
|
|
|
net_pkt_lladdr_dst(pkt)->addr = net_pkt_lladdr_src(orig)->addr;
|
|
|
|
net_pkt_lladdr_dst(pkt)->len = net_pkt_lladdr_src(orig)->len;
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2017-06-28 14:34:02 +02:00
|
|
|
/* Clear and then set the chksum */
|
2018-08-09 10:12:09 +02:00
|
|
|
err = net_icmpv6_set_chksum(pkt);
|
|
|
|
if (err < 0) {
|
|
|
|
goto drop;
|
|
|
|
}
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2018-08-16 13:32:31 +02:00
|
|
|
NET_DBG("Sending ICMPv6 Error Message type %d code %d param %d"
|
|
|
|
" from %s to %s", type, code, param,
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src)),
|
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst)));
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_send_data(pkt) >= 0) {
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_sent(iface);
|
2017-03-15 11:40:51 +01:00
|
|
|
return 0;
|
2016-06-14 08:42:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
drop:
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2017-03-15 09:15:45 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
drop_no_pkt:
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_drop(iface);
|
2016-06-14 08:42:01 +02:00
|
|
|
|
2017-03-15 09:15:45 +01:00
|
|
|
return err;
|
2016-06-14 08:42:01 +02:00
|
|
|
}
|
|
|
|
|
2018-07-25 14:44:30 +02:00
|
|
|
#define append(pkt, type, value) \
|
|
|
|
do { \
|
|
|
|
if (!net_pkt_append_##type##_timeout(pkt, value, \
|
|
|
|
PKT_WAIT_TIME)) { \
|
|
|
|
ret = -ENOMEM; \
|
|
|
|
goto drop; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2016-11-03 13:44:44 +01:00
|
|
|
int net_icmpv6_send_echo_request(struct net_if *iface,
|
|
|
|
struct in6_addr *dst,
|
2017-04-21 16:27:50 +02:00
|
|
|
u16_t identifier,
|
|
|
|
u16_t sequence)
|
2016-11-03 13:44:44 +01:00
|
|
|
{
|
|
|
|
const struct in6_addr *src;
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt;
|
2018-07-25 14:44:30 +02:00
|
|
|
int ret;
|
2016-11-03 13:44:44 +01:00
|
|
|
|
|
|
|
src = net_if_ipv6_select_src_addr(iface, dst);
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
pkt = net_pkt_get_reserve_tx(net_if_get_ll_reserve(iface, dst),
|
2018-07-25 14:44:30 +02:00
|
|
|
PKT_WAIT_TIME);
|
|
|
|
if (!pkt) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2018-07-25 14:44:30 +02:00
|
|
|
if (!net_ipv6_create(pkt, src, dst, iface, IPPROTO_ICMPV6)) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto drop;
|
|
|
|
}
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_set_family(pkt, AF_INET6);
|
|
|
|
net_pkt_set_iface(pkt, iface);
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2018-07-25 14:44:30 +02:00
|
|
|
append(pkt, u8, NET_ICMPV6_ECHO_REQUEST);
|
|
|
|
append(pkt, u8, 0); /* code */
|
|
|
|
append(pkt, be16, 0); /* checksum */
|
|
|
|
append(pkt, be16, identifier);
|
|
|
|
append(pkt, be16, sequence);
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2017-04-10 13:03:41 +02:00
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, src);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, dst);
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2018-06-29 10:56:15 +02:00
|
|
|
if (net_ipv6_finalize(pkt, IPPROTO_ICMPV6) < 0) {
|
2018-07-25 14:44:30 +02:00
|
|
|
ret = -ENOMEM;
|
2017-03-10 10:27:32 +01:00
|
|
|
goto drop;
|
|
|
|
}
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2018-08-16 13:32:31 +02:00
|
|
|
NET_DBG("Sending ICMPv6 Echo Request type %d from %s to %s",
|
|
|
|
NET_ICMPV6_ECHO_REQUEST,
|
2018-10-02 13:57:55 +02:00
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->src)),
|
|
|
|
log_strdup(net_sprint_ipv6_addr(&NET_IPV6_HDR(pkt)->dst)));
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_send_data(pkt) >= 0) {
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_sent(iface);
|
2016-11-03 13:44:44 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-25 14:44:30 +02:00
|
|
|
ret = -EIO;
|
|
|
|
|
2017-03-10 10:27:32 +01:00
|
|
|
drop:
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_unref(pkt);
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_drop(iface);
|
2016-11-03 13:44:44 +01:00
|
|
|
|
2018-07-25 14:44:30 +02:00
|
|
|
return ret;
|
2016-11-03 13:44:44 +01:00
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
enum net_verdict net_icmpv6_input(struct net_pkt *pkt,
|
2017-04-21 16:27:50 +02:00
|
|
|
u8_t type, u8_t code)
|
2016-05-19 11:42:01 +02:00
|
|
|
{
|
|
|
|
struct net_icmpv6_handler *cb;
|
|
|
|
|
2018-12-04 12:10:16 +01:00
|
|
|
if (net_calc_chksum_icmpv6(pkt) != 0) {
|
|
|
|
NET_DBG("DROP: invalid checksum");
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_recv(net_pkt_iface(pkt));
|
2017-03-15 10:02:26 +01:00
|
|
|
|
2017-02-08 15:08:23 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&handlers, cb, node) {
|
2016-05-19 11:42:01 +02:00
|
|
|
if (cb->type == type && (cb->code == code || cb->code == 0)) {
|
2017-04-05 08:37:44 +02:00
|
|
|
return cb->handler(pkt);
|
2016-05-19 11:42:01 +02:00
|
|
|
}
|
|
|
|
}
|
2018-12-04 12:10:16 +01:00
|
|
|
drop:
|
2018-03-27 10:31:31 +02:00
|
|
|
net_stats_update_icmp_drop(net_pkt_iface(pkt));
|
2017-03-15 10:02:26 +01:00
|
|
|
|
2016-05-19 11:42:01 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_icmpv6_handler echo_request_handler = {
|
|
|
|
.type = NET_ICMPV6_ECHO_REQUEST,
|
|
|
|
.code = 0,
|
|
|
|
.handler = handle_echo_request,
|
|
|
|
};
|
|
|
|
|
|
|
|
void net_icmpv6_init(void)
|
|
|
|
{
|
|
|
|
net_icmpv6_register_handler(&echo_request_handler);
|
|
|
|
}
|