net: Add support for ICMPv6 error message

Provide an API to send ICMPv6 error message. This can be used
for example in UDP and TCP to inform peer that there is no one
listening a certain port.

Change-Id: Ied682901f7fc406ba383293cb7c338ea21114b0d
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2016-06-14 09:42:01 +03:00
parent 813fdf85ed
commit 9612f97e94
2 changed files with 155 additions and 0 deletions

View file

@ -94,6 +94,142 @@ static enum net_verdict handle_echo_request(struct net_buf *buf)
return NET_OK;
}
#define NET_ICMPV6_UNUSED_LEN 4
static inline void setup_ipv6_header(struct net_buf *buf, uint8_t extra_len,
uint8_t hop_limit, uint8_t icmp_type,
uint8_t icmp_code)
{
NET_IPV6_BUF(buf)->vtc = 0x60;
NET_IPV6_BUF(buf)->tcflow = 0;
NET_IPV6_BUF(buf)->flow = 0;
NET_IPV6_BUF(buf)->len[0] = 0;
NET_IPV6_BUF(buf)->len[1] = NET_ICMPH_LEN + extra_len +
NET_ICMPV6_UNUSED_LEN;
NET_IPV6_BUF(buf)->nexthdr = IPPROTO_ICMPV6;
NET_IPV6_BUF(buf)->hop_limit = hop_limit;
net_nbuf_ip_hdr_len(buf) = sizeof(struct net_ipv6_hdr);
NET_ICMP_BUF(buf)->type = icmp_type;
NET_ICMP_BUF(buf)->code = icmp_code;
/* ICMPv6 header has 4 unused bytes that must be zero, RFC 4443 ch 3.1
*/
memset(net_nbuf_icmp_data(buf) + sizeof(struct net_icmp_hdr), 0,
NET_ICMPV6_UNUSED_LEN);
}
int net_icmpv6_send_error(struct net_buf *orig, uint8_t type, uint8_t code)
{
struct net_buf *buf, *frag;
struct net_if *iface;
struct in6_addr *src, *dst;
size_t extra_len, reserve;
if (NET_IPV6_BUF(orig)->nexthdr == IPPROTO_ICMPV6) {
if (NET_ICMP_BUF(orig)->code < 128) {
/* We must not send ICMP errors back */
return -EINVAL;
}
}
iface = net_nbuf_iface(orig);
buf = net_nbuf_get_reserve_tx(0);
/* There is unsed part in ICMPv6 error msg header */
reserve = sizeof(struct net_ipv6_hdr) + sizeof(struct net_icmp_hdr) +
NET_ICMPV6_UNUSED_LEN;
if (NET_IPV6_BUF(orig)->nexthdr == IPPROTO_UDP) {
extra_len = sizeof(struct net_ipv6_hdr) +
sizeof(struct net_udp_hdr);
} else if (NET_IPV6_BUF(orig)->nexthdr == IPPROTO_TCP) {
extra_len = sizeof(struct net_ipv6_hdr);
/* FIXME, add TCP header length too */
} else {
size_t space = CONFIG_NET_NBUF_DATA_SIZE -
net_if_get_ll_reserve(iface);
if (reserve > space) {
extra_len = 0;
} else {
extra_len = space - reserve;
}
}
/* We need to remember the original location of source and destination
* addresses as the net_nbuf_copy() will mangle the original buffer.
*/
src = &NET_IPV6_BUF(orig)->src;
dst = &NET_IPV6_BUF(orig)->dst;
/* We only copy minimal IPv6 + next header from original message.
* This is so that the memory pressure is minimized.
*/
frag = net_nbuf_copy(orig->frags, extra_len, reserve);
if (!frag) {
goto drop;
}
net_buf_frag_add(buf, frag);
net_nbuf_family(buf) = AF_INET6;
net_nbuf_iface(buf) = iface;
net_nbuf_ll_reserve(buf) = net_buf_headroom(frag);
net_nbuf_ext_len(buf) = 0;
setup_ipv6_header(buf, extra_len, net_if_ipv6_get_hop_limit(iface),
type, code);
if (net_is_ipv6_addr_mcast(dst)) {
net_ipaddr_copy(&NET_IPV6_BUF(buf)->dst, src);
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
net_if_ipv6_select_src_addr(iface, dst));
} else {
struct in6_addr addr;
net_ipaddr_copy(&addr, src);
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src, dst);
net_ipaddr_copy(&NET_IPV6_BUF(buf)->dst, &addr);
}
net_nbuf_ll_src(buf)->addr = net_nbuf_ll_dst(orig)->addr;
net_nbuf_ll_src(buf)->len = net_nbuf_ll_dst(orig)->len;
net_nbuf_ll_dst(buf)->addr = net_nbuf_ll_src(orig)->addr;
net_nbuf_ll_dst(buf)->len = net_nbuf_ll_src(orig)->len;
NET_ICMP_BUF(buf)->chksum = 0;
NET_ICMP_BUF(buf)->chksum = ~net_calc_chksum_icmpv6(buf);
#if NET_DEBUG > 0
do {
char out[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")];
snprintf(out, sizeof(out),
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->dst));
NET_DBG("Sending ICMPv6 Error Message type %d code %d "
"from %s to %s", type, code,
net_sprint_ipv6_addr(&NET_IPV6_BUF(buf)->src), out);
} while (0);
#endif /* NET_DEBUG > 0 */
if (net_send_data(buf) >= 0) {
NET_STATS(++net_stats.icmp.sent);
return -EIO;
}
drop:
net_nbuf_unref(buf);
NET_STATS(++net_stats.icmp.drop);
/* Note that we always return < 0 so that the caller knows to
* discard the original buffer.
*/
return -EIO;
}
enum net_verdict net_icmpv6_input(struct net_buf *buf, uint16_t len,
uint8_t type, uint8_t code)
{

View file

@ -116,6 +116,7 @@ struct net_icmpv6_nd_opt_prefix_info {
#define NET_ICMPV6_RA_FLAG_ONLINK 0x80
#define NET_ICMPV6_RA_FLAG_AUTONOMOUS 0x40
#define NET_ICMPV6_DST_UNREACH 1 /* Destination unreachable */
#define NET_ICMPV6_ECHO_REQUEST 128
#define NET_ICMPV6_ECHO_REPLY 129
#define NET_ICMPV6_RS 133 /* Router Solicitation */
@ -123,6 +124,15 @@ struct net_icmpv6_nd_opt_prefix_info {
#define NET_ICMPV6_NS 135 /* Neighbor Solicitation */
#define NET_ICMPV6_NA 136 /* Neighbor Advertisement */
/* Codes for ICMPv6 Destination Unreachable message */
#define NET_ICMPV6_DST_UNREACH_NO_ROUTE 0 /* No route to destination */
#define NET_ICMPV6_DST_UNREACH_ADMIN 1 /* Admin prohibited communication */
#define NET_ICMPV6_DST_UNREACH_SCOPE 2 /* Beoynd scope of source address */
#define NET_ICMPV6_DST_UNREACH_NO_ADDR 3 /* Address unrechable */
#define NET_ICMPV6_DST_UNREACH_NO_PORT 4 /* Port unreachable */
#define NET_ICMPV6_DST_UNREACH_SRC_ADDR 5 /* Source address failed */
#define NET_ICMPV6_DST_UNREACH_REJ_ROUTE 6 /* Reject route to destination */
typedef enum net_verdict (*icmpv6_callback_handler_t)(struct net_buf *buf);
struct net_icmpv6_handler {
@ -132,6 +142,15 @@ struct net_icmpv6_handler {
icmpv6_callback_handler_t handler;
};
/**
* @brief Send ICMPv6 error message.
* @param buf Network buffer that this error is related to.
* @param type Type of the error message.
* @param code Code of the type of the error message.
* @return Return 0 if the sending succeed, <0 otherwise.
*/
int net_icmpv6_send_error(struct net_buf *buf, uint8_t type, uint8_t code);
void net_icmpv6_register_handler(struct net_icmpv6_handler *handler);
enum net_verdict net_icmpv6_input(struct net_buf *buf, uint16_t len,
uint8_t type, uint8_t code);