690 lines
16 KiB
C
690 lines
16 KiB
C
/** @file
|
|
* @brief IPv6 related functions
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define LOG_MODULE_NAME net_ipv6
|
|
#define NET_LOG_LEVEL CONFIG_NET_IPV6_LOG_LEVEL
|
|
|
|
/* By default this prints too much data, set the value to 1 to see
|
|
* neighbor cache contents.
|
|
*/
|
|
#define NET_DEBUG_NBR 0
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <net/net_core.h>
|
|
#include <net/net_pkt.h>
|
|
#include <net/net_stats.h>
|
|
#include <net/net_context.h>
|
|
#include <net/net_mgmt.h>
|
|
#include <net/tcp.h>
|
|
#include "net_private.h"
|
|
#include "connection.h"
|
|
#include "icmpv6.h"
|
|
#include "udp_internal.h"
|
|
#include "tcp_internal.h"
|
|
#include "ipv6.h"
|
|
#include "nbr.h"
|
|
#include "6lo.h"
|
|
#include "route.h"
|
|
#include "rpl.h"
|
|
#include "net_stats.h"
|
|
|
|
/* Timeout value to be used when allocating net buffer during various
|
|
* neighbor discovery procedures.
|
|
*/
|
|
#define ND_NET_BUF_TIMEOUT K_MSEC(100)
|
|
|
|
/* Timeout for various buffer allocations in this file. */
|
|
#define NET_BUF_TIMEOUT K_MSEC(50)
|
|
|
|
/* Maximum reachable time value specified in RFC 4861 section
|
|
* 6.2.1. Router Configuration Variables, AdvReachableTime
|
|
*/
|
|
#define MAX_REACHABLE_TIME 3600000
|
|
|
|
/* IPv6 wildcard and loopback address defined by RFC2553 */
|
|
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
|
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
|
|
|
|
const struct in6_addr *net_ipv6_unspecified_address(void)
|
|
{
|
|
return &in6addr_any;
|
|
}
|
|
|
|
struct net_pkt *net_ipv6_create(struct net_pkt *pkt,
|
|
const struct in6_addr *src,
|
|
const struct in6_addr *dst,
|
|
struct net_if *iface,
|
|
u8_t next_header_proto)
|
|
{
|
|
struct net_buf *header;
|
|
|
|
header = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT);
|
|
if (!header) {
|
|
return NULL;
|
|
}
|
|
|
|
net_pkt_frag_insert(pkt, header);
|
|
|
|
NET_IPV6_HDR(pkt)->vtc = 0x60;
|
|
NET_IPV6_HDR(pkt)->tcflow = 0;
|
|
NET_IPV6_HDR(pkt)->flow = 0;
|
|
|
|
NET_IPV6_HDR(pkt)->nexthdr = 0;
|
|
|
|
/* User can tweak the default hop limit if needed */
|
|
NET_IPV6_HDR(pkt)->hop_limit = net_pkt_ipv6_hop_limit(pkt);
|
|
if (NET_IPV6_HDR(pkt)->hop_limit == 0) {
|
|
NET_IPV6_HDR(pkt)->hop_limit =
|
|
net_if_ipv6_get_hop_limit(iface);
|
|
}
|
|
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->dst, dst);
|
|
net_ipaddr_copy(&NET_IPV6_HDR(pkt)->src, src);
|
|
|
|
net_pkt_set_ipv6_ext_len(pkt, 0);
|
|
NET_IPV6_HDR(pkt)->nexthdr = next_header_proto;
|
|
|
|
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
|
|
net_pkt_set_family(pkt, AF_INET6);
|
|
|
|
net_buf_add(header, sizeof(struct net_ipv6_hdr));
|
|
|
|
return pkt;
|
|
}
|
|
|
|
int net_ipv6_finalize(struct net_pkt *pkt, u8_t next_header_proto)
|
|
{
|
|
/* Set the length of the IPv6 header */
|
|
size_t total_len;
|
|
int ret;
|
|
|
|
#if defined(CONFIG_NET_UDP) && defined(CONFIG_NET_RPL_INSERT_HBH_OPTION)
|
|
if (next_header_proto != IPPROTO_TCP &&
|
|
next_header_proto != IPPROTO_ICMPV6) {
|
|
/* Check if we need to add RPL header to sent UDP packet. */
|
|
if (net_rpl_insert_header(pkt) < 0) {
|
|
NET_DBG("RPL HBHO insert failed");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
#endif
|
|
net_pkt_compact(pkt);
|
|
|
|
total_len = net_pkt_get_len(pkt) - sizeof(struct net_ipv6_hdr);
|
|
|
|
NET_IPV6_HDR(pkt)->len = htons(total_len);
|
|
|
|
#if defined(CONFIG_NET_UDP)
|
|
if (next_header_proto == IPPROTO_UDP &&
|
|
net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) {
|
|
net_udp_set_chksum(pkt, pkt->frags);
|
|
} else
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_TCP)
|
|
if (next_header_proto == IPPROTO_TCP &&
|
|
net_if_need_calc_tx_checksum(net_pkt_iface(pkt))) {
|
|
net_tcp_set_chksum(pkt, pkt->frags);
|
|
} else
|
|
#endif
|
|
|
|
if (next_header_proto == IPPROTO_ICMPV6) {
|
|
ret = net_icmpv6_set_chksum(pkt);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline enum net_verdict process_icmpv6_pkt(struct net_pkt *pkt,
|
|
struct net_ipv6_hdr *ipv6)
|
|
{
|
|
struct net_icmp_hdr icmp_hdr;
|
|
u16_t chksum;
|
|
int ret;
|
|
|
|
ret = net_icmpv6_get_hdr(pkt, &icmp_hdr);
|
|
if (ret < 0) {
|
|
NET_DBG("NULL ICMPv6 header - dropping");
|
|
return NET_DROP;
|
|
}
|
|
|
|
chksum = icmp_hdr.chksum;
|
|
net_icmpv6_set_chksum(pkt);
|
|
(void)net_icmpv6_get_hdr(pkt, &icmp_hdr);
|
|
|
|
if (chksum != icmp_hdr.chksum) {
|
|
NET_DBG("ICMPv6 invalid checksum (0x%04x instead of 0x%04x)",
|
|
ntohs(chksum), ntohs(icmp_hdr.chksum));
|
|
return NET_DROP;
|
|
}
|
|
|
|
NET_DBG("ICMPv6 %s received type %d code %d",
|
|
net_icmpv6_type2str(icmp_hdr.type), icmp_hdr.type,
|
|
icmp_hdr.code);
|
|
|
|
return net_icmpv6_input(pkt, icmp_hdr.type, icmp_hdr.code);
|
|
}
|
|
|
|
static inline struct net_pkt *check_unknown_option(struct net_pkt *pkt,
|
|
u8_t opt_type,
|
|
u16_t length)
|
|
{
|
|
/* RFC 2460 chapter 4.2 tells how to handle the unknown
|
|
* options by the two highest order bits of the option:
|
|
*
|
|
* 00: Skip over this option and continue processing the header.
|
|
* 01: Discard the packet.
|
|
* 10: Discard the packet and, regardless of whether or not the
|
|
* packet's Destination Address was a multicast address,
|
|
* send an ICMP Parameter Problem, Code 2, message to the packet's
|
|
* Source Address, pointing to the unrecognized Option Type.
|
|
* 11: Discard the packet and, only if the packet's Destination
|
|
* Address was not a multicast address, send an ICMP Parameter
|
|
* Problem, Code 2, message to the packet's Source Address,
|
|
* pointing to the unrecognized Option Type.
|
|
*/
|
|
NET_DBG("Unknown option %d (0x%02x) MSB %d", opt_type, opt_type,
|
|
opt_type >> 6);
|
|
|
|
switch (opt_type & 0xc0) {
|
|
case 0x00:
|
|
break;
|
|
case 0x40:
|
|
return NULL;
|
|
case 0xc0:
|
|
if (net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) {
|
|
return NULL;
|
|
}
|
|
/* passthrough */
|
|
case 0x80:
|
|
net_icmpv6_send_error(pkt, NET_ICMPV6_PARAM_PROBLEM,
|
|
NET_ICMPV6_PARAM_PROB_OPTION,
|
|
(u32_t)length);
|
|
return NULL;
|
|
}
|
|
|
|
return pkt;
|
|
}
|
|
|
|
static inline struct net_buf *handle_ext_hdr_options(struct net_pkt *pkt,
|
|
struct net_buf *frag,
|
|
int total_len,
|
|
u16_t len,
|
|
u16_t offset,
|
|
u16_t *pos,
|
|
enum net_verdict *verdict)
|
|
{
|
|
u8_t opt_type, opt_len;
|
|
u16_t length = 0, loc;
|
|
#if defined(CONFIG_NET_RPL)
|
|
bool result;
|
|
#endif
|
|
|
|
if (len > total_len) {
|
|
NET_DBG("Corrupted packet, extension header %d too long "
|
|
"(max %d bytes)", len, total_len);
|
|
*verdict = NET_DROP;
|
|
return NULL;
|
|
}
|
|
|
|
length += 2;
|
|
|
|
/* Each extension option has type and length */
|
|
frag = net_frag_read_u8(frag, offset, &loc, &opt_type);
|
|
if (!frag && loc == 0xffff) {
|
|
goto drop;
|
|
}
|
|
|
|
if (opt_type != NET_IPV6_EXT_HDR_OPT_PAD1) {
|
|
frag = net_frag_read_u8(frag, loc, &loc, &opt_len);
|
|
if (!frag && loc == 0xffff) {
|
|
goto drop;
|
|
}
|
|
}
|
|
|
|
while (frag && (length < len)) {
|
|
switch (opt_type) {
|
|
case NET_IPV6_EXT_HDR_OPT_PAD1:
|
|
length++;
|
|
break;
|
|
case NET_IPV6_EXT_HDR_OPT_PADN:
|
|
NET_DBG("PADN option");
|
|
length += opt_len + 2;
|
|
loc += opt_len + 2;
|
|
break;
|
|
#if defined(CONFIG_NET_RPL)
|
|
case NET_IPV6_EXT_HDR_OPT_RPL:
|
|
NET_DBG("Processing RPL option");
|
|
frag = net_rpl_verify_header(pkt, frag, loc, &loc,
|
|
&result);
|
|
if (!result) {
|
|
NET_DBG("RPL option error, packet dropped");
|
|
goto drop;
|
|
}
|
|
|
|
if (!frag && *pos == 0xffff) {
|
|
goto drop;
|
|
}
|
|
|
|
*verdict = NET_CONTINUE;
|
|
return frag;
|
|
#endif
|
|
default:
|
|
if (!check_unknown_option(pkt, opt_type, length)) {
|
|
goto drop;
|
|
}
|
|
|
|
length += opt_len + 2;
|
|
|
|
/* No need to +2 here as loc already contains option
|
|
* header len.
|
|
*/
|
|
loc += opt_len;
|
|
|
|
break;
|
|
}
|
|
|
|
if (length >= len) {
|
|
break;
|
|
}
|
|
|
|
frag = net_frag_read_u8(frag, loc, &loc, &opt_type);
|
|
if (!frag && loc == 0xffff) {
|
|
goto drop;
|
|
}
|
|
|
|
if (opt_type != NET_IPV6_EXT_HDR_OPT_PAD1) {
|
|
frag = net_frag_read_u8(frag, loc, &loc, &opt_len);
|
|
if (!frag && loc == 0xffff) {
|
|
goto drop;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (length != len) {
|
|
goto drop;
|
|
}
|
|
|
|
*pos = loc;
|
|
|
|
*verdict = NET_CONTINUE;
|
|
return frag;
|
|
|
|
drop:
|
|
*verdict = NET_DROP;
|
|
return NULL;
|
|
}
|
|
|
|
static inline bool is_upper_layer_protocol_header(u8_t proto)
|
|
{
|
|
return (proto == IPPROTO_ICMPV6 || proto == IPPROTO_UDP ||
|
|
proto == IPPROTO_TCP);
|
|
}
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
static struct net_route_entry *add_route(struct net_if *iface,
|
|
struct in6_addr *addr,
|
|
u8_t prefix_len)
|
|
{
|
|
struct net_route_entry *route;
|
|
|
|
route = net_route_lookup(iface, addr);
|
|
if (route) {
|
|
return route;
|
|
}
|
|
|
|
route = net_route_add(iface, addr, prefix_len, addr);
|
|
|
|
NET_DBG("%s route to %s/%d iface %p", route ? "Add" : "Cannot add",
|
|
log_strdup(net_sprint_ipv6_addr(addr)), prefix_len, iface);
|
|
|
|
return route;
|
|
}
|
|
#endif /* CONFIG_NET_ROUTE */
|
|
|
|
static void no_route_info(struct net_pkt *pkt,
|
|
struct in6_addr *src,
|
|
struct in6_addr *dst)
|
|
{
|
|
NET_DBG("Will not route pkt %p ll src %s to dst %s between interfaces",
|
|
pkt, log_strdup(net_sprint_ipv6_addr(src)),
|
|
log_strdup(net_sprint_ipv6_addr(dst)));
|
|
}
|
|
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
static enum net_verdict route_ipv6_packet(struct net_pkt *pkt,
|
|
struct net_ipv6_hdr *hdr)
|
|
{
|
|
struct net_route_entry *route;
|
|
struct in6_addr *nexthop;
|
|
bool found;
|
|
|
|
/* Check if the packet can be routed */
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING)) {
|
|
found = net_route_get_info(NULL, &hdr->dst, &route,
|
|
&nexthop);
|
|
} else {
|
|
found = net_route_get_info(net_pkt_iface(pkt),
|
|
&hdr->dst, &route, &nexthop);
|
|
}
|
|
|
|
if (found) {
|
|
int ret;
|
|
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING) &&
|
|
(net_ipv6_is_ll_addr(&hdr->src) ||
|
|
net_ipv6_is_ll_addr(&hdr->dst))) {
|
|
/* RFC 4291 ch 2.5.6 */
|
|
no_route_info(pkt, &hdr->src, &hdr->dst);
|
|
goto drop;
|
|
}
|
|
|
|
/* Used when detecting if the original link
|
|
* layer address length is changed or not.
|
|
*/
|
|
net_pkt_set_orig_iface(pkt, net_pkt_iface(pkt));
|
|
|
|
if (route) {
|
|
net_pkt_set_iface(pkt, route->iface);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING) &&
|
|
net_pkt_orig_iface(pkt) != net_pkt_iface(pkt)) {
|
|
/* If the route interface to destination is
|
|
* different than the original route, then add
|
|
* route to original source.
|
|
*/
|
|
NET_DBG("Route pkt %p from %p to %p",
|
|
pkt, net_pkt_orig_iface(pkt),
|
|
net_pkt_iface(pkt));
|
|
|
|
add_route(net_pkt_orig_iface(pkt),
|
|
&NET_IPV6_HDR(pkt)->src, 128);
|
|
}
|
|
|
|
ret = net_route_packet(pkt, nexthop);
|
|
if (ret < 0) {
|
|
NET_DBG("Cannot re-route pkt %p via %s "
|
|
"at iface %p (%d)",
|
|
pkt, log_strdup(net_sprint_ipv6_addr(nexthop)),
|
|
net_pkt_iface(pkt), ret);
|
|
} else {
|
|
return NET_OK;
|
|
}
|
|
} else {
|
|
NET_DBG("No route to %s pkt %p dropped",
|
|
log_strdup(net_sprint_ipv6_addr(&hdr->dst)), pkt);
|
|
}
|
|
|
|
drop:
|
|
return NET_DROP;
|
|
}
|
|
#endif /* CONFIG_NET_ROUTE */
|
|
|
|
enum net_verdict net_ipv6_process_pkt(struct net_pkt *pkt, bool is_loopback)
|
|
{
|
|
struct net_ipv6_hdr *hdr = NET_IPV6_HDR(pkt);
|
|
int real_len = net_pkt_get_len(pkt);
|
|
int pkt_len = ntohs(hdr->len) + sizeof(*hdr);
|
|
struct net_buf *frag;
|
|
u8_t start_of_ext, prev_hdr;
|
|
u8_t next, next_hdr;
|
|
u8_t first_option;
|
|
u16_t offset;
|
|
u16_t length;
|
|
u16_t total_len = 0;
|
|
u8_t ext_bitmap;
|
|
|
|
if (real_len != pkt_len) {
|
|
NET_DBG("IPv6 packet size %d pkt len %d", pkt_len, real_len);
|
|
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
|
|
goto drop;
|
|
}
|
|
|
|
NET_DBG("IPv6 packet len %d received from %s to %s", real_len,
|
|
log_strdup(net_sprint_ipv6_addr(&hdr->src)),
|
|
log_strdup(net_sprint_ipv6_addr(&hdr->dst)));
|
|
|
|
if (!is_loopback && (net_ipv6_is_addr_loopback(&hdr->dst) ||
|
|
net_ipv6_is_addr_loopback(&hdr->src))) {
|
|
NET_DBG("Dropping ::1 packet");
|
|
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
|
|
goto drop;
|
|
}
|
|
|
|
if (net_ipv6_is_addr_mcast(&hdr->src) ||
|
|
net_ipv6_is_addr_mcast_scope(&hdr->dst, 0)) {
|
|
NET_DBG("Dropping multicast packet");
|
|
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
|
|
goto drop;
|
|
}
|
|
|
|
if (!is_loopback) {
|
|
bool is_empty_group = net_ipv6_is_addr_mcast_group(
|
|
&hdr->dst, net_ipv6_unspecified_address());
|
|
|
|
if (net_ipv6_is_addr_mcast_iface(&hdr->dst) ||
|
|
(is_empty_group &&
|
|
(net_ipv6_is_addr_mcast_site(&hdr->dst) ||
|
|
net_ipv6_is_addr_mcast_org(&hdr->dst)))) {
|
|
NET_DBG("Dropping invalid scope multicast packet");
|
|
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
|
|
goto drop;
|
|
}
|
|
}
|
|
|
|
/* Check extension headers */
|
|
net_pkt_set_next_hdr(pkt, &hdr->nexthdr);
|
|
net_pkt_set_ipv6_ext_len(pkt, 0);
|
|
net_pkt_set_ip_hdr_len(pkt, sizeof(struct net_ipv6_hdr));
|
|
net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_HDR(pkt)->hop_limit);
|
|
|
|
if (!net_ipv6_is_my_addr(&hdr->dst) &&
|
|
!net_ipv6_is_my_maddr(&hdr->dst) &&
|
|
!net_ipv6_is_addr_mcast(&hdr->dst)) {
|
|
#if defined(CONFIG_NET_ROUTE)
|
|
enum net_verdict verdict;
|
|
|
|
verdict = route_ipv6_packet(pkt, hdr);
|
|
if (verdict == NET_OK) {
|
|
return NET_OK;
|
|
}
|
|
#else /* CONFIG_NET_ROUTE */
|
|
NET_DBG("IPv6 packet in pkt %p not for me", pkt);
|
|
#endif /* CONFIG_NET_ROUTE */
|
|
|
|
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
|
|
goto drop;
|
|
}
|
|
|
|
/* If we receive a packet with ll source address fe80: and destination
|
|
* address is one of ours, and if the packet would cross interface
|
|
* boundary, then drop the packet. RFC 4291 ch 2.5.6
|
|
*/
|
|
if (IS_ENABLED(CONFIG_NET_ROUTING) &&
|
|
net_ipv6_is_ll_addr(&hdr->src) &&
|
|
!net_ipv6_is_addr_mcast(&hdr->dst) &&
|
|
!net_if_ipv6_addr_lookup_by_iface(net_pkt_iface(pkt),
|
|
&hdr->dst)) {
|
|
no_route_info(pkt, &hdr->src, &hdr->dst);
|
|
|
|
net_stats_update_ipv6_drop(net_pkt_iface(pkt));
|
|
goto drop;
|
|
}
|
|
|
|
/* Fast path for main upper layer protocols. The handling of extension
|
|
* headers can be slow so do this checking here. There cannot
|
|
* be any extension headers after the upper layer protocol header.
|
|
*/
|
|
next = *(net_pkt_next_hdr(pkt));
|
|
if (is_upper_layer_protocol_header(next)) {
|
|
goto upper_proto;
|
|
}
|
|
|
|
/* Go through the extensions */
|
|
frag = pkt->frags;
|
|
next = hdr->nexthdr;
|
|
first_option = next;
|
|
length = 0;
|
|
ext_bitmap = 0;
|
|
start_of_ext = 0;
|
|
offset = sizeof(struct net_ipv6_hdr);
|
|
prev_hdr = &NET_IPV6_HDR(pkt)->nexthdr - &NET_IPV6_HDR(pkt)->vtc;
|
|
|
|
while (frag) {
|
|
enum net_verdict verdict;
|
|
|
|
if (is_upper_layer_protocol_header(next)) {
|
|
NET_DBG("IPv6 next header %d", next);
|
|
goto upper_proto;
|
|
}
|
|
|
|
if (!start_of_ext) {
|
|
start_of_ext = offset;
|
|
}
|
|
|
|
frag = net_frag_read_u8(frag, offset, &offset, &next_hdr);
|
|
if (!frag) {
|
|
goto drop;
|
|
}
|
|
|
|
verdict = NET_OK;
|
|
|
|
NET_DBG("IPv6 next header %d", next);
|
|
|
|
switch (next) {
|
|
case NET_IPV6_NEXTHDR_NONE:
|
|
/* There is nothing after this header (see RFC 2460,
|
|
* ch 4.7), so we can drop the packet now.
|
|
* This is not an error case so do not update drop
|
|
* statistics.
|
|
*/
|
|
goto drop;
|
|
|
|
case NET_IPV6_NEXTHDR_DESTO:
|
|
frag = net_frag_read_u8(frag, offset, &offset,
|
|
(u8_t *)&length);
|
|
if (!frag) {
|
|
goto drop;
|
|
}
|
|
length = length * 8 + 8;
|
|
total_len += length;
|
|
|
|
ext_bitmap |= NET_IPV6_NEXTHDR_DESTO;
|
|
|
|
frag = handle_ext_hdr_options(pkt, frag, real_len,
|
|
length, offset, &offset,
|
|
&verdict);
|
|
break;
|
|
|
|
case NET_IPV6_NEXTHDR_HBHO:
|
|
if (ext_bitmap & NET_IPV6_EXT_HDR_BITMAP_HBHO) {
|
|
NET_ERR("Dropping packet with multiple HBHO");
|
|
goto drop;
|
|
}
|
|
|
|
frag = net_frag_read_u8(frag, offset, &offset,
|
|
(u8_t *)&length);
|
|
if (!frag) {
|
|
goto drop;
|
|
}
|
|
|
|
length = length * 8 + 8;
|
|
total_len += length;
|
|
|
|
/* HBH option needs to be the first one */
|
|
if (first_option != NET_IPV6_NEXTHDR_HBHO) {
|
|
goto bad_hdr;
|
|
}
|
|
|
|
ext_bitmap |= NET_IPV6_EXT_HDR_BITMAP_HBHO;
|
|
|
|
frag = handle_ext_hdr_options(pkt, frag, real_len,
|
|
length, offset, &offset,
|
|
&verdict);
|
|
break;
|
|
|
|
#if defined(CONFIG_NET_IPV6_FRAGMENT)
|
|
case NET_IPV6_NEXTHDR_FRAG:
|
|
net_pkt_set_ipv6_hdr_prev(pkt, prev_hdr);
|
|
|
|
net_pkt_set_ipv6_fragment_start(pkt,
|
|
sizeof(struct
|
|
net_ipv6_hdr) +
|
|
total_len);
|
|
|
|
total_len += 8;
|
|
return net_ipv6_handle_fragment_hdr(pkt, frag, real_len,
|
|
offset, &offset,
|
|
next_hdr);
|
|
#endif
|
|
default:
|
|
goto bad_hdr;
|
|
}
|
|
|
|
if (verdict == NET_DROP) {
|
|
goto drop;
|
|
}
|
|
|
|
prev_hdr = start_of_ext;
|
|
next = next_hdr;
|
|
}
|
|
|
|
upper_proto:
|
|
|
|
net_pkt_set_ipv6_ext_len(pkt, total_len);
|
|
net_pkt_set_transport_proto(pkt, next);
|
|
|
|
switch (next) {
|
|
case IPPROTO_ICMPV6:
|
|
return process_icmpv6_pkt(pkt, hdr);
|
|
case IPPROTO_UDP:
|
|
#if defined(CONFIG_NET_UDP)
|
|
return net_conn_input(IPPROTO_UDP, pkt);
|
|
#else
|
|
return NET_DROP;
|
|
#endif
|
|
case IPPROTO_TCP:
|
|
#if defined(CONFIG_NET_TCP)
|
|
return net_conn_input(IPPROTO_TCP, pkt);
|
|
#else
|
|
return NET_DROP;
|
|
#endif
|
|
}
|
|
|
|
drop:
|
|
return NET_DROP;
|
|
|
|
bad_hdr:
|
|
/* Send error message about parameter problem (RFC 2460)
|
|
*/
|
|
net_icmpv6_send_error(pkt, NET_ICMPV6_PARAM_PROBLEM,
|
|
NET_ICMPV6_PARAM_PROB_NEXTHEADER,
|
|
offset - 1);
|
|
|
|
NET_DBG("Unknown next header type");
|
|
net_stats_update_ip_errors_protoerr(net_pkt_iface(pkt));
|
|
|
|
return NET_DROP;
|
|
}
|
|
|
|
void net_ipv6_init(void)
|
|
{
|
|
net_ipv6_nbr_init();
|
|
|
|
#if defined(CONFIG_NET_IPV6_MLD)
|
|
net_ipv6_mld_init();
|
|
#endif
|
|
}
|