2016-04-29 14:52:09 +02:00
|
|
|
/** @file
|
|
|
|
* @brief Network initialization
|
|
|
|
*
|
2016-11-09 17:03:15 +01:00
|
|
|
* Initialize the network IP stack. Create one thread for reading data
|
|
|
|
* from IP stack and passing that data to applications (Rx thread).
|
2016-04-29 14:52:09 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation
|
|
|
|
*
|
2017-01-19 02:01:01 +01:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-04-29 14:52:09 +02:00
|
|
|
*/
|
|
|
|
|
2016-08-24 11:58:29 +02:00
|
|
|
#if defined(CONFIG_NET_DEBUG_CORE)
|
2016-05-09 14:36:46 +02:00
|
|
|
#define SYS_LOG_DOMAIN "net/core"
|
2016-12-15 13:55:01 +01:00
|
|
|
#define NET_LOG_ENABLED 1
|
2016-04-29 14:52:09 +02:00
|
|
|
#endif
|
|
|
|
|
2016-05-09 13:31:12 +02:00
|
|
|
#include <init.h>
|
2016-11-09 17:03:15 +01:00
|
|
|
#include <kernel.h>
|
2016-04-29 14:52:09 +02:00
|
|
|
#include <toolchain.h>
|
|
|
|
#include <sections.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2016-05-03 09:41:25 +02:00
|
|
|
#include <net/net_if.h>
|
2016-09-21 11:11:47 +02:00
|
|
|
#include <net/net_mgmt.h>
|
2016-05-24 16:30:03 +02:00
|
|
|
#include <net/arp.h>
|
2016-05-09 13:12:14 +02:00
|
|
|
#include <net/nbuf.h>
|
|
|
|
#include <net/net_core.h>
|
|
|
|
|
|
|
|
#include "net_private.h"
|
2016-11-03 13:47:08 +01:00
|
|
|
#include "net_shell.h"
|
2016-05-03 09:41:25 +02:00
|
|
|
|
2016-05-19 11:42:01 +02:00
|
|
|
#include "icmpv6.h"
|
2016-06-16 10:04:07 +02:00
|
|
|
#include "ipv6.h"
|
2016-05-19 11:42:01 +02:00
|
|
|
|
2016-05-19 15:19:59 +02:00
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
#include "icmpv4.h"
|
|
|
|
#endif
|
|
|
|
|
2017-02-22 13:36:31 +01:00
|
|
|
#if defined(CONFIG_NET_DHCPV4)
|
|
|
|
#include "dhcpv4.h"
|
|
|
|
#endif
|
|
|
|
|
2016-09-19 14:09:37 +02:00
|
|
|
#include "route.h"
|
2016-10-26 12:57:30 +02:00
|
|
|
#include "rpl.h"
|
|
|
|
|
2016-06-14 09:04:27 +02:00
|
|
|
#include "connection.h"
|
|
|
|
#include "udp.h"
|
2016-08-18 09:08:04 +02:00
|
|
|
#include "tcp.h"
|
2016-06-14 09:04:27 +02:00
|
|
|
|
2016-12-13 12:34:01 +01:00
|
|
|
#include "net_stats.h"
|
|
|
|
|
2016-11-09 17:03:15 +01:00
|
|
|
/* Stack for the rx thread.
|
2016-04-29 14:52:09 +02:00
|
|
|
*/
|
|
|
|
#if !defined(CONFIG_NET_RX_STACK_SIZE)
|
|
|
|
#define CONFIG_NET_RX_STACK_SIZE 1024
|
|
|
|
#endif
|
2016-11-18 12:28:45 +01:00
|
|
|
|
2016-11-28 14:05:02 +01:00
|
|
|
NET_STACK_DEFINE(RX, rx_stack, CONFIG_NET_RX_STACK_SIZE,
|
2016-11-18 12:28:45 +01:00
|
|
|
CONFIG_NET_RX_STACK_SIZE + CONFIG_NET_RX_STACK_RPL);
|
2016-11-18 14:10:08 +01:00
|
|
|
|
2016-11-09 17:03:15 +01:00
|
|
|
static struct k_fifo rx_queue;
|
|
|
|
static k_tid_t rx_tid;
|
2016-05-09 15:09:36 +02:00
|
|
|
|
2016-06-03 08:46:35 +02:00
|
|
|
#if defined(CONFIG_NET_IPV6)
|
2016-05-19 12:53:33 +02:00
|
|
|
static inline enum net_verdict process_icmpv6_pkt(struct net_buf *buf,
|
|
|
|
struct net_ipv6_hdr *ipv6)
|
|
|
|
{
|
|
|
|
struct net_icmp_hdr *hdr = NET_ICMP_BUF(buf);
|
|
|
|
|
2017-03-03 08:58:15 +01:00
|
|
|
NET_DBG("ICMPv6 packet received type %d code %d",
|
|
|
|
hdr->type, hdr->code);
|
2016-05-19 12:53:33 +02:00
|
|
|
|
2017-03-03 08:58:15 +01:00
|
|
|
return net_icmpv6_input(buf, hdr->type, hdr->code);
|
2016-05-19 12:53:33 +02:00
|
|
|
}
|
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
static inline struct net_buf *check_unknown_option(struct net_buf *buf,
|
|
|
|
uint8_t opt_type,
|
|
|
|
uint16_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 MSB %d", opt_type, opt_type >> 6);
|
|
|
|
|
|
|
|
switch (opt_type & 0xc0) {
|
|
|
|
case 0x00:
|
|
|
|
break;
|
|
|
|
case 0x40:
|
|
|
|
return NULL;
|
|
|
|
case 0xc0:
|
|
|
|
if (net_is_ipv6_addr_mcast(&NET_IPV6_BUF(buf)->dst)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* passthrough */
|
|
|
|
case 0x80:
|
|
|
|
net_icmpv6_send_error(buf, NET_ICMPV6_PARAM_PROBLEM,
|
|
|
|
NET_ICMPV6_PARAM_PROB_OPTION,
|
|
|
|
(uint32_t)length);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct net_buf *handle_ext_hdr_options(struct net_buf *buf,
|
|
|
|
struct net_buf *frag,
|
|
|
|
int total_len,
|
|
|
|
uint16_t len,
|
|
|
|
uint16_t offset,
|
|
|
|
uint16_t *pos,
|
|
|
|
enum net_verdict *verdict)
|
|
|
|
{
|
|
|
|
uint8_t opt_type, opt_len;
|
2017-03-03 12:40:44 +01:00
|
|
|
uint16_t length = 0, loc;
|
2017-03-03 12:40:44 +01:00
|
|
|
#if defined(CONFIG_NET_RPL)
|
|
|
|
bool result;
|
|
|
|
#endif
|
2016-10-24 12:23:12 +02:00
|
|
|
|
|
|
|
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 */
|
2017-03-03 12:40:44 +01:00
|
|
|
frag = net_nbuf_read_u8(frag, offset, &loc, &opt_type);
|
|
|
|
frag = net_nbuf_read_u8(frag, loc, &loc, &opt_len);
|
|
|
|
if (!frag && loc == 0xffff) {
|
2017-03-03 09:21:24 +01:00
|
|
|
goto drop;
|
|
|
|
}
|
2016-10-24 12:23:12 +02:00
|
|
|
|
|
|
|
while (frag && (length < len)) {
|
|
|
|
switch (opt_type) {
|
|
|
|
case NET_IPV6_EXT_HDR_OPT_PAD1:
|
|
|
|
NET_DBG("PAD1 option");
|
|
|
|
length++;
|
2017-03-03 12:40:44 +01:00
|
|
|
loc++;
|
2016-10-24 12:23:12 +02:00
|
|
|
break;
|
|
|
|
case NET_IPV6_EXT_HDR_OPT_PADN:
|
|
|
|
NET_DBG("PADN option");
|
|
|
|
length += opt_len + 2;
|
2017-03-03 12:40:44 +01:00
|
|
|
loc += opt_len + 2;
|
2016-10-24 12:23:12 +02:00
|
|
|
break;
|
2016-10-24 12:25:39 +02:00
|
|
|
#if defined(CONFIG_NET_RPL)
|
2017-03-03 09:26:48 +01:00
|
|
|
case NET_IPV6_EXT_HDR_OPT_RPL:
|
2016-10-24 12:25:39 +02:00
|
|
|
NET_DBG("Processing RPL option");
|
2017-03-03 12:40:44 +01:00
|
|
|
frag = net_rpl_verify_header(buf, frag, loc, &loc,
|
|
|
|
&result);
|
|
|
|
if (!result) {
|
2016-10-24 12:25:39 +02:00
|
|
|
NET_DBG("RPL option error, packet dropped");
|
2017-03-03 09:21:24 +01:00
|
|
|
goto drop;
|
2016-10-24 12:25:39 +02:00
|
|
|
}
|
2017-03-03 12:40:44 +01:00
|
|
|
|
|
|
|
if (!frag && *pos == 0xffff) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2016-10-24 12:25:39 +02:00
|
|
|
*verdict = NET_CONTINUE;
|
|
|
|
return frag;
|
2017-03-03 09:26:48 +01:00
|
|
|
#endif
|
2016-10-24 12:23:12 +02:00
|
|
|
default:
|
|
|
|
if (!check_unknown_option(frag, opt_type, length)) {
|
2017-03-03 09:21:24 +01:00
|
|
|
goto drop;
|
2016-10-24 12:23:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
length += opt_len + 2;
|
2017-03-03 12:40:44 +01:00
|
|
|
|
|
|
|
/* No need to +2 here as loc already contains option
|
|
|
|
* header len.
|
|
|
|
*/
|
|
|
|
loc += opt_len;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length >= len) {
|
2016-10-24 12:23:12 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-03 12:40:44 +01:00
|
|
|
frag = net_nbuf_read_u8(frag, loc, &loc, &opt_type);
|
|
|
|
frag = net_nbuf_read_u8(frag, loc, &loc, &opt_len);
|
|
|
|
if (!frag && loc == 0xffff) {
|
2017-03-03 09:21:24 +01:00
|
|
|
goto drop;
|
2016-10-24 12:23:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-03 12:40:44 +01:00
|
|
|
if (length != len) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pos += length;
|
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
*verdict = NET_CONTINUE;
|
|
|
|
return frag;
|
2017-03-03 09:21:24 +01:00
|
|
|
|
|
|
|
drop:
|
|
|
|
*verdict = NET_DROP;
|
|
|
|
return NULL;
|
2016-10-24 12:23:12 +02:00
|
|
|
}
|
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
static inline bool is_upper_layer_protocol_header(uint8_t proto)
|
|
|
|
{
|
|
|
|
return (proto == IPPROTO_ICMPV6 || proto == IPPROTO_UDP ||
|
|
|
|
proto == IPPROTO_TCP);
|
|
|
|
}
|
|
|
|
|
2016-05-17 12:03:02 +02:00
|
|
|
static inline enum net_verdict process_ipv6_pkt(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
struct net_ipv6_hdr *hdr = NET_IPV6_BUF(buf);
|
|
|
|
int real_len = net_buf_frags_len(buf);
|
|
|
|
int pkt_len = (hdr->len[0] << 8) + hdr->len[1] + sizeof(*hdr);
|
2016-10-24 12:23:12 +02:00
|
|
|
struct net_buf *frag;
|
|
|
|
uint8_t next, next_hdr, length;
|
|
|
|
uint8_t first_option;
|
2017-03-03 12:40:44 +01:00
|
|
|
uint16_t offset, total_len = 0;
|
2016-05-17 12:03:02 +02:00
|
|
|
|
2016-11-28 07:27:32 +01:00
|
|
|
if (real_len != pkt_len) {
|
2016-05-17 12:03:02 +02:00
|
|
|
NET_DBG("IPv6 packet size %d buf len %d", pkt_len, real_len);
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv6_drop();
|
2016-05-10 13:25:45 +02:00
|
|
|
goto drop;
|
2016-05-17 12:03:02 +02:00
|
|
|
}
|
|
|
|
|
2016-12-15 13:55:01 +01:00
|
|
|
#if defined(CONFIG_NET_DEBUG_CORE)
|
2016-05-17 12:03:02 +02:00
|
|
|
do {
|
2016-10-25 20:50:44 +02:00
|
|
|
char out[NET_IPV6_ADDR_LEN];
|
2017-01-23 12:05:40 +01:00
|
|
|
snprintk(out, sizeof(out), "%s",
|
|
|
|
net_sprint_ipv6_addr(&hdr->dst));
|
2016-05-17 12:03:02 +02:00
|
|
|
NET_DBG("IPv6 packet len %d received from %s to %s",
|
|
|
|
real_len, net_sprint_ipv6_addr(&hdr->src), out);
|
|
|
|
} while (0);
|
2016-12-15 13:55:01 +01:00
|
|
|
#endif /* CONFIG_NET_DEBUG_CORE */
|
2016-05-17 12:03:02 +02:00
|
|
|
|
2016-05-10 13:25:45 +02:00
|
|
|
if (net_is_ipv6_addr_mcast(&hdr->src)) {
|
|
|
|
NET_DBG("Dropping src multicast packet");
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv6_drop();
|
2016-05-10 13:25:45 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
2016-05-19 12:53:33 +02:00
|
|
|
if (!net_is_my_ipv6_addr(&hdr->dst) &&
|
|
|
|
!net_is_my_ipv6_maddr(&hdr->dst) &&
|
|
|
|
!net_is_ipv6_addr_mcast(&hdr->dst) &&
|
|
|
|
!net_is_ipv6_addr_loopback(&hdr->dst)) {
|
|
|
|
NET_DBG("IPv6 packet in buf %p not for me", buf);
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv6_drop();
|
2016-05-19 12:53:33 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check extension headers */
|
2016-06-20 17:22:07 +02:00
|
|
|
net_nbuf_set_next_hdr(buf, &hdr->nexthdr);
|
|
|
|
net_nbuf_set_ext_len(buf, 0);
|
|
|
|
net_nbuf_set_ext_bitmap(buf, 0);
|
2017-03-03 09:33:09 +01:00
|
|
|
net_nbuf_set_ip_hdr_len(buf, sizeof(struct net_ipv6_hdr));
|
2016-05-19 12:53:33 +02:00
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
2017-03-03 09:47:04 +01:00
|
|
|
next = *(net_nbuf_next_hdr(buf));
|
|
|
|
if (is_upper_layer_protocol_header(next)) {
|
|
|
|
goto upper_proto;
|
2016-10-24 12:23:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Go through the extensions */
|
|
|
|
frag = buf->frags;
|
|
|
|
next = hdr->nexthdr;
|
|
|
|
first_option = next;
|
|
|
|
offset = sizeof(struct net_ipv6_hdr);
|
|
|
|
|
|
|
|
while (frag) {
|
|
|
|
enum net_verdict verdict;
|
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
if (is_upper_layer_protocol_header(next)) {
|
|
|
|
NET_DBG("IPv6 next header %d", next);
|
|
|
|
net_nbuf_set_ext_len(buf, offset -
|
|
|
|
sizeof(struct net_ipv6_hdr));
|
|
|
|
goto upper_proto;
|
|
|
|
}
|
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
frag = net_nbuf_read_u8(frag, offset, &offset, &next_hdr);
|
|
|
|
frag = net_nbuf_read_u8(frag, offset, &offset, &length);
|
2017-03-03 09:47:04 +01:00
|
|
|
if (!frag && offset == 0xffff) {
|
2016-10-24 12:23:12 +02:00
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = length * 8 + 8;
|
2017-03-03 12:40:44 +01:00
|
|
|
total_len += length;
|
2016-10-24 12:23:12 +02:00
|
|
|
verdict = NET_OK;
|
|
|
|
|
|
|
|
/* Print the length only for IPv6 extension */
|
2017-03-03 09:47:04 +01:00
|
|
|
NET_DBG("IPv6 next header %d length %d bytes", next, length);
|
2016-05-19 12:53:33 +02:00
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
switch (next) {
|
2016-11-28 09:19:29 +01:00
|
|
|
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;
|
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
case NET_IPV6_NEXTHDR_HBHO:
|
2017-03-03 09:47:04 +01:00
|
|
|
/* HBH option needs to be the first one */
|
|
|
|
if (first_option != NET_IPV6_NEXTHDR_HBHO) {
|
|
|
|
goto bad_hdr;
|
|
|
|
}
|
|
|
|
|
2016-10-24 12:23:12 +02:00
|
|
|
/* Hop by hop option */
|
|
|
|
if (net_nbuf_ext_bitmap(buf) &
|
|
|
|
NET_IPV6_EXT_HDR_BITMAP_HBHO) {
|
|
|
|
goto bad_hdr;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_nbuf_add_ext_bitmap(buf,
|
|
|
|
NET_IPV6_EXT_HDR_BITMAP_HBHO);
|
|
|
|
|
|
|
|
frag = handle_ext_hdr_options(buf, frag, real_len,
|
|
|
|
length, offset, &offset,
|
|
|
|
&verdict);
|
2017-03-03 09:47:04 +01:00
|
|
|
break;
|
2016-10-24 12:23:12 +02:00
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
default:
|
|
|
|
goto bad_hdr;
|
|
|
|
}
|
2016-10-24 12:23:12 +02:00
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
if (verdict == NET_DROP) {
|
|
|
|
goto drop;
|
|
|
|
}
|
2016-10-24 12:23:12 +02:00
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
next = next_hdr;
|
|
|
|
}
|
2016-05-19 12:53:33 +02:00
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
upper_proto:
|
2017-03-03 12:40:44 +01:00
|
|
|
if (total_len > 0) {
|
|
|
|
NET_DBG("Extension len %d", total_len);
|
|
|
|
net_nbuf_set_ext_len(buf, total_len);
|
|
|
|
}
|
|
|
|
|
2017-03-03 09:47:04 +01:00
|
|
|
switch (next) {
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
return process_icmpv6_pkt(buf, hdr);
|
|
|
|
case IPPROTO_UDP:
|
2016-10-24 12:23:12 +02:00
|
|
|
#if defined(CONFIG_NET_UDP)
|
2017-03-03 09:47:04 +01:00
|
|
|
return net_conn_input(IPPROTO_UDP, buf);
|
|
|
|
#else
|
|
|
|
return NET_DROP;
|
2016-10-24 12:23:12 +02:00
|
|
|
#endif
|
2017-03-03 09:47:04 +01:00
|
|
|
case IPPROTO_TCP:
|
2016-10-24 12:23:12 +02:00
|
|
|
#if defined(CONFIG_NET_TCP)
|
2017-03-03 09:47:04 +01:00
|
|
|
return net_conn_input(IPPROTO_TCP, buf);
|
|
|
|
#else
|
|
|
|
return NET_DROP;
|
2016-10-24 12:23:12 +02:00
|
|
|
#endif
|
2016-05-19 12:53:33 +02:00
|
|
|
}
|
|
|
|
|
2016-05-10 13:25:45 +02:00
|
|
|
drop:
|
2016-05-17 12:03:02 +02:00
|
|
|
return NET_DROP;
|
2016-10-24 12:23:12 +02:00
|
|
|
|
|
|
|
bad_hdr:
|
|
|
|
/* Send error message about parameter problem (RFC 2460)
|
|
|
|
*/
|
|
|
|
net_icmpv6_send_error(buf, NET_ICMPV6_PARAM_PROBLEM,
|
|
|
|
NET_ICMPV6_PARAM_PROB_NEXTHEADER,
|
|
|
|
offset - 1);
|
|
|
|
|
|
|
|
NET_DBG("Unknown next header type");
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ip_errors_protoerr();
|
2016-10-24 12:23:12 +02:00
|
|
|
|
|
|
|
return NET_DROP;
|
2016-05-17 12:03:02 +02:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
|
2016-05-19 15:19:59 +02:00
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
static inline enum net_verdict process_icmpv4_pkt(struct net_buf *buf,
|
|
|
|
struct net_ipv4_hdr *ipv4)
|
|
|
|
{
|
|
|
|
struct net_icmp_hdr *hdr = NET_ICMP_BUF(buf);
|
|
|
|
uint16_t len = (ipv4->len[0] << 8) + ipv4->len[1];
|
|
|
|
|
|
|
|
NET_DBG("ICMPv4 packet received length %d type %d code %d",
|
|
|
|
len, hdr->type, hdr->code);
|
|
|
|
|
|
|
|
return net_icmpv4_input(buf, len, hdr->type, hdr->code);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
|
2016-05-17 12:17:15 +02:00
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
static inline enum net_verdict process_ipv4_pkt(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
struct net_ipv4_hdr *hdr = NET_IPV4_BUF(buf);
|
|
|
|
int real_len = net_buf_frags_len(buf);
|
|
|
|
int pkt_len = (hdr->len[0] << 8) + hdr->len[1];
|
2016-09-12 15:14:02 +02:00
|
|
|
enum net_verdict verdict = NET_DROP;
|
2016-05-17 12:17:15 +02:00
|
|
|
|
2016-11-28 07:27:32 +01:00
|
|
|
if (real_len != pkt_len) {
|
2016-05-17 12:17:15 +02:00
|
|
|
NET_DBG("IPv4 packet size %d buf len %d", pkt_len, real_len);
|
2016-09-12 15:14:02 +02:00
|
|
|
goto drop;
|
2016-05-17 12:17:15 +02:00
|
|
|
}
|
|
|
|
|
2016-12-15 13:55:01 +01:00
|
|
|
#if defined(CONFIG_NET_DEBUG_CORE)
|
2016-05-17 12:17:15 +02:00
|
|
|
do {
|
|
|
|
char out[sizeof("xxx.xxx.xxx.xxx")];
|
2017-01-23 12:05:40 +01:00
|
|
|
snprintk(out, sizeof(out), "%s",
|
|
|
|
net_sprint_ipv4_addr(&hdr->dst));
|
2016-05-17 12:17:15 +02:00
|
|
|
NET_DBG("IPv4 packet received from %s to %s",
|
|
|
|
net_sprint_ipv4_addr(&hdr->src), out);
|
|
|
|
} while (0);
|
2016-12-15 13:55:01 +01:00
|
|
|
#endif /* CONFIG_NET_DEBUG_CORE */
|
2016-05-17 12:17:15 +02:00
|
|
|
|
2016-09-12 15:14:02 +02:00
|
|
|
net_nbuf_set_ip_hdr_len(buf, sizeof(struct net_ipv4_hdr));
|
|
|
|
|
2016-05-17 12:17:15 +02:00
|
|
|
if (!net_is_my_ipv4_addr(&hdr->dst)) {
|
2016-09-12 15:14:02 +02:00
|
|
|
#if defined(CONFIG_NET_DHCPV4)
|
|
|
|
if (hdr->proto == IPPROTO_UDP &&
|
|
|
|
net_ipv4_addr_cmp(&hdr->dst,
|
|
|
|
net_ipv4_broadcast_address())) {
|
|
|
|
|
|
|
|
verdict = net_conn_input(IPPROTO_UDP, buf);
|
|
|
|
if (verdict != NET_DROP) {
|
|
|
|
return verdict;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-17 12:17:15 +02:00
|
|
|
NET_DBG("IPv4 packet in buf %p not for me", buf);
|
|
|
|
goto drop;
|
|
|
|
}
|
2016-05-19 15:19:59 +02:00
|
|
|
|
|
|
|
switch (hdr->proto) {
|
|
|
|
case IPPROTO_ICMP:
|
2016-09-12 15:14:02 +02:00
|
|
|
verdict = process_icmpv4_pkt(buf, hdr);
|
|
|
|
break;
|
2016-06-14 09:04:27 +02:00
|
|
|
case IPPROTO_UDP:
|
2016-09-12 15:14:02 +02:00
|
|
|
verdict = net_conn_input(IPPROTO_UDP, buf);
|
|
|
|
break;
|
2016-08-18 09:08:04 +02:00
|
|
|
case IPPROTO_TCP:
|
|
|
|
verdict = net_conn_input(IPPROTO_TCP, buf);
|
|
|
|
break;
|
2016-09-12 15:14:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (verdict != NET_DROP) {
|
|
|
|
return verdict;
|
2016-05-19 15:19:59 +02:00
|
|
|
}
|
2016-05-17 12:17:15 +02:00
|
|
|
|
|
|
|
drop:
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv4_drop();
|
2016-05-17 12:17:15 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
static inline enum net_verdict process_data(struct net_buf *buf,
|
|
|
|
bool is_loopback)
|
2016-05-09 15:09:36 +02:00
|
|
|
{
|
2016-06-01 15:06:27 +02:00
|
|
|
int ret;
|
|
|
|
|
2016-05-17 12:03:02 +02:00
|
|
|
/* If there is no data, then drop the packet. Also if
|
|
|
|
* the buffer is wrong type, then also drop the packet.
|
|
|
|
* The first buffer needs to have user data part that
|
|
|
|
* contains user data. The rest of the fragments should
|
|
|
|
* be data fragments without user data.
|
|
|
|
*/
|
2016-12-14 08:57:49 +01:00
|
|
|
if (!buf->frags || !buf->pool->user_data_size) {
|
2016-12-06 18:20:03 +01:00
|
|
|
NET_DBG("Corrupted buffer (frags %p, data size %u)",
|
2016-12-14 08:57:49 +01:00
|
|
|
buf->frags, buf->pool->user_data_size);
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_processing_error();
|
2016-07-12 15:33:33 +02:00
|
|
|
|
2016-05-17 12:03:02 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
if (!is_loopback) {
|
|
|
|
ret = net_if_recv_data(net_nbuf_iface(buf), buf);
|
|
|
|
if (ret != NET_CONTINUE) {
|
|
|
|
if (ret == NET_DROP) {
|
|
|
|
NET_DBG("Buffer %p discarded by L2", buf);
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_processing_error();
|
2016-11-12 12:02:27 +01:00
|
|
|
}
|
2016-06-01 15:06:27 +02:00
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2016-05-24 16:30:03 +02:00
|
|
|
}
|
|
|
|
|
2016-05-17 12:03:02 +02:00
|
|
|
/* IP version and header length. */
|
|
|
|
switch (NET_IPV6_BUF(buf)->vtc & 0xf0) {
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
|
|
case 0x60:
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv6_recv();
|
2016-06-20 17:22:07 +02:00
|
|
|
net_nbuf_set_family(buf, PF_INET6);
|
2016-05-17 12:03:02 +02:00
|
|
|
return process_ipv6_pkt(buf);
|
2016-05-17 12:17:15 +02:00
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
case 0x40:
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv4_recv();
|
2016-06-20 17:22:07 +02:00
|
|
|
net_nbuf_set_family(buf, PF_INET);
|
2016-05-17 12:17:15 +02:00
|
|
|
return process_ipv4_pkt(buf);
|
2016-05-17 12:03:02 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-11-09 21:36:56 +01:00
|
|
|
NET_DBG("Unknown IP family packet (0x%x)",
|
|
|
|
NET_IPV6_BUF(buf)->vtc & 0xf0);
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ip_errors_protoerr();
|
|
|
|
net_stats_update_ip_errors_vhlerr();
|
2016-07-12 15:33:33 +02:00
|
|
|
|
2016-05-09 15:09:36 +02:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
2016-04-29 14:52:09 +02:00
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
static void processing_data(struct net_buf *buf, bool is_loopback)
|
|
|
|
{
|
|
|
|
switch (process_data(buf, is_loopback)) {
|
|
|
|
case NET_OK:
|
|
|
|
NET_DBG("Consumed buf %p", buf);
|
|
|
|
break;
|
|
|
|
case NET_DROP:
|
|
|
|
default:
|
|
|
|
NET_DBG("Dropping buf %p", buf);
|
|
|
|
net_nbuf_unref(buf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-09 17:03:15 +01:00
|
|
|
static void net_rx_thread(void)
|
2016-04-29 14:52:09 +02:00
|
|
|
{
|
2016-05-09 15:09:36 +02:00
|
|
|
struct net_buf *buf;
|
|
|
|
|
2016-12-06 18:20:03 +01:00
|
|
|
NET_DBG("Starting RX thread (stack %zu bytes)", sizeof(rx_stack));
|
2016-05-09 15:09:36 +02:00
|
|
|
|
2016-06-01 09:35:00 +02:00
|
|
|
/* Starting TX side. The ordering is important here and the TX
|
|
|
|
* can only be started when RX side is ready to receive packets.
|
|
|
|
*/
|
|
|
|
net_if_init();
|
|
|
|
|
2016-05-09 15:09:36 +02:00
|
|
|
while (1) {
|
2017-02-03 11:36:16 +01:00
|
|
|
#if defined(CONFIG_NET_STATISTICS) || defined(CONFIG_NET_DEBUG_CORE)
|
|
|
|
size_t pkt_len;
|
|
|
|
#endif
|
|
|
|
|
2016-10-18 22:24:51 +02:00
|
|
|
buf = net_buf_get(&rx_queue, K_FOREVER);
|
2016-05-09 15:09:36 +02:00
|
|
|
|
2016-11-09 17:03:15 +01:00
|
|
|
net_analyze_stack("RX thread", rx_stack, sizeof(rx_stack));
|
2016-05-09 15:09:36 +02:00
|
|
|
|
2017-02-03 11:36:16 +01:00
|
|
|
#if defined(CONFIG_NET_STATISTICS) || defined(CONFIG_NET_DEBUG_CORE)
|
|
|
|
pkt_len = net_buf_frags_len(buf);
|
|
|
|
#endif
|
|
|
|
NET_DBG("Received buf %p len %zu", buf, pkt_len);
|
|
|
|
|
|
|
|
net_stats_update_bytes_recv(pkt_len);
|
2016-05-09 15:09:36 +02:00
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
processing_data(buf, false);
|
2016-05-09 16:39:09 +02:00
|
|
|
|
|
|
|
net_print_statistics();
|
2016-07-06 15:12:02 +02:00
|
|
|
net_nbuf_print();
|
2016-06-08 13:16:17 +02:00
|
|
|
|
2016-11-09 17:03:15 +01:00
|
|
|
k_yield();
|
2016-04-29 14:52:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_rx_queue(void)
|
|
|
|
{
|
2016-11-09 17:03:15 +01:00
|
|
|
k_fifo_init(&rx_queue);
|
2016-04-29 14:52:09 +02:00
|
|
|
|
2016-11-09 17:03:15 +01:00
|
|
|
rx_tid = k_thread_spawn(rx_stack, sizeof(rx_stack),
|
|
|
|
(k_thread_entry_t)net_rx_thread,
|
2017-03-07 11:31:20 +01:00
|
|
|
NULL, NULL, NULL, K_PRIO_COOP(8),
|
2017-03-07 11:32:48 +01:00
|
|
|
K_ESSENTIAL, K_NO_WAIT);
|
2016-04-29 14:52:09 +02:00
|
|
|
}
|
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
#if defined(CONFIG_NET_IP_ADDR_CHECK)
|
|
|
|
/* Check if the IPv{4|6} addresses are proper. As this can be expensive,
|
|
|
|
* make this optional.
|
|
|
|
*/
|
|
|
|
static inline int check_ip_addr(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
|
|
if (net_nbuf_family(buf) == AF_INET6) {
|
|
|
|
if (net_ipv6_addr_cmp(&NET_IPV6_BUF(buf)->dst,
|
|
|
|
net_ipv6_unspecified_address())) {
|
|
|
|
NET_DBG("IPv6 dst address missing");
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_is_ipv6_addr_loopback(&NET_IPV6_BUF(buf)->dst)) {
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
|
|
|
/* Swap the addresses so that in receiving side
|
|
|
|
* the packet is accepted.
|
|
|
|
*/
|
|
|
|
net_ipaddr_copy(&addr, &NET_IPV6_BUF(buf)->src);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_BUF(buf)->src,
|
|
|
|
&NET_IPV6_BUF(buf)->dst);
|
|
|
|
net_ipaddr_copy(&NET_IPV6_BUF(buf)->dst, &addr);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The source check must be done after the destination check
|
|
|
|
* as having src ::1 is perfectly ok if dst is ::1 too.
|
|
|
|
*/
|
|
|
|
if (net_is_ipv6_addr_loopback(&NET_IPV6_BUF(buf)->src)) {
|
|
|
|
NET_DBG("IPv6 loopback src address");
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
if (net_nbuf_family(buf) == AF_INET) {
|
|
|
|
if (net_ipv4_addr_cmp(&NET_IPV4_BUF(buf)->dst,
|
|
|
|
net_ipv4_unspecified_address())) {
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_is_ipv4_addr_loopback(&NET_IPV4_BUF(buf)->dst)) {
|
|
|
|
struct in_addr addr;
|
|
|
|
|
|
|
|
/* Swap the addresses so that in receiving side
|
|
|
|
* the packet is accepted.
|
|
|
|
*/
|
|
|
|
net_ipaddr_copy(&addr, &NET_IPV4_BUF(buf)->src);
|
|
|
|
net_ipaddr_copy(&NET_IPV4_BUF(buf)->src,
|
|
|
|
&NET_IPV4_BUF(buf)->dst);
|
|
|
|
net_ipaddr_copy(&NET_IPV4_BUF(buf)->dst, &addr);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The source check must be done after the destination check
|
|
|
|
* as having src 127.0.0.0/8 is perfectly ok if dst is in
|
|
|
|
* localhost subnet too.
|
|
|
|
*/
|
|
|
|
if (net_is_ipv4_addr_loopback(&NET_IPV4_BUF(buf)->src)) {
|
|
|
|
NET_DBG("IPv4 loopback src address");
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
|
|
|
|
{
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define check_ip_addr(buf) 0
|
|
|
|
#endif
|
|
|
|
|
2016-05-19 11:12:33 +02:00
|
|
|
/* Called when data needs to be sent to network */
|
|
|
|
int net_send_data(struct net_buf *buf)
|
|
|
|
{
|
2016-11-12 12:02:27 +01:00
|
|
|
int status;
|
|
|
|
|
2016-05-19 11:12:33 +02:00
|
|
|
if (!buf || !buf->frags) {
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!net_nbuf_iface(buf)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_STATISTICS)
|
|
|
|
switch (net_nbuf_family(buf)) {
|
|
|
|
case AF_INET:
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv4_sent();
|
2016-05-19 11:12:33 +02:00
|
|
|
break;
|
|
|
|
case AF_INET6:
|
2016-12-13 14:50:31 +01:00
|
|
|
net_stats_update_ipv6_sent();
|
2016-05-19 11:12:33 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-11-12 12:02:27 +01:00
|
|
|
status = check_ip_addr(buf);
|
|
|
|
if (status < 0) {
|
|
|
|
return status;
|
|
|
|
} else if (status > 0) {
|
|
|
|
/* Packet is destined back to us so send it directly
|
|
|
|
* to RX processing.
|
|
|
|
*/
|
|
|
|
processing_data(buf, true);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 11:56:49 +02:00
|
|
|
if (net_if_send_data(net_nbuf_iface(buf), buf) == NET_DROP) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2016-05-19 11:12:33 +02:00
|
|
|
}
|
|
|
|
|
2016-05-03 09:41:25 +02:00
|
|
|
/* Called by driver when an IP packet has been received */
|
2016-05-19 11:02:28 +02:00
|
|
|
int net_recv_data(struct net_if *iface, struct net_buf *buf)
|
2016-05-03 09:41:25 +02:00
|
|
|
{
|
|
|
|
if (!buf->frags) {
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
|
2016-12-06 18:20:03 +01:00
|
|
|
NET_DBG("fifo %p iface %p buf %p len %zu", &rx_queue, iface, buf,
|
2016-05-09 15:09:36 +02:00
|
|
|
net_buf_frags_len(buf));
|
|
|
|
|
2016-06-20 17:22:07 +02:00
|
|
|
net_nbuf_set_iface(buf, iface);
|
2016-05-09 15:09:36 +02:00
|
|
|
|
2016-06-08 07:36:55 +02:00
|
|
|
net_buf_put(&rx_queue, buf);
|
2016-05-03 09:41:25 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 12:01:42 +02:00
|
|
|
static inline void l3_init(void)
|
2016-04-29 14:52:09 +02:00
|
|
|
{
|
2016-05-19 11:42:01 +02:00
|
|
|
net_icmpv6_init();
|
2016-06-07 15:34:49 +02:00
|
|
|
net_ipv6_init();
|
2016-06-03 12:01:42 +02:00
|
|
|
|
2016-06-14 09:04:27 +02:00
|
|
|
#if defined(CONFIG_NET_UDP) || defined(CONFIG_NET_TCP)
|
|
|
|
net_conn_init();
|
|
|
|
#endif
|
|
|
|
net_udp_init();
|
2016-08-18 09:08:04 +02:00
|
|
|
net_tcp_init();
|
2016-06-14 09:04:27 +02:00
|
|
|
|
2016-09-19 14:09:37 +02:00
|
|
|
net_route_init();
|
|
|
|
|
2016-06-03 12:01:42 +02:00
|
|
|
NET_DBG("Network L3 init done");
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void l2_init(void)
|
|
|
|
{
|
|
|
|
net_arp_init();
|
|
|
|
|
|
|
|
NET_DBG("Network L2 init done");
|
2016-04-29 14:52:09 +02:00
|
|
|
}
|
|
|
|
|
2016-05-09 13:31:12 +02:00
|
|
|
static int net_init(struct device *unused)
|
2016-04-29 14:52:09 +02:00
|
|
|
{
|
2017-02-22 13:36:31 +01:00
|
|
|
int status = 0;
|
|
|
|
|
2016-05-09 13:31:12 +02:00
|
|
|
NET_DBG("Priority %d", CONFIG_NET_INIT_PRIO);
|
2016-04-29 14:52:09 +02:00
|
|
|
|
2016-11-03 13:47:08 +01:00
|
|
|
net_shell_init();
|
|
|
|
|
2016-05-09 13:12:14 +02:00
|
|
|
net_nbuf_init();
|
|
|
|
|
2016-06-01 09:35:00 +02:00
|
|
|
net_context_init();
|
2016-04-29 14:52:09 +02:00
|
|
|
|
2016-06-03 12:01:42 +02:00
|
|
|
l2_init();
|
|
|
|
l3_init();
|
2016-05-09 14:47:01 +02:00
|
|
|
|
2016-09-28 17:03:54 +02:00
|
|
|
net_mgmt_event_init();
|
2016-09-21 11:11:47 +02:00
|
|
|
|
2016-06-01 09:35:00 +02:00
|
|
|
init_rx_queue();
|
2016-05-09 14:49:48 +02:00
|
|
|
|
2017-02-22 13:36:31 +01:00
|
|
|
#if CONFIG_NET_DHCPV4
|
|
|
|
status = dhcpv4_init();
|
|
|
|
if (status) {
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return status;
|
2016-04-29 14:52:09 +02:00
|
|
|
}
|
2016-05-09 13:31:12 +02:00
|
|
|
|
2016-11-09 19:47:30 +01:00
|
|
|
SYS_INIT(net_init, POST_KERNEL, CONFIG_NET_INIT_PRIO);
|