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>
|
2019-06-07 17:44:33 +02:00
|
|
|
#if defined(CONFIG_NET_L2_CANBUS_ETH_TRANSLATOR)
|
|
|
|
#include <net/can.h>
|
|
|
|
#endif
|
2019-05-27 12:44:01 +08:00
|
|
|
|
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
|
|
|
|
2019-06-07 17:44:33 +02:00
|
|
|
#if defined(CONFIG_NET_L2_CANBUS_ETH_TRANSLATOR)
|
|
|
|
if (net_canbus_translate_eth_frame(iface, pkt) == NET_OK) {
|
|
|
|
return NET_OK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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
|
userspace: Support for split 64 bit arguments
System call arguments, at the arch layer, are single words. So
passing wider values requires splitting them into two registers at
call time. This gets even more complicated for values (e.g
k_timeout_t) that may have different sizes depending on configuration.
This patch adds a feature to gen_syscalls.py to detect functions with
wide arguments and automatically generates code to split/unsplit them.
Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't
work with functions like this, because for N arguments (our current
maximum N is 10) there are 2^N possible configurations of argument
widths. So this generates the complete functions for each handler and
wrapper, effectively doing in python what was originally done in the
preprocessor.
Another complexity is that traditional the z_hdlr_*() function for a
system call has taken the raw list of word arguments, which does not
work when some of those arguments must be 64 bit types. So instead of
using a single Z_SYSCALL_HANDLER macro, this splits the job of
z_hdlr_*() into two steps: An automatically-generated unmarshalling
function, z_mrsh_*(), which then calls a user-supplied verification
function z_vrfy_*(). The verification function is typesafe, and is a
simple C function with exactly the same argument and return signature
as the syscall impl function. It is also not responsible for
validating the pointers to the extra parameter array or a wide return
value, that code gets automatically generated.
This commit includes new vrfy/msrh handling for all syscalls invoked
during CI runs. Future commits will port the less testable code.
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 13:34:31 -07:00
|
|
|
static inline struct device *z_vrfy_net_eth_get_ptp_clock_by_index(int index)
|
2019-05-27 12:44:01 +08:00
|
|
|
{
|
userspace: Support for split 64 bit arguments
System call arguments, at the arch layer, are single words. So
passing wider values requires splitting them into two registers at
call time. This gets even more complicated for values (e.g
k_timeout_t) that may have different sizes depending on configuration.
This patch adds a feature to gen_syscalls.py to detect functions with
wide arguments and automatically generates code to split/unsplit them.
Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't
work with functions like this, because for N arguments (our current
maximum N is 10) there are 2^N possible configurations of argument
widths. So this generates the complete functions for each handler and
wrapper, effectively doing in python what was originally done in the
preprocessor.
Another complexity is that traditional the z_hdlr_*() function for a
system call has taken the raw list of word arguments, which does not
work when some of those arguments must be 64 bit types. So instead of
using a single Z_SYSCALL_HANDLER macro, this splits the job of
z_hdlr_*() into two steps: An automatically-generated unmarshalling
function, z_mrsh_*(), which then calls a user-supplied verification
function z_vrfy_*(). The verification function is typesafe, and is a
simple C function with exactly the same argument and return signature
as the syscall impl function. It is also not responsible for
validating the pointers to the extra parameter array or a wide return
value, that code gets automatically generated.
This commit includes new vrfy/msrh handling for all syscalls invoked
during CI runs. Future commits will port the less testable code.
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 13:34:31 -07:00
|
|
|
return z_impl_net_eth_get_ptp_clock_by_index(index);
|
2019-05-27 12:44:01 +08:00
|
|
|
}
|
userspace: Support for split 64 bit arguments
System call arguments, at the arch layer, are single words. So
passing wider values requires splitting them into two registers at
call time. This gets even more complicated for values (e.g
k_timeout_t) that may have different sizes depending on configuration.
This patch adds a feature to gen_syscalls.py to detect functions with
wide arguments and automatically generates code to split/unsplit them.
Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't
work with functions like this, because for N arguments (our current
maximum N is 10) there are 2^N possible configurations of argument
widths. So this generates the complete functions for each handler and
wrapper, effectively doing in python what was originally done in the
preprocessor.
Another complexity is that traditional the z_hdlr_*() function for a
system call has taken the raw list of word arguments, which does not
work when some of those arguments must be 64 bit types. So instead of
using a single Z_SYSCALL_HANDLER macro, this splits the job of
z_hdlr_*() into two steps: An automatically-generated unmarshalling
function, z_mrsh_*(), which then calls a user-supplied verification
function z_vrfy_*(). The verification function is typesafe, and is a
simple C function with exactly the same argument and return signature
as the syscall impl function. It is also not responsible for
validating the pointers to the extra parameter array or a wide return
value, that code gets automatically generated.
This commit includes new vrfy/msrh handling for all syscalls invoked
during CI runs. Future commits will port the less testable code.
Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 13:34:31 -07:00
|
|
|
#include <syscalls/net_eth_get_ptp_clock_by_index_mrsh.c>
|
2019-05-27 12:44:01 +08:00
|
|
|
#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;
|
|
|
|
}
|