2016-06-01 15:06:27 +02:00
|
|
|
/*
|
2019-01-07 15:18:53 +02:00
|
|
|
* Copyright (c) 2016-2018 Intel Corporation.
|
2016-06-01 15:06:27 +02:00
|
|
|
*
|
2017-01-18 17:01:01 -08:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2016-06-01 15:06:27 +02:00
|
|
|
*/
|
|
|
|
|
2018-11-30 12:54:56 +02:00
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(net_ethernet, CONFIG_NET_L2_ETHERNET_LOG_LEVEL);
|
2016-06-08 08:39:46 +03:00
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
#include <net/net_core.h>
|
|
|
|
#include <net/net_l2.h>
|
|
|
|
#include <net/net_if.h>
|
2018-06-04 13:37:17 +03:00
|
|
|
#include <net/net_mgmt.h>
|
2016-07-05 15:56:08 +03:00
|
|
|
#include <net/ethernet.h>
|
2018-06-04 13:37:17 +03:00
|
|
|
#include <net/ethernet_mgmt.h>
|
2018-01-24 14:33:35 +02:00
|
|
|
#include <net/gptp.h>
|
2019-02-26 09:56:06 +02:00
|
|
|
|
|
|
|
#if defined(CONFIG_NET_LLDP)
|
2017-04-05 17:17:12 -07:00
|
|
|
#include <net/lldp.h>
|
2019-02-26 09:56:06 +02:00
|
|
|
#endif
|
2016-06-01 15:06:27 +02:00
|
|
|
|
2019-05-27 12:44:01 +08:00
|
|
|
#include <syscall_handler.h>
|
|
|
|
|
2018-06-15 21:08:27 +02:00
|
|
|
#include "arp.h"
|
2018-12-05 14:39:43 +01:00
|
|
|
#include "eth_stats.h"
|
2016-06-08 08:39:46 +03:00
|
|
|
#include "net_private.h"
|
2016-06-28 17:33:16 +03:00
|
|
|
#include "ipv6.h"
|
2018-07-30 18:28:35 +03:00
|
|
|
#include "ipv4_autoconf_internal.h"
|
2016-06-08 08:39:46 +03:00
|
|
|
|
2018-06-08 15:35:27 +02:00
|
|
|
#define NET_BUF_TIMEOUT K_MSEC(100)
|
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
static const struct net_eth_addr multicast_eth_addr __unused = {
|
2016-06-10 09:08:29 +03:00
|
|
|
{ 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;
|
|
|
|
}
|
|
|
|
|
2017-12-04 17:24:08 +02:00
|
|
|
void net_eth_ipv6_mcast_to_mac_addr(const struct in6_addr *ipv6_addr,
|
|
|
|
struct net_eth_addr *mac_addr)
|
|
|
|
{
|
|
|
|
/* RFC 2464 7. Address Mapping -- Multicast
|
|
|
|
* "An IPv6 packet with a multicast destination address DST,
|
|
|
|
* consisting of the sixteen octets DST[1] through DST[16],
|
|
|
|
* is transmitted to the Ethernet multicast address whose
|
|
|
|
* first two octets are the value 3333 hexadecimal and whose
|
|
|
|
* last four octets are the last four octets of DST."
|
|
|
|
*/
|
|
|
|
mac_addr->addr[0] = mac_addr->addr[1] = 0x33;
|
|
|
|
memcpy(mac_addr->addr + 2, &ipv6_addr->s6_addr[12], 4);
|
|
|
|
}
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
#define print_ll_addrs(pkt, type, len, src, dst) \
|
2018-11-30 12:54:56 +02:00
|
|
|
if (CONFIG_NET_L2_ETHERNET_LOG_LEVEL >= LOG_LEVEL_DBG) { \
|
2016-06-08 08:39:46 +03:00
|
|
|
char out[sizeof("xx:xx:xx:xx:xx:xx")]; \
|
|
|
|
\
|
2017-01-23 13:05:40 +02:00
|
|
|
snprintk(out, sizeof(out), "%s", \
|
2018-01-19 12:24:33 +02:00
|
|
|
net_sprint_ll_addr((src)->addr, \
|
2016-06-08 08:39:46 +03:00
|
|
|
sizeof(struct net_eth_addr))); \
|
|
|
|
\
|
2018-01-19 12:24:33 +02:00
|
|
|
NET_DBG("iface %p src %s dst %s type 0x%x len %zu", \
|
2018-10-02 14:57:55 +03:00
|
|
|
net_pkt_iface(pkt), log_strdup(out), \
|
|
|
|
log_strdup(net_sprint_ll_addr((dst)->addr, \
|
|
|
|
sizeof(struct net_eth_addr))), \
|
2016-12-06 19:20:03 +02:00
|
|
|
type, (size_t)len); \
|
2018-07-06 11:35:07 +03:00
|
|
|
}
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
#ifdef CONFIG_NET_VLAN
|
2019-04-12 14:15:34 +03:00
|
|
|
#define print_vlan_ll_addrs(pkt, type, tci, len, src, dst, tagstrip) \
|
2018-11-30 12:54:56 +02:00
|
|
|
if (CONFIG_NET_L2_ETHERNET_LOG_LEVEL >= LOG_LEVEL_DBG) { \
|
2018-01-19 12:24:33 +02:00
|
|
|
char out[sizeof("xx:xx:xx:xx:xx:xx")]; \
|
|
|
|
\
|
|
|
|
snprintk(out, sizeof(out), "%s", \
|
|
|
|
net_sprint_ll_addr((src)->addr, \
|
|
|
|
sizeof(struct net_eth_addr))); \
|
|
|
|
\
|
2019-04-12 14:15:34 +03:00
|
|
|
NET_DBG("iface %p src %s dst %s type 0x%x " \
|
|
|
|
"tag %d %spri %d len %zu", \
|
2018-10-02 14:57:55 +03:00
|
|
|
net_pkt_iface(pkt), log_strdup(out), \
|
|
|
|
log_strdup(net_sprint_ll_addr((dst)->addr, \
|
2019-04-12 14:15:34 +03:00
|
|
|
sizeof(struct net_eth_addr))), \
|
2018-01-19 12:24:33 +02:00
|
|
|
type, net_eth_vlan_get_vid(tci), \
|
2019-04-12 14:15:34 +03:00
|
|
|
tagstrip ? "(stripped) " : "", \
|
2018-01-19 12:24:33 +02:00
|
|
|
net_eth_vlan_get_pcp(tci), (size_t)len); \
|
2018-07-06 11:35:07 +03:00
|
|
|
}
|
2018-06-25 10:22:54 +02:00
|
|
|
#else
|
|
|
|
#define print_vlan_ll_addrs(...)
|
|
|
|
#endif /* CONFIG_NET_VLAN */
|
2016-06-08 08:39:46 +03:00
|
|
|
|
2016-12-02 14:47:29 +01:00
|
|
|
static inline void ethernet_update_length(struct net_if *iface,
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt)
|
2016-12-02 14:47:29 +01:00
|
|
|
{
|
2017-04-21 09:27:50 -05:00
|
|
|
u16_t len;
|
2016-12-02 14:47:29 +01:00
|
|
|
|
|
|
|
/* Let's check IP payload's length. If it's smaller than 46 bytes,
|
|
|
|
* i.e. smaller than minimal Ethernet frame size minus ethernet
|
|
|
|
* header size,then Ethernet has padded so it fits in the minimal
|
|
|
|
* frame size of 60 bytes. In that case, we need to get rid of it.
|
|
|
|
*/
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
if (net_pkt_family(pkt) == AF_INET) {
|
2018-08-08 15:22:00 +03:00
|
|
|
len = ntohs(NET_IPV4_HDR(pkt)->len);
|
2016-12-02 14:47:29 +01:00
|
|
|
} else {
|
2018-08-13 09:57:00 +03:00
|
|
|
len = ntohs(NET_IPV6_HDR(pkt)->len) + NET_IPV6H_LEN;
|
2016-12-02 14:47:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (len < NET_ETH_MINIMAL_FRAME_SIZE - sizeof(struct net_eth_hdr)) {
|
|
|
|
struct net_buf *frag;
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
for (frag = pkt->frags; frag; frag = frag->frags) {
|
2016-12-02 14:47:29 +01:00
|
|
|
if (frag->len < len) {
|
|
|
|
len -= frag->len;
|
|
|
|
} else {
|
|
|
|
frag->len = len;
|
2018-11-29 11:23:03 -08:00
|
|
|
len = 0U;
|
2016-12-02 14:47:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-05 14:39:43 +01:00
|
|
|
static void ethernet_update_rx_stats(struct net_if *iface,
|
|
|
|
struct net_pkt *pkt, size_t length)
|
|
|
|
{
|
2019-01-07 15:18:53 +02:00
|
|
|
#if defined(CONFIG_NET_STATISTICS_ETHERNET)
|
2018-12-05 14:39:43 +01:00
|
|
|
struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
|
|
|
|
|
|
|
|
eth_stats_update_bytes_rx(iface, length);
|
|
|
|
eth_stats_update_pkts_rx(iface);
|
|
|
|
|
|
|
|
if (net_eth_is_addr_broadcast(&hdr->dst)) {
|
|
|
|
eth_stats_update_broadcast_rx(iface);
|
|
|
|
} else if (net_eth_is_addr_multicast(&hdr->dst)) {
|
|
|
|
eth_stats_update_multicast_rx(iface);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_STATISTICS_ETHERNET */
|
2019-01-07 15:18:53 +02:00
|
|
|
}
|
2018-12-05 14:39:43 +01:00
|
|
|
|
2019-04-12 14:15:34 +03:00
|
|
|
static inline bool eth_is_vlan_tag_stripped(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct device *dev = net_if_get_device(iface);
|
|
|
|
const struct ethernet_api *api = dev->driver_api;
|
|
|
|
|
|
|
|
return (api->get_capabilities(dev) & ETHERNET_HW_VLAN_TAG_STRIP);
|
|
|
|
}
|
|
|
|
|
2019-05-21 13:33:23 +03:00
|
|
|
/* Drop packet if it has broadcast destination MAC address but the IP
|
|
|
|
* address is not multicast or broadcast address. See RFC 1122 ch 3.3.6
|
|
|
|
*/
|
|
|
|
static inline
|
|
|
|
enum net_verdict ethernet_check_ipv4_bcast_addr(struct net_pkt *pkt,
|
|
|
|
struct net_eth_hdr *hdr)
|
|
|
|
{
|
|
|
|
if (net_eth_is_addr_broadcast(&hdr->dst) &&
|
|
|
|
!(net_ipv4_is_addr_mcast(&NET_IPV4_HDR(pkt)->dst) ||
|
|
|
|
net_ipv4_is_addr_bcast(net_pkt_iface(pkt),
|
|
|
|
&NET_IPV4_HDR(pkt)->dst))) {
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
static enum net_verdict ethernet_recv(struct net_if *iface,
|
2017-04-05 08:37:44 +02:00
|
|
|
struct net_pkt *pkt)
|
2016-06-01 15:06:27 +02:00
|
|
|
{
|
2018-01-19 12:24:33 +02:00
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
2017-04-10 13:03:41 +02:00
|
|
|
struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
|
2018-06-08 10:03:06 +02:00
|
|
|
u8_t hdr_len = sizeof(struct net_eth_hdr);
|
|
|
|
u16_t type = ntohs(hdr->type);
|
2016-06-07 14:17:51 +03:00
|
|
|
struct net_linkaddr *lladdr;
|
2016-10-05 19:42:43 -05:00
|
|
|
sa_family_t family;
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-08 10:03:06 +02:00
|
|
|
if (net_eth_is_vlan_enabled(ctx, iface) &&
|
2019-04-12 14:15:34 +03:00
|
|
|
type == NET_ETH_PTYPE_VLAN &&
|
|
|
|
!eth_is_vlan_tag_stripped(iface)) {
|
2018-06-08 10:03:06 +02:00
|
|
|
struct net_eth_vlan_hdr *hdr_vlan =
|
|
|
|
(struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt);
|
2016-06-03 12:40:19 +03:00
|
|
|
|
2018-06-08 10:03:06 +02:00
|
|
|
net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci));
|
|
|
|
type = ntohs(hdr_vlan->type);
|
|
|
|
hdr_len = sizeof(struct net_eth_vlan_hdr);
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
2016-06-03 12:40:19 +03:00
|
|
|
case NET_ETH_PTYPE_IP:
|
|
|
|
case NET_ETH_PTYPE_ARP:
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_set_family(pkt, AF_INET);
|
2016-10-05 19:42:43 -05:00
|
|
|
family = AF_INET;
|
2016-06-03 12:40:19 +03:00
|
|
|
break;
|
|
|
|
case NET_ETH_PTYPE_IPV6:
|
2017-04-05 08:37:44 +02:00
|
|
|
net_pkt_set_family(pkt, AF_INET6);
|
2016-10-05 19:42:43 -05:00
|
|
|
family = AF_INET6;
|
2016-06-03 12:40:19 +03:00
|
|
|
break;
|
2018-01-24 14:33:35 +02:00
|
|
|
#if defined(CONFIG_NET_GPTP)
|
|
|
|
case NET_ETH_PTYPE_PTP:
|
|
|
|
family = AF_UNSPEC;
|
|
|
|
break;
|
|
|
|
#endif
|
2017-04-05 17:17:12 -07:00
|
|
|
case NET_ETH_PTYPE_LLDP:
|
2018-08-20 11:40:53 +03:00
|
|
|
#if defined(CONFIG_NET_LLDP)
|
2018-06-27 15:06:18 +02:00
|
|
|
net_buf_pull(pkt->frags, hdr_len);
|
2018-08-20 11:40:53 +03:00
|
|
|
return net_lldp_recv(iface, pkt);
|
|
|
|
#else
|
|
|
|
NET_DBG("LLDP Rx agent not enabled");
|
2018-12-05 14:39:43 +01:00
|
|
|
goto drop;
|
2018-08-20 11:40:53 +03:00
|
|
|
#endif
|
2016-10-05 10:09:27 +02:00
|
|
|
default:
|
2018-03-03 17:07:49 +02:00
|
|
|
NET_DBG("Unknown hdr type 0x%04x iface %p", type, iface);
|
2018-12-05 14:39:43 +01:00
|
|
|
goto drop;
|
2016-06-03 12:40:19 +03:00
|
|
|
}
|
|
|
|
|
2016-06-07 14:17:51 +03:00
|
|
|
/* Set the pointers to ll src and dst addresses */
|
2018-09-11 09:16:03 +02:00
|
|
|
lladdr = net_pkt_lladdr_src(pkt);
|
2018-12-06 14:19:51 +01:00
|
|
|
lladdr->addr = hdr->src.addr;
|
2016-07-05 17:07:03 +03:00
|
|
|
lladdr->len = sizeof(struct net_eth_addr);
|
2017-02-15 13:20:31 +02:00
|
|
|
lladdr->type = NET_LINK_ETHERNET;
|
2016-06-07 14:17:51 +03:00
|
|
|
|
2018-09-11 09:16:03 +02:00
|
|
|
lladdr = net_pkt_lladdr_dst(pkt);
|
2018-12-06 14:19:51 +01:00
|
|
|
lladdr->addr = hdr->dst.addr;
|
2016-07-05 17:07:03 +03:00
|
|
|
lladdr->len = sizeof(struct net_eth_addr);
|
2017-02-15 13:20:31 +02:00
|
|
|
lladdr->type = NET_LINK_ETHERNET;
|
2016-06-07 14:17:51 +03:00
|
|
|
|
2018-06-08 10:03:06 +02:00
|
|
|
if (net_eth_is_vlan_enabled(ctx, iface)) {
|
2019-04-12 14:15:34 +03:00
|
|
|
if (type == NET_ETH_PTYPE_VLAN ||
|
|
|
|
(eth_is_vlan_tag_stripped(iface) &&
|
|
|
|
net_pkt_vlan_tci(pkt))) {
|
|
|
|
print_vlan_ll_addrs(pkt, type, net_pkt_vlan_tci(pkt),
|
|
|
|
net_pkt_get_len(pkt),
|
|
|
|
net_pkt_lladdr_src(pkt),
|
|
|
|
net_pkt_lladdr_dst(pkt),
|
|
|
|
eth_is_vlan_tag_stripped(iface));
|
|
|
|
} else {
|
|
|
|
print_ll_addrs(pkt, type, net_pkt_get_len(pkt),
|
|
|
|
net_pkt_lladdr_src(pkt),
|
|
|
|
net_pkt_lladdr_dst(pkt));
|
|
|
|
}
|
2018-06-08 10:03:06 +02:00
|
|
|
} else {
|
2018-01-19 12:24:33 +02:00
|
|
|
print_ll_addrs(pkt, type, net_pkt_get_len(pkt),
|
2018-09-11 09:16:03 +02:00
|
|
|
net_pkt_lladdr_src(pkt),
|
|
|
|
net_pkt_lladdr_dst(pkt));
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
2016-06-08 08:39:46 +03:00
|
|
|
|
2016-07-05 17:06:03 +03:00
|
|
|
if (!net_eth_is_addr_broadcast((struct net_eth_addr *)lladdr->addr) &&
|
|
|
|
!net_eth_is_addr_multicast((struct net_eth_addr *)lladdr->addr) &&
|
2018-01-24 14:33:35 +02:00
|
|
|
!net_eth_is_addr_lldp_multicast(
|
|
|
|
(struct net_eth_addr *)lladdr->addr) &&
|
2016-07-05 17:06:03 +03:00
|
|
|
!net_linkaddr_cmp(net_if_get_link_addr(iface), lladdr)) {
|
|
|
|
/* The ethernet frame is not for me as the link addresses
|
|
|
|
* are different.
|
|
|
|
*/
|
|
|
|
NET_DBG("Dropping frame, not for me [%s]",
|
2018-10-02 14:57:55 +03:00
|
|
|
log_strdup(net_sprint_ll_addr(
|
|
|
|
net_if_get_link_addr(iface)->addr,
|
|
|
|
sizeof(struct net_eth_addr))));
|
2018-12-05 14:39:43 +01:00
|
|
|
goto drop;
|
2016-07-05 17:06:03 +03:00
|
|
|
}
|
|
|
|
|
2018-06-27 15:06:18 +02:00
|
|
|
net_buf_pull(pkt->frags, hdr_len);
|
2016-10-06 10:03:52 +02:00
|
|
|
|
2019-05-21 13:33:23 +03:00
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4) && type == NET_ETH_PTYPE_IP &&
|
|
|
|
ethernet_check_ipv4_bcast_addr(pkt, hdr) == NET_DROP) {
|
|
|
|
goto drop;
|
|
|
|
}
|
|
|
|
|
|
|
|
ethernet_update_rx_stats(iface, pkt, net_pkt_get_len(pkt) + hdr_len);
|
|
|
|
|
2019-05-28 13:42:57 +02:00
|
|
|
if (IS_ENABLED(CONFIG_NET_ARP) &&
|
|
|
|
family == AF_INET && type == NET_ETH_PTYPE_ARP) {
|
2016-06-08 14:37:15 +03:00
|
|
|
NET_DBG("ARP packet from %s received",
|
2018-10-02 14:57:55 +03:00
|
|
|
log_strdup(net_sprint_ll_addr(
|
|
|
|
(u8_t *)hdr->src.addr,
|
|
|
|
sizeof(struct net_eth_addr))));
|
2019-05-28 13:42:57 +02:00
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4_AUTO) &&
|
|
|
|
net_ipv4_autoconf_input(iface, pkt) == NET_DROP) {
|
2018-07-30 18:28:35 +03:00
|
|
|
return NET_DROP;
|
|
|
|
}
|
2019-05-28 13:42:57 +02:00
|
|
|
|
2018-12-06 14:19:51 +01:00
|
|
|
return net_arp_input(pkt, hdr);
|
2016-06-01 15:06:27 +02:00
|
|
|
}
|
2018-01-24 14:33:35 +02:00
|
|
|
|
2019-05-28 13:42:57 +02:00
|
|
|
if (IS_ENABLED(CONFIG_NET_GPTP) && type == NET_ETH_PTYPE_PTP) {
|
2018-01-24 14:33:35 +02:00
|
|
|
return net_gptp_recv(iface, pkt);
|
|
|
|
}
|
|
|
|
|
2017-04-05 08:37:44 +02:00
|
|
|
ethernet_update_length(iface, pkt);
|
2016-12-02 14:47:29 +01:00
|
|
|
|
2016-06-01 15:06:27 +02:00
|
|
|
return NET_CONTINUE;
|
2018-12-05 14:39:43 +01:00
|
|
|
drop:
|
|
|
|
eth_stats_update_errors_rx(iface);
|
|
|
|
return NET_DROP;
|
2016-06-01 15:06:27 +02:00
|
|
|
}
|
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
#ifdef CONFIG_NET_IPV4
|
|
|
|
static inline bool ethernet_ipv4_dst_is_broadcast_or_mcast(struct net_pkt *pkt)
|
2016-09-12 16:03:37 +03:00
|
|
|
{
|
2018-06-25 10:22:54 +02:00
|
|
|
if (net_ipv4_is_addr_bcast(net_pkt_iface(pkt),
|
|
|
|
&NET_IPV4_HDR(pkt)->dst) ||
|
2019-03-26 19:57:45 -06:00
|
|
|
NET_IPV4_HDR(pkt)->dst.s4_addr[0] == 224U) {
|
2018-06-25 10:22:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
2016-09-12 16:03:37 +03:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-09-12 16:03:37 +03:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
static bool ethernet_fill_in_dst_on_ipv4_mcast(struct net_pkt *pkt,
|
|
|
|
struct net_eth_addr *dst)
|
|
|
|
{
|
|
|
|
if (net_pkt_family(pkt) == AF_INET &&
|
2019-03-26 19:57:45 -06:00
|
|
|
NET_IPV4_HDR(pkt)->dst.s4_addr[0] == 224U) {
|
2016-09-12 16:03:37 +03:00
|
|
|
/* Multicast address */
|
2018-06-25 10:22:54 +02:00
|
|
|
dst->addr[0] = 0x01;
|
|
|
|
dst->addr[1] = 0x00;
|
|
|
|
dst->addr[2] = 0x5e;
|
|
|
|
dst->addr[3] = NET_IPV4_HDR(pkt)->dst.s4_addr[1];
|
|
|
|
dst->addr[4] = NET_IPV4_HDR(pkt)->dst.s4_addr[2];
|
|
|
|
dst->addr[5] = NET_IPV4_HDR(pkt)->dst.s4_addr[3];
|
2016-09-12 16:03:37 +03:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
dst->addr[3] &= 0x7f;
|
2017-09-03 22:02:13 +03:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2018-06-26 14:41:54 +02:00
|
|
|
|
|
|
|
static struct net_pkt *ethernet_ll_prepare_on_ipv4(struct net_if *iface,
|
|
|
|
struct net_pkt *pkt)
|
|
|
|
{
|
|
|
|
if (ethernet_ipv4_dst_is_broadcast_or_mcast(pkt)) {
|
|
|
|
return pkt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_NET_ARP)) {
|
|
|
|
struct net_pkt *arp_pkt;
|
|
|
|
|
|
|
|
arp_pkt = net_arp_prepare(pkt, &NET_IPV4_HDR(pkt)->dst, NULL);
|
|
|
|
if (!arp_pkt) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pkt != arp_pkt) {
|
|
|
|
NET_DBG("Sending arp pkt %p (orig %p) to iface %p",
|
|
|
|
arp_pkt, pkt, iface);
|
|
|
|
net_pkt_unref(pkt);
|
|
|
|
return arp_pkt;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("Found ARP entry, sending pkt %p to iface %p",
|
|
|
|
pkt, iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pkt;
|
|
|
|
}
|
2018-06-25 10:22:54 +02:00
|
|
|
#else
|
|
|
|
#define ethernet_ipv4_dst_is_broadcast_or_mcast(...) false
|
|
|
|
#define ethernet_fill_in_dst_on_ipv4_mcast(...) false
|
2018-06-26 14:41:54 +02:00
|
|
|
#define ethernet_ll_prepare_on_ipv4(...) NULL
|
2018-06-25 10:22:54 +02:00
|
|
|
#endif /* CONFIG_NET_IPV4 */
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_IPV6
|
|
|
|
static bool ethernet_fill_in_dst_on_ipv6_mcast(struct net_pkt *pkt,
|
|
|
|
struct net_eth_addr *dst)
|
|
|
|
{
|
|
|
|
if (net_pkt_family(pkt) == AF_INET6 &&
|
|
|
|
net_ipv6_is_addr_mcast(&NET_IPV6_HDR(pkt)->dst)) {
|
|
|
|
memcpy(dst, (u8_t *)multicast_eth_addr.addr,
|
|
|
|
sizeof(struct net_eth_addr) - 4);
|
|
|
|
memcpy((u8_t *)dst + 2,
|
|
|
|
(u8_t *)(&NET_IPV6_HDR(pkt)->dst) + 12,
|
|
|
|
sizeof(struct net_eth_addr) - 2);
|
2016-09-12 16:03:37 +03:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2018-06-25 10:22:54 +02:00
|
|
|
#else
|
|
|
|
#define ethernet_fill_in_dst_on_ipv6_mcast(...) false
|
|
|
|
#endif /* CONFIG_NET_IPV6 */
|
2016-09-12 16:03:37 +03:00
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
|
|
static enum net_verdict set_vlan_tag(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface,
|
|
|
|
struct net_pkt *pkt)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (net_pkt_vlan_tag(pkt) != NET_VLAN_TAG_UNSPEC) {
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
|
|
if (net_pkt_family(pkt) == AF_INET6) {
|
|
|
|
struct net_if *target;
|
|
|
|
|
|
|
|
if (net_if_ipv6_addr_lookup(&NET_IPV6_HDR(pkt)->src,
|
|
|
|
&target)) {
|
|
|
|
if (target != iface) {
|
|
|
|
NET_DBG("Iface %p should be %p", iface,
|
|
|
|
target);
|
|
|
|
|
|
|
|
iface = target;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_NET_IPV4)
|
|
|
|
if (net_pkt_family(pkt) == AF_INET) {
|
|
|
|
struct net_if *target;
|
|
|
|
|
|
|
|
if (net_if_ipv4_addr_lookup(&NET_IPV4_HDR(pkt)->src,
|
|
|
|
&target)) {
|
|
|
|
if (target != iface) {
|
|
|
|
NET_DBG("Iface %p should be %p", iface,
|
|
|
|
target);
|
|
|
|
iface = target;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
|
|
|
if (ctx->vlan[i].tag == NET_VLAN_TAG_UNSPEC ||
|
|
|
|
ctx->vlan[i].iface != iface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Depending on source address, use the proper network
|
|
|
|
* interface when sending.
|
|
|
|
*/
|
|
|
|
net_pkt_set_vlan_tag(pkt, ctx->vlan[i].tag);
|
|
|
|
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NET_DROP;
|
|
|
|
}
|
2018-02-07 15:15:24 +02:00
|
|
|
|
|
|
|
static void set_vlan_priority(struct ethernet_context *ctx,
|
|
|
|
struct net_pkt *pkt)
|
|
|
|
{
|
2018-06-25 15:25:33 +02:00
|
|
|
u8_t vlan_priority;
|
|
|
|
|
|
|
|
vlan_priority = net_priority2vlan(net_pkt_priority(pkt));
|
|
|
|
net_pkt_set_vlan_priority(pkt, vlan_priority);
|
2018-02-07 15:15:24 +02:00
|
|
|
}
|
2018-06-08 10:03:06 +02:00
|
|
|
#else
|
|
|
|
#define set_vlan_tag(...) NET_DROP
|
|
|
|
#define set_vlan_priority(...)
|
2018-01-19 12:24:33 +02:00
|
|
|
#endif /* CONFIG_NET_VLAN */
|
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
static struct net_buf *ethernet_fill_header(struct ethernet_context *ctx,
|
|
|
|
struct net_pkt *pkt,
|
|
|
|
u32_t ptype)
|
2018-01-19 12:24:33 +02:00
|
|
|
{
|
2018-06-08 15:35:27 +02:00
|
|
|
struct net_buf *hdr_frag;
|
2018-01-19 12:24:33 +02:00
|
|
|
struct net_eth_hdr *hdr;
|
2018-06-08 15:35:27 +02:00
|
|
|
|
|
|
|
hdr_frag = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT);
|
|
|
|
if (!hdr_frag) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-08 10:03:06 +02:00
|
|
|
if (IS_ENABLED(CONFIG_NET_VLAN) &&
|
|
|
|
net_eth_is_vlan_enabled(ctx, net_pkt_iface(pkt))) {
|
2018-01-19 12:24:33 +02:00
|
|
|
struct net_eth_vlan_hdr *hdr_vlan;
|
|
|
|
|
2018-06-27 15:06:18 +02:00
|
|
|
hdr_vlan = (struct net_eth_vlan_hdr *)(hdr_frag->data);
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
if (!ethernet_fill_in_dst_on_ipv4_mcast(pkt, &hdr_vlan->dst) &&
|
|
|
|
!ethernet_fill_in_dst_on_ipv6_mcast(pkt, &hdr_vlan->dst)) {
|
|
|
|
memcpy(&hdr_vlan->dst, net_pkt_lladdr_dst(pkt)->addr,
|
2018-01-19 12:24:33 +02:00
|
|
|
sizeof(struct net_eth_addr));
|
|
|
|
}
|
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
memcpy(&hdr_vlan->src, net_pkt_lladdr_src(pkt)->addr,
|
|
|
|
sizeof(struct net_eth_addr));
|
2018-01-19 12:24:33 +02:00
|
|
|
|
|
|
|
hdr_vlan->type = ptype;
|
|
|
|
hdr_vlan->vlan.tpid = htons(NET_ETH_PTYPE_VLAN);
|
|
|
|
hdr_vlan->vlan.tci = htons(net_pkt_vlan_tci(pkt));
|
2019-04-11 11:21:59 +03:00
|
|
|
net_buf_add(hdr_frag, sizeof(struct net_eth_vlan_hdr));
|
2018-01-19 12:24:33 +02:00
|
|
|
|
|
|
|
print_vlan_ll_addrs(pkt, ntohs(hdr_vlan->type),
|
|
|
|
net_pkt_vlan_tci(pkt),
|
2018-06-08 15:35:27 +02:00
|
|
|
hdr_frag->len,
|
2019-04-12 14:15:34 +03:00
|
|
|
&hdr_vlan->src, &hdr_vlan->dst, false);
|
2018-06-08 15:35:27 +02:00
|
|
|
} else {
|
2018-06-27 15:06:18 +02:00
|
|
|
hdr = (struct net_eth_hdr *)(hdr_frag->data);
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
if (!ethernet_fill_in_dst_on_ipv4_mcast(pkt, &hdr->dst) &&
|
|
|
|
!ethernet_fill_in_dst_on_ipv6_mcast(pkt, &hdr->dst)) {
|
|
|
|
memcpy(&hdr->dst, net_pkt_lladdr_dst(pkt)->addr,
|
|
|
|
sizeof(struct net_eth_addr));
|
2018-06-08 15:35:27 +02:00
|
|
|
}
|
2018-06-12 11:38:50 +02:00
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
memcpy(&hdr->src, net_pkt_lladdr_src(pkt)->addr,
|
|
|
|
sizeof(struct net_eth_addr));
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-08 15:35:27 +02:00
|
|
|
hdr->type = ptype;
|
2018-06-27 15:06:18 +02:00
|
|
|
net_buf_add(hdr_frag, sizeof(struct net_eth_hdr));
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-08 15:35:27 +02:00
|
|
|
print_ll_addrs(pkt, ntohs(hdr->type),
|
|
|
|
hdr_frag->len, &hdr->src, &hdr->dst);
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
2018-06-08 15:35:27 +02:00
|
|
|
net_pkt_frag_insert(pkt, hdr_frag);
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-08 15:35:27 +02:00
|
|
|
return hdr_frag;
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
2018-12-05 14:39:43 +01:00
|
|
|
#if defined(CONFIG_NET_STATISTICS_ETHERNET)
|
2019-01-07 15:18:53 +02:00
|
|
|
static void ethernet_update_tx_stats(struct net_if *iface, struct net_pkt *pkt)
|
2018-12-05 14:39:43 +01:00
|
|
|
{
|
|
|
|
struct net_eth_hdr *hdr = NET_ETH_HDR(pkt);
|
|
|
|
|
2019-01-07 15:18:53 +02:00
|
|
|
eth_stats_update_bytes_tx(iface, net_pkt_get_len(pkt));
|
|
|
|
eth_stats_update_pkts_tx(iface);
|
2018-12-05 14:39:43 +01:00
|
|
|
|
2019-01-07 15:18:53 +02:00
|
|
|
if (net_eth_is_addr_multicast(&hdr->dst)) {
|
|
|
|
eth_stats_update_multicast_tx(iface);
|
|
|
|
} else if (net_eth_is_addr_broadcast(&hdr->dst)) {
|
|
|
|
eth_stats_update_broadcast_tx(iface);
|
2018-12-05 14:39:43 +01:00
|
|
|
}
|
|
|
|
}
|
2019-05-28 13:42:57 +02:00
|
|
|
#else
|
|
|
|
#define ethernet_update_tx_stats(...)
|
2019-01-07 15:18:53 +02:00
|
|
|
#endif /* CONFIG_NET_STATISTICS_ETHERNET */
|
|
|
|
|
2019-01-18 08:03:53 +01:00
|
|
|
static void ethernet_remove_l2_header(struct net_pkt *pkt)
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
/* Remove the buffer added in ethernet_fill_header() */
|
|
|
|
buf = pkt->buffer;
|
|
|
|
pkt->buffer = buf->frags;
|
|
|
|
buf->frags = NULL;
|
|
|
|
|
|
|
|
net_pkt_frag_unref(buf);
|
|
|
|
}
|
|
|
|
|
2018-06-26 14:51:05 +02:00
|
|
|
static int ethernet_send(struct net_if *iface, struct net_pkt *pkt)
|
2018-06-26 14:41:54 +02:00
|
|
|
{
|
|
|
|
const struct ethernet_api *api = net_if_get_device(iface)->driver_api;
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
u16_t ptype;
|
|
|
|
int ret;
|
|
|
|
|
2019-04-15 23:58:00 +03:00
|
|
|
if (!api) {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-06-26 14:41:54 +02:00
|
|
|
if (IS_ENABLED(CONFIG_NET_IPV4) &&
|
|
|
|
net_pkt_family(pkt) == AF_INET) {
|
|
|
|
struct net_pkt *tmp;
|
|
|
|
|
2019-04-12 12:24:43 -06:00
|
|
|
if (net_pkt_ipv4_auto(pkt)) {
|
2018-11-20 10:41:57 +02:00
|
|
|
ptype = htons(NET_ETH_PTYPE_ARP);
|
2018-06-25 10:22:54 +02:00
|
|
|
} else {
|
2019-04-12 12:24:43 -06:00
|
|
|
tmp = ethernet_ll_prepare_on_ipv4(iface, pkt);
|
|
|
|
if (!tmp) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
} else if (IS_ENABLED(CONFIG_NET_ARP) && tmp != pkt) {
|
|
|
|
/* Original pkt got queued and is replaced
|
|
|
|
* by an ARP request packet.
|
|
|
|
*/
|
|
|
|
pkt = tmp;
|
|
|
|
ptype = htons(NET_ETH_PTYPE_ARP);
|
|
|
|
net_pkt_set_family(pkt, AF_INET);
|
|
|
|
} else {
|
|
|
|
ptype = htons(NET_ETH_PTYPE_IP);
|
|
|
|
}
|
2018-06-25 10:22:54 +02:00
|
|
|
}
|
2018-06-26 14:41:54 +02:00
|
|
|
} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
|
|
|
|
net_pkt_family(pkt) == AF_INET6) {
|
|
|
|
ptype = htons(NET_ETH_PTYPE_IPV6);
|
2019-01-30 15:50:44 +02:00
|
|
|
} else if (IS_ENABLED(CONFIG_NET_SOCKETS_PACKET) &&
|
|
|
|
net_pkt_family(pkt) == AF_PACKET) {
|
|
|
|
goto send;
|
2018-11-12 11:42:44 +01:00
|
|
|
} else if (IS_ENABLED(CONFIG_NET_GPTP) && net_pkt_is_gptp(pkt)) {
|
|
|
|
ptype = htons(NET_ETH_PTYPE_PTP);
|
2019-01-11 13:35:11 +01:00
|
|
|
} else if (IS_ENABLED(CONFIG_NET_LLDP) && net_pkt_is_lldp(pkt)) {
|
|
|
|
ptype = htons(NET_ETH_PTYPE_LLDP);
|
2018-06-26 14:41:54 +02:00
|
|
|
} else if (IS_ENABLED(CONFIG_NET_ARP)) {
|
|
|
|
/* Unktown type: Unqueued pkt is an ARP reply.
|
|
|
|
*/
|
|
|
|
ptype = htons(NET_ETH_PTYPE_ARP);
|
|
|
|
net_pkt_set_family(pkt, AF_INET);
|
2018-06-25 10:22:54 +02:00
|
|
|
} else {
|
2018-06-26 14:41:54 +02:00
|
|
|
ret = -ENOTSUP;
|
|
|
|
goto error;
|
2016-06-07 14:20:15 +03:00
|
|
|
}
|
|
|
|
|
2018-06-26 14:41:54 +02:00
|
|
|
/* If the ll dst addr has not been set before, let's assume
|
2019-06-18 14:45:40 -04:00
|
|
|
* temporarily it's a broadcast one. When filling the header,
|
2018-06-26 14:41:54 +02:00
|
|
|
* it might detect this should be multicast and act accordingly.
|
2016-06-07 14:20:15 +03:00
|
|
|
*/
|
2018-09-11 09:16:03 +02:00
|
|
|
if (!net_pkt_lladdr_dst(pkt)->addr) {
|
2018-06-25 10:22:54 +02:00
|
|
|
net_pkt_lladdr_dst(pkt)->addr = (u8_t *)broadcast_eth_addr.addr;
|
2018-09-11 09:16:03 +02:00
|
|
|
net_pkt_lladdr_dst(pkt)->len = sizeof(struct net_eth_addr);
|
2016-06-08 14:38:45 +03:00
|
|
|
}
|
2016-06-07 14:19:12 +03:00
|
|
|
|
2018-06-08 10:03:06 +02:00
|
|
|
if (IS_ENABLED(CONFIG_NET_VLAN) &&
|
|
|
|
net_eth_is_vlan_enabled(ctx, iface)) {
|
2018-01-19 12:24:33 +02:00
|
|
|
if (set_vlan_tag(ctx, iface, pkt) == NET_DROP) {
|
2018-06-26 14:41:54 +02:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
2018-02-07 15:15:24 +02:00
|
|
|
|
|
|
|
set_vlan_priority(ctx, pkt);
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
2018-06-25 10:22:54 +02:00
|
|
|
/* Then set the ethernet header.
|
|
|
|
*/
|
|
|
|
if (!ethernet_fill_header(ctx, pkt, ptype)) {
|
2018-06-26 14:41:54 +02:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-11-27 20:16:42 +01:00
|
|
|
net_pkt_cursor_init(pkt);
|
|
|
|
|
2019-01-30 15:50:44 +02:00
|
|
|
send:
|
2018-06-26 14:41:54 +02:00
|
|
|
ret = api->send(net_if_get_device(iface), pkt);
|
2019-01-07 15:18:53 +02:00
|
|
|
if (ret != 0) {
|
|
|
|
eth_stats_update_errors_tx(iface);
|
2019-01-18 08:03:53 +01:00
|
|
|
ethernet_remove_l2_header(pkt);
|
2019-01-07 15:18:53 +02:00
|
|
|
goto error;
|
2018-06-25 10:22:54 +02:00
|
|
|
}
|
2019-05-28 13:42:57 +02:00
|
|
|
|
2019-01-07 15:18:53 +02:00
|
|
|
ethernet_update_tx_stats(iface, pkt);
|
2019-05-28 13:42:57 +02:00
|
|
|
|
2019-01-07 15:18:53 +02:00
|
|
|
ret = net_pkt_get_len(pkt);
|
2019-01-18 08:03:53 +01:00
|
|
|
ethernet_remove_l2_header(pkt);
|
2018-06-25 10:22:54 +02:00
|
|
|
|
2019-01-07 15:18:53 +02:00
|
|
|
net_pkt_unref(pkt);
|
2018-06-26 14:41:54 +02:00
|
|
|
error:
|
|
|
|
return ret;
|
2018-06-25 10:22:54 +02:00
|
|
|
}
|
|
|
|
|
2017-04-22 14:13:23 +08:00
|
|
|
static inline int ethernet_enable(struct net_if *iface, bool state)
|
|
|
|
{
|
2018-08-09 16:08:27 +03:00
|
|
|
const struct ethernet_api *eth =
|
|
|
|
net_if_get_device(iface)->driver_api;
|
|
|
|
|
2019-04-15 23:58:00 +03:00
|
|
|
if (!eth) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2017-04-22 14:13:23 +08:00
|
|
|
if (!state) {
|
2018-06-11 09:42:03 +02:00
|
|
|
net_arp_clear_cache(iface);
|
2018-08-09 16:08:27 +03:00
|
|
|
|
|
|
|
if (eth->stop) {
|
|
|
|
eth->stop(net_if_get_device(iface));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (eth->start) {
|
|
|
|
eth->start(net_if_get_device(iface));
|
|
|
|
}
|
2017-04-22 14:13:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-06 16:59:33 +03:00
|
|
|
enum net_l2_flags ethernet_flags(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
|
|
|
return ctx->ethernet_l2_flags;
|
|
|
|
}
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
#if defined(CONFIG_NET_VLAN)
|
|
|
|
struct net_if *net_eth_get_vlan_iface(struct net_if *iface, u16_t tag)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
2018-04-06 15:37:42 +03:00
|
|
|
struct net_if *first_non_vlan_iface = NULL;
|
2018-01-19 12:24:33 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
2018-04-06 15:37:42 +03:00
|
|
|
if (ctx->vlan[i].tag == NET_VLAN_TAG_UNSPEC) {
|
|
|
|
if (!first_non_vlan_iface) {
|
|
|
|
first_non_vlan_iface = ctx->vlan[i].iface;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->vlan[i].tag != tag) {
|
2018-01-19 12:24:33 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("[%d] vlan tag %d -> iface %p", i, tag,
|
|
|
|
ctx->vlan[i].iface);
|
|
|
|
|
|
|
|
return ctx->vlan[i].iface;
|
|
|
|
}
|
|
|
|
|
2018-04-06 15:37:42 +03:00
|
|
|
return first_non_vlan_iface;
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool enable_vlan_iface(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface)
|
|
|
|
{
|
|
|
|
int iface_idx = net_if_get_by_iface(iface);
|
|
|
|
|
|
|
|
if (iface_idx < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_set_bit(ctx->interfaces, iface_idx);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool disable_vlan_iface(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface)
|
|
|
|
{
|
|
|
|
int iface_idx = net_if_get_by_iface(iface);
|
|
|
|
|
|
|
|
if (iface_idx < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_clear_bit(ctx->interfaces, iface_idx);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool is_vlan_enabled_for_iface(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface)
|
|
|
|
{
|
|
|
|
int iface_idx = net_if_get_by_iface(iface);
|
|
|
|
|
|
|
|
if (iface_idx < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !!atomic_test_bit(ctx->interfaces, iface_idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool net_eth_is_vlan_enabled(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface)
|
|
|
|
{
|
|
|
|
if (ctx->vlan_enabled) {
|
|
|
|
if (ctx->vlan_enabled == NET_VLAN_MAX_COUNT) {
|
|
|
|
/* All network interface are using VLAN, no need
|
|
|
|
* to check further.
|
|
|
|
*/
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_vlan_enabled_for_iface(ctx, iface)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
u16_t net_eth_get_vlan_tag(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
|
|
|
if (ctx->vlan[i].iface == iface) {
|
|
|
|
return ctx->vlan[i].tag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NET_VLAN_TAG_UNSPEC;
|
|
|
|
}
|
|
|
|
|
2018-07-03 10:58:12 +03:00
|
|
|
bool net_eth_get_vlan_status(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
|
|
|
if (ctx->vlan_enabled &&
|
|
|
|
net_eth_get_vlan_tag(iface) != NET_VLAN_TAG_UNSPEC) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
static struct ethernet_vlan *get_vlan(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface,
|
|
|
|
u16_t vlan_tag)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
|
|
|
if (ctx->vlan[i].iface == iface &&
|
|
|
|
ctx->vlan[i].tag == vlan_tag) {
|
|
|
|
return &ctx->vlan[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int net_eth_vlan_enable(struct net_if *iface, u16_t tag)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
const struct ethernet_api *eth =
|
|
|
|
net_if_get_device(iface)->driver_api;
|
|
|
|
struct ethernet_vlan *vlan;
|
|
|
|
int i;
|
|
|
|
|
2019-04-15 23:58:00 +03:00
|
|
|
if (!eth) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ctx->is_init) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tag == NET_VLAN_TAG_UNSPEC) {
|
|
|
|
return -EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlan = get_vlan(ctx, iface, tag);
|
|
|
|
if (vlan) {
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
|
|
|
if (ctx->vlan[i].iface != iface) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->vlan[i].tag != NET_VLAN_TAG_UNSPEC) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("[%d] Adding vlan tag %d to iface %p", i, tag, iface);
|
|
|
|
|
|
|
|
ctx->vlan[i].tag = tag;
|
|
|
|
|
|
|
|
enable_vlan_iface(ctx, iface);
|
|
|
|
|
|
|
|
if (eth->vlan_setup) {
|
2018-04-05 17:01:06 +02:00
|
|
|
eth->vlan_setup(net_if_get_device(iface),
|
|
|
|
iface, tag, true);
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx->vlan_enabled++;
|
|
|
|
if (ctx->vlan_enabled > NET_VLAN_MAX_COUNT) {
|
|
|
|
ctx->vlan_enabled = NET_VLAN_MAX_COUNT;
|
|
|
|
}
|
|
|
|
|
2018-07-03 12:56:08 +03:00
|
|
|
ethernet_mgmt_raise_vlan_enabled_event(iface, tag);
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
|
|
|
|
int net_eth_vlan_disable(struct net_if *iface, u16_t tag)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
const struct ethernet_api *eth =
|
|
|
|
net_if_get_device(iface)->driver_api;
|
|
|
|
struct ethernet_vlan *vlan;
|
|
|
|
|
2019-04-15 23:58:00 +03:00
|
|
|
if (!eth) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tag == NET_VLAN_TAG_UNSPEC) {
|
|
|
|
return -EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlan = get_vlan(ctx, iface, tag);
|
|
|
|
if (!vlan) {
|
|
|
|
return -ESRCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DBG("Removing vlan tag %d from iface %p", vlan->tag, vlan->iface);
|
|
|
|
|
|
|
|
vlan->tag = NET_VLAN_TAG_UNSPEC;
|
|
|
|
|
|
|
|
disable_vlan_iface(ctx, iface);
|
|
|
|
|
|
|
|
if (eth->vlan_setup) {
|
2018-04-05 17:01:06 +02:00
|
|
|
eth->vlan_setup(net_if_get_device(iface), iface, tag, false);
|
2018-01-19 12:24:33 +02:00
|
|
|
}
|
|
|
|
|
2018-07-03 12:56:08 +03:00
|
|
|
ethernet_mgmt_raise_vlan_disabled_event(iface, tag);
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
ctx->vlan_enabled--;
|
|
|
|
if (ctx->vlan_enabled < 0) {
|
|
|
|
ctx->vlan_enabled = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-05-28 13:42:57 +02:00
|
|
|
#endif /* CONFIG_NET_VLAN */
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-28 09:58:50 +02:00
|
|
|
NET_L2_INIT(ETHERNET_L2, ethernet_recv, ethernet_send, ethernet_enable,
|
|
|
|
ethernet_flags);
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-04 13:37:17 +03:00
|
|
|
static void carrier_on(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = CONTAINER_OF(work,
|
|
|
|
struct ethernet_context,
|
|
|
|
carrier_mgmt.work);
|
|
|
|
|
|
|
|
NET_DBG("Carrier ON for interface %p", ctx->carrier_mgmt.iface);
|
|
|
|
|
|
|
|
ethernet_mgmt_raise_carrier_on_event(ctx->carrier_mgmt.iface);
|
|
|
|
|
|
|
|
net_if_up(ctx->carrier_mgmt.iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void carrier_off(struct k_work *work)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = CONTAINER_OF(work,
|
|
|
|
struct ethernet_context,
|
|
|
|
carrier_mgmt.work);
|
|
|
|
|
|
|
|
NET_DBG("Carrier OFF for interface %p", ctx->carrier_mgmt.iface);
|
|
|
|
|
|
|
|
ethernet_mgmt_raise_carrier_off_event(ctx->carrier_mgmt.iface);
|
|
|
|
|
|
|
|
net_if_carrier_down(ctx->carrier_mgmt.iface);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_carrier(struct ethernet_context *ctx,
|
|
|
|
struct net_if *iface,
|
|
|
|
k_work_handler_t handler)
|
|
|
|
{
|
|
|
|
k_work_init(&ctx->carrier_mgmt.work, handler);
|
|
|
|
|
|
|
|
ctx->carrier_mgmt.iface = iface;
|
|
|
|
|
|
|
|
k_work_submit(&ctx->carrier_mgmt.work);
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_eth_carrier_on(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
|
|
|
handle_carrier(ctx, iface, carrier_on);
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_eth_carrier_off(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
|
|
|
handle_carrier(ctx, iface, carrier_off);
|
|
|
|
}
|
|
|
|
|
2019-05-27 20:50:08 +08:00
|
|
|
#if defined(CONFIG_PTP_CLOCK)
|
2018-01-24 15:22:55 +02:00
|
|
|
struct device *net_eth_get_ptp_clock(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct device *dev = net_if_get_device(iface);
|
|
|
|
const struct ethernet_api *api = dev->driver_api;
|
|
|
|
|
2019-04-15 23:58:00 +03:00
|
|
|
if (!api) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-24 15:22:55 +02:00
|
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(api->get_capabilities(dev) & ETHERNET_PTP)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return api->get_ptp_clock(net_if_get_device(iface));
|
|
|
|
}
|
2019-05-27 20:50:08 +08:00
|
|
|
#endif /* CONFIG_PTP_CLOCK */
|
2018-01-24 15:22:55 +02:00
|
|
|
|
2019-05-24 14:43:56 +03:00
|
|
|
#if defined(CONFIG_PTP_CLOCK)
|
2019-05-27 12:44:01 +08:00
|
|
|
struct device *z_impl_net_eth_get_ptp_clock_by_index(int index)
|
2019-05-24 14:43:56 +03:00
|
|
|
{
|
|
|
|
struct net_if *iface;
|
|
|
|
|
|
|
|
iface = net_if_get_by_index(index);
|
|
|
|
if (!iface) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return net_eth_get_ptp_clock(iface);
|
|
|
|
}
|
2019-05-27 12:44:01 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
|
|
Z_SYSCALL_HANDLER(net_eth_get_ptp_clock_by_index, index)
|
|
|
|
{
|
|
|
|
return (u32_t)z_impl_net_eth_get_ptp_clock_by_index(index);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_USERSPACE */
|
|
|
|
#else /* CONFIG_PTP_CLOCK */
|
|
|
|
struct device *z_impl_net_eth_get_ptp_clock_by_index(int index)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(index);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PTP_CLOCK */
|
2019-05-24 14:43:56 +03:00
|
|
|
|
2018-01-24 14:33:35 +02:00
|
|
|
#if defined(CONFIG_NET_GPTP)
|
|
|
|
int net_eth_get_ptp_port(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
|
|
|
return ctx->port;
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_eth_set_ptp_port(struct net_if *iface, int port)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
|
|
|
|
|
|
|
ctx->port = port;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NET_GPTP */
|
|
|
|
|
2018-07-20 16:44:58 +03:00
|
|
|
int net_eth_promisc_mode(struct net_if *iface, bool enable)
|
|
|
|
{
|
|
|
|
struct ethernet_req_params params;
|
|
|
|
|
|
|
|
if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_PROMISC_MODE)) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
params.promisc_mode = enable;
|
|
|
|
|
|
|
|
return net_mgmt(NET_REQUEST_ETHERNET_SET_PROMISC_MODE, iface,
|
|
|
|
¶ms, sizeof(struct ethernet_req_params));
|
|
|
|
}
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
void ethernet_init(struct net_if *iface)
|
|
|
|
{
|
|
|
|
struct ethernet_context *ctx = net_if_l2_data(iface);
|
2018-06-04 13:37:17 +03:00
|
|
|
|
|
|
|
#if defined(CONFIG_NET_VLAN)
|
2018-01-19 12:24:33 +02:00
|
|
|
int i;
|
2018-06-04 13:37:17 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
NET_DBG("Initializing Ethernet L2 %p for iface %p", ctx, iface);
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-08-06 16:59:33 +03:00
|
|
|
ctx->ethernet_l2_flags = NET_L2_MULTICAST;
|
|
|
|
|
2018-08-06 17:46:37 +03:00
|
|
|
if (net_eth_get_hw_capabilities(iface) & ETHERNET_PROMISC_MODE) {
|
|
|
|
ctx->ethernet_l2_flags |= NET_L2_PROMISC_MODE;
|
|
|
|
}
|
|
|
|
|
2018-06-04 13:37:17 +03:00
|
|
|
#if defined(CONFIG_NET_VLAN)
|
2018-04-06 10:57:13 +02:00
|
|
|
if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_HW_VLAN)) {
|
2018-01-19 12:24:33 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_NET_VLAN_COUNT; i++) {
|
|
|
|
if (!ctx->vlan[i].iface) {
|
|
|
|
NET_DBG("[%d] alloc ctx %p iface %p", i, ctx, iface);
|
|
|
|
ctx->vlan[i].tag = NET_VLAN_TAG_UNSPEC;
|
|
|
|
ctx->vlan[i].iface = iface;
|
|
|
|
|
|
|
|
if (!ctx->is_init) {
|
|
|
|
atomic_clear(ctx->interfaces);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-06-04 13:37:17 +03:00
|
|
|
#endif
|
2018-01-19 12:24:33 +02:00
|
|
|
|
2018-06-15 21:08:27 +02:00
|
|
|
net_arp_init();
|
|
|
|
|
2018-01-19 12:24:33 +02:00
|
|
|
ctx->is_init = true;
|
|
|
|
}
|