2016-06-01 15:06:27 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Intel Corporation.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2016-06-08 08:39:46 +03:00
|
|
|
#if defined(CONFIG_NETWORK_IP_STACK_DEBUG_L2)
|
|
|
|
#define SYS_LOG_DOMAIN "net/l2"
|
|
|
|
#define NET_DEBUG 1
|
|
|
|
#endif
|
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
#include <net/net_core.h>
|
|
|
|
#include <net/net_l2.h>
|
|
|
|
#include <net/net_if.h>
|
|
|
|
#include <net/arp.h>
|
|
|
|
|
2016-06-08 08:39:46 +03:00
|
|
|
#include "net_private.h"
|
|
|
|
|
2016-06-10 09:08:29 +03:00
|
|
|
static const struct net_eth_addr multicast_eth_addr = {
|
|
|
|
{ 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 } };
|
|
|
|
|
2016-06-08 14:13:27 +03:00
|
|
|
static const struct net_eth_addr broadcast_eth_addr = {
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
|
|
|
|
|
|
|
|
const struct net_eth_addr *net_eth_broadcast_addr(void)
|
|
|
|
{
|
|
|
|
return &broadcast_eth_addr;
|
|
|
|
}
|
|
|
|
|
2016-06-08 08:39:46 +03:00
|
|
|
#if NET_DEBUG
|
2016-06-08 14:38:45 +03:00
|
|
|
#define print_ll_addrs(buf, type, len) \
|
2016-06-08 08:39:46 +03:00
|
|
|
do { \
|
|
|
|
char out[sizeof("xx:xx:xx:xx:xx:xx")]; \
|
|
|
|
\
|
|
|
|
snprintf(out, sizeof(out), \
|
|
|
|
net_sprint_ll_addr(net_nbuf_ll_src(buf)->addr, \
|
|
|
|
sizeof(struct net_eth_addr))); \
|
|
|
|
\
|
2016-06-08 14:38:45 +03:00
|
|
|
NET_DBG("src %s dst %s type 0x%x len %u", out, \
|
2016-06-08 08:39:46 +03:00
|
|
|
net_sprint_ll_addr(net_nbuf_ll_dst(buf)->addr, \
|
|
|
|
sizeof(struct net_eth_addr)), \
|
2016-06-08 14:38:45 +03:00
|
|
|
type, len); \
|
2016-06-08 08:39:46 +03:00
|
|
|
} while (0)
|
|
|
|
#else
|
|
|
|
#define print_ll_addrs(...)
|
|
|
|
#endif
|
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
static enum net_verdict ethernet_recv(struct net_if *iface,
|
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
2016-06-03 12:40:19 +03:00
|
|
|
struct net_eth_hdr *hdr = NET_ETH_BUF(buf);
|
2016-06-07 14:17:51 +03:00
|
|
|
struct net_linkaddr *lladdr;
|
2016-06-03 12:40:19 +03:00
|
|
|
|
|
|
|
switch (ntohs(hdr->type)) {
|
|
|
|
case NET_ETH_PTYPE_IP:
|
|
|
|
case NET_ETH_PTYPE_ARP:
|
|
|
|
net_nbuf_family(buf) = AF_INET;
|
|
|
|
break;
|
|
|
|
case NET_ETH_PTYPE_IPV6:
|
|
|
|
net_nbuf_family(buf) = AF_INET6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-06-08 14:32:31 +03:00
|
|
|
net_nbuf_ll_reserve(buf) = sizeof(struct net_eth_hdr);
|
|
|
|
|
2016-06-07 14:17:51 +03:00
|
|
|
/* Set the pointers to ll src and dst addresses */
|
|
|
|
lladdr = net_nbuf_ll_src(buf);
|
|
|
|
lladdr->addr = ((struct net_eth_hdr *)net_nbuf_ll(buf))->src.addr;
|
|
|
|
lladdr->len = sizeof(struct net_eth_hdr);
|
|
|
|
|
|
|
|
lladdr = net_nbuf_ll_dst(buf);
|
|
|
|
lladdr->addr = ((struct net_eth_hdr *)net_nbuf_ll(buf))->dst.addr;
|
|
|
|
lladdr->len = sizeof(struct net_eth_hdr);
|
|
|
|
|
2016-06-08 14:38:45 +03:00
|
|
|
print_ll_addrs(buf, ntohs(hdr->type), net_buf_frags_len(buf));
|
2016-06-08 08:39:46 +03:00
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
#ifdef CONFIG_NET_ARP
|
2016-06-02 17:22:42 +03:00
|
|
|
if (net_nbuf_family(buf) == AF_INET &&
|
2016-06-08 14:37:15 +03:00
|
|
|
hdr->type == htons(NET_ETH_PTYPE_ARP)) {
|
|
|
|
NET_DBG("ARP packet from %s received",
|
|
|
|
net_sprint_ll_addr((uint8_t *)hdr->src.addr,
|
|
|
|
sizeof(struct net_eth_addr)));
|
|
|
|
return net_arp_input(buf);
|
2016-06-01 15:06:27 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return NET_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum net_verdict ethernet_send(struct net_if *iface,
|
|
|
|
struct net_buf *buf)
|
|
|
|
{
|
2016-06-08 14:38:45 +03:00
|
|
|
struct net_eth_hdr *hdr = NET_ETH_BUF(buf);
|
|
|
|
struct net_buf *frag;
|
|
|
|
uint16_t ptype;
|
2016-06-07 14:19:12 +03:00
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
#ifdef CONFIG_NET_ARP
|
2016-06-02 17:22:42 +03:00
|
|
|
if (net_nbuf_family(buf) == AF_INET) {
|
|
|
|
struct net_buf *arp_buf = net_arp_prepare(buf);
|
2016-06-01 15:06:27 +02:00
|
|
|
|
2016-06-02 17:22:42 +03:00
|
|
|
if (!arp_buf) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
2016-06-01 15:06:27 +02:00
|
|
|
|
2016-06-08 14:38:45 +03:00
|
|
|
NET_DBG("Sending arp buf %p (orig %p) to iface %p",
|
|
|
|
arp_buf, buf, iface);
|
|
|
|
|
2016-06-02 17:22:42 +03:00
|
|
|
buf = arp_buf;
|
2016-06-08 14:38:45 +03:00
|
|
|
|
|
|
|
net_nbuf_ll_src(buf)->addr = (uint8_t *)&NET_ETH_BUF(buf)->src;
|
|
|
|
net_nbuf_ll_src(buf)->len = sizeof(struct net_eth_addr);
|
|
|
|
net_nbuf_ll_dst(buf)->addr = (uint8_t *)&NET_ETH_BUF(buf)->dst;
|
|
|
|
net_nbuf_ll_dst(buf)->len = sizeof(struct net_eth_addr);
|
|
|
|
|
|
|
|
/* For ARP message, we do not touch the packet further but will
|
|
|
|
* send it as it is because the arp.c has prepared the packet
|
|
|
|
* already.
|
|
|
|
*/
|
|
|
|
goto send;
|
2016-06-02 17:22:42 +03:00
|
|
|
}
|
2016-06-08 14:38:45 +03:00
|
|
|
#else
|
|
|
|
NET_DBG("Sending buf %p to iface %p", buf, iface);
|
2016-06-01 15:06:27 +02:00
|
|
|
#endif
|
|
|
|
|
2016-06-08 14:38:45 +03:00
|
|
|
/* If the ll address is not set at all, then we must set
|
2016-06-07 14:20:15 +03:00
|
|
|
* it here.
|
|
|
|
*/
|
2016-06-08 14:38:45 +03:00
|
|
|
if (!net_nbuf_ll_src(buf)->addr) {
|
2016-06-07 14:20:15 +03:00
|
|
|
net_nbuf_ll_src(buf)->addr = net_nbuf_ll_if(buf)->addr;
|
|
|
|
net_nbuf_ll_src(buf)->len = net_nbuf_ll_if(buf)->len;
|
2016-06-08 14:38:45 +03:00
|
|
|
} else {
|
|
|
|
/* If the destination address is my address, then
|
|
|
|
* swap src and dst.
|
|
|
|
*/
|
|
|
|
if (!memcmp(net_nbuf_ll_if(buf)->addr,
|
|
|
|
net_nbuf_ll_dst(buf)->addr,
|
|
|
|
sizeof(struct net_eth_addr))) {
|
|
|
|
net_nbuf_ll_swap(buf);
|
|
|
|
} else {
|
|
|
|
/* If the src ll address is multicast or broadcast, then
|
|
|
|
* what probably happened is that the RX buffer is used
|
|
|
|
* for sending data back to recipient. We must
|
|
|
|
* substitute the src address using the real ll address.
|
|
|
|
*/
|
|
|
|
if (net_eth_is_addr_broadcast((struct net_eth_addr *)
|
|
|
|
net_nbuf_ll_src(buf)->addr) ||
|
|
|
|
net_eth_is_addr_multicast((struct net_eth_addr *)
|
|
|
|
net_nbuf_ll_src(buf)->addr)) {
|
|
|
|
net_nbuf_ll_src(buf)->addr =
|
|
|
|
net_nbuf_ll_if(buf)->addr;
|
|
|
|
net_nbuf_ll_src(buf)->len =
|
|
|
|
net_nbuf_ll_if(buf)->len;
|
|
|
|
}
|
|
|
|
}
|
2016-06-07 14:20:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If the destination address is not set, then use broadcast
|
2016-06-10 09:08:29 +03:00
|
|
|
* or multicast address.
|
2016-06-07 14:20:15 +03:00
|
|
|
*/
|
|
|
|
if (!net_nbuf_ll_dst(buf)->addr) {
|
2016-06-10 09:08:29 +03:00
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
|
|
if (net_nbuf_family(buf) == AF_INET6 &&
|
|
|
|
net_is_ipv6_addr_mcast(&NET_IPV6_BUF(buf)->dst)) {
|
|
|
|
struct net_eth_addr *dst = &NET_ETH_BUF(buf)->dst;
|
|
|
|
|
|
|
|
memcpy(dst, (uint8_t *)multicast_eth_addr.addr,
|
|
|
|
sizeof(struct net_eth_addr) - 4);
|
|
|
|
memcpy((uint8_t *)dst + 2,
|
|
|
|
(uint8_t *)(&NET_IPV6_BUF(buf)->dst) + 12,
|
|
|
|
sizeof(struct net_eth_addr) - 2);
|
|
|
|
|
|
|
|
net_nbuf_ll_dst(buf)->addr = (uint8_t *)dst->addr;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
net_nbuf_ll_dst(buf)->addr =
|
|
|
|
(uint8_t *)broadcast_eth_addr.addr;
|
|
|
|
}
|
|
|
|
|
2016-06-07 14:20:15 +03:00
|
|
|
net_nbuf_ll_dst(buf)->len = sizeof(struct net_eth_addr);
|
|
|
|
|
2016-06-08 14:38:45 +03:00
|
|
|
NET_DBG("Destination address was not set, using %s",
|
|
|
|
net_sprint_ll_addr(net_nbuf_ll_dst(buf)->addr,
|
|
|
|
net_nbuf_ll_dst(buf)->len));
|
|
|
|
}
|
2016-06-07 14:19:12 +03:00
|
|
|
|
|
|
|
if (net_nbuf_family(buf) == AF_INET) {
|
2016-06-08 14:38:45 +03:00
|
|
|
ptype = htons(NET_ETH_PTYPE_IP);
|
2016-06-07 14:19:12 +03:00
|
|
|
} else {
|
2016-06-08 14:38:45 +03:00
|
|
|
ptype = htons(NET_ETH_PTYPE_IPV6);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Then go through the fragments and set the ethernet header.
|
|
|
|
*/
|
|
|
|
frag = buf->frags;
|
|
|
|
|
|
|
|
NET_ASSERT_INFO(frag, "No data!");
|
|
|
|
|
|
|
|
while (frag) {
|
2016-06-09 14:19:11 +03:00
|
|
|
hdr = (struct net_eth_hdr *)(frag->data -
|
|
|
|
net_nbuf_ll_reserve(buf));
|
2016-06-08 14:38:45 +03:00
|
|
|
memcpy(&hdr->dst, net_nbuf_ll_dst(buf)->addr,
|
|
|
|
sizeof(struct net_eth_addr));
|
|
|
|
memcpy(&hdr->src, net_nbuf_ll_src(buf)->addr,
|
|
|
|
sizeof(struct net_eth_addr));
|
|
|
|
hdr->type = ptype;
|
|
|
|
print_ll_addrs(buf, ntohs(hdr->type), frag->len);
|
|
|
|
|
|
|
|
frag = frag->frags;
|
2016-06-07 14:19:12 +03:00
|
|
|
}
|
|
|
|
|
2016-06-08 14:38:45 +03:00
|
|
|
send:
|
2016-06-01 15:06:27 +02:00
|
|
|
net_if_queue_tx(iface, buf);
|
|
|
|
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
2016-06-07 15:30:59 +03:00
|
|
|
static inline uint16_t get_reserve(struct net_if *iface)
|
|
|
|
{
|
|
|
|
return sizeof(struct net_eth_hdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_L2_INIT(ETHERNET_L2, ethernet_recv, ethernet_send, get_reserve);
|